From 25cd9ebf452867ef5bc21e5a307ae8850f5c73a9 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sun, 15 Sep 2013 21:56:47 +0400 Subject: [PATCH 001/889] Plugins are frameworks in Ogre 1.9. Version hack removed. --- components/files/ogreplugin.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/components/files/ogreplugin.cpp b/components/files/ogreplugin.cpp index c319f7758..6070c43a8 100644 --- a/components/files/ogreplugin.cpp +++ b/components/files/ogreplugin.cpp @@ -6,18 +6,12 @@ namespace Files { bool loadOgrePlugin(const std::string &pluginDir, std::string pluginName, Ogre::Root &ogreRoot) { -#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE - std::ostringstream verStream; - verStream << "." << OGRE_VERSION_MAJOR << "." << OGRE_VERSION_MINOR << "." << OGRE_VERSION_PATCH; - pluginName = pluginName + verStream.str(); -#endif - std::string pluginExt; #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 pluginExt = ".dll"; #endif #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE - pluginExt = ".dylib"; + pluginExt = ".framework"; #endif #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX pluginExt = ".so"; From 36fb89c6cdb203257e65d882123c13cf6c3d9829 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sun, 15 Sep 2013 23:03:07 +0400 Subject: [PATCH 002/889] Cleanup --- CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ec306e5a..96b0e1a32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -262,8 +262,6 @@ if (APPLE) set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_DBG}) endif () - #set(OGRE_PLUGIN_DIR "${OGRE_PLUGIN_DIR}/") - configure_file(${OpenMW_SOURCE_DIR}/files/mac/Info.plist "${APP_BUNDLE_DIR}/Contents/Info.plist") @@ -664,7 +662,7 @@ if (APPLE) set(CPACK_GENERATOR "DragNDrop") set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) set(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR}) - set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINO}) + set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE}) set(APPS "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}") From a18e3c9cc17dc69d085c376435e263f139a641bb Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sun, 15 Sep 2013 23:10:21 +0400 Subject: [PATCH 003/889] Fixed packaging when building against Ogre 1.9 --- CMakeLists.txt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 96b0e1a32..fa62d508f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -283,7 +283,7 @@ endif() add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}") add_definitions(-DOGRE_PLUGIN_DIR_DBG="${OGRE_PLUGIN_DIR_DBG}") if (APPLE AND OPENMW_OSX_DEPLOYMENT) - add_definitions(-DOGRE_PLUGIN_DIR="${APP_BUNDLE_NAME}/Contents/Plugins") + add_definitions(-DOGRE_PLUGIN_DIR="${APP_BUNDLE_NAME}/Contents/Frameworks") else() add_definitions(-DOGRE_PLUGIN_DIR="${OGRE_PLUGIN_DIR}") endif() @@ -666,7 +666,7 @@ if (APPLE) set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE}) set(APPS "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}") - set(PLUGINS "") + set(ABSOLUTE_PLUGINS "") foreach (PLUGIN ${USED_OGRE_PLUGINS}) @@ -674,11 +674,9 @@ if (APPLE) set(ABSOLUTE_PLUGINS ${PLUGIN_ABS} ${ABSOLUTE_PLUGINS}) endforeach () - set(PLUGIN_INSTALL_BASE "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}/Contents/Plugins") - install(FILES ${ABSOLUTE_PLUGINS} DESTINATION "${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}/Contents/Plugins" COMPONENT Runtime) + set(PLUGIN_INSTALL_BASE "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}/Contents/Frameworks") foreach (PLUGIN ${ABSOLUTE_PLUGINS}) - get_filename_component(PLUGIN_RELATIVE ${PLUGIN} NAME) - set(PLUGINS ${PLUGINS} "${PLUGIN_INSTALL_BASE}/${PLUGIN_RELATIVE}") + install(DIRECTORY ${PLUGIN} DESTINATION "${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}/Contents/Frameworks" COMPONENT Runtime) endforeach () #For now, search unresolved dependencies only in default system paths, so if you put unresolveable (i.e. with @executable_path in id name) lib or framework somewhere else, it would fail @@ -726,7 +724,7 @@ if (APPLE) cmake_policy(SET CMP0009 OLD) set(BU_CHMOD_BUNDLE_ITEMS ON) include(BundleUtilities) - fixup_bundle(\"${APPS}\" \"${PLUGINS}\" \"${DIRS}\") + fixup_bundle(\"${APPS}\" \"\" \"${DIRS}\") " COMPONENT Runtime) include(CPack) endif (APPLE) From ca700c4cfd0a99aa1c750f23a4ebf5fe99988071 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 22 Oct 2013 17:20:15 +0200 Subject: [PATCH 004/889] Creating the new branch for filters. --- manual/opencs/.gitignore | 4 ++++ manual/opencs/filters.tex | 13 +++++++++++++ manual/opencs/main.tex | 12 ++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 manual/opencs/.gitignore create mode 100644 manual/opencs/filters.tex create mode 100644 manual/opencs/main.tex diff --git a/manual/opencs/.gitignore b/manual/opencs/.gitignore new file mode 100644 index 000000000..12930f58e --- /dev/null +++ b/manual/opencs/.gitignore @@ -0,0 +1,4 @@ +*.backup +*.aux +*.log +*.toc \ No newline at end of file diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex new file mode 100644 index 000000000..5a9ca6d9c --- /dev/null +++ b/manual/opencs/filters.tex @@ -0,0 +1,13 @@ +\section{Filters} +\subsection{Introduction} +Filters are the key element of OpenCS use cases by allowing rapid and easy access to the seeked records presented in all tables. Therefore: in order to use this application fully effective you should make sure that all concepts and instructions written in the this section of the manual are perfectly clear to you.\\ +Don't be afraid though, filters are fairly intuitive and easy to use. +\subsection{Used Terms} +\begin{description} + \item[Filter] is generally speaking a tool able to ``Filter'' (that is: select some elements, while discarding others) according to the some criteria. In case of OpenCS: records are being filtred according to the criteria of user choice. Criteria are written down in language with simple syntax. + \item[Criteria] describes condition under with any any record is being select by the filter. + \item[Syntax] as you may noticed computers (in general) are rather strict, and expect only strictly formulated orders -- that is: written with correct syntax. Our syntax is simple and described in the {B}asics subsection. + \item[Expression] is a criteria, only written with OpenCS filter syntax. +\end{description} +\subsection{Basics} +\subsection{Advanced Usage} \ No newline at end of file diff --git a/manual/opencs/main.tex b/manual/opencs/main.tex new file mode 100644 index 000000000..da3457e13 --- /dev/null +++ b/manual/opencs/main.tex @@ -0,0 +1,12 @@ +\documentclass[american]{article} +\usepackage[T1]{fontenc} +\usepackage{babel} +\author{OpenMW Team} +\begin{document} + +\title{OpenCS User Manual} + +\maketitle +\tableofcontents{} +\input{filters} +\end{document} From 930aef7ae5025972f944874a06280d3af98a7bd2 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 22 Oct 2013 21:25:13 +0200 Subject: [PATCH 005/889] Added *.pdf to the gitignore. --- manual/opencs/.gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/manual/opencs/.gitignore b/manual/opencs/.gitignore index 12930f58e..cf62bd6fc 100644 --- a/manual/opencs/.gitignore +++ b/manual/opencs/.gitignore @@ -1,4 +1,5 @@ *.backup *.aux *.log -*.toc \ No newline at end of file +*.toc +*.pdf \ No newline at end of file From e34cbe857c7735c4a39dd3be12fd22ea94af2939 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 23 Oct 2013 12:37:28 +0200 Subject: [PATCH 006/889] Added a little more to the filters. --- manual/opencs/filters.tex | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index 5a9ca6d9c..d6ca6eb85 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -2,12 +2,41 @@ \subsection{Introduction} Filters are the key element of OpenCS use cases by allowing rapid and easy access to the seeked records presented in all tables. Therefore: in order to use this application fully effective you should make sure that all concepts and instructions written in the this section of the manual are perfectly clear to you.\\ Don't be afraid though, filters are fairly intuitive and easy to use. + \subsection{Used Terms} + \begin{description} \item[Filter] is generally speaking a tool able to ``Filter'' (that is: select some elements, while discarding others) according to the some criteria. In case of OpenCS: records are being filtred according to the criteria of user choice. Criteria are written down in language with simple syntax. \item[Criteria] describes condition under with any any record is being select by the filter. \item[Syntax] as you may noticed computers (in general) are rather strict, and expect only strictly formulated orders -- that is: written with correct syntax. Our syntax is simple and described in the {B}asics subsection. \item[Expression] is a criteria, only written with OpenCS filter syntax. + \item[Tokken] is any part of the expression, responsible for checking for the criteria in specified column. + \item[Node] is any part of the expression, responsible for performing logical operations on tokkens. That is: group two (or more) tokkens together in order to create a expression that will check for criteria placed in two (again: or more) columns (logical ``or'', ``and''); create any expression that will show only records that does not met criteria of specific tokken(logical ``not''). \end{description} + \subsection{Basics} -\subsection{Advanced Usage} \ No newline at end of file +To summarize and lay the very fundaments of this chapter: if you want to display filters of your choice (and you really do, because that's the use case on which opencs is designed) you have to know exactly what do you want to get in order to translate this into the tokkens and nodes. Finally, you will have to write this as legal expression -- that is: using correct syntax. As a result table will show only desired rows. + +\subsection{Interface} + +\subsection{Using predefined filters} + +\subsection{Filter scopes} + +\subsubsection{Tokkens} +Each tokken is used in similar manner. First off: you have to write it's name (for instance: ``string'') and secondly: condition that will be checked inside brackets (for instance string(something, something)). If conditions of your expression will be meet by a record (technical speaking: evaluated true) the record will show up in the table. +\linebreak +It is clear that you need to know what are you checking, that's is: what column of the table contains information that you are interested in and what should be inside specific cell inside this column to meet your requirements. In most cases first word inside brackets sets column you want to see, while the second one sets desired value inside of the cell. To separate column from the value use comma. + +\paragraph{String -- string(``column'', ``value'')} +String in programmers language is often just a word for anything composed of characters. In case of OpenCS this is in fact every value inside the column that is not composed of the pure numbers. Even columns containing only ``true`` and ``false`` values can be targeted by the string tokken.\footnote{There is no Boolean (''true'' or ``false'') value in the OpenCS. You should use string tokken for those.} String evaluates to true, when record contains in the specified column exactly the same value as specified.\footnote{This is not completely valid, however at this point this approach can be useful.} +\linebreak +Since majority of the columns contain string values, string tokken is among the most often used. Examples: +\begin{itemize} + \item string(``Record Type'', ``Weapon'') -- will evaluate to true for all records containing ``Weapon'' in the ``Record Type'' column cell. This group contains every weapon (including arrows and bolts) found in the game. + \item string(``Portable'', ``true'') -- will evaluate to true for all records containing word true inside ``Portable'' column cell. This group contains every portable light sources (lanterns, torches etc.). +\end{itemize} +String tokken can also use regular expressions (regexps) as it's value. This will be described in the ``Advanced'' section. + +\paragraph{Value -- value(``value'', (``open'', ``close''))} +While string tokken covers vast group of columns containing string values, there are in fact columns with just numerical values like ``weight`` \ No newline at end of file From 11779c27d308063274ffd30d9ac9920878c5f5a8 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 23 Oct 2013 21:34:59 +0200 Subject: [PATCH 007/889] =?UTF-8?q?Writting=20that=20manual=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manual/opencs/filters.tex | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index d6ca6eb85..be406b3c0 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -18,10 +18,38 @@ Don't be afraid though, filters are fairly intuitive and easy to use. To summarize and lay the very fundaments of this chapter: if you want to display filters of your choice (and you really do, because that's the use case on which opencs is designed) you have to know exactly what do you want to get in order to translate this into the tokkens and nodes. Finally, you will have to write this as legal expression -- that is: using correct syntax. As a result table will show only desired rows. \subsection{Interface} +Above each table there is a field that is used to enter filter: either predefined by the OpenMW developers or made by you, the user. You probabbly noticed it before. However there is alo completely new element, although using familiar table layout. Go to the application menu view, and click filters. You should see set of default filters, made by the OpenMW team in the table with the following columns: filter, description and modyfied. +\begin{description} + \item[Filter] column containing expression of the filter. + \item[Description] contains the short description of the filter function. + \item[Name] constains the name of the filter. + \item[Modyfied] just like in all other tables you have seen so far modyfied indicates if a filter was added, modyfied or removed. +\end{description} + +So let's learn how to actually use those to speed up your work. \subsection{Using predefined filters} +Using those filters is quite easy and involves typing inside the filter field above the table. For instance, try to open referencables table and type in the filters field the following: ``project::weapons''. As soon as you complete the text table will magicly alters and will show only the weapons. As you could noticed project::weapons is nothing else than a name of one of the predefined filters. That's it: in order to use the filter inside the table you simply type it's name inside the filter field. +\linebreak +To make life easier filter names follow simple convention. + +\begin{itemize} + \item Filter name filtring a specific record type contains usually the name of specific group. For instance project::weapons filter contains the word weapons (did you noticed?). Plural form is always used. + \item When filtering specific subgroup the name starts just like in the case of general filter. For instance project::weaponssilver will filter only silver weapons (new mechanic introduced by the Bloodmoon, silver weapons deal double damage against werewolfs) and project::weaponsmagical will filter only magical weapons (able to hurt ghosts and other supernatural creatures). + \item There are few exceptions from the above. For instance there is a project::added, project::removed, project::modyfied, project::base. You could probabbly except something more like ``project::statusadded'' but in this case typing this few extra characters would only help to break your keyboard faster. +\end{itemize} + +I strongly recommend to take a look at the filters table right now to see what you can filter with that. And try using it! It is very simple. \subsection{Filter scopes} +Back to the manual? Good. Now let's explain the cryptic project:: at the begining of every predefined filter. It is a scope. Scope determinates if the filter will be stored along with your project or if it will be forgotten as soon as OpenCS quits. +\begin{description} + \item[project::] scope indicates that filter is stored inside the project file. + \item[session::] scope indicates that filter is not stored inside the project file, and once you will quit OpenCS (close session) the filter will be gone. Forever! Untill then it can be found inside the filters table. +\end{description} +In addition to this two scopes, there is a third one; called one-shot. One-shot filters are not stored anywhere and as the name implies they are supposed to be created when needed just once. Good thing about the one-shot filters is that you don't need to open filters table in order to create it. Instead you type it directly inside the filter field, starting with ``!''. +\linebreak +Still, you may wonder how you are supposed to write expressions, what and nodes tokkens are avaible, and what syntax looks like. \subsubsection{Tokkens} Each tokken is used in similar manner. First off: you have to write it's name (for instance: ``string'') and secondly: condition that will be checked inside brackets (for instance string(something, something)). If conditions of your expression will be meet by a record (technical speaking: evaluated true) the record will show up in the table. From e89f1dd40a1f54a921037dc7e54970c909984b25 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 24 Oct 2013 18:33:35 +0200 Subject: [PATCH 008/889] Corrected, according to Zini. Not completly, though. --- manual/opencs/filters.tex | 54 +++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index be406b3c0..d6fafc4aa 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -1,70 +1,70 @@ \section{Filters} \subsection{Introduction} -Filters are the key element of OpenCS use cases by allowing rapid and easy access to the seeked records presented in all tables. Therefore: in order to use this application fully effective you should make sure that all concepts and instructions written in the this section of the manual are perfectly clear to you.\\ +Filters are the key element of OpenCS use cases by allowing rapid and easy access to the searched records presented in all tables. Therefore: in order to use this application fully effective you should make sure that all concepts and instructions written in the this section of the manual are perfectly clear to you.\\ Don't be afraid though, filters are fairly intuitive and easy to use. \subsection{Used Terms} \begin{description} - \item[Filter] is generally speaking a tool able to ``Filter'' (that is: select some elements, while discarding others) according to the some criteria. In case of OpenCS: records are being filtred according to the criteria of user choice. Criteria are written down in language with simple syntax. + \item[Filter] is generally speaking a tool able to ``Filter'' (that is: select some elements, while discarding others) according to the some criteria. In case of OpenCS: records are being filtered according to the criteria of user choice. Criteria are written down in language with simple syntax. \item[Criteria] describes condition under with any any record is being select by the filter. \item[Syntax] as you may noticed computers (in general) are rather strict, and expect only strictly formulated orders -- that is: written with correct syntax. Our syntax is simple and described in the {B}asics subsection. \item[Expression] is a criteria, only written with OpenCS filter syntax. - \item[Tokken] is any part of the expression, responsible for checking for the criteria in specified column. - \item[Node] is any part of the expression, responsible for performing logical operations on tokkens. That is: group two (or more) tokkens together in order to create a expression that will check for criteria placed in two (again: or more) columns (logical ``or'', ``and''); create any expression that will show only records that does not met criteria of specific tokken(logical ``not''). + \item[Token] is any part of the expression, responsible for checking for the criteria in specified column. + \item[Node] is any part of the expression, responsible for performing logical operations on tokens. That is: group two (or more) tokens together in order to create a expression that will check for criteria placed in two (again: or more) columns (logical ``or'', ``and''); create any expression that will show only records that does not met criteria of specific token(logical ``not''). \end{description} \subsection{Basics} -To summarize and lay the very fundaments of this chapter: if you want to display filters of your choice (and you really do, because that's the use case on which opencs is designed) you have to know exactly what do you want to get in order to translate this into the tokkens and nodes. Finally, you will have to write this as legal expression -- that is: using correct syntax. As a result table will show only desired rows. +In fact you don't need to learn everything about filters in order to use them. In fact all you need to know to achieve decent productivity with OpenCS is inside basics section. \subsection{Interface} -Above each table there is a field that is used to enter filter: either predefined by the OpenMW developers or made by you, the user. You probabbly noticed it before. However there is alo completely new element, although using familiar table layout. Go to the application menu view, and click filters. You should see set of default filters, made by the OpenMW team in the table with the following columns: filter, description and modyfied. +Above each table there is a field that is used to enter filter: either predefined by the OpenMW developers or made by you, the user. You probably noticed it before. However there is also completely new element, although using familiar table layout. Go to the application menu view, and click filters. You should see set of default filters, made by the OpenMW team in the table with the following columns: filter, description and modified. \begin{description} + \item[ID] contains the name of the filter. + \item[Modified] just like in all other tables you have seen so far modified indicates if a filter was added, modified or removed. \item[Filter] column containing expression of the filter. \item[Description] contains the short description of the filter function. - \item[Name] constains the name of the filter. - \item[Modyfied] just like in all other tables you have seen so far modyfied indicates if a filter was added, modyfied or removed. \end{description} So let's learn how to actually use those to speed up your work. \subsection{Using predefined filters} -Using those filters is quite easy and involves typing inside the filter field above the table. For instance, try to open referencables table and type in the filters field the following: ``project::weapons''. As soon as you complete the text table will magicly alters and will show only the weapons. As you could noticed project::weapons is nothing else than a name of one of the predefined filters. That's it: in order to use the filter inside the table you simply type it's name inside the filter field. -\linebreak +Using those filters is quite easy and involves typing inside the filter field above the table. For instance, try to open referencables table and type in the filters field the following: ``project::weapons''. As soon as you complete the text table will magicly alters and will show only the weapons. As you could noticed project::weapons is nothing else than a name of one of the predefined filters. That's it: in order to use the filter inside the table you simply type it's name inside the filter field.\\ To make life easier filter names follow simple convention. \begin{itemize} - \item Filter name filtring a specific record type contains usually the name of specific group. For instance project::weapons filter contains the word weapons (did you noticed?). Plural form is always used. + \item Filter name filtering a specific record type contains usually the name of specific group. For instance project::weapons filter contains the word weapons (did you noticed?). Plural form is always used. \item When filtering specific subgroup the name starts just like in the case of general filter. For instance project::weaponssilver will filter only silver weapons (new mechanic introduced by the Bloodmoon, silver weapons deal double damage against werewolfs) and project::weaponsmagical will filter only magical weapons (able to hurt ghosts and other supernatural creatures). - \item There are few exceptions from the above. For instance there is a project::added, project::removed, project::modyfied, project::base. You could probabbly except something more like ``project::statusadded'' but in this case typing this few extra characters would only help to break your keyboard faster. + \item There are few exceptions from the above. For instance there is a project::added, project::removed, project::modyfied, project::base. You would probably except something more like ``project::statusadded'' but in this case typing this few extra characters would only help to break your keyboard faster. \end{itemize} -I strongly recommend to take a look at the filters table right now to see what you can filter with that. And try using it! It is very simple. +We strongly recommend to take a look at the filters table right now to see what you can filter with that. And try using it! It is very simple. -\subsection{Filter scopes} -Back to the manual? Good. Now let's explain the cryptic project:: at the begining of every predefined filter. It is a scope. Scope determinates if the filter will be stored along with your project or if it will be forgotten as soon as OpenCS quits. +\subsection{Advanced} +If you want to create your own filter you have to know exactly what do you want to get in order to translate this into the tokens and nodes. Finally, you will have to write this as legal expression -- that is: using correct syntax. As a result table will show only desired rows.\\ +Advance subsection covers everything that you need to know in order to create any filter you may want to. +\subsection{Namespaces} +It is a ``namespace``, a term borrowed from the C++ language. In case of OpenCS namespace determinate if the filter will be stored along with your project or if it will be forgotten as soon as OpenCS quits. \begin{description} - \item[project::] scope indicates that filter is stored inside the project file. - \item[session::] scope indicates that filter is not stored inside the project file, and once you will quit OpenCS (close session) the filter will be gone. Forever! Untill then it can be found inside the filters table. + \item[project::] namespace indicates that filter is stored inside the project file. + \item[session::] namespace indicates that filter is not stored inside the project file, and once you will quit OpenCS (close session) the filter will be gone. Forever! Until then it can be found inside the filters table. \end{description} -In addition to this two scopes, there is a third one; called one-shot. One-shot filters are not stored anywhere and as the name implies they are supposed to be created when needed just once. Good thing about the one-shot filters is that you don't need to open filters table in order to create it. Instead you type it directly inside the filter field, starting with ``!''. -\linebreak -Still, you may wonder how you are supposed to write expressions, what and nodes tokkens are avaible, and what syntax looks like. +In addition to this two scopes, there is a third one; called one-shot. One-shot filters are not stored anywhere and as the name implies they are supposed to be created when needed only once. Good thing about the one-shot filters is that you don't need to open filters table in order to create it. Instead you just type it directly inside the filter field, starting with ``!''.\\ +Still, you may wonder how you are supposed to write expressions, what and nodes tokens are avaible, and what syntax looks like. -\subsubsection{Tokkens} -Each tokken is used in similar manner. First off: you have to write it's name (for instance: ``string'') and secondly: condition that will be checked inside brackets (for instance string(something, something)). If conditions of your expression will be meet by a record (technical speaking: evaluated true) the record will show up in the table. -\linebreak +\subsubsection{Tokens} +Each token is used in similar manner. First off: you have to write it's name (for instance: ``string'') and secondly: condition that will be checked inside brackets (for instance string(something, something)). If conditions of your expression will be meet by a record (technical speaking: evaluated true) the record will show up in the table.\\ It is clear that you need to know what are you checking, that's is: what column of the table contains information that you are interested in and what should be inside specific cell inside this column to meet your requirements. In most cases first word inside brackets sets column you want to see, while the second one sets desired value inside of the cell. To separate column from the value use comma. \paragraph{String -- string(``column'', ``value'')} -String in programmers language is often just a word for anything composed of characters. In case of OpenCS this is in fact every value inside the column that is not composed of the pure numbers. Even columns containing only ``true`` and ``false`` values can be targeted by the string tokken.\footnote{There is no Boolean (''true'' or ``false'') value in the OpenCS. You should use string tokken for those.} String evaluates to true, when record contains in the specified column exactly the same value as specified.\footnote{This is not completely valid, however at this point this approach can be useful.} +String in programmers language is often just a word for anything composed of characters. In case of OpenCS this is in fact true for every value inside the column that is not composed of the pure numbers. Even columns containing only ``true`` and ``false`` values can be targeted by the string token.\footnote{There is no Boolean (''true'' or ``false'') value in the OpenCS. You should use string token for those.} String evaluates to true, when record contains in the specified column exactly the same value as specified.\footnote{This is not completely valid, however at this point this approach can be useful.} \linebreak -Since majority of the columns contain string values, string tokken is among the most often used. Examples: +Since majority of the columns contain string values, string token is among the most often used. Examples: \begin{itemize} \item string(``Record Type'', ``Weapon'') -- will evaluate to true for all records containing ``Weapon'' in the ``Record Type'' column cell. This group contains every weapon (including arrows and bolts) found in the game. \item string(``Portable'', ``true'') -- will evaluate to true for all records containing word true inside ``Portable'' column cell. This group contains every portable light sources (lanterns, torches etc.). \end{itemize} -String tokken can also use regular expressions (regexps) as it's value. This will be described in the ``Advanced'' section. +String token can also use regular expressions (regexps) as it's value. This will be described in the ``Advanced'' section. \paragraph{Value -- value(``value'', (``open'', ``close''))} -While string tokken covers vast group of columns containing string values, there are in fact columns with just numerical values like ``weight`` \ No newline at end of file +While string token covers vast group of columns containing string values, there are in fact columns with just numerical values like ``weight``. To filter those we need a value token. This one works in similar manner to the string filter: first token name and criteria inside brackets. Clearly, conditions should hold column to test in. However in this case wanted value is specified as a range. \ No newline at end of file From cef3b30b25cdb48f6004dd68e12d3b58c90999ea Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 24 Oct 2013 19:24:37 +0200 Subject: [PATCH 009/889] Added tables.tex. It is almost empty at the moment. --- manual/opencs/filters.tex | 10 +++++++--- manual/opencs/tables.tex | 10 ++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 manual/opencs/tables.tex diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index d6fafc4aa..1863987dc 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -57,14 +57,18 @@ Each token is used in similar manner. First off: you have to write it's name (fo It is clear that you need to know what are you checking, that's is: what column of the table contains information that you are interested in and what should be inside specific cell inside this column to meet your requirements. In most cases first word inside brackets sets column you want to see, while the second one sets desired value inside of the cell. To separate column from the value use comma. \paragraph{String -- string(``column'', ``value'')} -String in programmers language is often just a word for anything composed of characters. In case of OpenCS this is in fact true for every value inside the column that is not composed of the pure numbers. Even columns containing only ``true`` and ``false`` values can be targeted by the string token.\footnote{There is no Boolean (''true'' or ``false'') value in the OpenCS. You should use string token for those.} String evaluates to true, when record contains in the specified column exactly the same value as specified.\footnote{This is not completely valid, however at this point this approach can be useful.} -\linebreak +String in programmers language is often\footnote{Often, not always. There are different programming languages using slightly different terms.} just a word for anything composed of characters. In case of OpenCS this is in fact true for every value inside the column that is not composed of the pure numbers. Even columns containing only ``true`` and ``false`` values can be targeted by the string token.\footnote{There is no Boolean (''true'' or ``false'') value in the OpenCS. You should use string token for those.} String evaluates to true, when record contains in the specified column exactly the same value as specified. +\\ Since majority of the columns contain string values, string token is among the most often used. Examples: \begin{itemize} \item string(``Record Type'', ``Weapon'') -- will evaluate to true for all records containing ``Weapon'' in the ``Record Type'' column cell. This group contains every weapon (including arrows and bolts) found in the game. \item string(``Portable'', ``true'') -- will evaluate to true for all records containing word true inside ``Portable'' column cell. This group contains every portable light sources (lanterns, torches etc.). \end{itemize} -String token can also use regular expressions (regexps) as it's value. This will be described in the ``Advanced'' section. +This is probably enough to create around 90\% string filters you would need. However, this token is even more powerfull -- it accepts regular expressions (also called regexps). Regular expressions is a way to create string criteria that will be matched by one than just one specific value in the column. For instance, you can display both left and right gauntlets with the following expression: ``string("armor type", ".* gauntlet"))`` because ''.*'' in regexps means just: ``anything''. This filter says: please, show me ``any'' gauntlet. There are left and right gauntlets in the morrowind so this will evaluate to true for both. Simple, isn't it? +\\ +Creating regexps can be a difficult and annoying -- especially when you need complex criteria. On the other hand, We are under impression that in reality complex expressions are needed only in sporadic cases. In fact, the truth is that mostly the mentioned ``.*'' is needed and therefore the following description of regexps can be skipped by vast majority of readers. + +%TO-DO: write the regexps essentials. \paragraph{Value -- value(``value'', (``open'', ``close''))} While string token covers vast group of columns containing string values, there are in fact columns with just numerical values like ``weight``. To filter those we need a value token. This one works in similar manner to the string filter: first token name and criteria inside brackets. Clearly, conditions should hold column to test in. However in this case wanted value is specified as a range. \ No newline at end of file diff --git a/manual/opencs/tables.tex b/manual/opencs/tables.tex new file mode 100644 index 000000000..eab308d42 --- /dev/null +++ b/manual/opencs/tables.tex @@ -0,0 +1,10 @@ +\section{Tables} +If you launched OpenCS already and played it with for a while you surely noticed that it is a very table oriented application. Your impression is surely correct: major part of Open{CS} is built around table pattern. But this is not excel clone! Table was just the most logical way of dealing with all different record types in a general way. +\subsection{Used Terms} + +\begin{description} +\end{description} + +\subsection{Basics} + +\subsection{Advanced} From 382ca00dcb15c6b8c684732e7ec3dd1e9a473f0e Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 29 Oct 2013 20:46:53 +0100 Subject: [PATCH 010/889] Still draft. I need to carefully read the text, and point out that this is all about record filters. --- manual/opencs/filters.tex | 46 +++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index 1863987dc..72b83d270 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -9,9 +9,10 @@ Don't be afraid though, filters are fairly intuitive and easy to use. \item[Filter] is generally speaking a tool able to ``Filter'' (that is: select some elements, while discarding others) according to the some criteria. In case of OpenCS: records are being filtered according to the criteria of user choice. Criteria are written down in language with simple syntax. \item[Criteria] describes condition under with any any record is being select by the filter. \item[Syntax] as you may noticed computers (in general) are rather strict, and expect only strictly formulated orders -- that is: written with correct syntax. Our syntax is simple and described in the {B}asics subsection. - \item[Expression] is a criteria, only written with OpenCS filter syntax. - \item[Token] is any part of the expression, responsible for checking for the criteria in specified column. - \item[Node] is any part of the expression, responsible for performing logical operations on tokens. That is: group two (or more) tokens together in order to create a expression that will check for criteria placed in two (again: or more) columns (logical ``or'', ``and''); create any expression that will show only records that does not met criteria of specific token(logical ``not''). + \item[Expression] is way we are actually performing filtering. Filter can be treated as ``functions'': accepts arguments, and evaluates either to the true or false for every column record. + \item[N-ary] is any expression that is useful to group two (or more) other expressions together in order to create filter that will check for criteria placed in two (again: or more) columns (logical ``or'', ``and''). + \item[unary] is any expression that expects one other expression. The example is ``not'' expression. + \item[nullary] is expression that does not accepts other expressions. It accepts arguments specified later. \end{description} \subsection{Basics} @@ -44,31 +45,54 @@ We strongly recommend to take a look at the filters table right now to see what If you want to create your own filter you have to know exactly what do you want to get in order to translate this into the tokens and nodes. Finally, you will have to write this as legal expression -- that is: using correct syntax. As a result table will show only desired rows.\\ Advance subsection covers everything that you need to know in order to create any filter you may want to. \subsection{Namespaces} -It is a ``namespace``, a term borrowed from the C++ language. In case of OpenCS namespace determinate if the filter will be stored along with your project or if it will be forgotten as soon as OpenCS quits. +It is a ``namespace``, a term borrowed from the C++ language. In case of OpenCS namespace of the filter determinate if the filter will be stored along with your project or if it will be forgotten as soon as OpenCS quits. \begin{description} \item[project::] namespace indicates that filter is stored inside the project file. \item[session::] namespace indicates that filter is not stored inside the project file, and once you will quit OpenCS (close session) the filter will be gone. Forever! Until then it can be found inside the filters table. \end{description} In addition to this two scopes, there is a third one; called one-shot. One-shot filters are not stored anywhere and as the name implies they are supposed to be created when needed only once. Good thing about the one-shot filters is that you don't need to open filters table in order to create it. Instead you just type it directly inside the filter field, starting with ``!''.\\ -Still, you may wonder how you are supposed to write expressions, what and nodes tokens are avaible, and what syntax looks like. +Still, you may wonder how you are supposed to write expressions, what expressions you should use, and what syntax looks like. -\subsubsection{Tokens} -Each token is used in similar manner. First off: you have to write it's name (for instance: ``string'') and secondly: condition that will be checked inside brackets (for instance string(something, something)). If conditions of your expression will be meet by a record (technical speaking: evaluated true) the record will show up in the table.\\ +\subsubsection{Nullary expressions} +Each expression is used in similar manner. First off: you have to write it's name (for instance: ``string'') and secondly: condition that will be checked inside brackets (for instance string(something, something)). If conditions of your expression will be meet by a record (technical speaking: evaluated true) the record will show up in the table.\\ It is clear that you need to know what are you checking, that's is: what column of the table contains information that you are interested in and what should be inside specific cell inside this column to meet your requirements. In most cases first word inside brackets sets column you want to see, while the second one sets desired value inside of the cell. To separate column from the value use comma. \paragraph{String -- string(``column'', ``value'')} -String in programmers language is often\footnote{Often, not always. There are different programming languages using slightly different terms.} just a word for anything composed of characters. In case of OpenCS this is in fact true for every value inside the column that is not composed of the pure numbers. Even columns containing only ``true`` and ``false`` values can be targeted by the string token.\footnote{There is no Boolean (''true'' or ``false'') value in the OpenCS. You should use string token for those.} String evaluates to true, when record contains in the specified column exactly the same value as specified. +String in programmers language is often\footnote{Often, not always. There are different programming languages using slightly different terms.} just a word for anything composed of characters. In case of OpenCS this is in fact true for every value inside the column that is not composed of the pure numbers. Even columns containing only ``true`` and ``false`` values can be targeted by the string token.\footnote{There is no Boolean (''true'' or ``false'') value in the OpenCS. You should use string for those.} String evaluates to true, when record contains in the specified column exactly the same value as specified. \\ -Since majority of the columns contain string values, string token is among the most often used. Examples: +Since majority of the columns contain string values, string is among the most often used expressions. Examples: \begin{itemize} \item string(``Record Type'', ``Weapon'') -- will evaluate to true for all records containing ``Weapon'' in the ``Record Type'' column cell. This group contains every weapon (including arrows and bolts) found in the game. \item string(``Portable'', ``true'') -- will evaluate to true for all records containing word true inside ``Portable'' column cell. This group contains every portable light sources (lanterns, torches etc.). \end{itemize} -This is probably enough to create around 90\% string filters you would need. However, this token is even more powerfull -- it accepts regular expressions (also called regexps). Regular expressions is a way to create string criteria that will be matched by one than just one specific value in the column. For instance, you can display both left and right gauntlets with the following expression: ``string("armor type", ".* gauntlet"))`` because ''.*'' in regexps means just: ``anything''. This filter says: please, show me ``any'' gauntlet. There are left and right gauntlets in the morrowind so this will evaluate to true for both. Simple, isn't it? +This is probably enough to create around 90\% string filters you would need. However, this expression is even more powerfull -- it accepts regular expressions (also called regexps). Regular expressions is a way to create string criteria that will be matched by one than just one specific value in the column. For instance, you can display both left and right gauntlets with the following expression: ``string("armor type", ".* gauntlet"))`` because ''.*'' in regexps means just: ``anything''. This filter says: please, show me ``any'' gauntlet. There are left and right gauntlets in the morrowind so this will evaluate to true for both. Simple, isn't it? \\ Creating regexps can be a difficult and annoying -- especially when you need complex criteria. On the other hand, We are under impression that in reality complex expressions are needed only in sporadic cases. In fact, the truth is that mostly the mentioned ``.*'' is needed and therefore the following description of regexps can be skipped by vast majority of readers. %TO-DO: write the regexps essentials. +\\ +Regular expressions is not the main topic of this manual. If you wish to learn more on this subject please, read the documentation on Qt regular expressions syntax, or TRE regexp syntax (it is almost like in Qt). \paragraph{Value -- value(``value'', (``open'', ``close''))} -While string token covers vast group of columns containing string values, there are in fact columns with just numerical values like ``weight``. To filter those we need a value token. This one works in similar manner to the string filter: first token name and criteria inside brackets. Clearly, conditions should hold column to test in. However in this case wanted value is specified as a range. \ No newline at end of file +While string expression covers vast group of columns containing string values, there are in fact columns with just numerical values like ``weight``. To filter those we need a value expression. This one works in similar manner to the string filter: first token name and criteria inside brackets. Clearly, conditions should hold column to test in. However in this case wanted value is specified as a range.\\ +As you would imagine the range can be specified as including a border value, or excluding. We are using two types of brackets for this: +\begin{itemize} + \item To include value use [] brackets. For value equal 5, expression value(something, [5, 10]) will evaluate to true. + \item To exclude value use () brackets. For value equal 5, expression value(something, (5, 10)) will evaluate to false. + \item Mixing brackets is completely legal. For value equal 10, expression value(something, [5, 10) will evaluate to true. The same expression will evaluate to false for value equal 10. +\end{itemize} + +\subsection{Logical expressions} +This subsection takes care of two remaining groups of expressions: binary and unary. The only unary expression present in the OpenCS is logical not, while the remaining binary expressions are: or, and. This clearly makes theme from user point of view belonging to the same group of logical expressions. + +\paragraph{not -- not expression()} +Sometimes you may be in need of reversing the output of the expression. This is where not comes in handy. Adding not before expression will revert it: if expression was returning true, it will return false; if it was returning false, it will return true. Brackets are not needed: not will revert only the first expression following it.\\ +To show this on know example, let's consider the ''string("armor type", ".* gauntlet"))`` filter. As We mentioned earlier this will return true for every gauntlet found in game. In order to show everything, but gauntlets we simply do ''not string("armor type", ".* gauntlet"))``. This is probably not the most useful filter on earth, but this is not a surprise: real value of not expression shines when combined with or, and filter. + +\paragraph{or -- or(expression1(), expression2())} +Or is a expression that will return true if one of the arguments evaluates to true. You can use two or more arguments, separated by the comma.\\ +Or expression is useful when showing two different group of records is needed. For instance the standard actor filter is using the following ''or(string(``record type'', npc), string(``record type'', creature))`` and will show both npcs and creatures. + +\paragraph{and -- and(expression1(), expression2())} +And is a expression that will return true if all arguments evaluates to true. As in the case of ''or`` you can use two or more arguments, separated by the comma.\\ +As We mentioned earlier in the ''not`` filter, combining not with and can be very useful. For instance to show all armor types, excluding gauntlets you can write the following: ''and (not string("armor type", ".* gauntlet"), string(''Record Type``, ''Armor``))''. \ No newline at end of file From 056833e21e50d8492589846f5f878f48779a0a15 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 30 Oct 2013 12:57:01 +0100 Subject: [PATCH 011/889] Still filters. A little less draft-ish quality. --- manual/opencs/filters.tex | 43 ++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index 72b83d270..418ead3fc 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -8,10 +8,10 @@ Don't be afraid though, filters are fairly intuitive and easy to use. \begin{description} \item[Filter] is generally speaking a tool able to ``Filter'' (that is: select some elements, while discarding others) according to the some criteria. In case of OpenCS: records are being filtered according to the criteria of user choice. Criteria are written down in language with simple syntax. \item[Criteria] describes condition under with any any record is being select by the filter. - \item[Syntax] as you may noticed computers (in general) are rather strict, and expect only strictly formulated orders -- that is: written with correct syntax. Our syntax is simple and described in the {B}asics subsection. - \item[Expression] is way we are actually performing filtering. Filter can be treated as ``functions'': accepts arguments, and evaluates either to the true or false for every column record. - \item[N-ary] is any expression that is useful to group two (or more) other expressions together in order to create filter that will check for criteria placed in two (again: or more) columns (logical ``or'', ``and''). - \item[unary] is any expression that expects one other expression. The example is ``not'' expression. + \item[Syntax] as you may noticed computers (in general) are rather strict, and expect only strictly formulated orders -- that is: written with correct syntax. + \item[Expression] is way we are actually performing filtering. Filter can be treated as ``functions'': accepts arguments, and evaluates either to the true or false for every column record at the time. + \item[N-ary] is any expression that expects two or more expressions as arguments. It is useful for grouping two (or more) other expressions together in order to create filter that will check for criteria placed in two (again: or more) columns (logical ``or'', ``and''). + \item[unary] is any expression that expects one other expression. The example is ``not'' expression. In fact ``not'' is the only useful unary expression in OpenCS record filters. \item[nullary] is expression that does not accepts other expressions. It accepts arguments specified later. \end{description} @@ -25,53 +25,54 @@ Above each table there is a field that is used to enter filter: either predefine \item[ID] contains the name of the filter. \item[Modified] just like in all other tables you have seen so far modified indicates if a filter was added, modified or removed. \item[Filter] column containing expression of the filter. - \item[Description] contains the short description of the filter function. + \item[Description] contains the short description of the filter function. Do not expect any surprises there. \end{description} So let's learn how to actually use those to speed up your work. \subsection{Using predefined filters} -Using those filters is quite easy and involves typing inside the filter field above the table. For instance, try to open referencables table and type in the filters field the following: ``project::weapons''. As soon as you complete the text table will magicly alters and will show only the weapons. As you could noticed project::weapons is nothing else than a name of one of the predefined filters. That's it: in order to use the filter inside the table you simply type it's name inside the filter field.\\ -To make life easier filter names follow simple convention. +Using those filters is quite easy and involves typing inside the filter field above the table. For instance, try to open referencables table and type in the filters field the following: ``project::weapons''. As soon as you complete the text, table will magicly alter and will show only the weapons. As you could noticed project::weapons is nothing else than a ID of one of the predefined filters. That's it: in order to use the filter inside the table you simply type it's name inside the filter field.\\ +To make life easier filter IDs follow simple convention. \begin{itemize} - \item Filter name filtering a specific record type contains usually the name of specific group. For instance project::weapons filter contains the word weapons (did you noticed?). Plural form is always used. - \item When filtering specific subgroup the name starts just like in the case of general filter. For instance project::weaponssilver will filter only silver weapons (new mechanic introduced by the Bloodmoon, silver weapons deal double damage against werewolfs) and project::weaponsmagical will filter only magical weapons (able to hurt ghosts and other supernatural creatures). - \item There are few exceptions from the above. For instance there is a project::added, project::removed, project::modyfied, project::base. You would probably except something more like ``project::statusadded'' but in this case typing this few extra characters would only help to break your keyboard faster. + \item Filter ID filtering a specific record type contains usually the name of a specific group. For instance project::weapons filter contains the word weapons (did you noticed?). Plural form is always used. + \item When filtering specific subgroup the ID starts just like in the case of general filter. For instance project::weaponssilver will filter only silver weapons (new mechanic introduced by the Bloodmoon, silver weapons deal double damage against werewolfs) and project::weaponsmagical will filter only magical weapons (able to hurt ghosts and other supernatural creatures). + \item There are few exceptions from the above rule. For instance there is a project::added, project::removed, project::modyfied, project::base. You would probably except something more like ``project::statusadded'' but in this case typing this few extra characters would only help to break your keyboard faster. \end{itemize} We strongly recommend to take a look at the filters table right now to see what you can filter with that. And try using it! It is very simple. \subsection{Advanced} -If you want to create your own filter you have to know exactly what do you want to get in order to translate this into the tokens and nodes. Finally, you will have to write this as legal expression -- that is: using correct syntax. As a result table will show only desired rows.\\ +Back to the manual? Great.\\ +If you want to create your own filter you have to know exactly what do you want to get in order to translate this into the expressions. Finally, you will have to write this with correct syntax. As a result table will show only desired rows.\\ Advance subsection covers everything that you need to know in order to create any filter you may want to. \subsection{Namespaces} -It is a ``namespace``, a term borrowed from the C++ language. In case of OpenCS namespace of the filter determinate if the filter will be stored along with your project or if it will be forgotten as soon as OpenCS quits. +Did you noticed that every default filter has ``project::`` prefix? It is a ``namespace``, a term borrowed from the C++ language. In case of OpenCS namespace of the filter determinate if the filter will be stored along with your project file or if it will be forgotten as soon as OpenCS quits. \begin{description} \item[project::] namespace indicates that filter is stored inside the project file. \item[session::] namespace indicates that filter is not stored inside the project file, and once you will quit OpenCS (close session) the filter will be gone. Forever! Until then it can be found inside the filters table. \end{description} In addition to this two scopes, there is a third one; called one-shot. One-shot filters are not stored anywhere and as the name implies they are supposed to be created when needed only once. Good thing about the one-shot filters is that you don't need to open filters table in order to create it. Instead you just type it directly inside the filter field, starting with ``!''.\\ -Still, you may wonder how you are supposed to write expressions, what expressions you should use, and what syntax looks like. +Still, you may wonder how you are supposed to write expressions, what expressions you should use, and what syntax looks like. Let's start with nullary expressions that will allow you to create a basic filter. \subsubsection{Nullary expressions} -Each expression is used in similar manner. First off: you have to write it's name (for instance: ``string'') and secondly: condition that will be checked inside brackets (for instance string(something, something)). If conditions of your expression will be meet by a record (technical speaking: evaluated true) the record will show up in the table.\\ -It is clear that you need to know what are you checking, that's is: what column of the table contains information that you are interested in and what should be inside specific cell inside this column to meet your requirements. In most cases first word inside brackets sets column you want to see, while the second one sets desired value inside of the cell. To separate column from the value use comma. +All nullary expressions are used in similar manner. First off: you have to write it's name (for instance: ``string'') and secondly: condition that will be checked inside brackets (for instance string(something, something)). If conditions of your expression will be meet by a record (technical speaking: expression will evaluate to true) the record will show up in the table.\\ +It is clear that you need to know what are you checking, that's is: what column of the table contains information that you are interested in and what should be inside specific cell inside this column to meet your requirements. In most cases first word inside brackets sets column you want to see, while the second one sets desired value inside of the cell. To separate column argument from the value argument use comma. \paragraph{String -- string(``column'', ``value'')} -String in programmers language is often\footnote{Often, not always. There are different programming languages using slightly different terms.} just a word for anything composed of characters. In case of OpenCS this is in fact true for every value inside the column that is not composed of the pure numbers. Even columns containing only ``true`` and ``false`` values can be targeted by the string token.\footnote{There is no Boolean (''true'' or ``false'') value in the OpenCS. You should use string for those.} String evaluates to true, when record contains in the specified column exactly the same value as specified. +String in programmers language is often\footnote{Often, not always. There are different programming languages using slightly different terms.} just a word for anything composed of characters. In case of OpenCS this is in fact true for every value inside the column that is not composed of the pure numbers. Even columns containing only ``true`` and ``false`` values can be targeted by the string expression.\footnote{There is no Boolean (''true'' or ``false'') value in the OpenCS. You should use string for those.} String evaluates to true, when record contains in the specified column exactly the same value as specified. \\ Since majority of the columns contain string values, string is among the most often used expressions. Examples: \begin{itemize} \item string(``Record Type'', ``Weapon'') -- will evaluate to true for all records containing ``Weapon'' in the ``Record Type'' column cell. This group contains every weapon (including arrows and bolts) found in the game. \item string(``Portable'', ``true'') -- will evaluate to true for all records containing word true inside ``Portable'' column cell. This group contains every portable light sources (lanterns, torches etc.). \end{itemize} -This is probably enough to create around 90\% string filters you would need. However, this expression is even more powerfull -- it accepts regular expressions (also called regexps). Regular expressions is a way to create string criteria that will be matched by one than just one specific value in the column. For instance, you can display both left and right gauntlets with the following expression: ``string("armor type", ".* gauntlet"))`` because ''.*'' in regexps means just: ``anything''. This filter says: please, show me ``any'' gauntlet. There are left and right gauntlets in the morrowind so this will evaluate to true for both. Simple, isn't it? +This is probably enough to create around 90\% string filters you will eventually need. However, this expression is even more powerful -- it accepts regular expressions (also called regexps). Regular expressions is a way to create string criteria that will be matched by one than just one specific value in the column. For instance, you can display both left and right gauntlets with the following expression: ``string("armor type", ".* gauntlet"))`` because ''.*'' in regexps means just: ``anything''. This filter says: please, show me ``any'' gauntlet. There are left and right gauntlets in the morrowind so this will evaluate to true for both. Simple, isn't it? \\ -Creating regexps can be a difficult and annoying -- especially when you need complex criteria. On the other hand, We are under impression that in reality complex expressions are needed only in sporadic cases. In fact, the truth is that mostly the mentioned ``.*'' is needed and therefore the following description of regexps can be skipped by vast majority of readers. +Creating regexps can be a difficult and annoying -- especially when you need complex criteria. On the other hand, We are under impression that in reality complex expressions are needed only in sporadic cases. In fact, the truth is: that most of the time only already mentioned ``.*'' is needed and therefore the following description of regexps can be skipped by vast majority of readers. %TO-DO: write the regexps essentials. \\ -Regular expressions is not the main topic of this manual. If you wish to learn more on this subject please, read the documentation on Qt regular expressions syntax, or TRE regexp syntax (it is almost like in Qt). +Regular expressions are not the main topic of this manual. If you wish to learn more on this subject please, read the documentation on Qt regular expressions syntax, or TRE regexp syntax (it is almost like in Qt). \paragraph{Value -- value(``value'', (``open'', ``close''))} While string expression covers vast group of columns containing string values, there are in fact columns with just numerical values like ``weight``. To filter those we need a value expression. This one works in similar manner to the string filter: first token name and criteria inside brackets. Clearly, conditions should hold column to test in. However in this case wanted value is specified as a range.\\ @@ -94,5 +95,5 @@ Or is a expression that will return true if one of the arguments evaluates to tr Or expression is useful when showing two different group of records is needed. For instance the standard actor filter is using the following ''or(string(``record type'', npc), string(``record type'', creature))`` and will show both npcs and creatures. \paragraph{and -- and(expression1(), expression2())} -And is a expression that will return true if all arguments evaluates to true. As in the case of ''or`` you can use two or more arguments, separated by the comma.\\ -As We mentioned earlier in the ''not`` filter, combining not with and can be very useful. For instance to show all armor types, excluding gauntlets you can write the following: ''and (not string("armor type", ".* gauntlet"), string(''Record Type``, ''Armor``))''. \ No newline at end of file +And is a expression that will return true if all arguments evaluates to true. As in the case of ''or`` you can use two or more arguments, separated by a comma.\\ +As We mentioned earlier in the ''not`` filter, combining ''not`` with ''and`` can be very useful. For instance to show all armor types, excluding gauntlets you can write the following: ''and (not string("armor type", ".* gauntlet"), string(''Record Type``, ''Armor``))''. \ No newline at end of file From cb8d111d36866b7ff98db3d80162f62908e95d45 Mon Sep 17 00:00:00 2001 From: TomKoenderink Date: Wed, 30 Oct 2013 21:51:37 +0100 Subject: [PATCH 012/889] Started tables.tex; import library for images --- manual/opencs/img/water.png | Bin 0 -> 914 bytes manual/opencs/main.tex | 3 +- manual/opencs/tables.tex | 78 ++++++++++++++++++++++++++++++++++-- 3 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 manual/opencs/img/water.png diff --git a/manual/opencs/img/water.png b/manual/opencs/img/water.png new file mode 100644 index 0000000000000000000000000000000000000000..885c2d9a732fbcc138dd5b82675136753af27848 GIT binary patch literal 914 zcmeAS@N?(olHy`uVBq!ia0y~yVC)34^ElXmezgNlPf3)4h~MgeXYCYD8t3&8?SXZ`dT4V2tws5Er)=Lxf_aUO7K^kkB`rpDzX zq#&Rm)DWNu#0o+T9V`j698?$tI1gx-dwuwsF3=HMlYdBsG2xISSk$D^qk&lhXf%@x zQ^UuaDZD;{<(~^9CbKXA%@j$TbwGu&!Gi&85Q7q<&k1n`S0)Dy7bXTEn#kaw(jZ_n zU4RqFI{-8ZuI|XQoh@MAGR}z%GAn@Y`Jo7N%K=3$B}NWKC6Ku@)LfVf3MSooB=p42 z;@tGt7ehrk1UTD|E&FdCWWpl&X!FXheXC?D7i{}eo6hNa)YIvRYf~qG=QQaZ-``su zP7L|_R&AjGr<%Qj+|Ew-fAc2tFMf4ixg|+&o~MO^Ou_*z`NmR)-Ph{$fMzlSt&1~I z6f$GIA^hb*Fq@fk*3A7jFa9M=65wE%+~P3d@6}ab6J{BBGH|T$VBm0--B*~q(0Rw- z$GR&l?r=0}D87#0TYBHXvxE6Yd{ZJ&lhHn{t!_TzN{Rv&F>3@5dYr!PYi2*?R`($# zp+^jFZXG|g@m#B5hf$Rr%e8LPNoqpx=bLx`lwWVJujeEbfByb89>wcD9vm#LW_wMO z-=;jC+0xrrdidP$XiW*F#L3BT3!eX7C%~O=e)Gvavkhe&yS6EP2v5i@S|huLePYD! zg=Us~ENi$YZa7%^V(<346`z<&R+c?JV72IZ)Xcw@M^7F*=9n-0@QtRW=Zy;y{xdXO z4!JFRd2H*l6*-byEfuRY)&xs@@HuoWx$Wg;tD`r+FMn;mF4tzkuAB3Ge;hMqc&wb* t1xzxqIA%zQP_fyM?b@h2jZF!oGqTO-KPjj#h$KyF6*2UngG_9Pgno| literal 0 HcmV?d00001 diff --git a/manual/opencs/main.tex b/manual/opencs/main.tex index da3457e13..64f9baf5c 100644 --- a/manual/opencs/main.tex +++ b/manual/opencs/main.tex @@ -1,12 +1,13 @@ \documentclass[american]{article} \usepackage[T1]{fontenc} \usepackage{babel} +\usepackage{graphicx} \author{OpenMW Team} \begin{document} \title{OpenCS User Manual} - \maketitle \tableofcontents{} +\input{tables} \input{filters} \end{document} diff --git a/manual/opencs/tables.tex b/manual/opencs/tables.tex index eab308d42..1a9b362d1 100644 --- a/manual/opencs/tables.tex +++ b/manual/opencs/tables.tex @@ -1,10 +1,82 @@ \section{Tables} -If you launched OpenCS already and played it with for a while you surely noticed that it is a very table oriented application. Your impression is surely correct: major part of Open{CS} is built around table pattern. But this is not excel clone! Table was just the most logical way of dealing with all different record types in a general way. + +\subsection{Introduction} +If you have launched OpenCS already and played around with it for a bit, you have probably gotten the impression that it contains lots of tables. You'd be spot on: OpenCS is built around using tables. This doesn't mean it works just like Excel or Calc, though. Due to the vast amounts of information involved with Morrowind, tables just made the most sense. You have to be able to spot information quickly and be able to change them on the fly. Let's browse through the various screens and see what all these tables show. + + + \subsection{Used Terms} +\subsubsection{Glossary} + \begin{description} + \item[Record:] An entry in OpenCS representing an item, location, sound, NPC or anything else. + + \item[Reference, Referenceable:] When an item is placed in the world, it doesn't create a new record each time. For example, the game world might contain a lot of exquisite belts on different NPCs and in many crates, but they all refer to one specific record: the Exquisite Belt record. In this case, all those belts in crates and on NPCs are references. The central Exquisite Belt record is called a referenceable. This allows modders to make changes to all items of the same type. For example, if you want all exquisite belts to have 4000 enchantment points rather than 400, you will only need to change the referenceable Exquisite Belt rather than all exquisite belts references individually. \end{description} -\subsection{Basics} +\subsubsection{Recurring Terms} -\subsection{Advanced} +Some columns are recurring throughout OpenCS. They show up in (nearly) every table in OpenCS. + +\begin{description} + \item[ID]: Each item, location, sound, etc. gets the same unique identifier in both OpenCS and Morrowind. This is usually a very self-explanatory name. For example, the ID for the (unique) black pants of Caius Cosades is "Caius_pants". This allows you to manipulate the game in many ways. For example, you could add these pants to your inventory by simply opening the console and write: player->addItem Caius_pants. Either way, in both Morrowind and OpenCS, the ID is the primary way to identify all these different parts of the game. + + \item[Modified]: This column shows what has happened (if something has happened) to this record. There are four possible states in which it can exist. + \begin{description} + \item[Base] means that this record is part of the base game and is in its original state. Usually, if you create a mod, the base game is Morrowind with optionally the Bloodmoon and Tribunal expansions. + \item[Added] means that this record was not in the base game and has been added by a modder. + \item[Modified] means that the record is part of the base game, but has been changed in some way. + \item[Deleted] means that this record used to be part of the base game, but has been removed as an entry. This does not mean, however, that the occurrences in the game itself have been removed! For example, if you remove the CharGen_Bed entry from morrowind.esm, it doesn't mean the bedroll in the basement of the Census and Excise Office in Seyda Neen is gone. You're going to have to delete that reference yourself or make sure that that object is replaced by something that still exists otherwise you'll get crashes in the worst case scenario. + \end{description} +\end{description} + + + +\subsection{World Screens} + +The contents of the game world can be changed by choosing one of the options in the appropriate menu at the top of the screen. + +\subsubsection{Regions} + +This describes the general areas of Vvardenfell. Each of these areas has different rules about things such as encounters and weather. + +\begin{description} + \item[Name:] This is how the game will show your location in-game. + \item[Map Colour:] This is a six-digit hexidecimal representation of the colour used to identify the region on the map available in World > Region Map. If you don't have an application with a colour picker, you can use your favourite search engine to find a colour picker online. + \item[Sleep Encounter:] These are the rules for what kind of enemies you might encounter when you sleep outside in the wild. +\end{description} + +\subsubsection{Cells} + +Expansive worlds such as Vvardenfell, with all its items, NPCs, etc. have a lot going on simultaneously. But if you are in Balmora, why would the computer need to keep track the exact locations of NPCs walking through the corridors in a Vivec canton? All that work would be quite useless and bring your system to its knees! So the world has been divided up into squares we call "cells". Once your character enters a cell, the game will load everything that is going on in that cell so you can interact with it. + +In the original Morrowind this could be seen when you were travelling and you would see a small loading bar at the bottom of the screen; you had just entered a new cell and the game would have to load all the items and NPCs. The Cells screen in OpenCS provides you with a list of cells in the game, both the interior cells (houses, dungeons, mines, etc.) and the exterior cells (the outside world). + +\begin{description} + \item[Sleep Forbidden:] Can the player sleep on the floor? In most cities it is forbidden to sleep outside. Sleeping in the wild carries its own risks of attack, though, and this entry lets you decide if a player should be allowed to sleep on the floor in this cell or not. + + \item[Interior Water:] Should water be rendered in this interior cell? The game world consists of an endless ocean at height 0. Then the landscape is added. If part of the landscape goes below height 0, the player will see water. (See illustration.) + + Setting the cell's Interior Water to true tells the game that this cell is both an interior cell (inside a building, for example, rather than in the open air) but that there still needs to be water at height 0. This is useful for dungeons or mines that have water in them. + + Setting the cell's Interior Water to false tells the game that the water at height 0 should not be used. Remember that cells that are in the outside world are exterior cells and should thus \textit{always} be set to false! + + \item[Interior Sky:] Should this interior cell have a sky? This is a rather unique case. The \textit{Tribunal} expansion took place in a city on the mainland. Normally this would require the city to be composed of exterior cells so it has a sky, weather and the like. But if the player is in an exterior cell and looks at his in-game map, he sees Vvardenfell with an overview of all exterior cells. The player would have to see the city's very own map, as if he was walking around in an interior cell. + + So the developers decided to create a workaround and take a bit of both: The whole city would technically work exactly like an interior cell, but it would need a sky as if it was an exterior cell. That's what this is. This is why the vast majority of the cells you will find in this screen will have this option set to false: It's only meant for these "fake exteriors". + + \item[Region:] To which Region does this cell belong? This has an impact on the way the game handles weather and encounters in this area. It is also possible for a cell not to belong to any region. + +\end{description} + +\subsubsection{Referenceables} + +This is a library of all the items, triggers, containers, NPCs, etc. in the game. There are several kinds of Record Types. Depending on which type a record is, it will need specific information to function. For example, an NPC needs a value attached to its aggression level. A chest, of course, does not. All Record Types contain at least a model. How else would the player see them? Usually they also have a Name, which is what you see when you hover your reticle over the object. + +Let's go through all Record Types and discuss what you can tell OpenCS about them. + +\begin{description} + \item[Activator:] This is an item that, when activated, starts a script or even just shows a tooltip. + \end{description} +\end{description} \ No newline at end of file From 0141526c557981e013816d982823bdae51bb9bc5 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 31 Oct 2013 10:21:10 +0100 Subject: [PATCH 013/889] Corrected tables.tex so it will compile. --- manual/opencs/filters.tex | 3 +-- manual/opencs/tables.tex | 20 ++++++-------------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index 418ead3fc..14181f68c 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -68,10 +68,9 @@ Since majority of the columns contain string values, string is among the most of \end{itemize} This is probably enough to create around 90\% string filters you will eventually need. However, this expression is even more powerful -- it accepts regular expressions (also called regexps). Regular expressions is a way to create string criteria that will be matched by one than just one specific value in the column. For instance, you can display both left and right gauntlets with the following expression: ``string("armor type", ".* gauntlet"))`` because ''.*'' in regexps means just: ``anything''. This filter says: please, show me ``any'' gauntlet. There are left and right gauntlets in the morrowind so this will evaluate to true for both. Simple, isn't it? \\ -Creating regexps can be a difficult and annoying -- especially when you need complex criteria. On the other hand, We are under impression that in reality complex expressions are needed only in sporadic cases. In fact, the truth is: that most of the time only already mentioned ``.*'' is needed and therefore the following description of regexps can be skipped by vast majority of readers. +Creating regexps can be a difficult and annoying -- especially when you need complex criteria. On the other hand, We are under impression that in reality complex expressions are needed only in sporadic cases. In fact, the truth is: that most of the time only already mentioned ``.*'' is needed and therefore the following description of regexps can be skipped by vast majority of readers.\\ %TO-DO: write the regexps essentials. -\\ Regular expressions are not the main topic of this manual. If you wish to learn more on this subject please, read the documentation on Qt regular expressions syntax, or TRE regexp syntax (it is almost like in Qt). \paragraph{Value -- value(``value'', (``open'', ``close''))} diff --git a/manual/opencs/tables.tex b/manual/opencs/tables.tex index 1a9b362d1..fb6d41ba8 100644 --- a/manual/opencs/tables.tex +++ b/manual/opencs/tables.tex @@ -3,8 +3,6 @@ \subsection{Introduction} If you have launched OpenCS already and played around with it for a bit, you have probably gotten the impression that it contains lots of tables. You'd be spot on: OpenCS is built around using tables. This doesn't mean it works just like Excel or Calc, though. Due to the vast amounts of information involved with Morrowind, tables just made the most sense. You have to be able to spot information quickly and be able to change them on the fly. Let's browse through the various screens and see what all these tables show. - - \subsection{Used Terms} \subsubsection{Glossary} @@ -20,18 +18,13 @@ If you have launched OpenCS already and played around with it for a bit, you hav Some columns are recurring throughout OpenCS. They show up in (nearly) every table in OpenCS. \begin{description} - \item[ID]: Each item, location, sound, etc. gets the same unique identifier in both OpenCS and Morrowind. This is usually a very self-explanatory name. For example, the ID for the (unique) black pants of Caius Cosades is "Caius_pants". This allows you to manipulate the game in many ways. For example, you could add these pants to your inventory by simply opening the console and write: player->addItem Caius_pants. Either way, in both Morrowind and OpenCS, the ID is the primary way to identify all these different parts of the game. - - \item[Modified]: This column shows what has happened (if something has happened) to this record. There are four possible states in which it can exist. - \begin{description} - \item[Base] means that this record is part of the base game and is in its original state. Usually, if you create a mod, the base game is Morrowind with optionally the Bloodmoon and Tribunal expansions. - \item[Added] means that this record was not in the base game and has been added by a modder. - \item[Modified] means that the record is part of the base game, but has been changed in some way. - \item[Deleted] means that this record used to be part of the base game, but has been removed as an entry. This does not mean, however, that the occurrences in the game itself have been removed! For example, if you remove the CharGen_Bed entry from morrowind.esm, it doesn't mean the bedroll in the basement of the Census and Excise Office in Seyda Neen is gone. You're going to have to delete that reference yourself or make sure that that object is replaced by something that still exists otherwise you'll get crashes in the worst case scenario. +\item[ID] Each item, location, sound, etc. gets the same unique identifier in both OpenCS and Morrowind. This is usually a very self-explanatory name. For example, the ID for the (unique) black pants of Caius Cosades is ``Caius\_pants''. This allows you to manipulate the game in many ways. For example, you could add these pants to your inventory by simply opening the console and write: ``player->addItem Caius\_pants''. Either way, in both Morrowind and OpenCS, the ID is the primary way to identify all these different parts of the game. %Wrong! Cells do not have ID, only name. +\item[Modified] This column shows what has happened (if something has happened) to this record. There are four possible states in which it can exist. +\item[Base] means that this record is part of the base game and is in its original state. Usually, if you create a mod, the base game is Morrowind with optionally the Bloodmoon and Tribunal expansions. +\item[Added] means that this record was not in the base game and has been added by a modder. +\item[Modified] means that the record is part of the base game, but has been changed in some way. +\item[Deleted] means that this record used to be part of the base game, but has been removed as an entry. This does not mean, however, that the occurrences in the game itself have been removed! For example, if you remove the CharGen\_Bed entry from morrowind.esm, it doesn't mean the bedroll in the basement of the Census and Excise Office in Seyda Neen is gone. You're going to have to delete that reference yourself or make sure that that object is replaced by something that still exists otherwise you'll get crashes in the worst case scenario. \end{description} -\end{description} - - \subsection{World Screens} @@ -78,5 +71,4 @@ Let's go through all Record Types and discuss what you can tell OpenCS about the \begin{description} \item[Activator:] This is an item that, when activated, starts a script or even just shows a tooltip. - \end{description} \end{description} \ No newline at end of file From 8ae38eaa1f03699b50c65548e318ac474f064cf9 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 1 Nov 2013 13:52:24 +0100 Subject: [PATCH 014/889] Corrected few things and added short description for creating and saving filter, as well as replacing the default filters set. --- manual/opencs/filters.tex | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index 14181f68c..8f4469fc7 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -45,7 +45,7 @@ We strongly recommend to take a look at the filters table right now to see what Back to the manual? Great.\\ If you want to create your own filter you have to know exactly what do you want to get in order to translate this into the expressions. Finally, you will have to write this with correct syntax. As a result table will show only desired rows.\\ Advance subsection covers everything that you need to know in order to create any filter you may want to. -\subsection{Namespaces} +\subsubsection{Namespaces} Did you noticed that every default filter has ``project::`` prefix? It is a ``namespace``, a term borrowed from the C++ language. In case of OpenCS namespace of the filter determinate if the filter will be stored along with your project file or if it will be forgotten as soon as OpenCS quits. \begin{description} \item[project::] namespace indicates that filter is stored inside the project file. @@ -82,7 +82,7 @@ As you would imagine the range can be specified as including a border value, or \item Mixing brackets is completely legal. For value equal 10, expression value(something, [5, 10) will evaluate to true. The same expression will evaluate to false for value equal 10. \end{itemize} -\subsection{Logical expressions} +\susubbsection{Logical expressions} This subsection takes care of two remaining groups of expressions: binary and unary. The only unary expression present in the OpenCS is logical not, while the remaining binary expressions are: or, and. This clearly makes theme from user point of view belonging to the same group of logical expressions. \paragraph{not -- not expression()} @@ -95,4 +95,12 @@ Or expression is useful when showing two different group of records is needed. F \paragraph{and -- and(expression1(), expression2())} And is a expression that will return true if all arguments evaluates to true. As in the case of ''or`` you can use two or more arguments, separated by a comma.\\ -As We mentioned earlier in the ''not`` filter, combining ''not`` with ''and`` can be very useful. For instance to show all armor types, excluding gauntlets you can write the following: ''and (not string("armor type", ".* gauntlet"), string(''Record Type``, ''Armor``))''. \ No newline at end of file +As We mentioned earlier in the ''not`` filter, combining ''not`` with ''and`` can be very useful. For instance to show all armor types, excluding gauntlets you can write the following: ''and (not string("armor type", ".* gauntlet"), string(''Record Type``, ''Armor``))''. + +\subsubsection{Creating and saving filter} +In order to create and save new filter, you should go to the filters table, right click and select option ``add record'' from the context menu. A horizontal widget group at the bottom of the table should show up. From there you should select a namespace responsible for scope of the filter (described earlier) and desired ID of the filter. After pressing ok button new entry will show up in the filters table. This filter does nothing at the moment, since it still lacks expressions. In order to add your formula simply double click the filter cell of the new entry and write it down there.\\ +Done! You are free to use your filter. + +\subsubsection{Replacing the default filters set} +{OpenCS} allows you to substitute default filters set provided by us, with your own filters. In order to do so you should create a new project, add desired filters, remove undesired and save. Rename the file to the ``defaultfilters'' (don't forget to remove .omwaddon.project extension) and place it inside your configuration directory.\\ +The file acts as template for all new project files from now. If you wish to go back to the old default set, simply rename or remove the custom file. \ No newline at end of file From 83029244642b109d1b66fcaa481a51cfb63b7fb3 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 1 Nov 2013 13:54:34 +0100 Subject: [PATCH 015/889] corrected typo --- manual/opencs/filters.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index 8f4469fc7..f69f6d7f1 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -82,7 +82,7 @@ As you would imagine the range can be specified as including a border value, or \item Mixing brackets is completely legal. For value equal 10, expression value(something, [5, 10) will evaluate to true. The same expression will evaluate to false for value equal 10. \end{itemize} -\susubbsection{Logical expressions} +\subsubsection{Logical expressions} This subsection takes care of two remaining groups of expressions: binary and unary. The only unary expression present in the OpenCS is logical not, while the remaining binary expressions are: or, and. This clearly makes theme from user point of view belonging to the same group of logical expressions. \paragraph{not -- not expression()} From 2f5ad4c16f0752bab3af669535953a1cdbd90da2 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 6 Nov 2013 21:10:00 +0100 Subject: [PATCH 016/889] Small changes. --- manual/opencs/filters.tex | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index f69f6d7f1..2c5e0939e 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -3,7 +3,7 @@ Filters are the key element of OpenCS use cases by allowing rapid and easy access to the searched records presented in all tables. Therefore: in order to use this application fully effective you should make sure that all concepts and instructions written in the this section of the manual are perfectly clear to you.\\ Don't be afraid though, filters are fairly intuitive and easy to use. -\subsection{Used Terms} +\subsubsection{Used Terms} \begin{description} \item[Filter] is generally speaking a tool able to ``Filter'' (that is: select some elements, while discarding others) according to the some criteria. In case of OpenCS: records are being filtered according to the criteria of user choice. Criteria are written down in language with simple syntax. @@ -15,10 +15,10 @@ Don't be afraid though, filters are fairly intuitive and easy to use. \item[nullary] is expression that does not accepts other expressions. It accepts arguments specified later. \end{description} -\subsection{Basics} +\subsubsection{Basics} In fact you don't need to learn everything about filters in order to use them. In fact all you need to know to achieve decent productivity with OpenCS is inside basics section. -\subsection{Interface} +\subsubsection{Interface} Above each table there is a field that is used to enter filter: either predefined by the OpenMW developers or made by you, the user. You probably noticed it before. However there is also completely new element, although using familiar table layout. Go to the application menu view, and click filters. You should see set of default filters, made by the OpenMW team in the table with the following columns: filter, description and modified. \begin{description} @@ -29,7 +29,7 @@ Above each table there is a field that is used to enter filter: either predefine \end{description} So let's learn how to actually use those to speed up your work. -\subsection{Using predefined filters} +\subsubsection{Using predefined filters} Using those filters is quite easy and involves typing inside the filter field above the table. For instance, try to open referencables table and type in the filters field the following: ``project::weapons''. As soon as you complete the text, table will magicly alter and will show only the weapons. As you could noticed project::weapons is nothing else than a ID of one of the predefined filters. That's it: in order to use the filter inside the table you simply type it's name inside the filter field.\\ To make life easier filter IDs follow simple convention. @@ -83,7 +83,7 @@ As you would imagine the range can be specified as including a border value, or \end{itemize} \subsubsection{Logical expressions} -This subsection takes care of two remaining groups of expressions: binary and unary. The only unary expression present in the OpenCS is logical not, while the remaining binary expressions are: or, and. This clearly makes theme from user point of view belonging to the same group of logical expressions. +This subsection takes care of two remaining groups of expressions: binary and unary. The only unary expression present in the OpenCS is logical not, while the remaining binary expressions are: or, and. This clearly makes them (from the user point of view) belonging to the same group of logical expressions. \paragraph{not -- not expression()} Sometimes you may be in need of reversing the output of the expression. This is where not comes in handy. Adding not before expression will revert it: if expression was returning true, it will return false; if it was returning false, it will return true. Brackets are not needed: not will revert only the first expression following it.\\ From 470616ce9274031bfbb26fec5e06482e030403b9 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 6 Nov 2013 22:01:37 +0100 Subject: [PATCH 017/889] Added windows.tex. Does not contain a lot at this point. --- manual/opencs/main.tex | 1 + manual/opencs/windows.tex | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 manual/opencs/windows.tex diff --git a/manual/opencs/main.tex b/manual/opencs/main.tex index 64f9baf5c..603e2d9ba 100644 --- a/manual/opencs/main.tex +++ b/manual/opencs/main.tex @@ -8,6 +8,7 @@ \title{OpenCS User Manual} \maketitle \tableofcontents{} +\input{windows} \input{tables} \input{filters} \end{document} diff --git a/manual/opencs/windows.tex b/manual/opencs/windows.tex new file mode 100644 index 000000000..ad1d2f951 --- /dev/null +++ b/manual/opencs/windows.tex @@ -0,0 +1,12 @@ +\section{Windows} +\subsection{Introduction} +This section describes the multiple windows interface of the OpenCS editor. This design principle was chosen in order to extend the flexibility of the editor, especially on the multiple screens setups and on environments providing advanced windows management features, like; for instance; multiple desktops found commonly on many open source desktop environments. However, it is enough to have a single large screen to see the advantages of this concept.\\ +OpenCS windows interface is easy to describe and understand. In fact We decided to minimize use of many windows concepts applied commonly in various applications. For instance dialog windows are really hard to find in the OpenCS. You are free to try, though.\\ +Because of this, and the fact that we expect that user is familiar with other applications using windows this section is mostly focused on practical ways of organizing work with the OpenCS. + +\subsection{Basics} +After starting Open{CS} and choosing content files to use a editor window should show up. It probably does not look surprising: there is a menubar at the top, and there is a large empty area. That's it: a brand new Open{CS} window contains only menubar and statusbar. In order to make it a little bit more useful you probably want to enable some window widgets. You are free to do so, just try to explore the menubar. \\ +You probably founded out the way to enable and disable some interesting tables, but those will be described later. For now, let's just focus on the windows itself. + +\paragraph{Creating new windows} +is easy! Just visit view menu, and use the ``New View'' item. Suddenly, out of the blue a new window will show up. As you would expect, it is also blank, and you are free to add any of the Open{CS} widgets. \ No newline at end of file From 1eaca1e26b4933ebb8ad3a29d69879059a8a6f7f Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 13 Nov 2013 21:01:48 +0100 Subject: [PATCH 018/889] Added regular expressions tutorial. Because serpentine failed me :( --- manual/opencs/filters.tex | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index 2c5e0939e..2a3d1a480 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -70,8 +70,15 @@ This is probably enough to create around 90\% string filters you will eventually \\ Creating regexps can be a difficult and annoying -- especially when you need complex criteria. On the other hand, We are under impression that in reality complex expressions are needed only in sporadic cases. In fact, the truth is: that most of the time only already mentioned ``.*'' is needed and therefore the following description of regexps can be skipped by vast majority of readers.\\ -%TO-DO: write the regexps essentials. -Regular expressions are not the main topic of this manual. If you wish to learn more on this subject please, read the documentation on Qt regular expressions syntax, or TRE regexp syntax (it is almost like in Qt). +Before working with Regular Expressions, you should understand what actually are regular expressions. Essentially, the idea is simple: when you are writing any word, you are using strictly defined latters -- that is: latters create a word. What you want to do with regular expression is to use set of rules that will match to many words. It is not that difficult to see what it's needed to do so: first, you will clearly need way to determinate what latters you want to match (word is composed by latters).\\ + +Before introducing other ways to choose between characters, I want explain anchors. Anchors allows you to decide where to ``look'' in the string. You surely should know about ``^'' anchor and ``\$''. Putting ``^`` will tell to Open{CS} to look on the beginning of string, while ''\$`` is used to mark the end of it. For instance, pattern ''^Pink.* elephant.\$`` Will match any sentence Beginning with the word ''Pink`` and ending with '' elephant.``. Pink fat elephant. Pink cute elephant. It doe not matter what is in between because ''.*`` is used.\\ + +You have already seen the power of the simple ``.*''. But what if you want to chose between only two (or more) latters? Well, this is when ``[|]'' comes in handy. If you write something like: ``^[a|k].*'' you are simply telling Open{CS} to filter anything that starts with either ``a'' or ``k''. Using ``^[a|k|l].*'' will work in the same manner, but it will also cover strings starting with ``l''.\\ + +And What if you want to match more than just one latter, just use ``(|)`` it is pretty similar to the above, but it is used to fit more than just one character. For instance: ''^(Pink|Green).* (elephant|crocodile).\$`` will be true for all sentences starting with ''Pink`` or ''Green`` and ending with either ''elephant.`` or ''crocodile.``.\\ + +Regular expressions are not the main topic of this manual. If you wish to learn more on this subject please, read the documentation on Qt regular expressions syntax, or TRE regexp syntax (it is almost like in Qt). Above is just enough to use Open{CS} effectively to be sure. \paragraph{Value -- value(``value'', (``open'', ``close''))} While string expression covers vast group of columns containing string values, there are in fact columns with just numerical values like ``weight``. To filter those we need a value expression. This one works in similar manner to the string filter: first token name and criteria inside brackets. Clearly, conditions should hold column to test in. However in this case wanted value is specified as a range.\\ From 078745c5d3a8f445127ec3dbc50020c3598251b0 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 13 Nov 2013 21:28:09 +0100 Subject: [PATCH 019/889] Added some shiny PR to the windows, honestly. It is just at the begining of the manual, so maybe it will raise morale of the user ;-) --- manual/opencs/filters.tex | 6 +++--- manual/opencs/windows.tex | 17 ++++++++++++++++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index 2a3d1a480..93c29c439 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -70,15 +70,15 @@ This is probably enough to create around 90\% string filters you will eventually \\ Creating regexps can be a difficult and annoying -- especially when you need complex criteria. On the other hand, We are under impression that in reality complex expressions are needed only in sporadic cases. In fact, the truth is: that most of the time only already mentioned ``.*'' is needed and therefore the following description of regexps can be skipped by vast majority of readers.\\ -Before working with Regular Expressions, you should understand what actually are regular expressions. Essentially, the idea is simple: when you are writing any word, you are using strictly defined latters -- that is: latters create a word. What you want to do with regular expression is to use set of rules that will match to many words. It is not that difficult to see what it's needed to do so: first, you will clearly need way to determinate what latters you want to match (word is composed by latters).\\ +Before working with Regular Expressions, you should understand what actually are regular expressions. Essentially, the idea is simple: when you are writing any word, you are using strictly defined letters -- that is: letters create a word. What you want to do with regular expression is to use set of rules that will match to many words. It is not that difficult to see what it's needed to do so: first, you will clearly need way to determinate what letters you want to match (word is composed by letters).\\ Before introducing other ways to choose between characters, I want explain anchors. Anchors allows you to decide where to ``look'' in the string. You surely should know about ``^'' anchor and ``\$''. Putting ``^`` will tell to Open{CS} to look on the beginning of string, while ''\$`` is used to mark the end of it. For instance, pattern ''^Pink.* elephant.\$`` Will match any sentence Beginning with the word ''Pink`` and ending with '' elephant.``. Pink fat elephant. Pink cute elephant. It doe not matter what is in between because ''.*`` is used.\\ -You have already seen the power of the simple ``.*''. But what if you want to chose between only two (or more) latters? Well, this is when ``[|]'' comes in handy. If you write something like: ``^[a|k].*'' you are simply telling Open{CS} to filter anything that starts with either ``a'' or ``k''. Using ``^[a|k|l].*'' will work in the same manner, but it will also cover strings starting with ``l''.\\ +You have already seen the power of the simple ``.*''. But what if you want to chose between only two (or more) letters? Well, this is when ``[|]'' comes in handy. If you write something like: ``^[a|k].*'' you are simply telling Open{CS} to filter anything that starts with either ``a'' or ``k''. Using ``^[a|k|l].*'' will work in the same manner, but it will also cover strings starting with ``l''.\\ And What if you want to match more than just one latter, just use ``(|)`` it is pretty similar to the above, but it is used to fit more than just one character. For instance: ''^(Pink|Green).* (elephant|crocodile).\$`` will be true for all sentences starting with ''Pink`` or ''Green`` and ending with either ''elephant.`` or ''crocodile.``.\\ -Regular expressions are not the main topic of this manual. If you wish to learn more on this subject please, read the documentation on Qt regular expressions syntax, or TRE regexp syntax (it is almost like in Qt). Above is just enough to use Open{CS} effectively to be sure. +Regular expressions are not the main topic of this manual. If you wish to learn more on this subject please, read the documentation on Qt regular expressions syntax, or TRE regexp syntax (it is almost like in Qt). Above is just enough to use Open{CS} effectively to be sure.\\ \paragraph{Value -- value(``value'', (``open'', ``close''))} While string expression covers vast group of columns containing string values, there are in fact columns with just numerical values like ``weight``. To filter those we need a value expression. This one works in similar manner to the string filter: first token name and criteria inside brackets. Clearly, conditions should hold column to test in. However in this case wanted value is specified as a range.\\ diff --git a/manual/opencs/windows.tex b/manual/opencs/windows.tex index ad1d2f951..3efe77f49 100644 --- a/manual/opencs/windows.tex +++ b/manual/opencs/windows.tex @@ -9,4 +9,19 @@ After starting Open{CS} and choosing content files to use a editor window should You probably founded out the way to enable and disable some interesting tables, but those will be described later. For now, let's just focus on the windows itself. \paragraph{Creating new windows} -is easy! Just visit view menu, and use the ``New View'' item. Suddenly, out of the blue a new window will show up. As you would expect, it is also blank, and you are free to add any of the Open{CS} widgets. \ No newline at end of file +is easy! Just visit view menu, and use the ``New View'' item. Suddenly, out of the blue a new window will show up. As you would expect, it is also blank, and you are free to add any of the Open{CS} widgets. + +\paragraph{Closing opened window} +is also easy! Simply close that window decoration button. We suspect that you knew that already, but better to be sure. Closing last Open{CS} window will also terminate application session. + +\paragraph{Multi-everything} +is the main foundation of Open{CS} interface. You are free to create as many windows as you want to, free to populate it with any widgets you may want to, and move everything as you wish to -- even if it makes no sense at all. If you just got crazy idea and you are wonder if you are able to have one hundred Open{CS} windows showing widget of the same type, well most likely you are able to do so.\\ + +The principle behind this design decision is easy to see for Bethesda made editor, but maybe not so clear for users who are just about to begin their wonderful journey of modding.\\ + +\subsection{Advanced} +So why? Why this is created in such manner. The answer is frankly simple: because it is effective. When creating a mod, you often have to work only with just one table. For instance you are just balancing weapons damage and other statistics. It makes sense to have all the space for just that one table. More often, you are required to work with two and switch them from time to time. All major graphical environments commonly present in operating systems comes with switcher feature, that is a key shortcut to change active window. It is very effective and fast when you have only two windows, each holding only one table. Sometimes you have to work with two at the time, and with one from time to time. Here, you can have one window holding two table widgets, and second holding just one.\\ + +Open{CS} is designed to simply make sense and do not slowdown users. It is as simple as possible (but not simpler), and uses one flexible approach in all cases.\\ + +There is no point in digging deeper in the windows of Open{CS}. Let's explore widgets, starting with tables. \ No newline at end of file From 6197ebd35f8e46fb51b7a699216bdb45e90f9852 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 13 Nov 2013 21:37:27 +0100 Subject: [PATCH 020/889] Actually builds now. --- manual/opencs/filters.tex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index 93c29c439..f13f51c8a 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -72,11 +72,11 @@ Creating regexps can be a difficult and annoying -- especially when you need com Before working with Regular Expressions, you should understand what actually are regular expressions. Essentially, the idea is simple: when you are writing any word, you are using strictly defined letters -- that is: letters create a word. What you want to do with regular expression is to use set of rules that will match to many words. It is not that difficult to see what it's needed to do so: first, you will clearly need way to determinate what letters you want to match (word is composed by letters).\\ -Before introducing other ways to choose between characters, I want explain anchors. Anchors allows you to decide where to ``look'' in the string. You surely should know about ``^'' anchor and ``\$''. Putting ``^`` will tell to Open{CS} to look on the beginning of string, while ''\$`` is used to mark the end of it. For instance, pattern ''^Pink.* elephant.\$`` Will match any sentence Beginning with the word ''Pink`` and ending with '' elephant.``. Pink fat elephant. Pink cute elephant. It doe not matter what is in between because ''.*`` is used.\\ +Before introducing other ways to choose between characters, I want explain anchors. Anchors allows you to decide where to ``look'' in the string. You surely should know about ``\^'' anchor and ``\textdollar''. Putting ``\^`` will tell to Open{CS} to look on the beginning of string, while ''\textdollar`` is used to mark the end of it. For instance, pattern ''\^Pink.* elephant.\textdollar`` Will match any sentence Beginning with the word ''Pink`` and ending with '' elephant.``. Pink fat elephant. Pink cute elephant. It doe not matter what is in between because ''.*`` is used.\\ -You have already seen the power of the simple ``.*''. But what if you want to chose between only two (or more) letters? Well, this is when ``[|]'' comes in handy. If you write something like: ``^[a|k].*'' you are simply telling Open{CS} to filter anything that starts with either ``a'' or ``k''. Using ``^[a|k|l].*'' will work in the same manner, but it will also cover strings starting with ``l''.\\ +You have already seen the power of the simple ``.*''. But what if you want to chose between only two (or more) letters? Well, this is when ``[|]'' comes in handy. If you write something like: ``\^[a|k].*'' you are simply telling Open{CS} to filter anything that starts with either ``a'' or ``k''. Using ``\^[a|k|l].*'' will work in the same manner, but it will also cover strings starting with ``l''.\\ -And What if you want to match more than just one latter, just use ``(|)`` it is pretty similar to the above, but it is used to fit more than just one character. For instance: ''^(Pink|Green).* (elephant|crocodile).\$`` will be true for all sentences starting with ''Pink`` or ''Green`` and ending with either ''elephant.`` or ''crocodile.``.\\ +And What if you want to match more than just one latter, just use ``(|)`` it is pretty similar to the above, but it is used to fit more than just one character. For instance: ''\^(Pink|Green).* (elephant|crocodile).\textdollar`` will be true for all sentences starting with ''Pink`` or ''Green`` and ending with either ''elephant.`` or ''crocodile.``.\\ Regular expressions are not the main topic of this manual. If you wish to learn more on this subject please, read the documentation on Qt regular expressions syntax, or TRE regexp syntax (it is almost like in Qt). Above is just enough to use Open{CS} effectively to be sure.\\ From 28a98df3aa2390fedcb1d9a6027a9c6b7ec49942 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 13 Nov 2013 21:49:29 +0100 Subject: [PATCH 021/889] Some corrections. --- manual/opencs/filters.tex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index f13f51c8a..0499fb7f9 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -1,4 +1,4 @@ -\section{Filters} +\section{Record filters} \subsection{Introduction} Filters are the key element of OpenCS use cases by allowing rapid and easy access to the searched records presented in all tables. Therefore: in order to use this application fully effective you should make sure that all concepts and instructions written in the this section of the manual are perfectly clear to you.\\ Don't be afraid though, filters are fairly intuitive and easy to use. @@ -10,7 +10,7 @@ Don't be afraid though, filters are fairly intuitive and easy to use. \item[Criteria] describes condition under with any any record is being select by the filter. \item[Syntax] as you may noticed computers (in general) are rather strict, and expect only strictly formulated orders -- that is: written with correct syntax. \item[Expression] is way we are actually performing filtering. Filter can be treated as ``functions'': accepts arguments, and evaluates either to the true or false for every column record at the time. - \item[N-ary] is any expression that expects two or more expressions as arguments. It is useful for grouping two (or more) other expressions together in order to create filter that will check for criteria placed in two (again: or more) columns (logical ``or'', ``and''). + \item[N-ary] is any expression that expects one or more expressions as arguments. It is useful for grouping two (or more) other expressions together in order to create filter that will check for criteria placed in two (again: or more) columns (logical ``or'', ``and''). \item[unary] is any expression that expects one other expression. The example is ``not'' expression. In fact ``not'' is the only useful unary expression in OpenCS record filters. \item[nullary] is expression that does not accepts other expressions. It accepts arguments specified later. \end{description} From 17d41ff032d1f3a70048cead94ef3a3a7699211c Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 14 Nov 2013 08:22:52 +0100 Subject: [PATCH 022/889] Corrected according to the zini suggestions. --- manual/opencs/filters.tex | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index 0499fb7f9..82834e02c 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -44,14 +44,14 @@ We strongly recommend to take a look at the filters table right now to see what \subsection{Advanced} Back to the manual? Great.\\ If you want to create your own filter you have to know exactly what do you want to get in order to translate this into the expressions. Finally, you will have to write this with correct syntax. As a result table will show only desired rows.\\ -Advance subsection covers everything that you need to know in order to create any filter you may want to. +Advance subsection covers everything that you need to know in order to create any filter you may want to %TODO the filter part is actually wrong \subsubsection{Namespaces} -Did you noticed that every default filter has ``project::`` prefix? It is a ``namespace``, a term borrowed from the C++ language. In case of OpenCS namespace of the filter determinate if the filter will be stored along with your project file or if it will be forgotten as soon as OpenCS quits. +Did you noticed that every default filter has ``project::`` prefix? It is a ``namespace``, a term borrowed from the C++ language. In case of OpenCS namespace always means scope of the said object\footnote{You are not supposed to understand this at the moment.}. But what does it mean in case of filters? Well, short explanation is actually simple. \begin{description} - \item[project::] namespace indicates that filter is stored inside the project file. - \item[session::] namespace indicates that filter is not stored inside the project file, and once you will quit OpenCS (close session) the filter will be gone. Forever! Until then it can be found inside the filters table. + \item[project::] namespace indicates that filter is used with the project, in multiple sessions. You can restart Open{CS} and filter is still there. + \item[session::] namespace indicates that filter is not stored trough multiple sessions and once you will quit Open{CS} (close session) the filter will be gone. Forever! Until then it can be found inside the filters table. \end{description} -In addition to this two scopes, there is a third one; called one-shot. One-shot filters are not stored anywhere and as the name implies they are supposed to be created when needed only once. Good thing about the one-shot filters is that you don't need to open filters table in order to create it. Instead you just type it directly inside the filter field, starting with ``!''.\\ +In addition to this two scopes, there is a third one; called one-shot. One-shot filters are not stored (even during single session) anywhere and as the name implies they are supposed to be created when needed only once. Good thing about the one-shot filters is that you don't need to open filters table in order to create it. Instead you just type it directly inside the filter field, starting with ``!''.\\ Still, you may wonder how you are supposed to write expressions, what expressions you should use, and what syntax looks like. Let's start with nullary expressions that will allow you to create a basic filter. \subsubsection{Nullary expressions} @@ -89,6 +89,9 @@ As you would imagine the range can be specified as including a border value, or \item Mixing brackets is completely legal. For value equal 10, expression value(something, [5, 10) will evaluate to true. The same expression will evaluate to false for value equal 10. \end{itemize} +\paragraph{''true`` and ''false``} +Nullary ''true`` and ''false`` do not accept any arguments, and always evaluates to true (in case of ''true``) and false (in case of ''false``) no matter what. The main usage of this expressions is the give users ability to quickly disable some part of the filter that makes heavy use of the logical expressions. + \subsubsection{Logical expressions} This subsection takes care of two remaining groups of expressions: binary and unary. The only unary expression present in the OpenCS is logical not, while the remaining binary expressions are: or, and. This clearly makes them (from the user point of view) belonging to the same group of logical expressions. From 99f72d4b612e40d5eea539b1fc3a8c94ec5cfd3d Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 14 Nov 2013 08:35:03 +0100 Subject: [PATCH 023/889] =?UTF-8?q?Filters:=20typos.=20Windows:=20widgets?= =?UTF-8?q?=E2=86=92name=20panels.=20I=20have=20no=20idea=20why=20we=20are?= =?UTF-8?q?=20inventin=20our=20own=20terms=20here.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manual/opencs/filters.tex | 4 ++-- manual/opencs/windows.tex | 12 +++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index 82834e02c..cf0fc7202 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -72,11 +72,11 @@ Creating regexps can be a difficult and annoying -- especially when you need com Before working with Regular Expressions, you should understand what actually are regular expressions. Essentially, the idea is simple: when you are writing any word, you are using strictly defined letters -- that is: letters create a word. What you want to do with regular expression is to use set of rules that will match to many words. It is not that difficult to see what it's needed to do so: first, you will clearly need way to determinate what letters you want to match (word is composed by letters).\\ -Before introducing other ways to choose between characters, I want explain anchors. Anchors allows you to decide where to ``look'' in the string. You surely should know about ``\^'' anchor and ``\textdollar''. Putting ``\^`` will tell to Open{CS} to look on the beginning of string, while ''\textdollar`` is used to mark the end of it. For instance, pattern ''\^Pink.* elephant.\textdollar`` Will match any sentence Beginning with the word ''Pink`` and ending with '' elephant.``. Pink fat elephant. Pink cute elephant. It doe not matter what is in between because ''.*`` is used.\\ +Before introducing other ways to choose between characters, I want explain anchors. Anchors allows you to decide where to ``look'' in the string. You surely should know about ``\^'' anchor and ``\textdollar''. Putting ``\^`` will tell to Open{CS} to look on the beginning of string, while ''\textdollar`` is used to mark the end of it. For instance, pattern ''\^Pink.* elephant.\textdollar`` Will match any sentence Beginning with the word ''Pink`` and ending with '' elephant.``. Pink fat elephant. Pink cute elephant. It does not matter what is in between, because ''.*`` is used.\\ You have already seen the power of the simple ``.*''. But what if you want to chose between only two (or more) letters? Well, this is when ``[|]'' comes in handy. If you write something like: ``\^[a|k].*'' you are simply telling Open{CS} to filter anything that starts with either ``a'' or ``k''. Using ``\^[a|k|l].*'' will work in the same manner, but it will also cover strings starting with ``l''.\\ -And What if you want to match more than just one latter, just use ``(|)`` it is pretty similar to the above, but it is used to fit more than just one character. For instance: ''\^(Pink|Green).* (elephant|crocodile).\textdollar`` will be true for all sentences starting with ''Pink`` or ''Green`` and ending with either ''elephant.`` or ''crocodile.``.\\ +And What if you want to match more than just one latter? Just use ``(|)``. it is pretty similar to the above one letter as you see, but it is used to fit more than just one character. For instance: ''\^(Pink|Green).* (elephant|crocodile).\textdollar`` will be true for all sentences starting with ''Pink`` or ''Green`` and ending with either ''elephant.`` or ''crocodile.``.\\ Regular expressions are not the main topic of this manual. If you wish to learn more on this subject please, read the documentation on Qt regular expressions syntax, or TRE regexp syntax (it is almost like in Qt). Above is just enough to use Open{CS} effectively to be sure.\\ diff --git a/manual/opencs/windows.tex b/manual/opencs/windows.tex index 3efe77f49..b5b34e788 100644 --- a/manual/opencs/windows.tex +++ b/manual/opencs/windows.tex @@ -5,23 +5,25 @@ OpenCS windows interface is easy to describe and understand. In fact We decided Because of this, and the fact that we expect that user is familiar with other applications using windows this section is mostly focused on practical ways of organizing work with the OpenCS. \subsection{Basics} -After starting Open{CS} and choosing content files to use a editor window should show up. It probably does not look surprising: there is a menubar at the top, and there is a large empty area. That's it: a brand new Open{CS} window contains only menubar and statusbar. In order to make it a little bit more useful you probably want to enable some window widgets. You are free to do so, just try to explore the menubar. \\ +After starting Open{CS} and choosing content files to use a editor window should show up. It probably does not look surprising: there is a menubar at the top, and there is a large empty area. That's it: a brand new Open{CS} window contains only menubar and statusbar. In order to make it a little bit more useful you probably want to enable some name panels\footnote{Also known as widgets.}. You are free to do so, just try to explore the menubar. \\ You probably founded out the way to enable and disable some interesting tables, but those will be described later. For now, let's just focus on the windows itself. \paragraph{Creating new windows} -is easy! Just visit view menu, and use the ``New View'' item. Suddenly, out of the blue a new window will show up. As you would expect, it is also blank, and you are free to add any of the Open{CS} widgets. +is easy! Just visit view menu, and use the ``New View'' item. Suddenly, out of the blue a new window will show up. As you would expect, it is also blank, and you are free to add any of the Open{CS} name panels. \paragraph{Closing opened window} is also easy! Simply close that window decoration button. We suspect that you knew that already, but better to be sure. Closing last Open{CS} window will also terminate application session. \paragraph{Multi-everything} -is the main foundation of Open{CS} interface. You are free to create as many windows as you want to, free to populate it with any widgets you may want to, and move everything as you wish to -- even if it makes no sense at all. If you just got crazy idea and you are wonder if you are able to have one hundred Open{CS} windows showing widget of the same type, well most likely you are able to do so.\\ +is the main foundation of Open{CS} interface. You are free to create as many windows as you want to, free to populate it with any name panels you may want to, and move everything as you wish to -- even if it makes no sense at all. If you just got crazy idea and you are wonder if you are able to have one hundred Open{CS} windows showing name panels of the same type, well most likely you are able to do so.\\ The principle behind this design decision is easy to see for Bethesda made editor, but maybe not so clear for users who are just about to begin their wonderful journey of modding.\\ \subsection{Advanced} -So why? Why this is created in such manner. The answer is frankly simple: because it is effective. When creating a mod, you often have to work only with just one table. For instance you are just balancing weapons damage and other statistics. It makes sense to have all the space for just that one table. More often, you are required to work with two and switch them from time to time. All major graphical environments commonly present in operating systems comes with switcher feature, that is a key shortcut to change active window. It is very effective and fast when you have only two windows, each holding only one table. Sometimes you have to work with two at the time, and with one from time to time. Here, you can have one window holding two table widgets, and second holding just one.\\ +So why? Why this is created in such manner. The answer is frankly simple: because it is effective. When creating a mod, you often have to work only with just one table. For instance you are just balancing weapons damage and other statistics. It makes sense to have all the space for just that one table. More often, you are required to work with two and switch them from time to time. All major graphical environments commonly present in operating systems comes with switcher feature, that is a key shortcut to change active window. It is very effective and fast when you have only two windows, each holding only one table. Sometimes you have to work with two at the time, and with one from time to time. Here, you can have one window holding two tables, and second holding just one.\\ Open{CS} is designed to simply make sense and do not slowdown users. It is as simple as possible (but not simpler), and uses one flexible approach in all cases.\\ -There is no point in digging deeper in the windows of Open{CS}. Let's explore widgets, starting with tables. \ No newline at end of file +There is no point in digging deeper in the windows of Open{CS}. Let's explore name panels, starting with tables. + +%We should write some tips and tricks here. \ No newline at end of file From dcfff7d457d913b4f694d983e06e0ee7be6a1907 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 14 Nov 2013 12:32:58 +0100 Subject: [PATCH 024/889] Correcting some epic fail. --- manual/opencs/windows.tex | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/manual/opencs/windows.tex b/manual/opencs/windows.tex index b5b34e788..3bdfa6362 100644 --- a/manual/opencs/windows.tex +++ b/manual/opencs/windows.tex @@ -5,17 +5,17 @@ OpenCS windows interface is easy to describe and understand. In fact We decided Because of this, and the fact that we expect that user is familiar with other applications using windows this section is mostly focused on practical ways of organizing work with the OpenCS. \subsection{Basics} -After starting Open{CS} and choosing content files to use a editor window should show up. It probably does not look surprising: there is a menubar at the top, and there is a large empty area. That's it: a brand new Open{CS} window contains only menubar and statusbar. In order to make it a little bit more useful you probably want to enable some name panels\footnote{Also known as widgets.}. You are free to do so, just try to explore the menubar. \\ +After starting Open{CS} and choosing content files to use a editor window should show up. It probably does not look surprising: there is a menubar at the top, and there is a large empty area. That's it: a brand new Open{CS} window contains only menubar and statusbar. In order to make it a little bit more useful you probably want to enable some panels\footnote{Also known as widgets.}. You are free to do so, just try to explore the menubar. \\ You probably founded out the way to enable and disable some interesting tables, but those will be described later. For now, let's just focus on the windows itself. \paragraph{Creating new windows} -is easy! Just visit view menu, and use the ``New View'' item. Suddenly, out of the blue a new window will show up. As you would expect, it is also blank, and you are free to add any of the Open{CS} name panels. +is easy! Just visit view menu, and use the ``New View'' item. Suddenly, out of the blue a new window will show up. As you would expect, it is also blank, and you are free to add any of the Open{CS} panels. \paragraph{Closing opened window} is also easy! Simply close that window decoration button. We suspect that you knew that already, but better to be sure. Closing last Open{CS} window will also terminate application session. \paragraph{Multi-everything} -is the main foundation of Open{CS} interface. You are free to create as many windows as you want to, free to populate it with any name panels you may want to, and move everything as you wish to -- even if it makes no sense at all. If you just got crazy idea and you are wonder if you are able to have one hundred Open{CS} windows showing name panels of the same type, well most likely you are able to do so.\\ +is the main foundation of Open{CS} interface. You are free to create as many windows as you want to, free to populate it with any panels you may want to, and move everything as you wish to -- even if it makes no sense at all. If you just got crazy idea and you are wonder if you are able to have one hundred Open{CS} windows showing panels of the same type, well most likely you are able to do so.\\ The principle behind this design decision is easy to see for Bethesda made editor, but maybe not so clear for users who are just about to begin their wonderful journey of modding.\\ @@ -24,6 +24,6 @@ So why? Why this is created in such manner. The answer is frankly simple: becaus Open{CS} is designed to simply make sense and do not slowdown users. It is as simple as possible (but not simpler), and uses one flexible approach in all cases.\\ -There is no point in digging deeper in the windows of Open{CS}. Let's explore name panels, starting with tables. +There is no point in digging deeper in the windows of Open{CS}. Let's explore panels, starting with tables. %We should write some tips and tricks here. \ No newline at end of file From eddd6bf48dca2986c592342dfefa8b7627d0a998 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 15 Nov 2013 10:29:53 +0100 Subject: [PATCH 025/889] enabled load and save items in main menu --- apps/openmw/mwgui/mainmenu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index fa7ed2ace..95753f540 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -35,8 +35,8 @@ namespace MWGui std::vector buttons; buttons.push_back("return"); buttons.push_back("newgame"); - //buttons.push_back("loadgame"); - //buttons.push_back("savegame"); + buttons.push_back("loadgame"); + buttons.push_back("savegame"); buttons.push_back("options"); //buttons.push_back("credits"); buttons.push_back("exitgame"); From 79b7fa258bfdb8b3a955fd9052e645d6fb49d819 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 16 Nov 2013 10:31:46 +0100 Subject: [PATCH 026/889] added new mwstate subsystem --- apps/openmw/CMakeLists.txt | 4 ++++ apps/openmw/engine.cpp | 9 ++++++--- apps/openmw/mwbase/environment.cpp | 18 +++++++++++++++++- apps/openmw/mwbase/environment.hpp | 6 ++++++ apps/openmw/mwbase/statemanager.hpp | 25 +++++++++++++++++++++++++ apps/openmw/mwstate/statemanagerimp.cpp | 7 +++++++ apps/openmw/mwstate/statemanagerimp.hpp | 17 +++++++++++++++++ 7 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 apps/openmw/mwbase/statemanager.hpp create mode 100644 apps/openmw/mwstate/statemanagerimp.cpp create mode 100644 apps/openmw/mwstate/statemanagerimp.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 04bd89f95..0358b96d1 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -72,6 +72,10 @@ add_openmw_dir (mwmechanics aiescort aiactivate repair enchanting pathfinding security spellsuccess ) +add_openmw_dir (mwstate + statemanagerimp + ) + add_openmw_dir (mwbase environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager inputmanager windowmanager diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 4a3c418f6..f900e6cb4 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -7,6 +7,8 @@ #include +#include + #include #include @@ -39,8 +41,7 @@ #include "mwmechanics/mechanicsmanagerimp.hpp" - -#include +#include "mwstate/statemanagerimp.hpp" void OMW::Engine::executeLocalScripts() { @@ -320,6 +321,8 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings) void OMW::Engine::prepareEngine (Settings::Manager & settings) { + mEnvironment.setStateManager (new MWState::StateManager); + Nif::NIFFile::CacheLock cachelock; std::string renderSystem = settings.getString("render system", "Video"); @@ -397,7 +400,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) for (size_t i = 0; i < mContentFiles.size(); i++) mTranslationDataStorage.loadTranslationData(mFileCollections, mContentFiles[i]); - Compiler::registerExtensions (mExtensions); + Compiler::registerExtensions (mExtensions); // Create sound system mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound)); diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 6b309025c..4a629743f 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -11,13 +11,15 @@ #include "mechanicsmanager.hpp" #include "inputmanager.hpp" #include "windowmanager.hpp" +#include "statemanager.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; bool MWBase::Environment::sExit = false; MWBase::Environment::Environment() : mWorld (0), mSoundManager (0), mScriptManager (0), mWindowManager (0), - mMechanicsManager (0), mDialogueManager (0), mJournal (0), mInputManager (0), mFrameDuration (0) + mMechanicsManager (0), mDialogueManager (0), mJournal (0), mInputManager (0), mFrameDuration (0), + mStateManager (0) { assert (!sThis); sThis = this; @@ -69,6 +71,11 @@ void MWBase::Environment::setInputManager (InputManager *inputManager) mInputManager = inputManager; } +void MWBase::Environment::setStateManager (StateManager *stateManager) +{ + mStateManager = stateManager; +} + void MWBase::Environment::setFrameDuration (float duration) { mFrameDuration = duration; @@ -122,6 +129,12 @@ MWBase::InputManager *MWBase::Environment::getInputManager() const return mInputManager; } +MWBase::StateManager *MWBase::Environment::getStateManager() const +{ + assert (mStateManager); + return mStateManager; +} + float MWBase::Environment::getFrameDuration() const { return mFrameDuration; @@ -152,6 +165,9 @@ void MWBase::Environment::cleanup() delete mInputManager; mInputManager = 0; + + delete mStateManager; + mStateManager = 0; } const MWBase::Environment& MWBase::Environment::get() diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index 466302907..d7c63601f 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -11,6 +11,7 @@ namespace MWBase class MechanicsManager; class InputManager; class WindowManager; + class StateManager; /// \brief Central hub for mw-subsystems /// @@ -30,6 +31,7 @@ namespace MWBase DialogueManager *mDialogueManager; Journal *mJournal; InputManager *mInputManager; + StateManager *mStateManager; float mFrameDuration; static bool sExit; @@ -65,6 +67,8 @@ namespace MWBase void setInputManager (InputManager *inputManager); + void setStateManager (StateManager *stateManager); + void setFrameDuration (float duration); ///< Set length of current frame in seconds. @@ -84,6 +88,8 @@ namespace MWBase InputManager *getInputManager() const; + StateManager *getStateManager() const; + float getFrameDuration() const; void cleanup(); diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp new file mode 100644 index 000000000..42b1dcc81 --- /dev/null +++ b/apps/openmw/mwbase/statemanager.hpp @@ -0,0 +1,25 @@ +#ifndef GAME_MWSTATE_STATEMANAGER_H +#define GAME_MWSTATE_STATEMANAGER_H + +namespace MWBase +{ + /// \brief Interface for game state manager (implemented in MWState) + class StateManager + { + private: + + StateManager (const StateManager&); + ///< not implemented + + StateManager& operator= (const StateManager&); + ///< not implemented + + public: + + StateManager() {} + + virtual ~StateManager() {} + }; +} + +#endif diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp new file mode 100644 index 000000000..fa8289419 --- /dev/null +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -0,0 +1,7 @@ + +#include "statemanagerimp.hpp" + +MWState::StateManager::StateManager() +{ + +} \ No newline at end of file diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp new file mode 100644 index 000000000..73cb0a86f --- /dev/null +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -0,0 +1,17 @@ +#ifndef GAME_STATE_STATEMANAGER_H +#define GAME_STATE_STATEMANAGER_H + +#include "../mwbase/statemanager.hpp" + +namespace MWState +{ + class StateManager : public MWBase::StateManager + { + public: + + StateManager(); + + }; +} + +#endif From f19973450f4ed6fddece051a11522d8ac1f7bad4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 16 Nov 2013 11:07:23 +0100 Subject: [PATCH 027/889] moved exit game flag from Environment to StateManager --- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/environment.cpp | 1 - apps/openmw/mwbase/environment.hpp | 5 ----- apps/openmw/mwbase/statemanager.hpp | 4 ++++ apps/openmw/mwgui/mainmenu.cpp | 5 ++--- apps/openmw/mwinput/inputmanagerimp.cpp | 3 ++- apps/openmw/mwstate/statemanagerimp.cpp | 11 +++++++++++ apps/openmw/mwstate/statemanagerimp.hpp | 5 +++++ 8 files changed, 25 insertions(+), 11 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index f900e6cb4..d11e72f75 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -487,7 +487,7 @@ void OMW::Engine::go() MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript); // Start the main rendering loop - while (!mEnvironment.getRequestExit()) + while (!mEnvironment.get().getStateManager()->hasQuitRequest()) Ogre::Root::getSingleton().renderOneFrame(); // Save user settings diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 4a629743f..052bba9ab 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -14,7 +14,6 @@ #include "statemanager.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; -bool MWBase::Environment::sExit = false; MWBase::Environment::Environment() : mWorld (0), mSoundManager (0), mScriptManager (0), mWindowManager (0), diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index d7c63601f..eb636ea2f 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -34,8 +34,6 @@ namespace MWBase StateManager *mStateManager; float mFrameDuration; - static bool sExit; - Environment (const Environment&); ///< not implemented @@ -48,9 +46,6 @@ namespace MWBase ~Environment(); - static void setRequestExit () { sExit = true; } - static bool getRequestExit () { return sExit; } - void setWorld (World *world); void setSoundManager (SoundManager *soundManager); diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index 42b1dcc81..e90687293 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -19,6 +19,10 @@ namespace MWBase StateManager() {} virtual ~StateManager() {} + + virtual void requestQuit() = 0; + + virtual bool hasQuitRequest() const = 0; }; } diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 95753f540..a58a3d0eb 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -1,13 +1,12 @@ #include "mainmenu.hpp" -#include - #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" #include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/statemanager.hpp" #include "savegamedialog.hpp" @@ -79,7 +78,7 @@ namespace MWGui else if (sender == mButtons["options"]) MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings); else if (sender == mButtons["exitgame"]) - MWBase::Environment::get().setRequestExit(); + MWBase::Environment::get().getStateManager()->requestQuit(); else if (sender == mButtons["newgame"]) { MWBase::Environment::get().getWorld()->startNewGame(); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 35487e339..c25e9ce52 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -19,6 +19,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/statemanager.hpp" #include "../mwgui/bookwindow.hpp" #include "../mwmechanics/creaturestats.hpp" @@ -644,7 +645,7 @@ namespace MWInput void InputManager::windowClosed() { - MWBase::Environment::setRequestExit(); + MWBase::Environment::get().getStateManager()->requestQuit(); } void InputManager::toggleMainMenu() diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index fa8289419..f5e71d0ce 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -2,6 +2,17 @@ #include "statemanagerimp.hpp" MWState::StateManager::StateManager() +: mQuitRequest (false) { +} + +void MWState::StateManager::requestQuit() +{ + mQuitRequest = true; +} + +bool MWState::StateManager::hasQuitRequest() const +{ + return mQuitRequest; } \ No newline at end of file diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 73cb0a86f..271403ce5 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -7,10 +7,15 @@ namespace MWState { class StateManager : public MWBase::StateManager { + bool mQuitRequest; + public: StateManager(); + virtual void requestQuit(); + + virtual bool hasQuitRequest() const; }; } From 7a4b6043763598b8dc25d7b3e33555f792c6dc9e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 16 Nov 2013 11:33:20 +0100 Subject: [PATCH 028/889] added --skip-menu switch --- apps/openmw/engine.cpp | 10 ++++++++++ apps/openmw/engine.hpp | 3 +++ apps/openmw/main.cpp | 4 ++++ 3 files changed, 17 insertions(+) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index d11e72f75..47cd01a80 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -139,6 +139,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) , mFpsLevel(0) , mVerboseScripts (false) , mNewGame (false) + , mSkipMenu (false) , mUseSound (true) , mCompileAll (false) , mScriptContext (0) @@ -282,6 +283,11 @@ void OMW::Engine::setNewGame(bool newGame) mNewGame = newGame; } +void OMW::Engine::setSkipMenu (bool skipMenu) +{ + mSkipMenu = skipMenu; +} + std::string OMW::Engine::loadSettings (Settings::Manager & settings) { // Create the settings manager and load default settings file @@ -486,6 +492,10 @@ void OMW::Engine::go() if (!mStartupScript.empty()) MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript); + // start in main menu + if (!mSkipMenu) + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + // Start the main rendering loop while (!mEnvironment.get().getStateManager()->hasQuitRequest()) Ogre::Root::getSingleton().renderOneFrame(); diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 553d29068..72d2041b8 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -72,6 +72,7 @@ namespace OMW int mFpsLevel; bool mVerboseScripts; bool mNewGame; + bool mSkipMenu; bool mUseSound; bool mCompileAll; std::string mFocusName; @@ -152,6 +153,8 @@ namespace OMW /// Start as a new game. void setNewGame(bool newGame); + void setSkipMenu (bool skipMenu); + /// Initialise and enter main loop. void go(); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 33f740b31..8c6bdf8a6 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -134,6 +134,9 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("new-game", bpo::value()->implicit_value(true) ->default_value(false), "activate char gen/new game mechanics") + ("skip-menu", bpo::value()->implicit_value(true) + ->default_value(false), "skip main menu on game startup") + ("fs-strict", bpo::value()->implicit_value(true) ->default_value(false), "strict file system handling (no case folding)") @@ -223,6 +226,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat // startup-settings engine.setCell(variables["start"].as()); engine.setNewGame(variables["new-game"].as()); + engine.setSkipMenu (variables["skip-menu"].as()); // other settings engine.setSoundUsage(!variables["nosound"].as()); From b3a7c8c0980d23a3614f1b557d8a81241b1eff4a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 16 Nov 2013 11:36:32 +0100 Subject: [PATCH 029/889] removed --new-game switch --- apps/openmw/engine.cpp | 33 +++++++++------------------------ apps/openmw/engine.hpp | 4 ---- apps/openmw/main.cpp | 4 ---- 3 files changed, 9 insertions(+), 32 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 47cd01a80..8a02c855a 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -138,7 +138,6 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) : mOgre (0) , mFpsLevel(0) , mVerboseScripts (false) - , mNewGame (false) , mSkipMenu (false) , mUseSound (true) , mCompileAll (false) @@ -278,11 +277,6 @@ void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity) mVerboseScripts = scriptsVerbosity; } -void OMW::Engine::setNewGame(bool newGame) -{ - mNewGame = newGame; -} - void OMW::Engine::setSkipMenu (bool skipMenu) { mSkipMenu = skipMenu; @@ -395,10 +389,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) input->setPlayer(&mEnvironment.getWorld()->getPlayer()); window->initUI(); - if (mNewGame) - // still redundant work here: recreate CharacterCreation(), - // double update visibility etc. - window->setNewGame(true); window->renderWorldMap(); //Load translation data @@ -430,22 +420,17 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mechanics->buildPlayer(); window->updatePlayer(); - if (!mNewGame) - { - // load cell - ESM::Position pos; - MWBase::World *world = MWBase::Environment::get().getWorld(); + // load cell + ESM::Position pos; + MWBase::World *world = MWBase::Environment::get().getWorld(); - if (world->findExteriorPosition(mCellName, pos)) { - world->changeToExteriorCell (pos); - } - else { - world->findInteriorPosition(mCellName, pos); - world->changeToInteriorCell (mCellName, pos); - } + if (world->findExteriorPosition(mCellName, pos)) { + world->changeToExteriorCell (pos); + } + else { + world->findInteriorPosition(mCellName, pos); + world->changeToInteriorCell (mCellName, pos); } - else - mEnvironment.getWorld()->startNewGame(); Ogre::FrameEvent event; event.timeSinceLastEvent = 0; diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 72d2041b8..02fb73705 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -71,7 +71,6 @@ namespace OMW std::vector mContentFiles; int mFpsLevel; bool mVerboseScripts; - bool mNewGame; bool mSkipMenu; bool mUseSound; bool mCompileAll; @@ -150,9 +149,6 @@ namespace OMW /// Disable or enable all sounds void setSoundUsage(bool soundUsage); - /// Start as a new game. - void setNewGame(bool newGame); - void setSkipMenu (bool skipMenu); /// Initialise and enter main loop. diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 8c6bdf8a6..2bdfb91c3 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -131,9 +131,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("script-run", bpo::value()->default_value(""), "select a file containing a list of console commands that is executed on startup") - ("new-game", bpo::value()->implicit_value(true) - ->default_value(false), "activate char gen/new game mechanics") - ("skip-menu", bpo::value()->implicit_value(true) ->default_value(false), "skip main menu on game startup") @@ -225,7 +222,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat // startup-settings engine.setCell(variables["start"].as()); - engine.setNewGame(variables["new-game"].as()); engine.setSkipMenu (variables["skip-menu"].as()); // other settings From 31ec973c9c07ac54e5f57962d6597ddcee050e3f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 16 Nov 2013 12:08:00 +0100 Subject: [PATCH 030/889] removed default value for --start --- apps/openmw/engine.cpp | 20 ++++++++++++++------ apps/openmw/main.cpp | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 8a02c855a..cfb5522f1 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -424,12 +424,21 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) ESM::Position pos; MWBase::World *world = MWBase::Environment::get().getWorld(); - if (world->findExteriorPosition(mCellName, pos)) { - world->changeToExteriorCell (pos); + if (!mCellName.empty()) + { + if (world->findExteriorPosition(mCellName, pos)) { + world->changeToExteriorCell (pos); + } + else { + world->findInteriorPosition(mCellName, pos); + world->changeToInteriorCell (mCellName, pos); + } } - else { - world->findInteriorPosition(mCellName, pos); - world->changeToInteriorCell (mCellName, pos); + else + { + pos.pos[0] = pos.pos[1] = pos.pos[2] = 0; + pos.rot[0] = pos.rot[1] = pos.pos[2] = 0; + world->changeToExteriorCell (pos); } Ogre::FrameEvent event; @@ -456,7 +465,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) void OMW::Engine::go() { - assert (!mCellName.empty()); assert (!mContentFiles.empty()); assert (!mOgre); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 2bdfb91c3..89613fda4 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -107,7 +107,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("resources", bpo::value()->default_value("resources"), "set resources directory") - ("start", bpo::value()->default_value("Beshara"), + ("start", bpo::value()->default_value(""), "set initial cell") ("content", bpo::value()->default_value(StringsVector(), "") From ec5b2e9a7e90f3e9c3eb8ebb59f3d50995845dad Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 16 Nov 2013 12:22:28 +0100 Subject: [PATCH 031/889] added running flag; moved new game code to MWState --- apps/openmw/engine.cpp | 2 ++ apps/openmw/mwbase/statemanager.hpp | 7 +++++ apps/openmw/mwgui/mainmenu.cpp | 5 +--- apps/openmw/mwstate/statemanagerimp.cpp | 34 +++++++++++++++++++++++-- apps/openmw/mwstate/statemanagerimp.hpp | 8 ++++++ 5 files changed, 50 insertions(+), 6 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index cfb5522f1..c013cdaae 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -488,6 +488,8 @@ void OMW::Engine::go() // start in main menu if (!mSkipMenu) MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + else + MWBase::Environment::get().getStateManager()->newGame (true); // Start the main rendering loop while (!mEnvironment.get().getStateManager()->hasQuitRequest()) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index e90687293..076e66b8c 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -23,6 +23,13 @@ namespace MWBase virtual void requestQuit() = 0; virtual bool hasQuitRequest() const = 0; + + virtual bool isGameRunning() const = 0; + + virtual void newGame (bool bypass = false) = 0; + ///< Start a new game. + /// + /// \param bypass Skip new game mechanics. }; } diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index a58a3d0eb..cb3436715 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -81,10 +81,7 @@ namespace MWGui MWBase::Environment::get().getStateManager()->requestQuit(); else if (sender == mButtons["newgame"]) { - MWBase::Environment::get().getWorld()->startNewGame(); - MWBase::Environment::get().getWindowManager()->setNewGame(true); - MWBase::Environment::get().getDialogueManager()->clear(); - MWBase::Environment::get().getJournal()->clear(); + MWBase::Environment::get().getStateManager()->newGame(); } else if (sender == mButtons["loadgame"]) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index f5e71d0ce..a226b166d 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -1,8 +1,14 @@ #include "statemanagerimp.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/journal.hpp" +#include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/windowmanager.hpp" + MWState::StateManager::StateManager() -: mQuitRequest (false) +: mQuitRequest (false), mRunning (false) { } @@ -15,4 +21,28 @@ void MWState::StateManager::requestQuit() bool MWState::StateManager::hasQuitRequest() const { return mQuitRequest; -} \ No newline at end of file +} + +bool MWState::StateManager::isGameRunning() const +{ + return mRunning; +} + +void MWState::StateManager::newGame (bool bypass) +{ + if (mRunning) + { + MWBase::Environment::get().getDialogueManager()->clear(); + MWBase::Environment::get().getJournal()->clear(); + mRunning = false; + } + + if (!bypass) + { + /// \todo extract cleanup code + MWBase::Environment::get().getWorld()->startNewGame(); + MWBase::Environment::get().getWindowManager()->setNewGame (true); + } + + mRunning = true; +} diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 271403ce5..9f8096a4a 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -8,6 +8,7 @@ namespace MWState class StateManager : public MWBase::StateManager { bool mQuitRequest; + bool mRunning; public: @@ -16,6 +17,13 @@ namespace MWState virtual void requestQuit(); virtual bool hasQuitRequest() const; + + virtual bool isGameRunning() const; + + virtual void newGame (bool bypass = false); + ///< Start a new game. + /// + /// \param bypass Skip new game mechanics. }; } From c5f81e3508e37ac7055728cb2cb197a84ba1dd0c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 16 Nov 2013 17:46:48 +0100 Subject: [PATCH 032/889] don't run udpates if no game is running --- apps/openmw/engine.cpp | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index c013cdaae..741f7564f 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -92,30 +92,31 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) MWBase::Environment::get().getSoundManager()->update(frametime); // global scripts - MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); + if (MWBase::Environment::get().getStateManager()->isGameRunning()) + { + MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); - bool changed = MWBase::Environment::get().getWorld()->hasCellChanged(); + bool changed = MWBase::Environment::get().getWorld()->hasCellChanged(); - // local scripts - executeLocalScripts(); // This does not handle the case where a global script causes a cell - // change, followed by a cell change in a local script during the same - // frame. + // local scripts + executeLocalScripts(); // This does not handle the case where a global script causes a + // cell change, followed by a cell change in a local script during + // the same frame. - // passing of time - if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) - MWBase::Environment::get().getWorld()->advanceTime( - frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); + if (changed) // keep change flag for another frame, if cell changed happened in local script + MWBase::Environment::get().getWorld()->markCellAsUnchanged(); + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) + MWBase::Environment::get().getWorld()->advanceTime( + frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); - if (changed) // keep change flag for another frame, if cell changed happend in local script - MWBase::Environment::get().getWorld()->markCellAsUnchanged(); + // update actors + MWBase::Environment::get().getMechanicsManager()->update(frametime, + MWBase::Environment::get().getWindowManager()->isGuiMode()); - // update actors - MWBase::Environment::get().getMechanicsManager()->update(frametime, - MWBase::Environment::get().getWindowManager()->isGuiMode()); - - // update world - MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode()); + // update world + MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode()); + } // update GUI Ogre::RenderWindow* window = mOgre->getWindow(); From 82c84953383aad3dbf2f8466f93a594b87318495 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 18 Nov 2013 15:15:47 +0100 Subject: [PATCH 033/889] removed boolean running flag with state enum --- apps/openmw/engine.cpp | 3 ++- apps/openmw/mwbase/statemanager.hpp | 11 ++++++++++- apps/openmw/mwstate/statemanagerimp.cpp | 12 ++++++------ apps/openmw/mwstate/statemanagerimp.hpp | 4 ++-- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 741f7564f..4de198b64 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -92,7 +92,8 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) MWBase::Environment::get().getSoundManager()->update(frametime); // global scripts - if (MWBase::Environment::get().getStateManager()->isGameRunning()) + if (MWBase::Environment::get().getStateManager()->getState()== + MWBase::StateManager::State_Running) { MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index 076e66b8c..4fd1a297d 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -6,6 +6,15 @@ namespace MWBase /// \brief Interface for game state manager (implemented in MWState) class StateManager { + public: + + enum State + { + State_NoGame, + State_Ended, + State_Running + }; + private: StateManager (const StateManager&); @@ -24,7 +33,7 @@ namespace MWBase virtual bool hasQuitRequest() const = 0; - virtual bool isGameRunning() const = 0; + virtual State getState() const = 0; virtual void newGame (bool bypass = false) = 0; ///< Start a new game. diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index a226b166d..c4cd45c8e 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -8,7 +8,7 @@ #include "../mwbase/windowmanager.hpp" MWState::StateManager::StateManager() -: mQuitRequest (false), mRunning (false) +: mQuitRequest (false), mState (State_NoGame) { } @@ -23,18 +23,18 @@ bool MWState::StateManager::hasQuitRequest() const return mQuitRequest; } -bool MWState::StateManager::isGameRunning() const +MWState::StateManager::State MWState::StateManager::getState() const { - return mRunning; + return mState; } void MWState::StateManager::newGame (bool bypass) { - if (mRunning) + if (mState!=State_NoGame) { MWBase::Environment::get().getDialogueManager()->clear(); MWBase::Environment::get().getJournal()->clear(); - mRunning = false; + mState = State_NoGame; } if (!bypass) @@ -44,5 +44,5 @@ void MWState::StateManager::newGame (bool bypass) MWBase::Environment::get().getWindowManager()->setNewGame (true); } - mRunning = true; + mState = State_Running; } diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 9f8096a4a..078a899b2 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -8,7 +8,7 @@ namespace MWState class StateManager : public MWBase::StateManager { bool mQuitRequest; - bool mRunning; + State mState; public: @@ -18,7 +18,7 @@ namespace MWState virtual bool hasQuitRequest() const; - virtual bool isGameRunning() const; + virtual State getState() const; virtual void newGame (bool bypass = false); ///< Start a new game. From f45cff8aff4d8bf29fd0a3200f78bb565cda0cb5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 18 Nov 2013 15:38:08 +0100 Subject: [PATCH 034/889] flag game as ended when player dies --- apps/openmw/mwbase/statemanager.hpp | 2 ++ apps/openmw/mwmechanics/actors.cpp | 25 +++++++++++++++---------- apps/openmw/mwstate/statemanagerimp.cpp | 5 +++++ apps/openmw/mwstate/statemanagerimp.hpp | 2 ++ 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index 4fd1a297d..74fcc3f7a 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -39,6 +39,8 @@ namespace MWBase ///< Start a new game. /// /// \param bypass Skip new game mechanics. + + virtual void endGame() = 0; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 3d52ce8e6..cc431a7b0 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -17,6 +17,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" + #include "../mwbase/statemanager.hpp" #include "npcstats.hpp" #include "creaturestats.hpp" @@ -344,20 +345,24 @@ namespace MWMechanics continue; } - // If it's the player and God Mode is turned on, keep it alive - if(iter->first.getRefData().getHandle()=="player" && - MWBase::Environment::get().getWorld()->getGodModeState()) + if (iter->first.getRefData().getHandle()=="player") { - MWMechanics::DynamicStat stat(stats.getHealth()); - - if(stat.getModified()<1) + // If it's the player and God Mode is turned on, keep it alive + if (MWBase::Environment::get().getWorld()->getGodModeState()) { - stat.setModified(1, 0); - stats.setHealth(stat); + MWMechanics::DynamicStat stat (stats.getHealth()); + + if (stat.getModified()<1) + { + stat.setModified(1, 0); + stats.setHealth(stat); + } + + stats.resurrect(); + continue; } - stats.resurrect(); - continue; + MWBase::Environment::get().getStateManager()->endGame(); } if(iter->second->isDead()) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index c4cd45c8e..66a44872c 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -46,3 +46,8 @@ void MWState::StateManager::newGame (bool bypass) mState = State_Running; } + +void MWState::StateManager::endGame() +{ + mState = State_Ended; +} diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 078a899b2..007a9b136 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -24,6 +24,8 @@ namespace MWState ///< Start a new game. /// /// \param bypass Skip new game mechanics. + + virtual void endGame(); }; } From 1c7a4d4b3a2556c3064f622355e0200c91759a11 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 18 Nov 2013 15:52:25 +0100 Subject: [PATCH 035/889] adjust availability of main menu items based on game state --- apps/openmw/mwgui/mainmenu.cpp | 110 ++++++++++++++++++++------------- apps/openmw/mwgui/mainmenu.hpp | 23 ++++--- 2 files changed, 84 insertions(+), 49 deletions(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index cb3436715..d4a4e74ba 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -15,56 +15,25 @@ namespace MWGui MainMenu::MainMenu(int w, int h) : OEngine::GUI::Layout("openmw_mainmenu.layout") - , mButtonBox(0) + , mButtonBox(0), mWidth (w), mHeight (h) { - onResChange(w,h); + updateMenu(); } void MainMenu::onResChange(int w, int h) { - setCoord(0,0,w,h); + mWidth = w; + mHeight = h; + updateMenu(); + } - if (mButtonBox) - MyGUI::Gui::getInstance ().destroyWidget(mButtonBox); + void MainMenu::setVisible (bool visible) + { + if (visible) + updateMenu(); - mButtonBox = mMainWidget->createWidget("", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default); - int curH = 0; - - std::vector buttons; - buttons.push_back("return"); - buttons.push_back("newgame"); - buttons.push_back("loadgame"); - buttons.push_back("savegame"); - buttons.push_back("options"); - //buttons.push_back("credits"); - buttons.push_back("exitgame"); - - int maxwidth = 0; - - mButtons.clear(); - for (std::vector::iterator it = buttons.begin(); it != buttons.end(); ++it) - { - MWGui::ImageButton* button = mButtonBox->createWidget - ("ImageBox", MyGUI::IntCoord(0, curH, 0, 0), MyGUI::Align::Default); - button->setProperty("ImageHighlighted", "textures\\menu_" + *it + "_over.dds"); - button->setProperty("ImageNormal", "textures\\menu_" + *it + ".dds"); - button->setProperty("ImagePushed", "textures\\menu_" + *it + "_pressed.dds"); - MyGUI::IntSize requested = button->getRequestedSize(); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::onButtonClicked); - mButtons[*it] = button; - curH += requested.height; - - if (requested.width > maxwidth) - maxwidth = requested.width; - } - for (std::map::iterator it = mButtons.begin(); it != mButtons.end(); ++it) - { - MyGUI::IntSize requested = it->second->getRequestedSize(); - it->second->setCoord((maxwidth-requested.width) / 2, it->second->getTop(), requested.width, requested.height); - } - - mButtonBox->setCoord (w/2 - maxwidth/2, h/2 - curH/2, maxwidth, curH); + OEngine::GUI::Layout::setVisible (visible); } void MainMenu::onButtonClicked(MyGUI::Widget *sender) @@ -98,4 +67,61 @@ namespace MWGui } } + void MainMenu::updateMenu() + { + setCoord(0,0, mWidth, mHeight); + + + if (mButtonBox) + MyGUI::Gui::getInstance ().destroyWidget(mButtonBox); + + mButtonBox = mMainWidget->createWidget("", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default); + int curH = 0; + + MWBase::StateManager::State state = MWBase::Environment::get().getStateManager()->getState(); + + std::vector buttons; + + if (state==MWBase::StateManager::State_Running) + buttons.push_back("return"); + + buttons.push_back("newgame"); + + /// \todo hide, if no saved game is available + buttons.push_back("loadgame"); + + if (state==MWBase::StateManager::State_Running) + buttons.push_back("savegame"); + + buttons.push_back("options"); + //buttons.push_back("credits"); + buttons.push_back("exitgame"); + + int maxwidth = 0; + + mButtons.clear(); + for (std::vector::iterator it = buttons.begin(); it != buttons.end(); ++it) + { + MWGui::ImageButton* button = mButtonBox->createWidget + ("ImageBox", MyGUI::IntCoord(0, curH, 0, 0), MyGUI::Align::Default); + button->setProperty("ImageHighlighted", "textures\\menu_" + *it + "_over.dds"); + button->setProperty("ImageNormal", "textures\\menu_" + *it + ".dds"); + button->setProperty("ImagePushed", "textures\\menu_" + *it + "_pressed.dds"); + MyGUI::IntSize requested = button->getRequestedSize(); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::onButtonClicked); + mButtons[*it] = button; + curH += requested.height; + + if (requested.width > maxwidth) + maxwidth = requested.width; + } + for (std::map::iterator it = mButtons.begin(); it != mButtons.end(); ++it) + { + MyGUI::IntSize requested = it->second->getRequestedSize(); + it->second->setCoord((maxwidth-requested.width) / 2, it->second->getTop(), requested.width, requested.height); + } + + mButtonBox->setCoord (mWidth/2 - maxwidth/2, mHeight/2 - curH/2, maxwidth, curH); + + } } diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index 4e76a64df..511f72672 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -7,17 +7,26 @@ namespace MWGui class MainMenu : public OEngine::GUI::Layout { - public: - MainMenu(int w, int h); + int mWidth; + int mHeight; - void onResChange(int w, int h); + public: - private: - MyGUI::Widget* mButtonBox; + MainMenu(int w, int h); - std::map mButtons; + void onResChange(int w, int h); - void onButtonClicked (MyGUI::Widget* sender); + virtual void setVisible (bool visible); + + private: + + MyGUI::Widget* mButtonBox; + + std::map mButtons; + + void onButtonClicked (MyGUI::Widget* sender); + + void updateMenu(); }; } From dc75627d53b3404458276cb84db822db4c23f8bd Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 19 Nov 2013 10:51:30 +0100 Subject: [PATCH 036/889] added secondary saved game header record --- components/CMakeLists.txt | 1 + components/esm/defs.hpp | 3 +++ components/esm/savedgame.cpp | 36 +++++++++++++++++++++++++++++++ components/esm/savedgame.hpp | 41 ++++++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+) create mode 100644 components/esm/savedgame.cpp create mode 100644 components/esm/savedgame.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 59fb084a8..ce5965be1 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -40,6 +40,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter + savedgame ) add_component_dir (misc diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index dd7ebfe93..5a5ef9f1c 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -83,6 +83,9 @@ enum RecNameInts REC_STAT = 0x54415453, REC_WEAP = 0x50414557, + // format 0 - saved games + REC_SAVE = 0x45564153, + // format 1 REC_FILT = 0x544C4946 }; diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp new file mode 100644 index 000000000..a717c6469 --- /dev/null +++ b/components/esm/savedgame.cpp @@ -0,0 +1,36 @@ + +#include "savedgame.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" +#include "defs.hpp" + +unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; + +void ESM::SavedGame::load (ESMReader &esm) +{ + mPlayerName = esm.getHNString("PNAM"); + esm.getHNOT (mPlayerLevel, "PLEV"); + mPlayerClass = esm.getHNString("PCLA"); + mPlayerCell = esm.getHNString("PCEL"); + esm.getHNT (mInGameTime, "TSTM", 16); + esm.getHNT (mTimePlayed, "TIME"); + + while (esm.isNextSub ("DEPE")) + mContentFiles.push_back (esm.getHString()); +} + +void ESM::SavedGame::save (ESMWriter &esm) const +{ + esm.writeHNCString (mPlayerName, "PNAM"); + esm.writeHNT ("PLEV", mPlayerLevel); + esm.writeHNCString (mPlayerClass, "PCLA"); + esm.writeHNCString (mPlayerCell, "PCEL"); + esm.writeHNT ("TSTM", mInGameTime, 16); + esm.writeHNT ("TIME", mTimePlayed); + + for (std::vector::const_iterator iter (mContentFiles.begin()); + iter!=mContentFiles.end(); ++iter) + esm.writeHNCString (*iter, "DEPE"); + +} diff --git a/components/esm/savedgame.hpp b/components/esm/savedgame.hpp new file mode 100644 index 000000000..ae8f79263 --- /dev/null +++ b/components/esm/savedgame.hpp @@ -0,0 +1,41 @@ +#ifndef OPENMW_ESM_SAVEDGAME_H +#define OPENMW_ESM_SAVEDGAME_H + +#include +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + struct SavedGame + { + static unsigned int sRecordId; + + struct TimeStamp + { + float mGameHour; + int mDay; + int mMonth; + int mYear; + }; + + std::vector mContentFiles; + std::string mPlayerName; + int mPlayerLevel; + std::string mPlayerClass; + std::string mPlayerCell; + TimeStamp mInGameTime; + float mTimePlayed; + + /// \todo add field for screenshot + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif From 903e867c24559740c9e16fc193e9e09d8cca6ea4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 19 Nov 2013 13:44:18 +0100 Subject: [PATCH 037/889] change to TES3 record (moved format field to the top) --- components/esm/loadtes3.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/esm/loadtes3.cpp b/components/esm/loadtes3.cpp index 87a8d1d57..e5d6ec837 100644 --- a/components/esm/loadtes3.cpp +++ b/components/esm/loadtes3.cpp @@ -19,8 +19,6 @@ void ESM::Header::blank() void ESM::Header::load (ESMReader &esm) { - esm.getHNT (mData, "HEDR", 300); - if (esm.isNextSub ("FORM")) { esm.getHT (mFormat); @@ -30,6 +28,8 @@ void ESM::Header::load (ESMReader &esm) else mFormat = 0; + esm.getHNT (mData, "HEDR", 300); + while (esm.isNextSub ("MAST")) { MasterData m; @@ -41,11 +41,11 @@ void ESM::Header::load (ESMReader &esm) void ESM::Header::save (ESMWriter &esm) { - esm.writeHNT ("HEDR", mData, 300); - if (mFormat>0) esm.writeHNT ("FORM", mFormat); + esm.writeHNT ("HEDR", mData, 300); + for (std::vector::iterator iter = mMaster.begin(); iter != mMaster.end(); ++iter) { From 35bf98a940aeb95fee28529869388a409c5a8611 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 19 Nov 2013 13:46:24 +0100 Subject: [PATCH 038/889] modified esm reader/writer to use UTF8 when no encoder is given --- components/esm/esmreader.cpp | 5 ++++- components/esm/esmwriter.cpp | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 51d86a2ee..f02ed2d6e 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -303,7 +303,10 @@ std::string ESMReader::getString(int size) getExact(ptr, size); // Convert to UTF8 and return - return mEncoder->getUtf8(ptr, size); + if (mEncoder) + return mEncoder->getUtf8(ptr, size); + + return std::string (ptr, size); } void ESMReader::fail(const std::string &msg) diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index f39aa2b89..c9ef61b63 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -6,7 +6,7 @@ namespace ESM { - ESMWriter::ESMWriter() : mRecordCount (0), mCounting (true) {} + ESMWriter::ESMWriter() : mEncoder (0), mRecordCount (0), mCounting (true) {} unsigned int ESMWriter::getVersion() const { @@ -152,9 +152,9 @@ namespace ESM else { // Convert to UTF8 and return - std::string ascii = mEncoder->getLegacyEnc(data); + std::string string = mEncoder ? mEncoder->getLegacyEnc(data) : data; - write(ascii.c_str(), ascii.size()); + write(string.c_str(), string.size()); } } From 4c61deca8df2ea4167091a4598a525f61e5d49b6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 19 Nov 2013 15:31:39 +0100 Subject: [PATCH 039/889] fixed save code for SavedGame record --- components/esm/savedgame.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index a717c6469..5a5fc9fa8 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -22,10 +22,10 @@ void ESM::SavedGame::load (ESMReader &esm) void ESM::SavedGame::save (ESMWriter &esm) const { - esm.writeHNCString (mPlayerName, "PNAM"); + esm.writeHNCString ("PNAM", mPlayerName); esm.writeHNT ("PLEV", mPlayerLevel); - esm.writeHNCString (mPlayerClass, "PCLA"); - esm.writeHNCString (mPlayerCell, "PCEL"); + esm.writeHNCString ("PCLA", mPlayerClass); + esm.writeHNCString ("PCEL", mPlayerCell); esm.writeHNT ("TSTM", mInGameTime, 16); esm.writeHNT ("TIME", mTimePlayed); From 5e64888227f3f8e6a7796cf74ce70ac268bed5ae Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 19 Nov 2013 15:38:26 +0100 Subject: [PATCH 040/889] added basic save slot management and connected main menu save to save function (bypassing the save GUI for now) --- apps/openmw/CMakeLists.txt | 4 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/statemanager.hpp | 14 ++++ apps/openmw/mwgui/mainmenu.cpp | 7 +- apps/openmw/mwstate/character.cpp | 101 +++++++++++++++++++++++ apps/openmw/mwstate/character.hpp | 45 ++++++++++ apps/openmw/mwstate/charactermanager.cpp | 57 +++++++++++++ apps/openmw/mwstate/charactermanager.hpp | 37 +++++++++ apps/openmw/mwstate/statemanagerimp.cpp | 31 ++++++- apps/openmw/mwstate/statemanagerimp.hpp | 15 +++- 10 files changed, 304 insertions(+), 9 deletions(-) create mode 100644 apps/openmw/mwstate/character.cpp create mode 100644 apps/openmw/mwstate/character.hpp create mode 100644 apps/openmw/mwstate/charactermanager.cpp create mode 100644 apps/openmw/mwstate/charactermanager.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 0358b96d1..2b078a7ff 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -73,12 +73,12 @@ add_openmw_dir (mwmechanics ) add_openmw_dir (mwstate - statemanagerimp + statemanagerimp charactermanager character ) add_openmw_dir (mwbase environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager - inputmanager windowmanager + inputmanager windowmanager statemanager ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 4de198b64..cda068347 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -323,7 +323,7 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings) void OMW::Engine::prepareEngine (Settings::Manager & settings) { - mEnvironment.setStateManager (new MWState::StateManager); + mEnvironment.setStateManager (new MWState::StateManager (mCfgMgr.getUserPath() / "saves")); Nif::NIFFile::CacheLock cachelock; diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index 74fcc3f7a..b341fbb03 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -1,6 +1,12 @@ #ifndef GAME_MWSTATE_STATEMANAGER_H #define GAME_MWSTATE_STATEMANAGER_H +namespace MWState +{ + struct Slot; + class Character; +} + namespace MWBase { /// \brief Interface for game state manager (implemented in MWState) @@ -41,6 +47,14 @@ namespace MWBase /// \param bypass Skip new game mechanics. virtual void endGame() = 0; + + virtual void saveGame (const MWState::Slot *slot = 0) = 0; + ///< Write a saved game to \a slot or create a new slot if \a slot == 0. + /// + /// \note Slot must belong to the current character. + + virtual MWState::Character *getCurrentCharacter() = 0; + ///< Must not be called, if there is no current character. }; } diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index d4a4e74ba..f25b72d37 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -61,9 +61,10 @@ namespace MWGui } else if (sender == mButtons["savegame"]) { - MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); - dialog->setLoadOrSave(false); - dialog->setVisible(true); + MWBase::Environment::get().getStateManager()->saveGame (0); +// MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); +// dialog->setLoadOrSave(false); +// dialog->setVisible(true); } } diff --git a/apps/openmw/mwstate/character.cpp b/apps/openmw/mwstate/character.cpp new file mode 100644 index 000000000..dc16085d8 --- /dev/null +++ b/apps/openmw/mwstate/character.cpp @@ -0,0 +1,101 @@ + +#include "character.hpp" + +#include + +#include +#include +#include + +#include + +bool MWState::operator< (const Slot& left, const Slot& right) +{ + return left.mTimeStamp> index) && index>=mNext) + mNext = index+1; + } + + std::sort (mSlots.begin(), mSlots.end()); + } +} + +const MWState::Slot *MWState::Character::createSlot (const ESM::SavedGame& profile) +{ + addSlot (profile); + + return &mSlots.back(); +} + +const MWState::Slot *MWState::Character::updateSlot (const Slot *slot, const ESM::SavedGame& profile) +{ + int index = slot - &mSlots[0]; + + if (index<0 || index>=static_cast (mSlots.size())) + { + // sanity check; not entirely reliable + throw std::logic_error ("slot not found"); + } + + Slot newSlot = *slot; + newSlot.mProfile = profile; + newSlot.mTimeStamp = std::time (0); + + mSlots.erase (mSlots.begin()+index); + + mSlots.push_back (newSlot); + + return &mSlots.back(); +} \ No newline at end of file diff --git a/apps/openmw/mwstate/character.hpp b/apps/openmw/mwstate/character.hpp new file mode 100644 index 000000000..30182e404 --- /dev/null +++ b/apps/openmw/mwstate/character.hpp @@ -0,0 +1,45 @@ +#ifndef GAME_STATE_CHARACTER_H +#define GAME_STATE_CHARACTER_H + +#include + +#include + +namespace MWState +{ + struct Slot + { + boost::filesystem::path mPath; + ESM::SavedGame mProfile; + std::time_t mTimeStamp; + }; + + bool operator< (const Slot& left, const Slot& right); + + class Character + { + boost::filesystem::path mPath; + std::vector mSlots; + int mNext; + + void addSlot (const boost::filesystem::path& path); + + void addSlot (const ESM::SavedGame& profile); + + public: + + Character (const boost::filesystem::path& saves); + + const Slot *createSlot (const ESM::SavedGame& profile); + ///< Create new slot. + /// + /// \attention The ownership of the slot is not transferred. + + const Slot *updateSlot (const Slot *slot, const ESM::SavedGame& profile); + /// \note Slot must belong to this character. + /// + /// \attention The \æ slot pointer will be invalidated by this call. + }; +} + +#endif diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp new file mode 100644 index 000000000..c73adb5bb --- /dev/null +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -0,0 +1,57 @@ + +#include "charactermanager.hpp" + +#include +#include + +#include + +MWState::CharacterManager::CharacterManager (const boost::filesystem::path& saves) +: mPath (saves), mNext (0), mCurrent (0) +{ + if (!boost::filesystem::is_directory (mPath)) + { + boost::filesystem::create_directories (mPath); + } + else + { + for (boost::filesystem::directory_iterator iter (mPath); + iter!=boost::filesystem::directory_iterator(); ++iter) + { + boost::filesystem::path characterDir = *iter; + + if (boost::filesystem::is_directory (characterDir)) + { + Character character (characterDir); + mCharacters.push_back (character); + } + + std::istringstream stream (characterDir.filename().string()); + + int index = 0; + + if ((stream >> index) && index>=mNext) + mNext = index+1; + } + } +} + +MWState::Character *MWState::CharacterManager::getCurrentCharacter() +{ + if (!mCurrent) + throw std::logic_error ("no character selected"); + + return mCurrent; +} + +void MWState::CharacterManager::createCharacter() +{ + std::ostringstream stream; + stream << mNext++; + + boost::filesystem::path path = mPath / stream.str(); + + mCharacters.push_back (Character (path)); + + mCurrent = &mCharacters.back(); +} \ No newline at end of file diff --git a/apps/openmw/mwstate/charactermanager.hpp b/apps/openmw/mwstate/charactermanager.hpp new file mode 100644 index 000000000..a3fe17131 --- /dev/null +++ b/apps/openmw/mwstate/charactermanager.hpp @@ -0,0 +1,37 @@ +#ifndef GAME_STATE_CHARACTERMANAGER_H +#define GAME_STATE_CHARACTERMANAGER_H + +#include + +#include "character.hpp" + +namespace MWState +{ + class CharacterManager + { + boost::filesystem::path mPath; + int mNext; + std::vector mCharacters; + Character *mCurrent; + + private: + + CharacterManager (const CharacterManager&); + ///< Not implemented + + CharacterManager& operator= (const CharacterManager&); + ///< Not implemented + + public: + + CharacterManager (const boost::filesystem::path& saves); + + Character *getCurrentCharacter(); + ///< Must not be called, if there is no current character. + + void createCharacter(); + ///< Create new character within saved game management + }; +} + +#endif diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 66a44872c..f068093a0 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -1,14 +1,16 @@ #include "statemanagerimp.hpp" +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/windowmanager.hpp" -MWState::StateManager::StateManager() -: mQuitRequest (false), mState (State_NoGame) +MWState::StateManager::StateManager (const boost::filesystem::path& saves) +: mQuitRequest (false), mState (State_NoGame), mCharacterManager (saves) { } @@ -44,6 +46,8 @@ void MWState::StateManager::newGame (bool bypass) MWBase::Environment::get().getWindowManager()->setNewGame (true); } + mCharacterManager.createCharacter(); + mState = State_Running; } @@ -51,3 +55,26 @@ void MWState::StateManager::endGame() { mState = State_Ended; } + +void MWState::StateManager::saveGame (const Slot *slot) +{ + ESM::SavedGame profile; + + /// \todo configure profile + + if (!slot) + slot = mCharacterManager.getCurrentCharacter()->createSlot (profile); + else + slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile); + + ESM::ESMWriter writer; +// writer.setFormat (); + writer.save (slot->mPath.string()); + slot->mProfile.save (writer); + writer.close(); +} + +MWState::Character *MWState::StateManager::getCurrentCharacter() +{ + return mCharacterManager.getCurrentCharacter(); +} \ No newline at end of file diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 007a9b136..9dfcc4d8f 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -3,16 +3,21 @@ #include "../mwbase/statemanager.hpp" +#include + +#include "charactermanager.hpp" + namespace MWState { class StateManager : public MWBase::StateManager { bool mQuitRequest; State mState; + CharacterManager mCharacterManager; public: - StateManager(); + StateManager (const boost::filesystem::path& saves); virtual void requestQuit(); @@ -26,6 +31,14 @@ namespace MWState /// \param bypass Skip new game mechanics. virtual void endGame(); + + virtual void saveGame (const Slot *slot = 0); + ///< Write a saved game to \a slot or create a new slot if \a slot == 0. + /// + /// \note Slot must belong to the current character. + + virtual Character *getCurrentCharacter(); + ///< Must not be called, if there is no current character. }; } From 9487bd33c3057f5d3f55e2b23ee108fa62778995 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 19 Nov 2013 16:07:36 +0100 Subject: [PATCH 041/889] removed broken save function from ESMWriter --- apps/openmw/mwstate/statemanagerimp.cpp | 5 ++++- components/esm/esmwriter.cpp | 6 ------ components/esm/esmwriter.hpp | 3 --- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index f068093a0..4b1d9e0f6 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -67,10 +67,13 @@ void MWState::StateManager::saveGame (const Slot *slot) else slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile); + std::ofstream stream (slot->mPath.string().c_str()); ESM::ESMWriter writer; // writer.setFormat (); - writer.save (slot->mPath.string()); + writer.save (stream); + writer.startRecord ("SAVE"); slot->mProfile.save (writer); + writer.endRecord ("SAVE"); writer.close(); } diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index c9ef61b63..069d75c7b 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -51,12 +51,6 @@ namespace ESM mHeader.mMaster.push_back(d); } - void ESMWriter::save(const std::string& file) - { - std::ofstream fs(file.c_str(), std::ios_base::out | std::ios_base::trunc); - save(fs); - } - void ESMWriter::save(std::ostream& file) { mRecordCount = 0; diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index 104f97f90..d6646471b 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -36,9 +36,6 @@ class ESMWriter void addMaster(const std::string& name, uint64_t size); - void save(const std::string& file); - ///< Start saving a file by writing the TES3 header. - void save(std::ostream& file); ///< Start saving a file by writing the TES3 header. From 9b34e4a523a0251631a4e55b94b2e0ffe8085439 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 19 Nov 2013 20:35:57 +0100 Subject: [PATCH 042/889] Creating new files. --- manual/opencs/creating_file.tex | 0 manual/opencs/files_and_directories.tex | 24 ++++++++++++++++++++++++ manual/opencs/main.tex | 2 ++ 3 files changed, 26 insertions(+) create mode 100644 manual/opencs/creating_file.tex create mode 100644 manual/opencs/files_and_directories.tex diff --git a/manual/opencs/creating_file.tex b/manual/opencs/creating_file.tex new file mode 100644 index 000000000..e69de29bb diff --git a/manual/opencs/files_and_directories.tex b/manual/opencs/files_and_directories.tex new file mode 100644 index 000000000..4938ff0a9 --- /dev/null +++ b/manual/opencs/files_and_directories.tex @@ -0,0 +1,24 @@ +\section{Files and Directories} +\subsection{Introduction} +As you imagine, there is no other way to store and distribute computer data without files. You surely know that each file in your file system is identified by the unique path. This is basic knowledge, and applies to both Open{MW} and Open{CS} alike.\\ + +Needless to say, this chapter describes paths and files that are used/created/edited with OpenCS editor.\\ + +\subsection{Used terms} %TODO + +\subsection{Basics} + +\paragraph{Directories} +Open{CS} user should consider two directory paths in his system. So called ``User Configuration Path'' and ``Data Path''. ``User Configuration Path'' stores basically all Open{MW} specific files that are supposed to be touched by the user, while ``Data Path''%This is wrong, pay no mind to it. + +\paragraph{Files} +Bethesda Morrowind engine is using two types of files: esm (master) and esp (plugin). The distinction between those is not clear, and often confusing, mostly because large esp files could hurt stability. Open{MW} supports both esm and esp files, but in order to make use of new features of OpenMW one should consider using new file types designed with our engine in mind: game files and addon files.\\ + +Game and Addon files are concept descended from old esm/esp system, but much more flexible and cleaner. Finally, We can describe the difference between those two file types use case with pure statements. If you want to make new game using Open{MW} as engine (so called ``total conversion'') you should create a game file. If you want to create a addon for existing game file -- simply create addon file. The addon size is not a factor here. The only distinction you should consider is if your project is about changing other game, or creating a new one. Simple as that. + +%TODO describe some characteristics, like file extensions. What about project files? + +The actual creating of new files is described in the next chapter. Here We are gonna focus only on details that you need to know in order to create your first Open{CS} file while full understanding your needs. + +\paragraph{Dependencies} +Since addon is supposed to change the game it is logical that it also depends on the said game. It simply can't work otherwise. Just think about it: your modification is changing prize of the iron sword. But what if there is no iron sword in game? That's right: we get nonsense. What you want to do is to tie your addon to the files you are changing. Those can be either game files (expansion island for a game) or other addon files (house on the said island). It is a a good idea to be dependent only on files that are really changed in your addon obviously, but sadly there is no other way to achieve this than knowing what you want to do. \ No newline at end of file diff --git a/manual/opencs/main.tex b/manual/opencs/main.tex index 603e2d9ba..577b7078b 100644 --- a/manual/opencs/main.tex +++ b/manual/opencs/main.tex @@ -8,6 +8,8 @@ \title{OpenCS User Manual} \maketitle \tableofcontents{} +\input{files_and_directories} +\input{creating_file} \input{windows} \input{tables} \input{filters} From 9bf38372180032739fbd9ffb370276e1ac9aa2e5 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 19 Nov 2013 20:38:22 +0100 Subject: [PATCH 043/889] minor edit --- manual/opencs/files_and_directories.tex | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/manual/opencs/files_and_directories.tex b/manual/opencs/files_and_directories.tex index 4938ff0a9..530d7a76a 100644 --- a/manual/opencs/files_and_directories.tex +++ b/manual/opencs/files_and_directories.tex @@ -11,7 +11,7 @@ Needless to say, this chapter describes paths and files that are used/created/ed \paragraph{Directories} Open{CS} user should consider two directory paths in his system. So called ``User Configuration Path'' and ``Data Path''. ``User Configuration Path'' stores basically all Open{MW} specific files that are supposed to be touched by the user, while ``Data Path''%This is wrong, pay no mind to it. -\paragraph{Files} +\paragraph{Content files} Bethesda Morrowind engine is using two types of files: esm (master) and esp (plugin). The distinction between those is not clear, and often confusing, mostly because large esp files could hurt stability. Open{MW} supports both esm and esp files, but in order to make use of new features of OpenMW one should consider using new file types designed with our engine in mind: game files and addon files.\\ Game and Addon files are concept descended from old esm/esp system, but much more flexible and cleaner. Finally, We can describe the difference between those two file types use case with pure statements. If you want to make new game using Open{MW} as engine (so called ``total conversion'') you should create a game file. If you want to create a addon for existing game file -- simply create addon file. The addon size is not a factor here. The only distinction you should consider is if your project is about changing other game, or creating a new one. Simple as that. @@ -21,4 +21,6 @@ Game and Addon files are concept descended from old esm/esp system, but much mor The actual creating of new files is described in the next chapter. Here We are gonna focus only on details that you need to know in order to create your first Open{CS} file while full understanding your needs. \paragraph{Dependencies} -Since addon is supposed to change the game it is logical that it also depends on the said game. It simply can't work otherwise. Just think about it: your modification is changing prize of the iron sword. But what if there is no iron sword in game? That's right: we get nonsense. What you want to do is to tie your addon to the files you are changing. Those can be either game files (expansion island for a game) or other addon files (house on the said island). It is a a good idea to be dependent only on files that are really changed in your addon obviously, but sadly there is no other way to achieve this than knowing what you want to do. \ No newline at end of file +Since addon is supposed to change the game it is logical that it also depends on the said game. It simply can't work otherwise. Just think about it: your modification is changing prize of the iron sword. But what if there is no iron sword in game? That's right: we get nonsense. What you want to do is to tie your addon to the files you are changing. Those can be either game files (expansion island for a game) or other addon files (house on the said island). It is a a good idea to be dependent only on files that are really changed in your addon obviously, but sadly there is no other way to achieve this than knowing what you want to do. + +\paragraph{Resources files} From 240a44f932292db6a5afcab9d12c47c2eeb3eff6 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 19 Nov 2013 20:43:29 +0100 Subject: [PATCH 044/889] Files chapter will actually require resarch to be done :/ --- manual/opencs/files_and_directories.tex | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/manual/opencs/files_and_directories.tex b/manual/opencs/files_and_directories.tex index 530d7a76a..e88018af2 100644 --- a/manual/opencs/files_and_directories.tex +++ b/manual/opencs/files_and_directories.tex @@ -21,6 +21,11 @@ Game and Addon files are concept descended from old esm/esp system, but much mor The actual creating of new files is described in the next chapter. Here We are gonna focus only on details that you need to know in order to create your first Open{CS} file while full understanding your needs. \paragraph{Dependencies} -Since addon is supposed to change the game it is logical that it also depends on the said game. It simply can't work otherwise. Just think about it: your modification is changing prize of the iron sword. But what if there is no iron sword in game? That's right: we get nonsense. What you want to do is to tie your addon to the files you are changing. Those can be either game files (expansion island for a game) or other addon files (house on the said island). It is a a good idea to be dependent only on files that are really changed in your addon obviously, but sadly there is no other way to achieve this than knowing what you want to do. +Since addon is supposed to change the game it is logical that it also depends on the said game. It simply can't work otherwise. Just think about it: your modification is changing prize of the iron sword. But what if there is no iron sword in game? That's right: we get nonsense. What you want to do is to tie your addon to the files you are changing. Those can be either game files (expansion island for a game) or other addon files (house on the said island). It is a a good idea to be dependent only on files that are really changed in your addon obviously, but sadly there is no other way to achieve this than knowing what you want to do.\\ + +Game files are not intend to have any dependencies for a very simple reasons: player is using only one game file at the time and therefore no game file can depend on other game file, and since game file makes the base for addon files -- it can't depend on addon files. + +\paragraph{Loading order} \paragraph{Resources files} +%textures, sounds, whatever \ No newline at end of file From 000606efc02c28cc8bad6636af407aa34a6b5095 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 20 Nov 2013 20:05:22 +0100 Subject: [PATCH 045/889] =?UTF-8?q?working,=20working=E2=80=A6=20it=20take?= =?UTF-8?q?s=20ages.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manual/opencs/files_and_directories.tex | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/manual/opencs/files_and_directories.tex b/manual/opencs/files_and_directories.tex index e88018af2..307a5565f 100644 --- a/manual/opencs/files_and_directories.tex +++ b/manual/opencs/files_and_directories.tex @@ -12,14 +12,20 @@ Needless to say, this chapter describes paths and files that are used/created/ed Open{CS} user should consider two directory paths in his system. So called ``User Configuration Path'' and ``Data Path''. ``User Configuration Path'' stores basically all Open{MW} specific files that are supposed to be touched by the user, while ``Data Path''%This is wrong, pay no mind to it. \paragraph{Content files} -Bethesda Morrowind engine is using two types of files: esm (master) and esp (plugin). The distinction between those is not clear, and often confusing, mostly because large esp files could hurt stability. Open{MW} supports both esm and esp files, but in order to make use of new features of OpenMW one should consider using new file types designed with our engine in mind: game files and addon files.\\ +Bethesda Morrowind engine is using two types of files: esm (master) and esp (plugin). The distinction between those is not clear, and often confusing, mostly because large esp files could hurt stability. Open{MW} supports both esm and esp files, but in order to make use of new features of OpenMW one should consider using new file types designed with our engine in mind: game files and addon files together called ``content files``.\\ -Game and Addon files are concept descended from old esm/esp system, but much more flexible and cleaner. Finally, We can describe the difference between those two file types use case with pure statements. If you want to make new game using Open{MW} as engine (so called ``total conversion'') you should create a game file. If you want to create a addon for existing game file -- simply create addon file. The addon size is not a factor here. The only distinction you should consider is if your project is about changing other game, or creating a new one. Simple as that. +Game and Addon files are concept descended from old esm/esp system, but much more flexible and cleaner. Finally, We can describe the difference between those two file types use case with pure statements. If you want to make new game using Open{MW} as engine (so called ``total conversion'') you should create a game file. If you want to create a addon for existing game file -- simply create addon file. The addon size is not a factor here. The only distinction you should consider is if your project is about changing other game, or creating a new one. Simple as that.\\ %TODO describe some characteristics, like file extensions. What about project files? The actual creating of new files is described in the next chapter. Here We are gonna focus only on details that you need to know in order to create your first Open{CS} file while full understanding your needs. +\paragraph{Project files} +Project files act as containers for data not used by the Openm{MW} game engine itself, but still useful for OpenCS. The shining example of this data category are without doubt record filters (described in the later section of the manual you are reading currently). As a mod author you probably don't need or want to distribute project files among players, but for sure you will want to aid all your colleagues working together with you on, well project. As you would imagine, project file makes sense only in combination with actual content files. In fact, each time you start to work on new content file and project file was not found it will be created.\\ +Project files extension is, to not surprise ``.project''. The whole name of the project file is the whole name of the content file with appended extensions. For instance awesomeswords.omwaddon file is associated with awesomeswords.omwaddon.project file. + +%TODO where are they stored. + \paragraph{Dependencies} Since addon is supposed to change the game it is logical that it also depends on the said game. It simply can't work otherwise. Just think about it: your modification is changing prize of the iron sword. But what if there is no iron sword in game? That's right: we get nonsense. What you want to do is to tie your addon to the files you are changing. Those can be either game files (expansion island for a game) or other addon files (house on the said island). It is a a good idea to be dependent only on files that are really changed in your addon obviously, but sadly there is no other way to achieve this than knowing what you want to do.\\ From 3f1eb42d9128bbbd2f5ca62b20e2de1d120e63a4 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 20 Nov 2013 20:54:35 +0100 Subject: [PATCH 046/889] I hate this chapter. --- manual/opencs/files_and_directories.tex | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/manual/opencs/files_and_directories.tex b/manual/opencs/files_and_directories.tex index 307a5565f..9ffa13828 100644 --- a/manual/opencs/files_and_directories.tex +++ b/manual/opencs/files_and_directories.tex @@ -9,22 +9,27 @@ Needless to say, this chapter describes paths and files that are used/created/ed \subsection{Basics} \paragraph{Directories} -Open{CS} user should consider two directory paths in his system. So called ``User Configuration Path'' and ``Data Path''. ``User Configuration Path'' stores basically all Open{MW} specific files that are supposed to be touched by the user, while ``Data Path''%This is wrong, pay no mind to it. +Open{MW} and Open{CS} uses multiple directories on file systems, however from end user perspective only two matters. That's it: user path and game path.\\ + +User path is the per user path that holds both configuration files and the content/project files (described later). Game path on the other hand, is the location of your game (most likely Morrowind\texttrademark). + +%TODO give correct path \paragraph{Content files} Bethesda Morrowind engine is using two types of files: esm (master) and esp (plugin). The distinction between those is not clear, and often confusing, mostly because large esp files could hurt stability. Open{MW} supports both esm and esp files, but in order to make use of new features of OpenMW one should consider using new file types designed with our engine in mind: game files and addon files together called ``content files``.\\ -Game and Addon files are concept descended from old esm/esp system, but much more flexible and cleaner. Finally, We can describe the difference between those two file types use case with pure statements. If you want to make new game using Open{MW} as engine (so called ``total conversion'') you should create a game file. If you want to create a addon for existing game file -- simply create addon file. The addon size is not a factor here. The only distinction you should consider is if your project is about changing other game, or creating a new one. Simple as that.\\ +Game and Addon files are concept descended from old esm/esp system, but much more flexible and cleaner. We can describe the difference between those two file types use case with clean statements. If you want to make new game using Open{MW} as engine (so called ``total conversion'') you should create a game file. If you want to create a addon for existing game file -- simply create addon file. The addon size is not a factor here. The only distinction you should consider is if your project is about changing other game, or creating a new one. Simple as that.\\ %TODO describe some characteristics, like file extensions. What about project files? -The actual creating of new files is described in the next chapter. Here We are gonna focus only on details that you need to know in order to create your first Open{CS} file while full understanding your needs. +The actual creating of new files is described in the next chapter. Here We are gonna focus only on details that you need to know in order to create your first Open{CS} file while full understanding your needs. For now let's jut remember that content files are created inside the user directory, in the the \textbf{data} subfolder. Open{CS} scans for content files not only data subfolder but also the game path, most likely directory where you installed or unpacked Morrowind\texttrademark. However it is probably better to not pollute game path, and use only data path for Open{MW} content files, both when creating and only playing with others content files.\\ \paragraph{Project files} Project files act as containers for data not used by the Openm{MW} game engine itself, but still useful for OpenCS. The shining example of this data category are without doubt record filters (described in the later section of the manual you are reading currently). As a mod author you probably don't need or want to distribute project files among players, but for sure you will want to aid all your colleagues working together with you on, well project. As you would imagine, project file makes sense only in combination with actual content files. In fact, each time you start to work on new content file and project file was not found it will be created.\\ -Project files extension is, to not surprise ``.project''. The whole name of the project file is the whole name of the content file with appended extensions. For instance awesomeswords.omwaddon file is associated with awesomeswords.omwaddon.project file. +Project files extension is, to not surprise ``.project''. The whole name of the project file is the whole name of the content file with appended extensions. For instance awesomeswords.omwaddon file is associated with awesomeswords.omwaddon.project file.\\ %TODO where are they stored. +Project files are stored inside the user directory, in the \textbf{projects} subfolder. This is the path location for both freshly created project files, and the place where Open{CS} look for already existing files. \paragraph{Dependencies} Since addon is supposed to change the game it is logical that it also depends on the said game. It simply can't work otherwise. Just think about it: your modification is changing prize of the iron sword. But what if there is no iron sword in game? That's right: we get nonsense. What you want to do is to tie your addon to the files you are changing. Those can be either game files (expansion island for a game) or other addon files (house on the said island). It is a a good idea to be dependent only on files that are really changed in your addon obviously, but sadly there is no other way to achieve this than knowing what you want to do.\\ From d6e2701dd605fb0c8a4972e92ba35fd391101be1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 21 Nov 2013 10:10:41 +0100 Subject: [PATCH 047/889] changed played time data type from float to double --- components/esm/savedgame.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm/savedgame.hpp b/components/esm/savedgame.hpp index ae8f79263..c0e6f1aeb 100644 --- a/components/esm/savedgame.hpp +++ b/components/esm/savedgame.hpp @@ -29,7 +29,7 @@ namespace ESM std::string mPlayerClass; std::string mPlayerCell; TimeStamp mInGameTime; - float mTimePlayed; + double mTimePlayed; /// \todo add field for screenshot From e938c5a0eec1c10929c177415af086d8e4906e93 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 21 Nov 2013 10:20:50 +0100 Subject: [PATCH 048/889] changed character creation logic (create on save instead of on new game) --- apps/openmw/mwbase/statemanager.hpp | 1 - apps/openmw/mwstate/charactermanager.cpp | 7 ++++++- apps/openmw/mwstate/charactermanager.hpp | 4 +++- apps/openmw/mwstate/statemanagerimp.cpp | 3 +-- apps/openmw/mwstate/statemanagerimp.hpp | 1 - 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index b341fbb03..3aa464ebe 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -54,7 +54,6 @@ namespace MWBase /// \note Slot must belong to the current character. virtual MWState::Character *getCurrentCharacter() = 0; - ///< Must not be called, if there is no current character. }; } diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp index c73adb5bb..6eccb63dc 100644 --- a/apps/openmw/mwstate/charactermanager.cpp +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -39,7 +39,7 @@ MWState::CharacterManager::CharacterManager (const boost::filesystem::path& save MWState::Character *MWState::CharacterManager::getCurrentCharacter() { if (!mCurrent) - throw std::logic_error ("no character selected"); + createCharacter(); return mCurrent; } @@ -54,4 +54,9 @@ void MWState::CharacterManager::createCharacter() mCharacters.push_back (Character (path)); mCurrent = &mCharacters.back(); +} + +void MWState::CharacterManager::clearCurrentCharacter() +{ + mCurrent = 0; } \ No newline at end of file diff --git a/apps/openmw/mwstate/charactermanager.hpp b/apps/openmw/mwstate/charactermanager.hpp index a3fe17131..543e0c94a 100644 --- a/apps/openmw/mwstate/charactermanager.hpp +++ b/apps/openmw/mwstate/charactermanager.hpp @@ -27,10 +27,12 @@ namespace MWState CharacterManager (const boost::filesystem::path& saves); Character *getCurrentCharacter(); - ///< Must not be called, if there is no current character. + ///< A character is implicitly created, if there is none. void createCharacter(); ///< Create new character within saved game management + + void clearCurrentCharacter(); }; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 4b1d9e0f6..8f22408fe 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -37,6 +37,7 @@ void MWState::StateManager::newGame (bool bypass) MWBase::Environment::get().getDialogueManager()->clear(); MWBase::Environment::get().getJournal()->clear(); mState = State_NoGame; + mCharacterManager.clearCurrentCharacter(); } if (!bypass) @@ -46,8 +47,6 @@ void MWState::StateManager::newGame (bool bypass) MWBase::Environment::get().getWindowManager()->setNewGame (true); } - mCharacterManager.createCharacter(); - mState = State_Running; } diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 9dfcc4d8f..8d1ff0641 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -38,7 +38,6 @@ namespace MWState /// \note Slot must belong to the current character. virtual Character *getCurrentCharacter(); - ///< Must not be called, if there is no current character. }; } From fc1501a5109a93316674afe2e29b5ae90f0b849a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 21 Nov 2013 10:53:42 +0100 Subject: [PATCH 049/889] store character profile information in saved game file --- apps/openmw/mwstate/statemanagerimp.cpp | 20 +++++++++++++++++++- components/esm/savedgame.hpp | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 8f22408fe..0f4e1e020 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -9,6 +9,11 @@ #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" + +#include "../mwmechanics/npcstats.hpp" + MWState::StateManager::StateManager (const boost::filesystem::path& saves) : mQuitRequest (false), mState (State_NoGame), mCharacterManager (saves) { @@ -59,7 +64,20 @@ void MWState::StateManager::saveGame (const Slot *slot) { ESM::SavedGame profile; - /// \todo configure profile + MWBase::World& world = *MWBase::Environment::get().getWorld(); + + MWWorld::Ptr player = world.getPlayer().getPlayer(); + + /// \todo store content file list + profile.mPlayerName = player.getClass().getName (player); + profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel(); + profile.mPlayerClass = player.get()->mBase->mId; + /// \todo player cell + /// \todo gamehour + profile.mInGameTime.mDay = world.getDay(); + profile.mInGameTime.mMonth = world.getMonth(); + /// \todo year + /// \todo time played if (!slot) slot = mCharacterManager.getCurrentCharacter()->createSlot (profile); diff --git a/components/esm/savedgame.hpp b/components/esm/savedgame.hpp index c0e6f1aeb..e712e8f1f 100644 --- a/components/esm/savedgame.hpp +++ b/components/esm/savedgame.hpp @@ -26,7 +26,7 @@ namespace ESM std::vector mContentFiles; std::string mPlayerName; int mPlayerLevel; - std::string mPlayerClass; + std::string mPlayerClass; // this is the ID and not the name of the class std::string mPlayerCell; TimeStamp mInGameTime; double mTimePlayed; From cbbdf390ad9902fc4d55e74e43f829e34c8ae45b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 21 Nov 2013 11:10:18 +0100 Subject: [PATCH 050/889] added function for inspection of saved characters and character slots --- apps/openmw/mwbase/statemanager.hpp | 8 ++++++++ apps/openmw/mwstate/character.cpp | 10 ++++++++++ apps/openmw/mwstate/character.hpp | 11 +++++++++++ apps/openmw/mwstate/charactermanager.cpp | 12 +++++++++++- apps/openmw/mwstate/charactermanager.hpp | 4 ++++ apps/openmw/mwstate/statemanagerimp.cpp | 12 +++++++++++- apps/openmw/mwstate/statemanagerimp.hpp | 4 ++++ 7 files changed, 59 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index 3aa464ebe..ce8094632 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -1,6 +1,8 @@ #ifndef GAME_MWSTATE_STATEMANAGER_H #define GAME_MWSTATE_STATEMANAGER_H +#include + namespace MWState { struct Slot; @@ -21,6 +23,8 @@ namespace MWBase State_Running }; + typedef std::vector::const_iterator CharacterIterator; + private: StateManager (const StateManager&); @@ -54,6 +58,10 @@ namespace MWBase /// \note Slot must belong to the current character. virtual MWState::Character *getCurrentCharacter() = 0; + + virtual CharacterIterator characterBegin() = 0; + + virtual CharacterIterator characterEnd() = 0; }; } diff --git a/apps/openmw/mwstate/character.cpp b/apps/openmw/mwstate/character.cpp index dc16085d8..660af7d89 100644 --- a/apps/openmw/mwstate/character.cpp +++ b/apps/openmw/mwstate/character.cpp @@ -98,4 +98,14 @@ const MWState::Slot *MWState::Character::updateSlot (const Slot *slot, const ESM mSlots.push_back (newSlot); return &mSlots.back(); +} + +MWState::Character::SlotIterator MWState::Character::begin() const +{ + return mSlots.rbegin(); +} + +MWState::Character::SlotIterator MWState::Character::end() const +{ + return mSlots.rend(); } \ No newline at end of file diff --git a/apps/openmw/mwstate/character.hpp b/apps/openmw/mwstate/character.hpp index 30182e404..a6cb6fa6d 100644 --- a/apps/openmw/mwstate/character.hpp +++ b/apps/openmw/mwstate/character.hpp @@ -18,6 +18,12 @@ namespace MWState class Character { + public: + + typedef std::vector::const_reverse_iterator SlotIterator; + + private: + boost::filesystem::path mPath; std::vector mSlots; int mNext; @@ -39,6 +45,11 @@ namespace MWState /// \note Slot must belong to this character. /// /// \attention The \æ slot pointer will be invalidated by this call. + + SlotIterator begin() const; + ///< First slot is the most recent. Other slots follow in descending order of save date. + + SlotIterator end() const; }; } diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp index 6eccb63dc..6b67d0d04 100644 --- a/apps/openmw/mwstate/charactermanager.cpp +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -59,4 +59,14 @@ void MWState::CharacterManager::createCharacter() void MWState::CharacterManager::clearCurrentCharacter() { mCurrent = 0; -} \ No newline at end of file +} + +std::vector::const_iterator MWState::CharacterManager::begin() const +{ + return mCharacters.begin(); +} + +std::vector::const_iterator MWState::CharacterManager::end() const +{ + return mCharacters.end(); +} diff --git a/apps/openmw/mwstate/charactermanager.hpp b/apps/openmw/mwstate/charactermanager.hpp index 543e0c94a..ce82ccc46 100644 --- a/apps/openmw/mwstate/charactermanager.hpp +++ b/apps/openmw/mwstate/charactermanager.hpp @@ -33,6 +33,10 @@ namespace MWState ///< Create new character within saved game management void clearCurrentCharacter(); + + std::vector::const_iterator begin() const; + + std::vector::const_iterator end() const; }; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 0f4e1e020..9d8fcafb0 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -97,4 +97,14 @@ void MWState::StateManager::saveGame (const Slot *slot) MWState::Character *MWState::StateManager::getCurrentCharacter() { return mCharacterManager.getCurrentCharacter(); -} \ No newline at end of file +} + +MWState::StateManager::CharacterIterator MWState::StateManager::characterBegin() +{ + return mCharacterManager.begin(); +} + +MWState::StateManager::CharacterIterator MWState::StateManager::characterEnd() +{ + return mCharacterManager.end(); +} diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 8d1ff0641..764a09a7e 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -38,6 +38,10 @@ namespace MWState /// \note Slot must belong to the current character. virtual Character *getCurrentCharacter(); + + virtual CharacterIterator characterBegin(); + + virtual CharacterIterator characterEnd(); }; } From 5ba56a5ea5f50c2469b780ebe4d8a1abf10311e9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 21 Nov 2013 11:18:56 +0100 Subject: [PATCH 051/889] character signatures --- apps/openmw/mwstate/character.cpp | 19 +++++++++++++++++++ apps/openmw/mwstate/character.hpp | 5 +++++ 2 files changed, 24 insertions(+) diff --git a/apps/openmw/mwstate/character.cpp b/apps/openmw/mwstate/character.cpp index 660af7d89..54d2b9dd8 100644 --- a/apps/openmw/mwstate/character.cpp +++ b/apps/openmw/mwstate/character.cpp @@ -108,4 +108,23 @@ MWState::Character::SlotIterator MWState::Character::begin() const MWState::Character::SlotIterator MWState::Character::end() const { return mSlots.rend(); +} + +ESM::SavedGame MWState::Character::getSignature() const +{ + if (mSlots.empty()) + throw std::logic_error ("character signature not available"); + + std::vector::const_iterator iter (mSlots.begin()); + + Slot slot = *iter; + + for (++iter; iter!=mSlots.end(); ++iter) + if (iter->mProfile.mPlayerLevel>slot.mProfile.mPlayerLevel) + slot = *iter; + else if (iter->mProfile.mPlayerLevel==slot.mProfile.mPlayerLevel && + iter->mTimeStamp>slot.mTimeStamp) + slot = *iter; + + return slot.mProfile; } \ No newline at end of file diff --git a/apps/openmw/mwstate/character.hpp b/apps/openmw/mwstate/character.hpp index a6cb6fa6d..676c04680 100644 --- a/apps/openmw/mwstate/character.hpp +++ b/apps/openmw/mwstate/character.hpp @@ -50,6 +50,11 @@ namespace MWState ///< First slot is the most recent. Other slots follow in descending order of save date. SlotIterator end() const; + + ESM::SavedGame getSignature() const; + ///< Return signature information for this character. + /// + /// \todo attention This function must not be called if there are no slots. }; } From c165894869e227cfe8e3f541e3abdd2166ac60d0 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 21 Nov 2013 12:24:24 +0100 Subject: [PATCH 052/889] load saved game record --- apps/openmw/mwbase/statemanager.hpp | 5 +++++ apps/openmw/mwgui/mainmenu.cpp | 22 +++++++++++++++++----- apps/openmw/mwstate/character.cpp | 13 ++++++++++++- apps/openmw/mwstate/charactermanager.cpp | 10 ++++++++++ apps/openmw/mwstate/charactermanager.hpp | 2 ++ apps/openmw/mwstate/statemanagerimp.cpp | 22 ++++++++++++++++++++++ apps/openmw/mwstate/statemanagerimp.hpp | 5 +++++ 7 files changed, 73 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index ce8094632..3369fd3bc 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -57,6 +57,11 @@ namespace MWBase /// /// \note Slot must belong to the current character. + virtual void loadGame (const MWState::Character *character, const MWState::Slot *slot) = 0; + ///< Load a saved game file from \a slot. + /// + /// \note \a slot must belong to \a character. + virtual MWState::Character *getCurrentCharacter() = 0; virtual CharacterIterator characterBegin() = 0; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index f25b72d37..ac272d1c4 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -8,6 +8,8 @@ #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/statemanager.hpp" +#include "../mwstate/character.hpp" + #include "savegamedialog.hpp" namespace MWGui @@ -55,13 +57,22 @@ namespace MWGui else if (sender == mButtons["loadgame"]) { - MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); - dialog->setLoadOrSave(true); - dialog->setVisible(true); + // for testing purpose, pick the first slot of the first character: + const MWState::Character& character = + *MWBase::Environment::get().getStateManager()->characterBegin(); + const MWState::Slot& slot = *character.begin(); + + MWBase::Environment::get().getStateManager()->loadGame (&character, &slot); + +// MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); +// dialog->setLoadOrSave(true); +// dialog->setVisible(true); } else if (sender == mButtons["savegame"]) { + // for testing purpose, save into a new slot: MWBase::Environment::get().getStateManager()->saveGame (0); + // MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); // dialog->setLoadOrSave(false); // dialog->setVisible(true); @@ -88,8 +99,9 @@ namespace MWGui buttons.push_back("newgame"); - /// \todo hide, if no saved game is available - buttons.push_back("loadgame"); + if (MWBase::Environment::get().getStateManager()->characterBegin()!= + MWBase::Environment::get().getStateManager()->characterEnd()) + buttons.push_back("loadgame"); if (state==MWBase::StateManager::State_Running) buttons.push_back("savegame"); diff --git a/apps/openmw/mwstate/character.cpp b/apps/openmw/mwstate/character.cpp index 54d2b9dd8..7185ce89d 100644 --- a/apps/openmw/mwstate/character.cpp +++ b/apps/openmw/mwstate/character.cpp @@ -9,6 +9,9 @@ #include +#include +#include + bool MWState::operator< (const Slot& left, const Slot& right) { return left.mTimeStamp ignore + + reader.getRecHeader(); + + slot.mProfile.load (reader); mSlots.push_back (slot); } diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp index 6b67d0d04..c632f61e4 100644 --- a/apps/openmw/mwstate/charactermanager.cpp +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -56,6 +56,16 @@ void MWState::CharacterManager::createCharacter() mCurrent = &mCharacters.back(); } +void MWState::CharacterManager::setCurrentCharacter (const Character *character) +{ + int index = character - &mCharacters[0]; + + if (index<0 || index>=static_cast (mCharacters.size())) + throw std::logic_error ("invalid character"); + + mCurrent = &mCharacters[index]; +} + void MWState::CharacterManager::clearCurrentCharacter() { mCurrent = 0; diff --git a/apps/openmw/mwstate/charactermanager.hpp b/apps/openmw/mwstate/charactermanager.hpp index ce82ccc46..9995393aa 100644 --- a/apps/openmw/mwstate/charactermanager.hpp +++ b/apps/openmw/mwstate/charactermanager.hpp @@ -32,6 +32,8 @@ namespace MWState void createCharacter(); ///< Create new character within saved game management + void setCurrentCharacter (const Character *character); + void clearCurrentCharacter(); std::vector::const_iterator begin() const; diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 9d8fcafb0..12bd29596 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -2,6 +2,7 @@ #include "statemanagerimp.hpp" #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -94,6 +95,27 @@ void MWState::StateManager::saveGame (const Slot *slot) writer.close(); } +void MWState::StateManager::loadGame (const Character *character, const Slot *slot) +{ + if (mState!=State_NoGame) + { + MWBase::Environment::get().getDialogueManager()->clear(); + MWBase::Environment::get().getJournal()->clear(); + mState = State_NoGame; + mCharacterManager.clearCurrentCharacter(); + } + + ESM::ESMReader reader; + reader.open (slot->mPath.string()); + + reader.getRecName(); // don't need to read that here + reader.getRecHeader(); + + /// \todo read saved game data + + mState = State_Running; +} + MWState::Character *MWState::StateManager::getCurrentCharacter() { return mCharacterManager.getCurrentCharacter(); diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 764a09a7e..d387404cb 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -37,6 +37,11 @@ namespace MWState /// /// \note Slot must belong to the current character. + virtual void loadGame (const Character *character, const Slot *slot); + ///< Load a saved game file from \a slot. + /// + /// \note \a slot must belong to \a character. + virtual Character *getCurrentCharacter(); virtual CharacterIterator characterBegin(); From b5f99522c72759149ddb1888815abc3739db84e6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 21 Nov 2013 12:29:24 +0100 Subject: [PATCH 053/889] added a few comments --- apps/openmw/mwbase/statemanager.hpp | 4 ++++ apps/openmw/mwstate/statemanagerimp.hpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index 3369fd3bc..3bd99c315 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -63,8 +63,12 @@ namespace MWBase /// \note \a slot must belong to \a character. virtual MWState::Character *getCurrentCharacter() = 0; + ///< \attention Do not call this function to check if there is a current character. Use + /// characterBegin()!=characterEnd() instead. virtual CharacterIterator characterBegin() = 0; + ///< Any call to SaveGame and getCurrentCharacter can invalidate the returned + /// iterator. virtual CharacterIterator characterEnd() = 0; }; diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index d387404cb..08b0776c1 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -43,8 +43,12 @@ namespace MWState /// \note \a slot must belong to \a character. virtual Character *getCurrentCharacter(); + ///< \attention Do not call this function to check if there is a current character. Use + /// characterBegin()!=characterEnd() instead. virtual CharacterIterator characterBegin(); + ///< Any call to SaveGame and getCurrentCharacter can invalidate the returned + /// iterator. virtual CharacterIterator characterEnd(); }; From 2702d10911a3a2a82da21b9bae95d8a3215521e9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 21 Nov 2013 12:31:04 +0100 Subject: [PATCH 054/889] more comments --- apps/openmw/mwstate/character.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwstate/character.hpp b/apps/openmw/mwstate/character.hpp index 676c04680..d678c165b 100644 --- a/apps/openmw/mwstate/character.hpp +++ b/apps/openmw/mwstate/character.hpp @@ -44,17 +44,19 @@ namespace MWState const Slot *updateSlot (const Slot *slot, const ESM::SavedGame& profile); /// \note Slot must belong to this character. /// - /// \attention The \æ slot pointer will be invalidated by this call. + /// \attention The \a slot pointer will be invalidated by this call. SlotIterator begin() const; ///< First slot is the most recent. Other slots follow in descending order of save date. + /// + /// Any call to createSlot and updateSlot can invalidate the returned iterator. SlotIterator end() const; ESM::SavedGame getSignature() const; ///< Return signature information for this character. /// - /// \todo attention This function must not be called if there are no slots. + /// \attention This function must not be called if there are no slots. }; } From 1875dea243b5fbc5bc365cec9c31f4e2f6cbc0cb Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 21 Nov 2013 13:47:01 +0100 Subject: [PATCH 055/889] Making progress on corrections. --- manual/opencs/files_and_directories.tex | 28 ++++++++++++++----------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/manual/opencs/files_and_directories.tex b/manual/opencs/files_and_directories.tex index 9ffa13828..0fd5932d5 100644 --- a/manual/opencs/files_and_directories.tex +++ b/manual/opencs/files_and_directories.tex @@ -1,35 +1,39 @@ \section{Files and Directories} \subsection{Introduction} -As you imagine, there is no other way to store and distribute computer data without files. You surely know that each file in your file system is identified by the unique path. This is basic knowledge, and applies to both Open{MW} and Open{CS} alike.\\ - -Needless to say, this chapter describes paths and files that are used/created/edited with OpenCS editor.\\ +This section of the manual covers usage of files and directories by the OpenCS. Files and directories are file system concepts, and you are probably already familiar with it. We won't try to explain this concepts, we will just focus on Open{CS}. \subsection{Used terms} %TODO \subsection{Basics} \paragraph{Directories} -Open{MW} and Open{CS} uses multiple directories on file systems, however from end user perspective only two matters. That's it: user path and game path.\\ +Open{MW} and Open{CS} uses multiple directories on file systems. First of, there is a \textbf{user directory} that holds configuration files and few different folders. The location of the user directory is hard coded for each supported operating system.\\ -User path is the per user path that holds both configuration files and the content/project files (described later). Game path on the other hand, is the location of your game (most likely Morrowind\texttrademark). +%TODO list paths. -%TODO give correct path +In addition to this single hard coded directory, both Open{MW} and Open{CS} need a place to seek for actual data files of the game: textures, models, sounds and files that store records of objects in game; dialogues and so one -- so called content files. We support multiple such paths (We call it \textbf{data paths}) as specified in the configuration. Usually one data path points to the directory where original Morrowind\texttrademark is either installed or unpacked. You are free to specify as many data paths as you would like, however, there is one special data path that, as described later, is used to store newly created content files. \paragraph{Content files} -Bethesda Morrowind engine is using two types of files: esm (master) and esp (plugin). The distinction between those is not clear, and often confusing, mostly because large esp files could hurt stability. Open{MW} supports both esm and esp files, but in order to make use of new features of OpenMW one should consider using new file types designed with our engine in mind: game files and addon files together called ``content files``.\\ +Bethesda Morrowind\texttrademark engine is using two types of files: esm (master) and esp (plugin). The distinction between those is not clear, and often confusing. You would expect the esm (master) file is used to specify one master, that is modified by the esps plugins, and indeed: this is the basic idea. However, original expansions also were made as esm files, even though they essentially could be described as a really large plugins, and therefore rather use esp files. There were technical reasons behind this decision -- somewhat valid in the case of original engine, but clearly it's better to create a system that can be used is more sensible way. Open{MW} achieves this with our own content file types.\\ +We support both esm and esp files, but in order to make use of new features of OpenMW one should consider using new file types designed with our engine in mind: game files and addon files together called ``content files``.\\ -Game and Addon files are concept descended from old esm/esp system, but much more flexible and cleaner. We can describe the difference between those two file types use case with clean statements. If you want to make new game using Open{MW} as engine (so called ``total conversion'') you should create a game file. If you want to create a addon for existing game file -- simply create addon file. The addon size is not a factor here. The only distinction you should consider is if your project is about changing other game, or creating a new one. Simple as that.\\ +The actual creating of new files is described in the next chapter. Here We are gonna focus only on details that you need to know in order to create your first Open{CS} file while full understanding your needs. For now let's jut remember that content files are created inside the user directory, in the the \textbf{data} subfolder (that is the one special data path mentioned earlier).\\ +\subparagraph{Open{MW} content files} +Game and Addon files are concept somewhat similar to the old esm/esp, only in the way it should be from the very beginning. Nothing easier to describe. If you want to make new game using Open{MW} as engine (so called ``total conversion'') you should create a game file. If you want to create a addon for existing game file -- simply create addon file. Nothing else matters: The only distinction you should consider is if your project is about changing other game, or creating a new one. Simple as that.\\ -%TODO describe some characteristics, like file extensions. What about project files? +Other simple thing about content files are extensions. We are using .omwaddon for addon files and .omwgame for game files.\\ -The actual creating of new files is described in the next chapter. Here We are gonna focus only on details that you need to know in order to create your first Open{CS} file while full understanding your needs. For now let's jut remember that content files are created inside the user directory, in the the \textbf{data} subfolder. Open{CS} scans for content files not only data subfolder but also the game path, most likely directory where you installed or unpacked Morrowind\texttrademark. However it is probably better to not pollute game path, and use only data path for Open{MW} content files, both when creating and only playing with others content files.\\ +%TODO describe what content files contains. and what not. + +\subparagraph{Morrowind content files} +Using our content files is recommended solution for projects that are intended to used with Open{MW} engine. However some players wish to use original Morrowind engine, even with it large flaws and lacking features\footnote{If this is actually wrong, We are very successful project. Yay!}. Also, more than ten years \paragraph{Project files} -Project files act as containers for data not used by the Openm{MW} game engine itself, but still useful for OpenCS. The shining example of this data category are without doubt record filters (described in the later section of the manual you are reading currently). As a mod author you probably don't need or want to distribute project files among players, but for sure you will want to aid all your colleagues working together with you on, well project. As you would imagine, project file makes sense only in combination with actual content files. In fact, each time you start to work on new content file and project file was not found it will be created.\\ +Project files act as containers for data not used by the Openm{MW} game engine itself, but still useful for OpenCS. The shining example of this data category are without doubt record filters (described in the later section of the manual you are reading currently). As a mod author you probably don't need and/or want to distribute project files among players, but for sure you will want to aid all your colleagues working together with you on, well project. As you would imagine, project file makes sense only in combination with actual content files. In fact, each time you start to work on new content file and project file was not found, it will be created.\\ Project files extension is, to not surprise ``.project''. The whole name of the project file is the whole name of the content file with appended extensions. For instance awesomeswords.omwaddon file is associated with awesomeswords.omwaddon.project file.\\ %TODO where are they stored. -Project files are stored inside the user directory, in the \textbf{projects} subfolder. This is the path location for both freshly created project files, and the place where Open{CS} look for already existing files. +Project files are stored inside the user directory, in the \textbf{projects} subfolder. This is the path location for both freshly created project files, and the place where Open{CS} look for already existing files.\\ \paragraph{Dependencies} Since addon is supposed to change the game it is logical that it also depends on the said game. It simply can't work otherwise. Just think about it: your modification is changing prize of the iron sword. But what if there is no iron sword in game? That's right: we get nonsense. What you want to do is to tie your addon to the files you are changing. Those can be either game files (expansion island for a game) or other addon files (house on the said island). It is a a good idea to be dependent only on files that are really changed in your addon obviously, but sadly there is no other way to achieve this than knowing what you want to do.\\ From b52b6a8e7e426304f01812bff4d3d08fbaaaf704 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 21 Nov 2013 13:50:23 +0100 Subject: [PATCH 056/889] Small correction, added tiny comment. --- manual/opencs/files_and_directories.tex | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/manual/opencs/files_and_directories.tex b/manual/opencs/files_and_directories.tex index 0fd5932d5..85d6ccec9 100644 --- a/manual/opencs/files_and_directories.tex +++ b/manual/opencs/files_and_directories.tex @@ -26,10 +26,11 @@ Other simple thing about content files are extensions. We are using .omwaddon fo %TODO describe what content files contains. and what not. \subparagraph{Morrowind content files} -Using our content files is recommended solution for projects that are intended to used with Open{MW} engine. However some players wish to use original Morrowind engine, even with it large flaws and lacking features\footnote{If this is actually wrong, We are very successful project. Yay!}. Also, more than ten years +Using our content files is recommended solution for projects that are intended to used with Open{MW} engine. However some players wish to use original Morrowind engine, even with it large flaws and lacking features\footnote{If this is actually wrong, We are very successful project. Yay!}. Also, more than ten years %not finished \paragraph{Project files} -Project files act as containers for data not used by the Openm{MW} game engine itself, but still useful for OpenCS. The shining example of this data category are without doubt record filters (described in the later section of the manual you are reading currently). As a mod author you probably don't need and/or want to distribute project files among players, but for sure you will want to aid all your colleagues working together with you on, well project. As you would imagine, project file makes sense only in combination with actual content files. In fact, each time you start to work on new content file and project file was not found, it will be created.\\ +Project files act as containers for data not used by the Openm{MW} game engine itself, but still useful for OpenCS. The shining example of this data category are without doubt record filters (described in the later section of the manual you are reading currently). As a mod author you probably don't need and/or want to distribute project files at all, they are meant to be used only by you.\\ +As you would imagine, project file makes sense only in combination with actual content files. In fact, each time you start to work on new content file and project file was not found, it will be created.\\ Project files extension is, to not surprise ``.project''. The whole name of the project file is the whole name of the content file with appended extensions. For instance awesomeswords.omwaddon file is associated with awesomeswords.omwaddon.project file.\\ %TODO where are they stored. From cf79a83d4fcc06a4893176fb9f1ba51bf6269b6e Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 21 Nov 2013 19:07:54 +0100 Subject: [PATCH 057/889] Avoid recreating widgets in MainMenu::updateMenu. Fix crash when pressing new game due to the button being destroyed from within it's own delegate. --- apps/openmw/mwgui/mainmenu.cpp | 61 +++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index ac272d1c4..962b5dd31 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -40,22 +40,23 @@ namespace MWGui void MainMenu::onButtonClicked(MyGUI::Widget *sender) { + std::string name = *sender->getUserData(); MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); - if (sender == mButtons["return"]) + if (name == "return") { MWBase::Environment::get().getSoundManager ()->resumeSounds (MWBase::SoundManager::Play_TypeSfx); MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); } - else if (sender == mButtons["options"]) + else if (name == "options") MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings); - else if (sender == mButtons["exitgame"]) + else if (name == "exitgame") MWBase::Environment::get().getStateManager()->requestQuit(); - else if (sender == mButtons["newgame"]) + else if (name == "newgame") { MWBase::Environment::get().getStateManager()->newGame(); } - else if (sender == mButtons["loadgame"]) + else if (name == "loadgame") { // for testing purpose, pick the first slot of the first character: const MWState::Character& character = @@ -68,7 +69,7 @@ namespace MWGui // dialog->setLoadOrSave(true); // dialog->setVisible(true); } - else if (sender == mButtons["savegame"]) + else if (name == "savegame") { // for testing purpose, save into a new slot: MWBase::Environment::get().getStateManager()->saveGame (0); @@ -84,10 +85,9 @@ namespace MWGui setCoord(0,0, mWidth, mHeight); - if (mButtonBox) - MyGUI::Gui::getInstance ().destroyWidget(mButtonBox); + if (!mButtonBox) + mButtonBox = mMainWidget->createWidget("", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default); - mButtonBox = mMainWidget->createWidget("", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default); int curH = 0; MWBase::StateManager::State state = MWBase::Environment::get().getStateManager()->getState(); @@ -110,28 +110,41 @@ namespace MWGui //buttons.push_back("credits"); buttons.push_back("exitgame"); - int maxwidth = 0; - - mButtons.clear(); + // Create new buttons if needed for (std::vector::iterator it = buttons.begin(); it != buttons.end(); ++it) { - MWGui::ImageButton* button = mButtonBox->createWidget - ("ImageBox", MyGUI::IntCoord(0, curH, 0, 0), MyGUI::Align::Default); - button->setProperty("ImageHighlighted", "textures\\menu_" + *it + "_over.dds"); - button->setProperty("ImageNormal", "textures\\menu_" + *it + ".dds"); - button->setProperty("ImagePushed", "textures\\menu_" + *it + "_pressed.dds"); - MyGUI::IntSize requested = button->getRequestedSize(); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::onButtonClicked); - mButtons[*it] = button; - curH += requested.height; + if (mButtons.find(*it) == mButtons.end()) + { + MWGui::ImageButton* button = mButtonBox->createWidget + ("ImageBox", MyGUI::IntCoord(0, curH, 0, 0), MyGUI::Align::Default); + button->setProperty("ImageHighlighted", "textures\\menu_" + *it + "_over.dds"); + button->setProperty("ImageNormal", "textures\\menu_" + *it + ".dds"); + button->setProperty("ImagePushed", "textures\\menu_" + *it + "_pressed.dds"); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::onButtonClicked); + button->setUserData(std::string(*it)); + mButtons[*it] = button; + } + } + // Start by hiding all buttons + int maxwidth = 0; + for (std::map::iterator it = mButtons.begin(); it != mButtons.end(); ++it) + { + it->second->setVisible(false); + MyGUI::IntSize requested = it->second->getRequestedSize(); if (requested.width > maxwidth) maxwidth = requested.width; } - for (std::map::iterator it = mButtons.begin(); it != mButtons.end(); ++it) + + // Now show and position the ones we want + for (std::vector::iterator it = buttons.begin(); it != buttons.end(); ++it) { - MyGUI::IntSize requested = it->second->getRequestedSize(); - it->second->setCoord((maxwidth-requested.width) / 2, it->second->getTop(), requested.width, requested.height); + assert(mButtons.find(*it) != mButtons.end()); + MWGui::ImageButton* button = mButtons[*it]; + button->setVisible(true); + MyGUI::IntSize requested = button->getRequestedSize(); + button->setCoord((maxwidth-requested.width) / 2, curH, requested.width, requested.height); + curH += requested.height; } mButtonBox->setCoord (mWidth/2 - maxwidth/2, mHeight/2 - curH/2, maxwidth, curH); From 45203c160fa42b75d70c39802c1c27bd310ec142 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 21 Nov 2013 20:07:37 +0100 Subject: [PATCH 058/889] rearranging, correcting, small changes -- nothing interesting. --- manual/opencs/files_and_directories.tex | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/manual/opencs/files_and_directories.tex b/manual/opencs/files_and_directories.tex index 85d6ccec9..3d418d597 100644 --- a/manual/opencs/files_and_directories.tex +++ b/manual/opencs/files_and_directories.tex @@ -17,7 +17,6 @@ In addition to this single hard coded directory, both Open{MW} and Open{CS} need Bethesda Morrowind\texttrademark engine is using two types of files: esm (master) and esp (plugin). The distinction between those is not clear, and often confusing. You would expect the esm (master) file is used to specify one master, that is modified by the esps plugins, and indeed: this is the basic idea. However, original expansions also were made as esm files, even though they essentially could be described as a really large plugins, and therefore rather use esp files. There were technical reasons behind this decision -- somewhat valid in the case of original engine, but clearly it's better to create a system that can be used is more sensible way. Open{MW} achieves this with our own content file types.\\ We support both esm and esp files, but in order to make use of new features of OpenMW one should consider using new file types designed with our engine in mind: game files and addon files together called ``content files``.\\ -The actual creating of new files is described in the next chapter. Here We are gonna focus only on details that you need to know in order to create your first Open{CS} file while full understanding your needs. For now let's jut remember that content files are created inside the user directory, in the the \textbf{data} subfolder (that is the one special data path mentioned earlier).\\ \subparagraph{Open{MW} content files} Game and Addon files are concept somewhat similar to the old esm/esp, only in the way it should be from the very beginning. Nothing easier to describe. If you want to make new game using Open{MW} as engine (so called ``total conversion'') you should create a game file. If you want to create a addon for existing game file -- simply create addon file. Nothing else matters: The only distinction you should consider is if your project is about changing other game, or creating a new one. Simple as that.\\ @@ -26,7 +25,16 @@ Other simple thing about content files are extensions. We are using .omwaddon fo %TODO describe what content files contains. and what not. \subparagraph{Morrowind content files} -Using our content files is recommended solution for projects that are intended to used with Open{MW} engine. However some players wish to use original Morrowind engine, even with it large flaws and lacking features\footnote{If this is actually wrong, We are very successful project. Yay!}. Also, more than ten years %not finished +Using our content files is recommended solution for projects that are intended to used with Open{MW} engine. However some players wish to use original Morrowind engine, even with it large flaws and lacking features\footnote{If this is actually wrong, We are very successful project. Yay!}. Also, since 2002 thousands of esp/ems files were created, some with really outstanding content. Because of this Open{CS} simply has no other choice but support esp/esm files. However, if you decided to choose esp/esm file instead using our own content file types you are most likely aim at the original engine compatibility. This subject is covered in the very last section of this manual.\\ %not finished TODO add the said section. Most likely when more features are present. + +The actual creation of new files is described in the next chapter. Here We are gonna focus only on details that you need to know in order to create your first Open{CS} file while full understanding your needs. For now let's jut remember that content files are created inside the user directory, in the the \textbf{data} subfolder (that is the one special data directory mentioned earlier).\\ + +\subparagraph{Dependencies} +Since addon is supposed to change the game it is logical that it also depends on the said game. It simply can't work otherwise. Just think about it: your modification is changing prize of the iron sword. But what if there is no iron sword in game? That's right: we get nonsense. What you want to do is to tie your addon to the files you are changing. Those can be either game files (expansion island for a game) or other addon files (house on the said island). It is a a good idea to be dependent only on files that are really changed in your addon obviously, but sadly there is no other way to achieve this than knowing what you want to do. Again, please remember that this section of the manual does not cover creating the content files -- it is only theoretical introduction to the subject. For now just keep in mind that dependencies exist, and is up to you what to decide if your content file should depend on other content file.\\ + +Game files are not intend to have any dependencies for a very simple reasons: player is using only one game file (excluding original and dirty esp/esm system) at the time and therefore no game file can depend on other game file, and since game file makes the base for addon files -- it can't depend on addon files.\\ + +\subparagraph{Loading order} \paragraph{Project files} Project files act as containers for data not used by the Openm{MW} game engine itself, but still useful for OpenCS. The shining example of this data category are without doubt record filters (described in the later section of the manual you are reading currently). As a mod author you probably don't need and/or want to distribute project files at all, they are meant to be used only by you.\\ @@ -36,12 +44,5 @@ Project files extension is, to not surprise ``.project''. The whole name of the %TODO where are they stored. Project files are stored inside the user directory, in the \textbf{projects} subfolder. This is the path location for both freshly created project files, and the place where Open{CS} look for already existing files.\\ -\paragraph{Dependencies} -Since addon is supposed to change the game it is logical that it also depends on the said game. It simply can't work otherwise. Just think about it: your modification is changing prize of the iron sword. But what if there is no iron sword in game? That's right: we get nonsense. What you want to do is to tie your addon to the files you are changing. Those can be either game files (expansion island for a game) or other addon files (house on the said island). It is a a good idea to be dependent only on files that are really changed in your addon obviously, but sadly there is no other way to achieve this than knowing what you want to do.\\ - -Game files are not intend to have any dependencies for a very simple reasons: player is using only one game file at the time and therefore no game file can depend on other game file, and since game file makes the base for addon files -- it can't depend on addon files. - -\paragraph{Loading order} - \paragraph{Resources files} %textures, sounds, whatever \ No newline at end of file From 2e87cbc2313c568510de3b70cca4cd52c5f9016a Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 21 Nov 2013 20:24:58 +0100 Subject: [PATCH 059/889] Add basic functionality to SaveGameDialog --- apps/openmw/mwbase/statemanager.hpp | 4 +- apps/openmw/mwgui/mainmenu.cpp | 34 ++-- apps/openmw/mwgui/mainmenu.hpp | 5 + apps/openmw/mwgui/savegamedialog.cpp | 179 ++++++++++++++++++++++ apps/openmw/mwgui/savegamedialog.hpp | 12 ++ apps/openmw/mwstate/statemanagerimp.cpp | 27 +++- files/mygui/openmw_savegame_dialog.layout | 9 -- 7 files changed, 236 insertions(+), 34 deletions(-) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index 3bd99c315..e9d10a796 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -63,8 +63,8 @@ namespace MWBase /// \note \a slot must belong to \a character. virtual MWState::Character *getCurrentCharacter() = 0; - ///< \attention Do not call this function to check if there is a current character. Use - /// characterBegin()!=characterEnd() instead. + ///< \attention Do not call this function to check if there is a current character. + /// Instead, assume there is a character if getState() == Running. virtual CharacterIterator characterBegin() = 0; ///< Any call to SaveGame and getCurrentCharacter can invalidate the returned diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 962b5dd31..ff8ab8c93 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -18,10 +18,16 @@ namespace MWGui MainMenu::MainMenu(int w, int h) : OEngine::GUI::Layout("openmw_mainmenu.layout") , mButtonBox(0), mWidth (w), mHeight (h) + , mSaveGameDialog(NULL) { updateMenu(); } + MainMenu::~MainMenu() + { + delete mSaveGameDialog; + } + void MainMenu::onResChange(int w, int h) { mWidth = w; @@ -56,27 +62,15 @@ namespace MWGui MWBase::Environment::get().getStateManager()->newGame(); } - else if (name == "loadgame") + else { - // for testing purpose, pick the first slot of the first character: - const MWState::Character& character = - *MWBase::Environment::get().getStateManager()->characterBegin(); - const MWState::Slot& slot = *character.begin(); - - MWBase::Environment::get().getStateManager()->loadGame (&character, &slot); - -// MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); -// dialog->setLoadOrSave(true); -// dialog->setVisible(true); - } - else if (name == "savegame") - { - // for testing purpose, save into a new slot: - MWBase::Environment::get().getStateManager()->saveGame (0); - -// MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); -// dialog->setLoadOrSave(false); -// dialog->setVisible(true); + if (!mSaveGameDialog) + mSaveGameDialog = new SaveGameDialog(); + if (name == "loadgame") + mSaveGameDialog->setLoadOrSave(true); + else if (name == "savegame") + mSaveGameDialog->setLoadOrSave(false); + mSaveGameDialog->setVisible(true); } } diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index 511f72672..6d52f26d5 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -5,6 +5,8 @@ namespace MWGui { + class SaveGameDialog; + class MainMenu : public OEngine::GUI::Layout { int mWidth; @@ -13,6 +15,7 @@ namespace MWGui public: MainMenu(int w, int h); + ~MainMenu(); void onResChange(int w, int h); @@ -27,6 +30,8 @@ namespace MWGui void onButtonClicked (MyGUI::Widget* sender); void updateMenu(); + + SaveGameDialog* mSaveGameDialog; }; } diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index a1acd3588..648dd4683 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -1,12 +1,68 @@ #include "savegamedialog.hpp" #include "widgets.hpp" +#include "../mwbase/statemanager.hpp" +#include "../mwbase/environment.hpp" + + +#include "../mwstate/character.hpp" + +namespace +{ +std::string getMonth(int m) +{ + std::string month; + switch (m) { + case 0: + month = "#{sMonthMorningstar}"; + break; + case 1: + month = "#{sMonthSunsdawn}"; + break; + case 2: + month = "#{sMonthFirstseed}"; + break; + case 3: + month = "#{sMonthRainshand}"; + break; + case 4: + month = "#{sMonthSecondseed}"; + break; + case 5: + month = "#{sMonthMidyear}"; + break; + case 6: + month = "#{sMonthSunsheight}"; + break; + case 7: + month = "#{sMonthLastseed}"; + break; + case 8: + month = "#{sMonthHeartfire}"; + break; + case 9: + month = "#{sMonthFrostfall}"; + break; + case 10: + month = "#{sMonthSunsdusk}"; + break; + case 11: + month = "#{sMonthEveningstar}"; + break; + default: + break; + } + return month; +} +} namespace MWGui { SaveGameDialog::SaveGameDialog() : WindowModal("openmw_savegame_dialog.layout") + , mSaving(true) + , mCurrentCharacter(NULL) { getWidget(mScreenshot, "Screenshot"); getWidget(mCharacterSelection, "SelectCharacter"); @@ -18,21 +74,57 @@ namespace MWGui getWidget(mSpacer, "Spacer"); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onOkButtonClicked); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked); + mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected); + mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected); } void SaveGameDialog::open() { + WindowModal::open(); + center(); + + MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager(); + if (mgr->characterBegin() == mgr->characterEnd()) + return; + + // If we are running, there must be a current character + if (mgr->getState() == MWBase::StateManager::State_Running) + { + mCurrentCharacter = mgr->getCurrentCharacter(); + } + + mCharacterSelection->removeAllItems(); + for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it) + { + std::stringstream title; + title << it->getSignature().mPlayerName; + title << " (Level " << it->getSignature().mPlayerLevel << " " << it->getSignature().mPlayerClass << ")"; + + mCharacterSelection->addItem (title.str()); + + if (mCurrentCharacter == &*it) + mCharacterSelection->setIndexSelected(mCharacterSelection->getItemCount()-1); + } + + fillSaveList(); + } void SaveGameDialog::setLoadOrSave(bool load) { + mSaving = !load; mSaveNameEdit->setVisible(!load); mCharacterSelection->setUserString("Hidden", load ? "false" : "true"); mCharacterSelection->setVisible(load); mSpacer->setUserString("Hidden", load ? "false" : "true"); + if (!load) + { + mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(); + } + center(); } @@ -43,7 +135,94 @@ namespace MWGui void SaveGameDialog::onOkButtonClicked(MyGUI::Widget *sender) { + // Get the selected slot, if any + unsigned int i=0; + const MWState::Slot* slot = NULL; + for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it,++i) + { + if (i == mSaveList->getIndexSelected()) + slot = &*it; + } + + if (mSaving) + { + MWBase::Environment::get().getStateManager()->saveGame (slot); + } + else + { + MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, slot); + } + setVisible(false); } + void SaveGameDialog::onCharacterSelected(MyGUI::ComboBox *sender, size_t pos) + { + MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager(); + + unsigned int i=0; + const MWState::Character* character = NULL; + for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it, ++i) + { + if (i == pos) + character = &*it; + } + assert(character && "Can't find selected character"); + + mCurrentCharacter = character; + fillSaveList(); + } + + void SaveGameDialog::fillSaveList() + { + mSaveList->removeAllItems(); + if (!mCurrentCharacter) + return; + for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it) + { + mSaveList->addItem(it->mPath.string()); + } + onSlotSelected(mSaveList, MyGUI::ITEM_NONE); + } + + void SaveGameDialog::onSlotSelected(MyGUI::ListBox *sender, size_t pos) + { + if (pos == MyGUI::ITEM_NONE) + { + mInfoText->setCaption(""); + return; + } + + const MWState::Slot* slot = NULL; + unsigned int i=0; + for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it, ++i) + { + if (i == pos) + slot = &*it; + } + assert(slot && "Can't find selected slot"); + + std::stringstream text; + time_t time = slot->mTimeStamp; + struct tm* timeinfo; + timeinfo = localtime(&time); + + text << asctime(timeinfo) << "\n"; + text << "Level " << slot->mProfile.mPlayerLevel << "\n"; + text << slot->mProfile.mPlayerCell << "\n"; + //text << "Time played: " << slot->mProfile.mTimePlayed << "\n"; + + int hour = int(slot->mProfile.mInGameTime.mGameHour); + bool pm = hour >= 12; + if (hour >= 13) hour -= 12; + if (hour == 0) hour = 12; + + text << + slot->mProfile.mInGameTime.mDay << " " + << getMonth(slot->mProfile.mInGameTime.mMonth) + << " " << hour << " " << (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); + + mInfoText->setCaptionWithReplacing(text.str()); + + } } diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp index 1a3178ef3..2a188061c 100644 --- a/apps/openmw/mwgui/savegamedialog.hpp +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -3,6 +3,11 @@ #include "windowbase.hpp" +namespace MWState +{ + class Character; +} + namespace MWGui { @@ -17,10 +22,15 @@ namespace MWGui void onCancelButtonClicked (MyGUI::Widget* sender); void onOkButtonClicked (MyGUI::Widget* sender); + void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos); + void onSlotSelected (MyGUI::ListBox* sender, size_t pos); + + void fillSaveList(); private: MyGUI::ImageBox* mScreenshot; + bool mSaving; MyGUI::ComboBox* mCharacterSelection; MyGUI::EditBox* mInfoText; @@ -30,6 +40,8 @@ namespace MWGui MyGUI::EditBox* mSaveNameEdit; MyGUI::Widget* mSpacer; + const MWState::Character* mCurrentCharacter; + }; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 12bd29596..6aba93dc8 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -72,9 +72,28 @@ void MWState::StateManager::saveGame (const Slot *slot) /// \todo store content file list profile.mPlayerName = player.getClass().getName (player); profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel(); - profile.mPlayerClass = player.get()->mBase->mId; - /// \todo player cell - /// \todo gamehour + profile.mPlayerClass = player.get()->mBase->mClass; + + std::string cellName; + if (player.getCell()->mCell->isExterior()) + { + if (player.getCell()->mCell->mName != "") + cellName = player.getCell()->mCell->mName; + else + { + const ESM::Region* region = + MWBase::Environment::get().getWorld()->getStore().get().search(player.getCell()->mCell->mRegion); + if (region) + cellName = region->mName; + else + cellName = MWBase::Environment::get().getWindowManager()->getGameSettingString("sDefaultCellname", "Wilderness"); + } + } + else + cellName = player.getCell()->mCell->mName; + profile.mPlayerCell = cellName; + + profile.mInGameTime.mGameHour = world.getTimeStamp().getHour(); profile.mInGameTime.mDay = world.getDay(); profile.mInGameTime.mMonth = world.getMonth(); /// \todo year @@ -113,6 +132,8 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl /// \todo read saved game data + mCharacterManager.setCurrentCharacter(character); + mState = State_Running; } diff --git a/files/mygui/openmw_savegame_dialog.layout b/files/mygui/openmw_savegame_dialog.layout index 18de6a239..ceb1a8428 100644 --- a/files/mygui/openmw_savegame_dialog.layout +++ b/files/mygui/openmw_savegame_dialog.layout @@ -17,8 +17,6 @@ - - @@ -26,11 +24,6 @@ - - - - - @@ -51,8 +44,6 @@ - - From 05245c15af9e660cea93a2a02eaad9be89c420ff Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 22 Nov 2013 09:44:32 +0100 Subject: [PATCH 060/889] Nothing interesting. Just keeping sync. --- manual/opencs/files_and_directories.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manual/opencs/files_and_directories.tex b/manual/opencs/files_and_directories.tex index 3d418d597..1ef53b137 100644 --- a/manual/opencs/files_and_directories.tex +++ b/manual/opencs/files_and_directories.tex @@ -42,7 +42,7 @@ As you would imagine, project file makes sense only in combination with actual c Project files extension is, to not surprise ``.project''. The whole name of the project file is the whole name of the content file with appended extensions. For instance awesomeswords.omwaddon file is associated with awesomeswords.omwaddon.project file.\\ %TODO where are they stored. -Project files are stored inside the user directory, in the \textbf{projects} subfolder. This is the path location for both freshly created project files, and the place where Open{CS} look for already existing files.\\ +Project files are stored inside the user directory, in the \textbf{projects} subfolder. This is the path location for both freshly created project files, and a place where Open{CS} looks for already existing files.\\ \paragraph{Resources files} %textures, sounds, whatever \ No newline at end of file From c5acfbf1337cf753c5be5d32877238d6ab67a7bd Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 22 Nov 2013 14:44:39 +0100 Subject: [PATCH 061/889] Started introduction to the resources files. --- manual/opencs/files_and_directories.tex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/manual/opencs/files_and_directories.tex b/manual/opencs/files_and_directories.tex index 1ef53b137..032a7cdce 100644 --- a/manual/opencs/files_and_directories.tex +++ b/manual/opencs/files_and_directories.tex @@ -45,4 +45,5 @@ Project files extension is, to not surprise ``.project''. The whole name of the Project files are stored inside the user directory, in the \textbf{projects} subfolder. This is the path location for both freshly created project files, and a place where Open{CS} looks for already existing files.\\ \paragraph{Resources files} -%textures, sounds, whatever \ No newline at end of file +%textures, sounds, whatever +Unless we are talking about the fully text based game, like Zork or Rogue, you are expecting that a video game is using some media files: models with textures, pictures acting as icons, sounds and everything else. Since content files, no matter if it is esp, esm or new Open{MW} file type do not contain any of those, it's clear that they have to be deliver with a different file. It is also clear that this, let's call it ``resources file``, have to be supported by the engine. Without code handling those files, it is nothing more than a mathematical abstraction -- something, that lacks meaning for human beings\footnote{Unless we call programmers a human beings.}. Therefore this section must cover ways to add resources files to your content file, and point out what is supported. We are going to do just that. Later, you will learn how to make use of those files in your content. \ No newline at end of file From 18a3b38fb49e42d410fe2729fd26d9db99ee7abb Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 24 Nov 2013 14:42:58 +0100 Subject: [PATCH 062/889] when scanning saved game directory, reject characters without a valid saved game --- apps/openmw/mwstate/charactermanager.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp index c632f61e4..2b9c49fcc 100644 --- a/apps/openmw/mwstate/charactermanager.cpp +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -23,7 +23,9 @@ MWState::CharacterManager::CharacterManager (const boost::filesystem::path& save if (boost::filesystem::is_directory (characterDir)) { Character character (characterDir); - mCharacters.push_back (character); + + if (character.begin()!=character.end()) + mCharacters.push_back (character); } std::istringstream stream (characterDir.filename().string()); From 7efac4c9a52127c806979182d40d36501dc7174f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 24 Nov 2013 15:05:00 +0100 Subject: [PATCH 063/889] fixed handling of characters without a valid slot --- apps/openmw/mwbase/statemanager.hpp | 2 -- apps/openmw/mwgui/savegamedialog.cpp | 21 ++++++++++----------- apps/openmw/mwstate/statemanagerimp.hpp | 2 -- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index e9d10a796..7101be25d 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -63,8 +63,6 @@ namespace MWBase /// \note \a slot must belong to \a character. virtual MWState::Character *getCurrentCharacter() = 0; - ///< \attention Do not call this function to check if there is a current character. - /// Instead, assume there is a character if getState() == Running. virtual CharacterIterator characterBegin() = 0; ///< Any call to SaveGame and getCurrentCharacter can invalidate the returned diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 648dd4683..2ea83cfa3 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -89,23 +89,22 @@ namespace MWGui if (mgr->characterBegin() == mgr->characterEnd()) return; - // If we are running, there must be a current character - if (mgr->getState() == MWBase::StateManager::State_Running) - { - mCurrentCharacter = mgr->getCurrentCharacter(); - } + mCurrentCharacter = mgr->getCurrentCharacter(); mCharacterSelection->removeAllItems(); for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it) { - std::stringstream title; - title << it->getSignature().mPlayerName; - title << " (Level " << it->getSignature().mPlayerLevel << " " << it->getSignature().mPlayerClass << ")"; + if (it->begin()!=it->end()) + { + std::stringstream title; + title << it->getSignature().mPlayerName; + title << " (Level " << it->getSignature().mPlayerLevel << " " << it->getSignature().mPlayerClass << ")"; - mCharacterSelection->addItem (title.str()); + mCharacterSelection->addItem (title.str()); - if (mCurrentCharacter == &*it) - mCharacterSelection->setIndexSelected(mCharacterSelection->getItemCount()-1); + if (mgr->getCurrentCharacter() == &*it) + mCharacterSelection->setIndexSelected(mCharacterSelection->getItemCount()-1); + } } fillSaveList(); diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 08b0776c1..a380c9fee 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -43,8 +43,6 @@ namespace MWState /// \note \a slot must belong to \a character. virtual Character *getCurrentCharacter(); - ///< \attention Do not call this function to check if there is a current character. Use - /// characterBegin()!=characterEnd() instead. virtual CharacterIterator characterBegin(); ///< Any call to SaveGame and getCurrentCharacter can invalidate the returned From 67cd0887e64afa88ec5139fcb4b8a60e74bd8a3f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 24 Nov 2013 15:19:56 +0100 Subject: [PATCH 064/889] added description field to saved game record; make use of description in GUI --- apps/openmw/mwbase/statemanager.hpp | 3 ++- apps/openmw/mwgui/savegamedialog.cpp | 6 ++++-- apps/openmw/mwstate/statemanagerimp.cpp | 3 ++- apps/openmw/mwstate/statemanagerimp.hpp | 2 +- components/esm/savedgame.cpp | 2 ++ components/esm/savedgame.hpp | 1 + 6 files changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index 7101be25d..f1f130824 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -2,6 +2,7 @@ #define GAME_MWSTATE_STATEMANAGER_H #include +#include namespace MWState { @@ -52,7 +53,7 @@ namespace MWBase virtual void endGame() = 0; - virtual void saveGame (const MWState::Slot *slot = 0) = 0; + virtual void saveGame (const std::string& description, const MWState::Slot *slot = 0) = 0; ///< Write a saved game to \a slot or create a new slot if \a slot == 0. /// /// \note Slot must belong to the current character. diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 2ea83cfa3..38574e2fd 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -83,6 +83,8 @@ namespace MWGui { WindowModal::open(); + mSaveNameEdit->setCaption (""); + center(); MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager(); @@ -145,7 +147,7 @@ namespace MWGui if (mSaving) { - MWBase::Environment::get().getStateManager()->saveGame (slot); + MWBase::Environment::get().getStateManager()->saveGame (mSaveNameEdit->getCaption(), slot); } else { @@ -179,7 +181,7 @@ namespace MWGui return; for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it) { - mSaveList->addItem(it->mPath.string()); + mSaveList->addItem(it->mProfile.mDescription); } onSlotSelected(mSaveList, MyGUI::ITEM_NONE); } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 6aba93dc8..9d8cf2a81 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -61,7 +61,7 @@ void MWState::StateManager::endGame() mState = State_Ended; } -void MWState::StateManager::saveGame (const Slot *slot) +void MWState::StateManager::saveGame (const std::string& description, const Slot *slot) { ESM::SavedGame profile; @@ -98,6 +98,7 @@ void MWState::StateManager::saveGame (const Slot *slot) profile.mInGameTime.mMonth = world.getMonth(); /// \todo year /// \todo time played + profile.mDescription = description; if (!slot) slot = mCharacterManager.getCurrentCharacter()->createSlot (profile); diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index a380c9fee..abf7e123b 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -32,7 +32,7 @@ namespace MWState virtual void endGame(); - virtual void saveGame (const Slot *slot = 0); + virtual void saveGame (const std::string& description, const Slot *slot = 0); ///< Write a saved game to \a slot or create a new slot if \a slot == 0. /// /// \note Slot must belong to the current character. diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index 5a5fc9fa8..8169b01a1 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -15,6 +15,7 @@ void ESM::SavedGame::load (ESMReader &esm) mPlayerCell = esm.getHNString("PCEL"); esm.getHNT (mInGameTime, "TSTM", 16); esm.getHNT (mTimePlayed, "TIME"); + mDescription = esm.getHNString ("DESC"); while (esm.isNextSub ("DEPE")) mContentFiles.push_back (esm.getHString()); @@ -28,6 +29,7 @@ void ESM::SavedGame::save (ESMWriter &esm) const esm.writeHNCString ("PCEL", mPlayerCell); esm.writeHNT ("TSTM", mInGameTime, 16); esm.writeHNT ("TIME", mTimePlayed); + esm.writeHNCString ("DESC", mDescription); for (std::vector::const_iterator iter (mContentFiles.begin()); iter!=mContentFiles.end(); ++iter) diff --git a/components/esm/savedgame.hpp b/components/esm/savedgame.hpp index e712e8f1f..6c11d318f 100644 --- a/components/esm/savedgame.hpp +++ b/components/esm/savedgame.hpp @@ -30,6 +30,7 @@ namespace ESM std::string mPlayerCell; TimeStamp mInGameTime; double mTimePlayed; + std::string mDescription; /// \todo add field for screenshot From e3670cff8a14bc6d028e0116d5bf500064660358 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 24 Nov 2013 16:58:41 +0100 Subject: [PATCH 065/889] improved character selection logic --- apps/openmw/mwbase/statemanager.hpp | 3 ++- apps/openmw/mwgui/savegamedialog.cpp | 20 +++++++++++++------- apps/openmw/mwstate/charactermanager.cpp | 4 ++-- apps/openmw/mwstate/charactermanager.hpp | 4 ++-- apps/openmw/mwstate/statemanagerimp.cpp | 4 ++-- apps/openmw/mwstate/statemanagerimp.hpp | 3 ++- 6 files changed, 23 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index f1f130824..f376d72c1 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -63,7 +63,8 @@ namespace MWBase /// /// \note \a slot must belong to \a character. - virtual MWState::Character *getCurrentCharacter() = 0; + virtual MWState::Character *getCurrentCharacter (bool create = true) = 0; + ///< \param create Create a new character, if there is no current character. virtual CharacterIterator characterBegin() = 0; ///< Any call to SaveGame and getCurrentCharacter can invalidate the returned diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 38574e2fd..2ce0f0fbf 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -91,9 +91,10 @@ namespace MWGui if (mgr->characterBegin() == mgr->characterEnd()) return; - mCurrentCharacter = mgr->getCurrentCharacter(); + mCurrentCharacter = mgr->getCurrentCharacter (false); mCharacterSelection->removeAllItems(); + for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it) { if (it->begin()!=it->end()) @@ -104,7 +105,7 @@ namespace MWGui mCharacterSelection->addItem (title.str()); - if (mgr->getCurrentCharacter() == &*it) + if (mCurrentCharacter == &*it) mCharacterSelection->setIndexSelected(mCharacterSelection->getItemCount()-1); } } @@ -123,7 +124,7 @@ namespace MWGui if (!load) { - mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(); + mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter (false); } center(); @@ -139,10 +140,14 @@ namespace MWGui // Get the selected slot, if any unsigned int i=0; const MWState::Slot* slot = NULL; - for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it,++i) + + if (mCurrentCharacter) { - if (i == mSaveList->getIndexSelected()) - slot = &*it; + for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it,++i) + { + if (i == mSaveList->getIndexSelected()) + slot = &*it; + } } if (mSaving) @@ -151,7 +156,8 @@ namespace MWGui } else { - MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, slot); + if (mCurrentCharacter && slot) + MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, slot); } setVisible(false); diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp index 2b9c49fcc..cb64da2c2 100644 --- a/apps/openmw/mwstate/charactermanager.cpp +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -38,9 +38,9 @@ MWState::CharacterManager::CharacterManager (const boost::filesystem::path& save } } -MWState::Character *MWState::CharacterManager::getCurrentCharacter() +MWState::Character *MWState::CharacterManager::getCurrentCharacter (bool create) { - if (!mCurrent) + if (!mCurrent && create) createCharacter(); return mCurrent; diff --git a/apps/openmw/mwstate/charactermanager.hpp b/apps/openmw/mwstate/charactermanager.hpp index 9995393aa..a02927d58 100644 --- a/apps/openmw/mwstate/charactermanager.hpp +++ b/apps/openmw/mwstate/charactermanager.hpp @@ -26,8 +26,8 @@ namespace MWState CharacterManager (const boost::filesystem::path& saves); - Character *getCurrentCharacter(); - ///< A character is implicitly created, if there is none. + Character *getCurrentCharacter (bool create = true); + ///< \param create Create a new character, if there is no current character. void createCharacter(); ///< Create new character within saved game management diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 9d8cf2a81..9a7e3d158 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -138,9 +138,9 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl mState = State_Running; } -MWState::Character *MWState::StateManager::getCurrentCharacter() +MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) { - return mCharacterManager.getCurrentCharacter(); + return mCharacterManager.getCurrentCharacter (create); } MWState::StateManager::CharacterIterator MWState::StateManager::characterBegin() diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index abf7e123b..8bf1fab20 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -42,7 +42,8 @@ namespace MWState /// /// \note \a slot must belong to \a character. - virtual Character *getCurrentCharacter(); + virtual Character *getCurrentCharacter (bool create = true); + ///< \param create Create a new character, if there is no current character. virtual CharacterIterator characterBegin(); ///< Any call to SaveGame and getCurrentCharacter can invalidate the returned From 55544e931cee0b55ad8550958f64f493ba9fa8ec Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 25 Nov 2013 10:21:49 +0100 Subject: [PATCH 066/889] reject newer formats when scanning saved games --- apps/openmw/mwstate/character.cpp | 3 +++ apps/openmw/mwstate/statemanagerimp.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwstate/character.cpp b/apps/openmw/mwstate/character.cpp index 7185ce89d..2bd2af139 100644 --- a/apps/openmw/mwstate/character.cpp +++ b/apps/openmw/mwstate/character.cpp @@ -27,6 +27,9 @@ void MWState::Character::addSlot (const boost::filesystem::path& path) ESM::ESMReader reader; reader.open (slot.mPath.string()); + if (reader.getFormat()>ESM::Header::CurrentFormat) + return; // format is too new -> ignore + if (reader.getRecName()!=ESM::REC_SAVE) return; // invalid save file -> ignore diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 9a7e3d158..46239f5a2 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -107,7 +107,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot std::ofstream stream (slot->mPath.string().c_str()); ESM::ESMWriter writer; -// writer.setFormat (); + writer.setFormat (ESM::Header::CurrentFormat); writer.save (stream); writer.startRecord ("SAVE"); slot->mProfile.save (writer); From 1ecadccb280545474cadeb1389e3d1ece4f48cc0 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 25 Nov 2013 12:59:40 +0100 Subject: [PATCH 067/889] fixed save function of SavedGame record --- components/esm/savedgame.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index 8169b01a1..a37043da6 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -33,6 +33,6 @@ void ESM::SavedGame::save (ESMWriter &esm) const for (std::vector::const_iterator iter (mContentFiles.begin()); iter!=mContentFiles.end(); ++iter) - esm.writeHNCString (*iter, "DEPE"); + esm.writeHNCString ("DEPE", *iter); } From 616e3aa32ffcfa36ed594a8b049e439d2c9288f6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 25 Nov 2013 13:00:05 +0100 Subject: [PATCH 068/889] store content file list in saved games and reject saved games not matching the current game --- apps/openmw/engine.cpp | 3 ++- apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwstate/character.cpp | 14 +++++++++++--- apps/openmw/mwstate/character.hpp | 4 ++-- apps/openmw/mwstate/charactermanager.cpp | 9 +++++---- apps/openmw/mwstate/charactermanager.hpp | 3 ++- apps/openmw/mwstate/statemanagerimp.cpp | 7 ++++--- apps/openmw/mwstate/statemanagerimp.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 7 ++++++- apps/openmw/mwworld/worldimp.hpp | 3 +++ 10 files changed, 38 insertions(+), 16 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index cda068347..bee7fa8fd 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -323,7 +323,8 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings) void OMW::Engine::prepareEngine (Settings::Manager & settings) { - mEnvironment.setStateManager (new MWState::StateManager (mCfgMgr.getUserPath() / "saves")); + mEnvironment.setStateManager ( + new MWState::StateManager (mCfgMgr.getUserPath() / "saves", mContentFiles.at (0))); Nif::NIFFile::CacheLock cachelock; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index e1adfbec4..fe7d8f7ac 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -416,6 +416,8 @@ namespace MWBase virtual void launchProjectile (const std::string& id, const ESM::EffectList& effects, const MWWorld::Ptr& actor, const std::string& sourceName) = 0; + + virtual const std::vector& getContentFiles() const = 0; }; } diff --git a/apps/openmw/mwstate/character.cpp b/apps/openmw/mwstate/character.cpp index 2bd2af139..0766753ca 100644 --- a/apps/openmw/mwstate/character.cpp +++ b/apps/openmw/mwstate/character.cpp @@ -12,13 +12,18 @@ #include #include +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + bool MWState::operator< (const Slot& left, const Slot& right) { return left.mTimeStamp ignore + mSlots.push_back (slot); } @@ -54,7 +62,7 @@ void MWState::Character::addSlot (const ESM::SavedGame& profile) mSlots.push_back (slot); } -MWState::Character::Character (const boost::filesystem::path& saves) +MWState::Character::Character (const boost::filesystem::path& saves, const std::string& game) : mPath (saves), mNext (0) { if (!boost::filesystem::is_directory (mPath)) @@ -70,7 +78,7 @@ MWState::Character::Character (const boost::filesystem::path& saves) try { - addSlot (slotPath); + addSlot (slotPath, game); } catch (...) {} // ignoring bad saved game files for now diff --git a/apps/openmw/mwstate/character.hpp b/apps/openmw/mwstate/character.hpp index d678c165b..61e4e5b25 100644 --- a/apps/openmw/mwstate/character.hpp +++ b/apps/openmw/mwstate/character.hpp @@ -28,13 +28,13 @@ namespace MWState std::vector mSlots; int mNext; - void addSlot (const boost::filesystem::path& path); + void addSlot (const boost::filesystem::path& path, const std::string& game); void addSlot (const ESM::SavedGame& profile); public: - Character (const boost::filesystem::path& saves); + Character (const boost::filesystem::path& saves, const std::string& game); const Slot *createSlot (const ESM::SavedGame& profile); ///< Create new slot. diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp index cb64da2c2..2a40fb1cc 100644 --- a/apps/openmw/mwstate/charactermanager.cpp +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -6,8 +6,9 @@ #include -MWState::CharacterManager::CharacterManager (const boost::filesystem::path& saves) -: mPath (saves), mNext (0), mCurrent (0) +MWState::CharacterManager::CharacterManager (const boost::filesystem::path& saves, + const std::string& game) +: mPath (saves), mNext (0), mCurrent (0), mGame (game) { if (!boost::filesystem::is_directory (mPath)) { @@ -22,7 +23,7 @@ MWState::CharacterManager::CharacterManager (const boost::filesystem::path& save if (boost::filesystem::is_directory (characterDir)) { - Character character (characterDir); + Character character (characterDir, mGame); if (character.begin()!=character.end()) mCharacters.push_back (character); @@ -53,7 +54,7 @@ void MWState::CharacterManager::createCharacter() boost::filesystem::path path = mPath / stream.str(); - mCharacters.push_back (Character (path)); + mCharacters.push_back (Character (path, mGame)); mCurrent = &mCharacters.back(); } diff --git a/apps/openmw/mwstate/charactermanager.hpp b/apps/openmw/mwstate/charactermanager.hpp index a02927d58..bc2e23f89 100644 --- a/apps/openmw/mwstate/charactermanager.hpp +++ b/apps/openmw/mwstate/charactermanager.hpp @@ -13,6 +13,7 @@ namespace MWState int mNext; std::vector mCharacters; Character *mCurrent; + std::string mGame; private: @@ -24,7 +25,7 @@ namespace MWState public: - CharacterManager (const boost::filesystem::path& saves); + CharacterManager (const boost::filesystem::path& saves, const std::string& game); Character *getCurrentCharacter (bool create = true); ///< \param create Create a new character, if there is no current character. diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 46239f5a2..4cb97ed50 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -15,8 +15,8 @@ #include "../mwmechanics/npcstats.hpp" -MWState::StateManager::StateManager (const boost::filesystem::path& saves) -: mQuitRequest (false), mState (State_NoGame), mCharacterManager (saves) +MWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::string& game) +: mQuitRequest (false), mState (State_NoGame), mCharacterManager (saves, game) { } @@ -69,7 +69,8 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot MWWorld::Ptr player = world.getPlayer().getPlayer(); - /// \todo store content file list + profile.mContentFiles = world.getContentFiles(); + profile.mPlayerName = player.getClass().getName (player); profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel(); profile.mPlayerClass = player.get()->mBase->mClass; diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 8bf1fab20..3965632cc 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -17,7 +17,7 @@ namespace MWState public: - StateManager (const boost::filesystem::path& saves); + StateManager (const boost::filesystem::path& saves, const std::string& game); virtual void requestQuit(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index eca2ebb79..5a4192529 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -215,7 +215,7 @@ namespace MWWorld mSky (true), mCells (mStore, mEsm), mActivationDistanceOverride (mActivationDistanceOverride), mFallback(fallbackMap), mPlayIntro(0), mTeleportEnabled(true), - mFacedDistance(FLT_MAX), mGodMode(false) + mFacedDistance(FLT_MAX), mGodMode(false), mContentFiles (contentFiles) { mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); @@ -2246,4 +2246,9 @@ namespace MWWorld ++it; } } + + const std::vector& World::getContentFiles() const + { + return mContentFiles; + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 19890b831..32ad84865 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -73,6 +73,7 @@ namespace MWWorld OEngine::Physic::PhysicEngine* mPhysEngine; bool mGodMode; + std::vector mContentFiles; // not implemented World (const World&); @@ -492,6 +493,8 @@ namespace MWWorld virtual void launchProjectile (const std::string& id, const ESM::EffectList& effects, const MWWorld::Ptr& actor, const std::string& sourceName); + + virtual const std::vector& getContentFiles() const; }; } From ad143e0524278e6af70c37417a5545a47ed9449c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 25 Nov 2013 14:56:05 +0100 Subject: [PATCH 069/889] case fix (content file names) --- apps/openmw/mwstate/character.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwstate/character.cpp b/apps/openmw/mwstate/character.cpp index 0766753ca..304eaddd3 100644 --- a/apps/openmw/mwstate/character.cpp +++ b/apps/openmw/mwstate/character.cpp @@ -42,7 +42,8 @@ void MWState::Character::addSlot (const boost::filesystem::path& path, const std slot.mProfile.load (reader); - if (Misc::StringUtils::lowerCase (slot.mProfile.mContentFiles.at (0))!=game) + if (Misc::StringUtils::lowerCase (slot.mProfile.mContentFiles.at (0))!= + Misc::StringUtils::lowerCase (game)) return; // this file is for a different game -> ignore mSlots.push_back (slot); From b40c0f2a07eb592c695ef3c2a188374a7112969f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 26 Nov 2013 09:56:08 +0100 Subject: [PATCH 070/889] one more fix to SavedGame record saving --- components/esm/savedgame.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index a37043da6..e98608fe6 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -23,13 +23,13 @@ void ESM::SavedGame::load (ESMReader &esm) void ESM::SavedGame::save (ESMWriter &esm) const { - esm.writeHNCString ("PNAM", mPlayerName); + esm.writeHNString ("PNAM", mPlayerName); esm.writeHNT ("PLEV", mPlayerLevel); - esm.writeHNCString ("PCLA", mPlayerClass); - esm.writeHNCString ("PCEL", mPlayerCell); + esm.writeHNString ("PCLA", mPlayerClass); + esm.writeHNString ("PCEL", mPlayerCell); esm.writeHNT ("TSTM", mInGameTime, 16); esm.writeHNT ("TIME", mTimePlayed); - esm.writeHNCString ("DESC", mDescription); + esm.writeHNString ("DESC", mDescription); for (std::vector::const_iterator iter (mContentFiles.begin()); iter!=mContentFiles.end(); ++iter) From eea433f14192e2d36a84494a85762b06787dfcf5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 26 Nov 2013 10:37:58 +0100 Subject: [PATCH 071/889] restore last played character selection across sessions --- apps/openmw/mwgui/savegamedialog.cpp | 12 ++++++++++-- apps/openmw/mwstate/statemanagerimp.cpp | 8 ++++++++ files/settings-default.cfg | 7 +++++-- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 2ce0f0fbf..8ec6dbfcf 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -1,10 +1,13 @@ #include "savegamedialog.hpp" #include "widgets.hpp" +#include + +#include + #include "../mwbase/statemanager.hpp" #include "../mwbase/environment.hpp" - #include "../mwstate/character.hpp" namespace @@ -93,6 +96,9 @@ namespace MWGui mCurrentCharacter = mgr->getCurrentCharacter (false); + std::string directory = + Misc::StringUtils::lowerCase (Settings::Manager::getString ("character", "Saves")); + mCharacterSelection->removeAllItems(); for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it) @@ -105,7 +111,9 @@ namespace MWGui mCharacterSelection->addItem (title.str()); - if (mCurrentCharacter == &*it) + if (mCurrentCharacter == &*it || + (!mCurrentCharacter && directory==Misc::StringUtils::lowerCase ( + it->begin()->mPath.parent_path().filename().string()))) mCharacterSelection->setIndexSelected(mCharacterSelection->getItemCount()-1); } } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 4cb97ed50..97f8b8e55 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" @@ -114,6 +116,9 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot slot->mProfile.save (writer); writer.endRecord ("SAVE"); writer.close(); + + Settings::Manager::setString ("character", "Saves", + slot->mPath.parent_path().filename().string()); } void MWState::StateManager::loadGame (const Character *character, const Slot *slot) @@ -137,6 +142,9 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl mCharacterManager.setCurrentCharacter(character); mState = State_Running; + + Settings::Manager::setString ("character", "Saves", + slot->mPath.parent_path().filename().string()); } MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index f191430df..b3b186142 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -1,4 +1,4 @@ -# WARNING: Editing this file might have no effect, as these +# WARNING: Editing this file might have no effect, as these # settings are overwritten by your user settings file. [Video] @@ -13,7 +13,7 @@ screen = 0 # Valid values: # OpenGL Rendering Subsystem # Direct3D9 Rendering Subsystem -render system = +render system = # Valid values: # none @@ -169,3 +169,6 @@ ui y multiplier = 1.0 [Game] # Always use the most powerful attack when striking with a weapon (chop, slash or thrust) best attack = false + +[Saves] +character = \ No newline at end of file From bc6fe682c91c81ab0080ca85041f34c90e4c7eb4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 26 Nov 2013 11:39:58 +0100 Subject: [PATCH 072/889] replaced getCurrentCellName function with a more general getCellName function --- apps/openmw/mwbase/world.hpp | 6 +++- apps/openmw/mwscript/interpretercontext.cpp | 3 +- apps/openmw/mwworld/worldimp.cpp | 38 +++++---------------- apps/openmw/mwworld/worldimp.hpp | 6 +++- 4 files changed, 20 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index fe7d8f7ac..37d3c10da 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -149,7 +149,11 @@ namespace MWBase virtual std::vector getGlobals () const = 0; - virtual std::string getCurrentCellName() const = 0; + virtual std::string getCellName (const MWWorld::CellStore *cell = 0) const = 0; + ///< Return name of the cell. + /// + /// \note If cell==0, the cell the player is currently in will be used instead to + /// generate a name. virtual void removeRefScript (MWWorld::RefData *ref) = 0; //< Remove the script attached to ref from mLocalScripts diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index b8fc9ed47..5639ea208 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -322,8 +322,7 @@ namespace MWScript std::string InterpreterContext::getCurrentCellName() const { - MWBase::World *world = MWBase::Environment::get().getWorld(); - return world->getCurrentCellName(); + return MWBase::Environment::get().getWorld()->getCellName(); } bool InterpreterContext::isScriptRunning (const std::string& name) const diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5a4192529..7d2da389c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -442,40 +442,20 @@ namespace MWWorld return mGlobalVariables->getGlobals(); } - std::string World::getCurrentCellName () const + std::string World::getCellName (const MWWorld::CellStore *cell) const { - std::string name; + if (!cell) + cell = mWorldScene->getCurrentCell(); - Ptr::CellStore *cell = mWorldScene->getCurrentCell(); - if (cell->mCell->isExterior()) - { - if (cell->mCell->mName != "") - { - name = cell->mCell->mName; - } - else - { - const ESM::Region* region = - MWBase::Environment::get().getWorld()->getStore().get().search(cell->mCell->mRegion); - if (region) - name = region->mName; - else - { - const ESM::GameSetting *setting = - MWBase::Environment::get().getWorld()->getStore().get().search("sDefaultCellname"); + if (!cell->mCell->isExterior() || !cell->mCell->mName.empty()) + return cell->mCell->mName; - if (setting && setting->mValue.getType()==ESM::VT_String) - name = setting->mValue.getString(); - } + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - } - } - else - { - name = cell->mCell->mName; - } + if (const ESM::Region* region = store.get().search (cell->mCell->mRegion)) + return region->mName; - return name; + return store.get().find ("sDefaultCellname")->mValue.getString(); } void World::removeRefScript (MWWorld::RefData *ref) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 32ad84865..33f6f1c2f 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -213,7 +213,11 @@ namespace MWWorld virtual std::vector getGlobals () const; - virtual std::string getCurrentCellName () const; + virtual std::string getCellName (const MWWorld::CellStore *cell = 0) const; + ///< Return name of the cell. + /// + /// \note If cell==0, the cell the player is currently in will be used instead to + /// generate a name. virtual void removeRefScript (MWWorld::RefData *ref); //< Remove the script attached to ref from mLocalScripts From e6dc927f1163d3933533afaf7797a864830153ee Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 26 Nov 2013 11:49:07 +0100 Subject: [PATCH 073/889] removed duplicates of the cell name function --- apps/openmw/mwgui/windowmanagerimp.cpp | 26 ++++++------------------- apps/openmw/mwstate/statemanagerimp.cpp | 19 +----------------- 2 files changed, 7 insertions(+), 38 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 83325de23..3524c6d70 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -752,29 +752,18 @@ namespace MWGui void WindowManager::changeCell(MWWorld::Ptr::CellStore* cell) { + std::string name = MWBase::Environment::get().getWorld()->getCellName (cell); + + mMap->setCellName( name ); + mHud->setCellName( name ); + if (cell->mCell->isExterior()) { - std::string name; - if (cell->mCell->mName != "") - { - name = cell->mCell->mName; + if (!cell->mCell->mName.empty()) mMap->addVisitedLocation ("#{sCell=" + name + "}", cell->mCell->getGridX (), cell->mCell->getGridY ()); - } - else - { - const ESM::Region* region = - MWBase::Environment::get().getWorld()->getStore().get().search(cell->mCell->mRegion); - if (region) - name = region->mName; - else - name = getGameSettingString("sDefaultCellname", "Wilderness"); - } mMap->cellExplored(cell->mCell->getGridX(), cell->mCell->getGridY()); - mMap->setCellName( name ); - mHud->setCellName( name ); - mMap->setCellPrefix("Cell"); mHud->setCellPrefix("Cell"); mMap->setActiveCell( cell->mCell->getGridX(), cell->mCell->getGridY() ); @@ -782,12 +771,9 @@ namespace MWGui } else { - mMap->setCellName( cell->mCell->mName ); - mHud->setCellName( cell->mCell->mName ); mMap->setCellPrefix( cell->mCell->mName ); mHud->setCellPrefix( cell->mCell->mName ); } - } void WindowManager::setInteriorMapTexture(const int x, const int y) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 97f8b8e55..8faab1609 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -77,24 +77,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel(); profile.mPlayerClass = player.get()->mBase->mClass; - std::string cellName; - if (player.getCell()->mCell->isExterior()) - { - if (player.getCell()->mCell->mName != "") - cellName = player.getCell()->mCell->mName; - else - { - const ESM::Region* region = - MWBase::Environment::get().getWorld()->getStore().get().search(player.getCell()->mCell->mRegion); - if (region) - cellName = region->mName; - else - cellName = MWBase::Environment::get().getWindowManager()->getGameSettingString("sDefaultCellname", "Wilderness"); - } - } - else - cellName = player.getCell()->mCell->mName; - profile.mPlayerCell = cellName; + profile.mPlayerCell = world.getCellName(); profile.mInGameTime.mGameHour = world.getTimeStamp().getHour(); profile.mInGameTime.mDay = world.getDay(); From 99ea63dc4ab648063e332cb08cb1383c4d043c67 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 26 Nov 2013 12:47:30 +0100 Subject: [PATCH 074/889] factored out code for generating month names --- apps/openmw/mwbase/world.hpp | 7 ++- apps/openmw/mwgui/journalviewmodel.cpp | 46 +++---------------- apps/openmw/mwgui/savegamedialog.cpp | 62 +++----------------------- apps/openmw/mwgui/waitdialog.cpp | 44 +----------------- apps/openmw/mwworld/worldimp.cpp | 30 ++++++++++--- apps/openmw/mwworld/worldimp.hpp | 7 ++- 6 files changed, 47 insertions(+), 149 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 37d3c10da..dd9e20de1 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -186,8 +186,11 @@ namespace MWBase virtual void setDay (int day) = 0; ///< Set in-game time day. - virtual int getDay() = 0; - virtual int getMonth() = 0; + virtual int getDay() const = 0; + virtual int getMonth() const = 0; + + virtual std::string getMonthName (int month = -1) const = 0; + ///< Return name of month (-1: current month) virtual MWWorld::TimeStamp getTimeStamp() const = 0; ///< Return current in-game time stamp. diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index 79a77070a..89885d303 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -20,8 +20,6 @@ namespace MWGui { struct JournalViewModelImpl; -static void injectMonthName (std::ostream & os, int month); - struct JournalViewModelImpl : JournalViewModel { typedef KeywordSearch KeywordSearchT; @@ -242,14 +240,14 @@ struct JournalViewModelImpl : JournalViewModel { if (timestamp_buffer.empty ()) { + std::string dayStr = MyGUI::LanguageManager::getInstance().replaceTags("#{sDay}"); + std::ostringstream os; - os << itr->mDayOfMonth << ' '; - - injectMonthName (os, itr->mMonth); - - const std::string& dayStr = MyGUI::LanguageManager::getInstance().replaceTags("#{sDay}"); - os << " (" << dayStr << " " << (itr->mDay + 1) << ')'; + os + << itr->mDayOfMonth << ' ' + << MWBase::Environment::get().getWorld()->getMonthName (itr->mMonth) + << " (" << dayStr << " " << (itr->mDay + 1) << ')'; timestamp_buffer = os.str (); } @@ -349,38 +347,6 @@ struct JournalViewModelImpl : JournalViewModel } }; -static void injectMonthName (std::ostream & os, int month) -{ - MyGUI::LanguageManager& lm = MyGUI::LanguageManager::getInstance(); - - if (month == 0) - os << lm.replaceTags ("#{sMonthMorningstar}"); - else if (month == 1) - os << lm.replaceTags ("#{sMonthSunsdawn}"); - else if (month == 2) - os << lm.replaceTags ("#{sMonthFirstseed}"); - else if (month == 3) - os << lm.replaceTags ("#{sMonthRainshand}"); - else if (month == 4) - os << lm.replaceTags ("#{sMonthSecondseed}"); - else if (month == 5) - os << lm.replaceTags ("#{sMonthMidyear}"); - else if (month == 6) - os << lm.replaceTags ("#{sMonthSunsheight}"); - else if (month == 7) - os << lm.replaceTags ("#{sMonthLastseed}"); - else if (month == 8) - os << lm.replaceTags ("#{sMonthHeartfire}"); - else if (month == 9) - os << lm.replaceTags ("#{sMonthFrostfall}"); - else if (month == 10) - os << lm.replaceTags ("#{sMonthSunsdusk}"); - else if (month == 11) - os << lm.replaceTags ("#{sMonthEveningstar}"); - else - os << month; -} - JournalViewModel::Ptr JournalViewModel::create () { return boost::make_shared (); diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 8ec6dbfcf..5f9b6a3c1 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -7,61 +7,12 @@ #include "../mwbase/statemanager.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwstate/character.hpp" -namespace -{ -std::string getMonth(int m) -{ - std::string month; - switch (m) { - case 0: - month = "#{sMonthMorningstar}"; - break; - case 1: - month = "#{sMonthSunsdawn}"; - break; - case 2: - month = "#{sMonthFirstseed}"; - break; - case 3: - month = "#{sMonthRainshand}"; - break; - case 4: - month = "#{sMonthSecondseed}"; - break; - case 5: - month = "#{sMonthMidyear}"; - break; - case 6: - month = "#{sMonthSunsheight}"; - break; - case 7: - month = "#{sMonthLastseed}"; - break; - case 8: - month = "#{sMonthHeartfire}"; - break; - case 9: - month = "#{sMonthFrostfall}"; - break; - case 10: - month = "#{sMonthSunsdusk}"; - break; - case 11: - month = "#{sMonthEveningstar}"; - break; - default: - break; - } - return month; -} -} - namespace MWGui { - SaveGameDialog::SaveGameDialog() : WindowModal("openmw_savegame_dialog.layout") , mSaving(true) @@ -225,19 +176,18 @@ namespace MWGui text << asctime(timeinfo) << "\n"; text << "Level " << slot->mProfile.mPlayerLevel << "\n"; text << slot->mProfile.mPlayerCell << "\n"; - //text << "Time played: " << slot->mProfile.mTimePlayed << "\n"; + // text << "Time played: " << slot->mProfile.mTimePlayed << "\n"; int hour = int(slot->mProfile.mInGameTime.mGameHour); bool pm = hour >= 12; if (hour >= 13) hour -= 12; if (hour == 0) hour = 12; - text << - slot->mProfile.mInGameTime.mDay << " " - << getMonth(slot->mProfile.mInGameTime.mMonth) - << " " << hour << " " << (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); + text + << slot->mProfile.mInGameTime.mDay << " " + << MWBase::Environment::get().getWorld()->getMonthName(slot->mProfile.mInGameTime.mMonth) + << " " << hour << " " << (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); mInfoText->setCaptionWithReplacing(text.str()); - } } diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 2467f6c40..071bb8804 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -87,49 +87,7 @@ namespace MWGui onHourSliderChangedPosition(mHourSlider, 0); mHourSlider->setScrollPosition (0); - // http://www.uesp.net/wiki/Lore:Calendar - std::string month; - int m = MWBase::Environment::get().getWorld ()->getMonth (); - switch (m) { - case 0: - month = "#{sMonthMorningstar}"; - break; - case 1: - month = "#{sMonthSunsdawn}"; - break; - case 2: - month = "#{sMonthFirstseed}"; - break; - case 3: - month = "#{sMonthRainshand}"; - break; - case 4: - month = "#{sMonthSecondseed}"; - break; - case 5: - month = "#{sMonthMidyear}"; - break; - case 6: - month = "#{sMonthSunsheight}"; - break; - case 7: - month = "#{sMonthLastseed}"; - break; - case 8: - month = "#{sMonthHeartfire}"; - break; - case 9: - month = "#{sMonthFrostfall}"; - break; - case 10: - month = "#{sMonthSunsdusk}"; - break; - case 11: - month = "#{sMonthEveningstar}"; - break; - default: - break; - } + std::string month = MWBase::Environment::get().getWorld ()->getMonthName(); int hour = MWBase::Environment::get().getWorld ()->getTimeStamp ().getHour (); bool pm = hour >= 12; if (hour >= 13) hour -= 12; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 7d2da389c..3d64585f9 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -450,12 +450,10 @@ namespace MWWorld if (!cell->mCell->isExterior() || !cell->mCell->mName.empty()) return cell->mCell->mName; - const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - - if (const ESM::Region* region = store.get().search (cell->mCell->mRegion)) + if (const ESM::Region* region = getStore().get().search (cell->mCell->mRegion)) return region->mName; - return store.get().find ("sDefaultCellname")->mValue.getString(); + return getStore().get().find ("sDefaultCellname")->mValue.getString(); } void World::removeRefScript (MWWorld::RefData *ref) @@ -673,16 +671,36 @@ namespace MWWorld mRendering->skySetDate (mGlobalVariables->getInt ("day"), month); } - int World::getDay() + int World::getDay() const { return mGlobalVariables->getInt("day"); } - int World::getMonth() + int World::getMonth() const { return mGlobalVariables->getInt("month"); } + std::string World::getMonthName (int month) const + { + if (month==-1) + month = getMonth(); + + const int months = 12; + + if (month<0 || month>=months) + return ""; + + static const char *monthNames[months] = + { + "sMonthMorningstar", "sMonthSunsdawn", "sMonthFirstseed", "sMonthRainshand", + "sMonthSecondseed", "sMonthMidyear", "sMonthSunsheight", "sMonthLastseed", + "sMonthHeartfire", "sMonthFrostfall", "sMonthSunsdusk", "sMonthEveningstar" + }; + + return getStore().get().find (monthNames[month])->mValue.getString(); + } + TimeStamp World::getTimeStamp() const { return TimeStamp (mGlobalVariables->getFloat ("gamehour"), diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 33f6f1c2f..08a3182e9 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -251,8 +251,11 @@ namespace MWWorld virtual void setDay (int day); ///< Set in-game time day. - virtual int getDay(); - virtual int getMonth(); + virtual int getDay() const; + virtual int getMonth() const; + + virtual std::string getMonthName (int month = -1) const; + ///< Return name of month (-1: current month) virtual TimeStamp getTimeStamp() const; ///< Return current in-game time stamp. From 400831faf82bd31a4ba123df89899eef65ac66ad Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 26 Nov 2013 15:51:59 +0100 Subject: [PATCH 075/889] Added short overiview of resources avaible. Thanks for assistance and insight on this, serpentine, lgro and scrawl. --- manual/opencs/files_and_directories.tex | 29 +++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/manual/opencs/files_and_directories.tex b/manual/opencs/files_and_directories.tex index 032a7cdce..b4af951b3 100644 --- a/manual/opencs/files_and_directories.tex +++ b/manual/opencs/files_and_directories.tex @@ -35,15 +35,40 @@ Since addon is supposed to change the game it is logical that it also depends on Game files are not intend to have any dependencies for a very simple reasons: player is using only one game file (excluding original and dirty esp/esm system) at the time and therefore no game file can depend on other game file, and since game file makes the base for addon files -- it can't depend on addon files.\\ \subparagraph{Loading order} +%TODO \paragraph{Project files} Project files act as containers for data not used by the Openm{MW} game engine itself, but still useful for OpenCS. The shining example of this data category are without doubt record filters (described in the later section of the manual you are reading currently). As a mod author you probably don't need and/or want to distribute project files at all, they are meant to be used only by you.\\ As you would imagine, project file makes sense only in combination with actual content files. In fact, each time you start to work on new content file and project file was not found, it will be created.\\ -Project files extension is, to not surprise ``.project''. The whole name of the project file is the whole name of the content file with appended extensions. For instance awesomeswords.omwaddon file is associated with awesomeswords.omwaddon.project file.\\ +Project files extension is, to not surprise ``.project''. The whole name of the project file is the whole name of the content file with appended extensions. For instance swords.omwaddon file is associated with swords.omwaddon.project file.\\ %TODO where are they stored. Project files are stored inside the user directory, in the \textbf{projects} subfolder. This is the path location for both freshly created project files, and a place where Open{CS} looks for already existing files.\\ \paragraph{Resources files} %textures, sounds, whatever -Unless we are talking about the fully text based game, like Zork or Rogue, you are expecting that a video game is using some media files: models with textures, pictures acting as icons, sounds and everything else. Since content files, no matter if it is esp, esm or new Open{MW} file type do not contain any of those, it's clear that they have to be deliver with a different file. It is also clear that this, let's call it ``resources file``, have to be supported by the engine. Without code handling those files, it is nothing more than a mathematical abstraction -- something, that lacks meaning for human beings\footnote{Unless we call programmers a human beings.}. Therefore this section must cover ways to add resources files to your content file, and point out what is supported. We are going to do just that. Later, you will learn how to make use of those files in your content. \ No newline at end of file +Unless we are talking about the fully text based game, like Zork or Rogue, you are expecting that a video game is using some media files: models with textures, pictures acting as icons, sounds and everything else. Since content files, no matter if it is esp, esm or new Open{MW} file type do not contain any of those, it's clear that they have to be deliver with a different file. It is also clear that this, let's call it ``resources file``, have to be supported by the engine. Without code handling those files, it is nothing more than a mathematical abstraction -- something, that lacks meaning for human beings\footnote{Unless we call programmers a human beings.}. Therefore this section must cover ways to add resources files to your content file, and point out what is supported. We are going to do just that. Later, you will learn how to make use of those files in your content. + +\subparagraph{Audio} +Open{MW} is using {FF}mpeg for audio playback, and so we support every audio type that is supported by this library. This makes a huge list. Below is only small portion of supported file types. + +\begin{description} + \item mp3 popular format and \textit{de facto} standard for storing audio. Used by the Morrowind game. + \item mp4 format with a better compression rate than mp3, but also requiring more {CPU} intensive decoding -- this makes it probably less suited for video games. + \item ogg open source, audio container file using high quality vorbis codec. Recommended. +\end{description} + + +\subparagraph{Video} +As in the case of audio files, we are using {FFmepg} to decode video files. The list of supported files is long, we will cover only the most significant. + +\begin{description} + \item bik videos used by original Morrowind game. + \item webm is a new, shiny and open source video format with excellent compression. It needs quite a lot of processing power to be decoded, but since game logic is not running during cut scenes we can recommended it for use with Open{MW}. + \item ogv alternative, open source container using theora codec for video and vorbis for audio. +\end{description} + +\subparagraph{Textures and images} +Original Morrowind game uses dds and tga file for all kind of two dimensional images and textures alike. In addition, engine supported bmp files for some reason (bmp is a terrible format for a video game). We also support extended set of image files -- including jpeg and png. Jpeg and png files can be useful in some cases, for instance jpeg file is a valid option for skybox texture and png can useful for masks. However please, keep in mind that jpeg can grow into large sizes quickly and are not the best option with directx rendering backend. + +\subparagraph{Meshes} %TODO once we will support something more than just nifs \ No newline at end of file From 71436b11609525d9e9dd6a0ddf7a8db91f60f5ad Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 28 Nov 2013 09:10:38 +0100 Subject: [PATCH 076/889] changed interface for global variable access --- apps/openmw/mwbase/world.hpp | 12 +++++-- apps/openmw/mwdialogue/filter.cpp | 2 +- apps/openmw/mwdialogue/journalentry.cpp | 6 ++-- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- apps/openmw/mwscript/interpretercontext.cpp | 12 +++---- apps/openmw/mwscript/miscextensions.cpp | 35 ++++++++++++++++----- apps/openmw/mwworld/worldimp.cpp | 18 ++++++++--- apps/openmw/mwworld/worldimp.hpp | 12 +++++-- 8 files changed, 72 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index dd9e20de1..41cacd365 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -140,9 +140,17 @@ namespace MWBase virtual bool isPositionExplored (float nX, float nY, int x, int y, bool interior) = 0; ///< see MWRender::LocalMap::isPositionExplored - virtual MWWorld::Globals::Data& getGlobalVariable (const std::string& name) = 0; + virtual void setGlobalInt (const std::string& name, int value) = 0; + ///< Set value independently from real type. - virtual MWWorld::Globals::Data getGlobalVariable (const std::string& name) const = 0; + virtual void setGlobalFloat (const std::string& name, float value) = 0; + ///< Set value independently from real type. + + virtual int getGlobalInt (const std::string& name) const = 0; + ///< Get value independently from real type. + + virtual float getGlobalFloat (const std::string& name) const = 0; + ///< Get value independently from real type. virtual char getGlobalVariableType (const std::string& name) const = 0; ///< Return ' ', if there is no global variable with this name. diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 11dccde42..c6089c9e4 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -170,7 +170,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c // internally all globals are float :( return select.selectCompare ( - MWBase::Environment::get().getWorld()->getGlobalVariable (select.getName()).mFloat); + MWBase::Environment::get().getWorld()->getGlobalFloat (select.getName())); case SelectWrapper::Function_Local: { diff --git a/apps/openmw/mwdialogue/journalentry.cpp b/apps/openmw/mwdialogue/journalentry.cpp index 5ffde5499..dd1ad3f66 100644 --- a/apps/openmw/mwdialogue/journalentry.cpp +++ b/apps/openmw/mwdialogue/journalentry.cpp @@ -60,9 +60,9 @@ namespace MWDialogue StampedJournalEntry StampedJournalEntry::makeFromQuest (const std::string& topic, int index) { - int day = MWBase::Environment::get().getWorld()->getGlobalVariable ("dayspassed").mLong; - int month = MWBase::Environment::get().getWorld()->getGlobalVariable ("month").mLong; - int dayOfMonth = MWBase::Environment::get().getWorld()->getGlobalVariable ("day").mLong; + int day = MWBase::Environment::get().getWorld()->getGlobalInt ("dayspassed"); + int month = MWBase::Environment::get().getWorld()->getGlobalInt ("month"); + int dayOfMonth = MWBase::Environment::get().getWorld()->getGlobalInt ("day"); return StampedJournalEntry (topic, idFromIndex (topic, index), day, month, dayOfMonth); } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 3524c6d70..2be88b7f0 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1238,7 +1238,7 @@ namespace MWGui bool WindowManager::getRestEnabled() { //Enable rest dialogue if character creation finished - if(mRestAllowed==false && MWBase::Environment::get().getWorld()->getGlobalVariable ("chargenstate").mFloat==-1) + if(mRestAllowed==false && MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")==-1) mRestAllowed=true; return mRestAllowed; } diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 5639ea208..bbfa77e18 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -127,18 +127,18 @@ namespace MWScript int InterpreterContext::getGlobalShort (const std::string& name) const { - return MWBase::Environment::get().getWorld()->getGlobalVariable (name).mShort; + return MWBase::Environment::get().getWorld()->getGlobalInt (name); } int InterpreterContext::getGlobalLong (const std::string& name) const { // a global long is internally a float. - return MWBase::Environment::get().getWorld()->getGlobalVariable (name).mLong; + return MWBase::Environment::get().getWorld()->getGlobalInt (name); } float InterpreterContext::getGlobalFloat (const std::string& name) const { - return MWBase::Environment::get().getWorld()->getGlobalVariable (name).mFloat; + return MWBase::Environment::get().getWorld()->getGlobalFloat (name); } void InterpreterContext::setGlobalShort (const std::string& name, int value) @@ -150,7 +150,7 @@ namespace MWScript else if (name=="month") MWBase::Environment::get().getWorld()->setMonth (value); else - MWBase::Environment::get().getWorld()->getGlobalVariable (name).mShort = value; + MWBase::Environment::get().getWorld()->setGlobalInt (name, value); } void InterpreterContext::setGlobalLong (const std::string& name, int value) @@ -162,7 +162,7 @@ namespace MWScript else if (name=="month") MWBase::Environment::get().getWorld()->setMonth (value); else - MWBase::Environment::get().getWorld()->getGlobalVariable (name).mLong = value; + MWBase::Environment::get().getWorld()->setGlobalInt (name, value); } void InterpreterContext::setGlobalFloat (const std::string& name, float value) @@ -174,7 +174,7 @@ namespace MWScript else if (name=="month") MWBase::Environment::get().getWorld()->setMonth (value); else - MWBase::Environment::get().getWorld()->getGlobalVariable (name).mFloat = value; + MWBase::Environment::get().getWorld()->setGlobalFloat (name, value); } std::vector InterpreterContext::getGlobals () const diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 35f7a4044..c9d27b09a 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -657,6 +657,8 @@ namespace MWScript void printGlobalVars(Interpreter::Runtime &runtime) { + Interpreter::Context& context = runtime.getContext(); + std::stringstream str; str<< "Global variables:"; @@ -664,16 +666,33 @@ namespace MWScript std::vector names = world->getGlobals(); for(size_t i = 0;i < names.size();++i) { - char type = world->getGlobalVariableType(names[i]); - if(type == 's') - str<getGlobalVariableType (names[i]); + str << std::endl << " " << names[i] << " = "; + + switch (type) + { + case 's': + + str << context.getGlobalShort (names[i]) << " (short)"; + break; + + case 'l': + + str << context.getGlobalLong (names[i]) << " (long)"; + break; + + case 'f': + + str << context.getGlobalFloat (names[i]) << " (float)"; + break; + + default: + + str << ""; + } } - runtime.getContext().report(str.str()); + context.report (str.str()); } public: diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3d64585f9..afa434cd2 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -422,14 +422,24 @@ namespace MWWorld return mWorldScene->hasCellChanged(); } - Globals::Data& World::getGlobalVariable (const std::string& name) + void World::setGlobalInt (const std::string& name, int value) { - return (*mGlobalVariables)[name]; + mGlobalVariables->setInt (name, value); } - Globals::Data World::getGlobalVariable (const std::string& name) const + void World::setGlobalFloat (const std::string& name, float value) { - return (*mGlobalVariables)[name]; + mGlobalVariables->setFloat (name, value); + } + + int World::getGlobalInt (const std::string& name) const + { + return mGlobalVariables->getInt (name); + } + + float World::getGlobalFloat (const std::string& name) const + { + return mGlobalVariables->getFloat (name); } char World::getGlobalVariableType (const std::string& name) const diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 08a3182e9..eda79b433 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -204,9 +204,17 @@ namespace MWWorld virtual bool isPositionExplored (float nX, float nY, int x, int y, bool interior); ///< see MWRender::LocalMap::isPositionExplored - virtual Globals::Data& getGlobalVariable (const std::string& name); + virtual void setGlobalInt (const std::string& name, int value); + ///< Set value independently from real type. - virtual Globals::Data getGlobalVariable (const std::string& name) const; + virtual void setGlobalFloat (const std::string& name, float value); + ///< Set value independently from real type. + + virtual int getGlobalInt (const std::string& name) const; + ///< Get value independently from real type. + + virtual float getGlobalFloat (const std::string& name) const; + ///< Get value independently from real type. virtual char getGlobalVariableType (const std::string& name) const; ///< Return ' ', if there is no global variable with this name. From b0eb5938bfdd7ff3a24f219e6722bb2b5b790042 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 28 Nov 2013 09:13:54 +0100 Subject: [PATCH 077/889] removed some redundant code --- apps/openmw/mwscript/interpretercontext.cpp | 28 +++------------------ apps/openmw/mwworld/worldimp.cpp | 18 +++++++++++-- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index bbfa77e18..a977d3440 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -143,45 +143,23 @@ namespace MWScript void InterpreterContext::setGlobalShort (const std::string& name, int value) { - if (name=="gamehour") - MWBase::Environment::get().getWorld()->setHour (value); - else if (name=="day") - MWBase::Environment::get().getWorld()->setDay (value); - else if (name=="month") - MWBase::Environment::get().getWorld()->setMonth (value); - else - MWBase::Environment::get().getWorld()->setGlobalInt (name, value); + MWBase::Environment::get().getWorld()->setGlobalInt (name, value); } void InterpreterContext::setGlobalLong (const std::string& name, int value) { - if (name=="gamehour") - MWBase::Environment::get().getWorld()->setHour (value); - else if (name=="day") - MWBase::Environment::get().getWorld()->setDay (value); - else if (name=="month") - MWBase::Environment::get().getWorld()->setMonth (value); - else - MWBase::Environment::get().getWorld()->setGlobalInt (name, value); + MWBase::Environment::get().getWorld()->setGlobalInt (name, value); } void InterpreterContext::setGlobalFloat (const std::string& name, float value) { - if (name=="gamehour") - MWBase::Environment::get().getWorld()->setHour (value); - else if (name=="day") - MWBase::Environment::get().getWorld()->setDay (value); - else if (name=="month") - MWBase::Environment::get().getWorld()->setMonth (value); - else - MWBase::Environment::get().getWorld()->setGlobalFloat (name, value); + MWBase::Environment::get().getWorld()->setGlobalFloat (name, value); } std::vector InterpreterContext::getGlobals () const { MWBase::World *world = MWBase::Environment::get().getWorld(); return world->getGlobals(); - } char InterpreterContext::getGlobalType (const std::string& name) const diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index afa434cd2..0767718b8 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -424,12 +424,26 @@ namespace MWWorld void World::setGlobalInt (const std::string& name, int value) { - mGlobalVariables->setInt (name, value); + if (name=="gamehour") + setHour (value); + else if (name=="day") + setDay (value); + else if (name=="month") + setMonth (value); + else + mGlobalVariables->setInt (name, value); } void World::setGlobalFloat (const std::string& name, float value) { - mGlobalVariables->setFloat (name, value); + if (name=="gamehour") + setHour (value); + else if (name=="day") + setDay (value); + else if (name=="month") + setMonth (value); + else + mGlobalVariables->setFloat (name, value); } int World::getGlobalInt (const std::string& name) const From 7e2819c62e8721020ba5459d8a8177e44a36a560 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 28 Nov 2013 09:27:10 +0100 Subject: [PATCH 078/889] store year in saved game profile --- apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwstate/statemanagerimp.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 5 +++++ apps/openmw/mwworld/worldimp.hpp | 1 + 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 41cacd365..87a9b5bbc 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -196,6 +196,7 @@ namespace MWBase virtual int getDay() const = 0; virtual int getMonth() const = 0; + virtual int getYear() const = 0; virtual std::string getMonthName (int month = -1) const = 0; ///< Return name of month (-1: current month) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 8faab1609..60b19fd46 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -82,7 +82,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot profile.mInGameTime.mGameHour = world.getTimeStamp().getHour(); profile.mInGameTime.mDay = world.getDay(); profile.mInGameTime.mMonth = world.getMonth(); - /// \todo year + profile.mInGameTime.mYear = world.getYear(); /// \todo time played profile.mDescription = description; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0767718b8..1e7b8122f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -705,6 +705,11 @@ namespace MWWorld return mGlobalVariables->getInt("month"); } + int World::getYear() const + { + return mGlobalVariables->getInt("year"); + } + std::string World::getMonthName (int month) const { if (month==-1) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index eda79b433..2b7d157ff 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -261,6 +261,7 @@ namespace MWWorld virtual int getDay() const; virtual int getMonth() const; + virtual int getYear() const; virtual std::string getMonthName (int month = -1) const; ///< Return name of month (-1: current month) From 35e8e2303745dc38ff4180ca5bfdcbfb62ce7456 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 28 Nov 2013 09:33:50 +0100 Subject: [PATCH 079/889] keep track of total play time per character --- apps/openmw/engine.cpp | 3 +++ apps/openmw/mwbase/statemanager.hpp | 2 ++ apps/openmw/mwstate/statemanagerimp.cpp | 12 ++++++++++-- apps/openmw/mwstate/statemanagerimp.hpp | 3 +++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index bee7fa8fd..7dd3214eb 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -117,6 +117,9 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) // update world MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode()); + + // update game state + MWBase::Environment::get().getStateManager()->update (frametime); } // update GUI diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index f376d72c1..8548a74f7 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -71,6 +71,8 @@ namespace MWBase /// iterator. virtual CharacterIterator characterEnd() = 0; + + virtual void update (float duration) = 0; }; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 60b19fd46..a273aced7 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -18,7 +18,7 @@ #include "../mwmechanics/npcstats.hpp" MWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::string& game) -: mQuitRequest (false), mState (State_NoGame), mCharacterManager (saves, game) +: mQuitRequest (false), mState (State_NoGame), mCharacterManager (saves, game), mTimePlayed (0) { } @@ -46,6 +46,7 @@ void MWState::StateManager::newGame (bool bypass) MWBase::Environment::get().getJournal()->clear(); mState = State_NoGame; mCharacterManager.clearCurrentCharacter(); + mTimePlayed = 0; } if (!bypass) @@ -83,7 +84,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot profile.mInGameTime.mDay = world.getDay(); profile.mInGameTime.mMonth = world.getMonth(); profile.mInGameTime.mYear = world.getYear(); - /// \todo time played + profile.mTimePlayed = mTimePlayed; profile.mDescription = description; if (!slot) @@ -114,6 +115,8 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl mCharacterManager.clearCurrentCharacter(); } + mTimePlayed = slot->mProfile.mTimePlayed; + ESM::ESMReader reader; reader.open (slot->mPath.string()); @@ -144,3 +147,8 @@ MWState::StateManager::CharacterIterator MWState::StateManager::characterEnd() { return mCharacterManager.end(); } + +void MWState::StateManager::update (float duration) +{ + mTimePlayed += duration; +} diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 3965632cc..720b1b6b0 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -14,6 +14,7 @@ namespace MWState bool mQuitRequest; State mState; CharacterManager mCharacterManager; + double mTimePlayed; public: @@ -50,6 +51,8 @@ namespace MWState /// iterator. virtual CharacterIterator characterEnd(); + + virtual void update (float duration); }; } From 5aea6ef80f6bce0718b8987afc9c414e9fea59b6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 28 Nov 2013 11:22:34 +0100 Subject: [PATCH 080/889] some clean up for the cleanup code --- apps/openmw/mwstate/statemanagerimp.cpp | 33 +++++++++++++------------ apps/openmw/mwstate/statemanagerimp.hpp | 4 +++ apps/openmw/mwworld/worldimp.cpp | 3 ++- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index a273aced7..b750f56fe 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -17,6 +17,18 @@ #include "../mwmechanics/npcstats.hpp" +void MWState::StateManager::cleanup() +{ + if (mState!=State_NoGame) + { + MWBase::Environment::get().getDialogueManager()->clear(); + MWBase::Environment::get().getJournal()->clear(); + mState = State_NoGame; + mCharacterManager.clearCurrentCharacter(); + mTimePlayed = 0; + } +} + MWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::string& game) : mQuitRequest (false), mState (State_NoGame), mCharacterManager (saves, game), mTimePlayed (0) { @@ -40,18 +52,10 @@ MWState::StateManager::State MWState::StateManager::getState() const void MWState::StateManager::newGame (bool bypass) { - if (mState!=State_NoGame) - { - MWBase::Environment::get().getDialogueManager()->clear(); - MWBase::Environment::get().getJournal()->clear(); - mState = State_NoGame; - mCharacterManager.clearCurrentCharacter(); - mTimePlayed = 0; - } + cleanup(); if (!bypass) { - /// \todo extract cleanup code MWBase::Environment::get().getWorld()->startNewGame(); MWBase::Environment::get().getWindowManager()->setNewGame (true); } @@ -99,6 +103,9 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot writer.startRecord ("SAVE"); slot->mProfile.save (writer); writer.endRecord ("SAVE"); + + /// \todo write saved game data + writer.close(); Settings::Manager::setString ("character", "Saves", @@ -107,13 +114,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot void MWState::StateManager::loadGame (const Character *character, const Slot *slot) { - if (mState!=State_NoGame) - { - MWBase::Environment::get().getDialogueManager()->clear(); - MWBase::Environment::get().getJournal()->clear(); - mState = State_NoGame; - mCharacterManager.clearCurrentCharacter(); - } + cleanup(); mTimePlayed = slot->mProfile.mTimePlayed; diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 720b1b6b0..78b578766 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -16,6 +16,10 @@ namespace MWState CharacterManager mCharacterManager; double mTimePlayed; + private: + + void cleanup(); + public: StateManager (const boost::filesystem::path& saves, const std::string& game); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 1e7b8122f..d7fdc3bc1 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -290,7 +290,6 @@ namespace MWWorld pos.rot[2] = 0; mWorldScene->changeToExteriorCell(pos); - // enable collision if(!mPhysics->toggleCollisionMode()) mPhysics->toggleCollisionMode(); @@ -300,6 +299,7 @@ namespace MWWorld // global variables delete mGlobalVariables; + mGlobalVariables = 0; mGlobalVariables = new Globals (mStore); // set new game mark @@ -308,6 +308,7 @@ namespace MWWorld // we don't want old weather to persist on a new game delete mWeatherManager; + mWeatherManager = 0; mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback); MWBase::Environment::get().getScriptManager()->resetGlobalScripts(); From e432ab5e8ace6e4a7e7f03e9635c244e6d66bffc Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 28 Nov 2013 11:51:21 +0100 Subject: [PATCH 081/889] fixed static problem in Land recrod save function --- components/esm/loadland.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index ede200d79..bc16c65d3 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -14,7 +14,7 @@ void Land::LandData::save(ESMWriter &esm) esm.writeHNT("VNML", mNormals, sizeof(VNML)); } if (mDataTypes & Land::DATA_VHGT) { - static VHGT offsets; + VHGT offsets; offsets.mHeightOffset = mHeights[0] / HEIGHT_SCALE; offsets.mUnk1 = mUnk1; offsets.mUnk2 = mUnk2; From 750133c0dd18c39c795402ff7148740bb09a7b5e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 30 Nov 2013 11:05:07 +0100 Subject: [PATCH 082/889] one more fix to SavedGame record saving --- components/esm/savedgame.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index e98608fe6..7c76f4000 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -33,6 +33,6 @@ void ESM::SavedGame::save (ESMWriter &esm) const for (std::vector::const_iterator iter (mContentFiles.begin()); iter!=mContentFiles.end(); ++iter) - esm.writeHNCString ("DEPE", *iter); + esm.writeHNString ("DEPE", *iter); } From aebc2791a50f09d518a0cc40f35286f90d813c38 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 30 Nov 2013 11:08:41 +0100 Subject: [PATCH 083/889] fixed selecting current character based on value stored in settings --- apps/openmw/mwgui/savegamedialog.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 5f9b6a3c1..d1e047c09 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -65,7 +65,10 @@ namespace MWGui if (mCurrentCharacter == &*it || (!mCurrentCharacter && directory==Misc::StringUtils::lowerCase ( it->begin()->mPath.parent_path().filename().string()))) + { + mCurrentCharacter = &*it; mCharacterSelection->setIndexSelected(mCharacterSelection->getItemCount()-1); + } } } From 9d64c92d33efb3aac740e3cbe5f8afa8607434f9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 30 Nov 2013 11:57:33 +0100 Subject: [PATCH 084/889] store text in journal entries --- apps/openmw/mwdialogue/journalentry.cpp | 16 +++++++++++----- apps/openmw/mwdialogue/journalentry.hpp | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwdialogue/journalentry.cpp b/apps/openmw/mwdialogue/journalentry.cpp index dd1ad3f66..20963eb79 100644 --- a/apps/openmw/mwdialogue/journalentry.cpp +++ b/apps/openmw/mwdialogue/journalentry.cpp @@ -14,21 +14,27 @@ namespace MWDialogue JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId) : mTopic (topic), mInfoId (infoId) - {} - - std::string JournalEntry::getText (const MWWorld::ESMStore& store) const { const ESM::Dialogue *dialogue = - store.get().find (mTopic); + MWBase::Environment::get().getWorld()->getStore().get().find (mTopic); for (std::vector::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) if (iter->mId == mInfoId) - return iter->mResponse; + { + /// \todo text replacement + mText = iter->mResponse; + return; + } throw std::runtime_error ("unknown info ID " + mInfoId + " for topic " + mTopic); } + std::string JournalEntry::getText (const MWWorld::ESMStore& store) const + { + return mText; + } + JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index) { return JournalEntry (topic, idFromIndex (topic, index)); diff --git a/apps/openmw/mwdialogue/journalentry.hpp b/apps/openmw/mwdialogue/journalentry.hpp index 9d009b48b..ab4adece9 100644 --- a/apps/openmw/mwdialogue/journalentry.hpp +++ b/apps/openmw/mwdialogue/journalentry.hpp @@ -15,6 +15,7 @@ namespace MWDialogue { std::string mTopic; std::string mInfoId; + std::string mText; JournalEntry(); From eed46960fe7a769b979f91a1fafa3ba7e0af13d7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 30 Nov 2013 12:02:56 +0100 Subject: [PATCH 085/889] some spelling fixes --- apps/openmw/mwdialogue/quest.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwdialogue/quest.hpp b/apps/openmw/mwdialogue/quest.hpp index 3afa81fac..566eef6bf 100644 --- a/apps/openmw/mwdialogue/quest.hpp +++ b/apps/openmw/mwdialogue/quest.hpp @@ -5,7 +5,7 @@ namespace MWDialogue { - /// \brief A quest in progress or a compelted quest + /// \brief A quest in progress or a completed quest class Quest : public Topic { int mIndex; @@ -23,7 +23,7 @@ namespace MWDialogue int getIndex() const; void setIndex (int index); - ///< Calling this function with a non-existant index while throw an exception. + ///< Calling this function with a non-existent index will throw an exception. bool isFinished() const; From 177aab536dff53175b01241984296fca386e669b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 30 Nov 2013 12:41:18 +0100 Subject: [PATCH 086/889] storing topic name in MWDialogue::Topic (avoids a lookup in the GUI and also serves as preparation for better localisation support in OpenMW 1.1) --- apps/openmw/mwdialogue/quest.cpp | 2 +- apps/openmw/mwdialogue/quest.hpp | 2 +- apps/openmw/mwdialogue/topic.cpp | 11 ++++++++++- apps/openmw/mwdialogue/topic.hpp | 3 ++- apps/openmw/mwgui/journalviewmodel.cpp | 9 ++------- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwdialogue/quest.cpp b/apps/openmw/mwdialogue/quest.cpp index 5e2739be1..75dcaa028 100644 --- a/apps/openmw/mwdialogue/quest.cpp +++ b/apps/openmw/mwdialogue/quest.cpp @@ -16,7 +16,7 @@ namespace MWDialogue : Topic (topic), mIndex (0), mFinished (false) {} - const std::string Quest::getName() const + std::string Quest::getName() const { const ESM::Dialogue *dialogue = MWBase::Environment::get().getWorld()->getStore().get().find (mTopic); diff --git a/apps/openmw/mwdialogue/quest.hpp b/apps/openmw/mwdialogue/quest.hpp index 566eef6bf..c6f0d0bec 100644 --- a/apps/openmw/mwdialogue/quest.hpp +++ b/apps/openmw/mwdialogue/quest.hpp @@ -17,7 +17,7 @@ namespace MWDialogue Quest (const std::string& topic); - const std::string getName() const; + virtual std::string getName() const; ///< May be an empty string int getIndex() const; diff --git a/apps/openmw/mwdialogue/topic.cpp b/apps/openmw/mwdialogue/topic.cpp index 3253b20d6..fc8545b5e 100644 --- a/apps/openmw/mwdialogue/topic.cpp +++ b/apps/openmw/mwdialogue/topic.cpp @@ -1,6 +1,9 @@ #include "topic.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "../mwworld/esmstore.hpp" namespace MWDialogue @@ -9,7 +12,8 @@ namespace MWDialogue {} Topic::Topic (const std::string& topic) - : mTopic (topic) + : mTopic (topic), mName ( + MWBase::Environment::get().getWorld()->getStore().get().find (topic)->mId) {} Topic::~Topic() @@ -27,6 +31,11 @@ namespace MWDialogue mEntries.push_back (entry.mInfoId); } + std::string Topic::getName() const + { + return mName; + } + Topic::TEntryIter Topic::begin() const { return mEntries.begin(); diff --git a/apps/openmw/mwdialogue/topic.hpp b/apps/openmw/mwdialogue/topic.hpp index c3f0baabc..17601977a 100644 --- a/apps/openmw/mwdialogue/topic.hpp +++ b/apps/openmw/mwdialogue/topic.hpp @@ -19,6 +19,7 @@ namespace MWDialogue protected: std::string mTopic; + std::string mName; TEntryContainer mEntries; // info-IDs public: @@ -34,7 +35,7 @@ namespace MWDialogue /// /// \note Redundant entries are ignored. - std::string const & getName () const { return mTopic; } + virtual std::string getName () const; TEntryIter begin() const; ///< Iterator pointing to the begin of the journal for this topic. diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index 89885d303..e35d35013 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -288,9 +288,7 @@ struct JournalViewModelImpl : JournalViewModel void visitTopicName (TopicId topicId, boost::function visitor) const { MWDialogue::Topic const & topic = * reinterpret_cast (topicId); - // This is to get the correct case for the topic - const std::string& name = MWBase::Environment::get().getWorld()->getStore().get().find(topic.getName())->mId; - visitor (toUtf8Span (name)); + visitor (toUtf8Span (topic.getName())); } void visitTopicNamesStartingWith (char character, boost::function < void (TopicId , Utf8Span) > visitor) const @@ -302,10 +300,7 @@ struct JournalViewModelImpl : JournalViewModel if (i->first [0] != std::tolower (character, mLocale)) continue; - // This is to get the correct case for the topic - const std::string& name = MWBase::Environment::get().getWorld()->getStore().get().find(i->first)->mId; - - visitor (TopicId (&i->second), toUtf8Span (name)); + visitor (TopicId (&i->second), toUtf8Span (i->second.getName())); } } From 43f5f16731cb195497f4832426222930213b7185 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 30 Nov 2013 12:54:44 +0100 Subject: [PATCH 087/889] removed a todo comment --- apps/openmw/mwgui/journalviewmodel.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index e35d35013..049515ac0 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -317,7 +317,6 @@ struct JournalViewModelImpl : JournalViewModel std::string getText () const { - /// \todo defines are not replaced (%PCName etc). should probably be done elsewhere though since we need the actor return mTopic.getEntry (*itr).getText(MWBase::Environment::get().getWorld()->getStore()); } From eec9821cd8345235e95479a46819e9b0a06ea2f9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 30 Nov 2013 14:41:12 +0100 Subject: [PATCH 088/889] added records for storing journals in saved game files --- components/CMakeLists.txt | 2 +- components/esm/defs.hpp | 2 ++ components/esm/journalentry.cpp | 35 +++++++++++++++++++++++++++++++++ components/esm/journalentry.hpp | 35 +++++++++++++++++++++++++++++++++ components/esm/queststate.cpp | 19 ++++++++++++++++++ components/esm/queststate.hpp | 24 ++++++++++++++++++++++ 6 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 components/esm/journalentry.cpp create mode 100644 components/esm/journalentry.hpp create mode 100644 components/esm/queststate.cpp create mode 100644 components/esm/queststate.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index ce5965be1..3223ab1a7 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -40,7 +40,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame + savedgame journalentry queststate ) add_component_dir (misc diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 5a5ef9f1c..03091d9d8 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -85,6 +85,8 @@ enum RecNameInts // format 0 - saved games REC_SAVE = 0x45564153, + REC_JOUR = 0x524f55a4, + REC_QUES = 0x53455551, // format 1 REC_FILT = 0x544C4946 diff --git a/components/esm/journalentry.cpp b/components/esm/journalentry.cpp new file mode 100644 index 000000000..514bf3597 --- /dev/null +++ b/components/esm/journalentry.cpp @@ -0,0 +1,35 @@ + +#include "journalentry.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::JournalEntry::load (ESMReader &esm) +{ + esm.getHNOT (mType, "JETY"); + mTopic = esm.getHNString ("YETO"); + mInfo = esm.getHNString ("YEIN"); + mText = esm.getHNString ("TEXT"); + + if (mType==Type_Journal) + { + esm.getHNT (mDay, "JEDA"); + esm.getHNT (mMonth, "JEMO"); + esm.getHNT (mDayOfMonth, "JEDM"); + } +} + +void ESM::JournalEntry::save (ESMWriter &esm) const +{ + esm.writeHNT ("JETY", mType); + esm.writeHNString ("YETO", mTopic); + esm.writeHNString ("YEIN", mInfo); + esm.writeHNString ("TEXT", mText); + + if (mType==Type_Journal) + { + esm.writeHNT ("JEDA", mDay); + esm.writeHNT ("JEMO", mMonth); + esm.writeHNT ("JEDM", mDayOfMonth); + } +} \ No newline at end of file diff --git a/components/esm/journalentry.hpp b/components/esm/journalentry.hpp new file mode 100644 index 000000000..94808dde6 --- /dev/null +++ b/components/esm/journalentry.hpp @@ -0,0 +1,35 @@ +#ifndef OPENMW_ESM_JOURNALENTRY_H +#define OPENMW_ESM_JOURNALENTRY_H + +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + struct JournalEntry + { + enum Type + { + Type_Journal = 0, + Type_Topic = 1, + Type_Quest = 2 + }; + + int mType; + std::string mTopic; + std::string mInfo; + std::string mText; + int mDay; // time stamp + int mMonth; + int mDayOfMonth; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif diff --git a/components/esm/queststate.cpp b/components/esm/queststate.cpp new file mode 100644 index 000000000..5931e8b90 --- /dev/null +++ b/components/esm/queststate.cpp @@ -0,0 +1,19 @@ + +#include "queststate.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::QuestState::load (ESMReader &esm) +{ + mTopic = esm.getHNString ("YETO"); + esm.getHNOT (mState, "QSTAT"); + esm.getHNOT (mFinished, "QFIN"); +} + +void ESM::QuestState::save (ESMWriter &esm) const +{ + esm.writeHNString ("YETO", mTopic); + esm.writeHNT ("QSTAT", mState); + esm.writeHNT ("QFIN", mFinished); +} \ No newline at end of file diff --git a/components/esm/queststate.hpp b/components/esm/queststate.hpp new file mode 100644 index 000000000..1769336f2 --- /dev/null +++ b/components/esm/queststate.hpp @@ -0,0 +1,24 @@ +#ifndef OPENMW_ESM_QUESTSTATE_H +#define OPENMW_ESM_QUESTSTATE_H + +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + struct QuestState + { + std::string mTopic; + int mState; + unsigned char mFinished; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif \ No newline at end of file From e03b8ac39313217d4f36e1f8807a3f0ab8a01563 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 1 Dec 2013 01:06:03 +0100 Subject: [PATCH 089/889] Added *.out files to gitignore. Signed-off-by: Lukasz Gromanowski --- manual/opencs/.gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/manual/opencs/.gitignore b/manual/opencs/.gitignore index cf62bd6fc..ce5852a37 100644 --- a/manual/opencs/.gitignore +++ b/manual/opencs/.gitignore @@ -2,4 +2,5 @@ *.aux *.log *.toc -*.pdf \ No newline at end of file +*.pdf +*.out From 7c2afc43f4cac1c55f149e37a33a68edae16e937 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 1 Dec 2013 01:07:08 +0100 Subject: [PATCH 090/889] Small reformatting. Broked long lines, removed unneeded hard line breaks, added defs for common words. Signed-off-by: Lukasz Gromanowski --- manual/opencs/files_and_directories.tex | 107 ++++++++++---- manual/opencs/filters.tex | 179 ++++++++++++++++++------ manual/opencs/main.tex | 18 ++- manual/opencs/tables.tex | 73 +++++++--- manual/opencs/windows.tex | 47 +++++-- 5 files changed, 313 insertions(+), 111 deletions(-) diff --git a/manual/opencs/files_and_directories.tex b/manual/opencs/files_and_directories.tex index b4af951b3..ce686d56c 100644 --- a/manual/opencs/files_and_directories.tex +++ b/manual/opencs/files_and_directories.tex @@ -1,74 +1,119 @@ \section{Files and Directories} \subsection{Introduction} -This section of the manual covers usage of files and directories by the OpenCS. Files and directories are file system concepts, and you are probably already familiar with it. We won't try to explain this concepts, we will just focus on Open{CS}. +This section of the manual covers usage of files and directories by the OpenCS. Files and directories are file system concepts, +and you are probably already familiar with it. We won't try to explain this concepts, we will just focus on Open{CS}. \subsection{Used terms} %TODO \subsection{Basics} \paragraph{Directories} -Open{MW} and Open{CS} uses multiple directories on file systems. First of, there is a \textbf{user directory} that holds configuration files and few different folders. The location of the user directory is hard coded for each supported operating system.\\ +Open{MW} and Open{CS} uses multiple directories on file systems. First of, there is a \textbf{user directory} that holds configuration +files and few different folders. The location of the user directory is hard coded for each supported operating system. %TODO list paths. - -In addition to this single hard coded directory, both Open{MW} and Open{CS} need a place to seek for actual data files of the game: textures, models, sounds and files that store records of objects in game; dialogues and so one -- so called content files. We support multiple such paths (We call it \textbf{data paths}) as specified in the configuration. Usually one data path points to the directory where original Morrowind\texttrademark is either installed or unpacked. You are free to specify as many data paths as you would like, however, there is one special data path that, as described later, is used to store newly created content files. +In addition to this single hard coded directory, both Open{MW} and Open{CS} need a place to seek for actual data files of the game: +textures, models, sounds and files that store records of objects in game; dialogues and so one -- so called content files. We support +multiple such paths (We call it \textbf{data paths}) as specified in the configuration. Usually one data path points to the directory +where original Morrowind\texttrademark is either installed or unpacked. You are free to specify as many data paths as you would like, +however, there is one special data path that, as described later, is used to store newly created content files. \paragraph{Content files} -Bethesda Morrowind\texttrademark engine is using two types of files: esm (master) and esp (plugin). The distinction between those is not clear, and often confusing. You would expect the esm (master) file is used to specify one master, that is modified by the esps plugins, and indeed: this is the basic idea. However, original expansions also were made as esm files, even though they essentially could be described as a really large plugins, and therefore rather use esp files. There were technical reasons behind this decision -- somewhat valid in the case of original engine, but clearly it's better to create a system that can be used is more sensible way. Open{MW} achieves this with our own content file types.\\ -We support both esm and esp files, but in order to make use of new features of OpenMW one should consider using new file types designed with our engine in mind: game files and addon files together called ``content files``.\\ +\BS \MW engine is using two types of files: ESM (master) and ESP (plugin). The distinction between those +is not clear, and often confusing. You would expect the ESM (master) file is used to specify one master, that is modified by the ESPs plugins, +and indeed: this is the basic idea. However, original expansions also were made as ESM files, even though they essentially could be +described as a really large plugins, and therefore rather use ESP files. There were technical reasons behind this decision -- somewhat valid +in the case of original engine, but clearly it's better to create a system that can be used is more sensible way. Open{MW} achieves +his with our own content file types. + +We support both ESM and ESP files, but in order to make use of new features of OpenMW one should consider using new file types designed +with our engine in mind: game files and addon files together called ``content files``. \subparagraph{Open{MW} content files} -Game and Addon files are concept somewhat similar to the old esm/esp, only in the way it should be from the very beginning. Nothing easier to describe. If you want to make new game using Open{MW} as engine (so called ``total conversion'') you should create a game file. If you want to create a addon for existing game file -- simply create addon file. Nothing else matters: The only distinction you should consider is if your project is about changing other game, or creating a new one. Simple as that.\\ +Game and Addon files are concept somewhat similar to the old ESM/ESP, only in the way it should be from the very beginning. Nothing easier +to describe. If you want to make new game using Open{MW} as engine (so called ``total conversion'') you should create a game file. +If you want to create a addon for existing game file -- simply create addon file. Nothing else matters: The only distinction you should +consider is if your project is about changing other game, or creating a new one. Simple as that. -Other simple thing about content files are extensions. We are using .omwaddon for addon files and .omwgame for game files.\\ +Other simple thing about content files are extensions. We are using .omwaddon for addon files and .omwgame for game files. %TODO describe what content files contains. and what not. +\subparagraph{\MW content files} +Using our content files is recommended solution for projects that are intended to used with Open{MW} engine. However some players +wish to use original Morrowind engine, even with it large flaws and lacking features\footnote{If this is actually wrong, We are very +successful project. Yay!}. Also, since 2002 thousands of ESP/ESM files were created, some with really outstanding content. +Because of this Open{CS} simply has no other choice but support ESP/ESM files. However, if you decided to choose ESP/ESM file instead +using our own content file types you are most likely aim at the original engine compatibility. This subject is covered in the very +last section of this manual. %not finished TODO add the said section. Most likely when more features are present. -\subparagraph{Morrowind content files} -Using our content files is recommended solution for projects that are intended to used with Open{MW} engine. However some players wish to use original Morrowind engine, even with it large flaws and lacking features\footnote{If this is actually wrong, We are very successful project. Yay!}. Also, since 2002 thousands of esp/ems files were created, some with really outstanding content. Because of this Open{CS} simply has no other choice but support esp/esm files. However, if you decided to choose esp/esm file instead using our own content file types you are most likely aim at the original engine compatibility. This subject is covered in the very last section of this manual.\\ %not finished TODO add the said section. Most likely when more features are present. - -The actual creation of new files is described in the next chapter. Here We are gonna focus only on details that you need to know in order to create your first Open{CS} file while full understanding your needs. For now let's jut remember that content files are created inside the user directory, in the the \textbf{data} subfolder (that is the one special data directory mentioned earlier).\\ +The actual creation of new files is described in the next chapter. Here we are gonna focus only on details that you need to know +in order to create your first Open{CS} file while full understanding your needs. For now let's jut remember that content files +are created inside the user directory, in the the \textbf{data} subfolder (that is the one special data directory mentioned earlier). \subparagraph{Dependencies} -Since addon is supposed to change the game it is logical that it also depends on the said game. It simply can't work otherwise. Just think about it: your modification is changing prize of the iron sword. But what if there is no iron sword in game? That's right: we get nonsense. What you want to do is to tie your addon to the files you are changing. Those can be either game files (expansion island for a game) or other addon files (house on the said island). It is a a good idea to be dependent only on files that are really changed in your addon obviously, but sadly there is no other way to achieve this than knowing what you want to do. Again, please remember that this section of the manual does not cover creating the content files -- it is only theoretical introduction to the subject. For now just keep in mind that dependencies exist, and is up to you what to decide if your content file should depend on other content file.\\ +Since addon is supposed to change the game it is logical that it also depends on the said game. It simply can not work otherwise. +Just think about it: your modification is changing prize of the iron sword. But what if there is no iron sword in game? That is right: +we get nonsense. What you want to do is to tie your addon to the files you are changing. Those can be either game files (expansion island +for a game) or other addon files (house on the said island). It is a good idea to be dependent only on files that are really changed +in your addon obviously, but sadly there is no other way to achieve this than knowing what you want to do. Again, please remember that +this section of the manual does not cover creating the content files -- it is only theoretical introduction to the subject. For now just +keep in mind that dependencies exist, and is up to you what to decide if your content file should depend on other content file. -Game files are not intend to have any dependencies for a very simple reasons: player is using only one game file (excluding original and dirty esp/esm system) at the time and therefore no game file can depend on other game file, and since game file makes the base for addon files -- it can't depend on addon files.\\ - -\subparagraph{Loading order} -%TODO +Game files are not intend to have any dependencies for a very simple reasons: player is using only one game file (excluding original +and dirty ESP/ESM system) at the time and therefore no game file can depend on other game file, and since game file makes the base +for addon files -- it can not depend on addon files. +%\subparagraph{Loading order} %TODO \paragraph{Project files} -Project files act as containers for data not used by the Openm{MW} game engine itself, but still useful for OpenCS. The shining example of this data category are without doubt record filters (described in the later section of the manual you are reading currently). As a mod author you probably don't need and/or want to distribute project files at all, they are meant to be used only by you.\\ -As you would imagine, project file makes sense only in combination with actual content files. In fact, each time you start to work on new content file and project file was not found, it will be created.\\ -Project files extension is, to not surprise ``.project''. The whole name of the project file is the whole name of the content file with appended extensions. For instance swords.omwaddon file is associated with swords.omwaddon.project file.\\ +Project files act as containers for data not used by the Open{MW} game engine itself, but still useful for OpenCS. The shining example +of this data category are without doubt record filters (described in the later section of the manual you are reading currently). +As a mod author you probably do not need and/or want to distribute project files at all, they are meant to be used only by you. + +As you would imagine, project file makes sense only in combination with actual content files. In fact, each time you start to work +on new content file and project file was not found, it will be created. +Project files extension is, to not surprise ``.project''. The whole name of the project file is the whole name of the content file +with appended extensions. For instance swords.omwaddon file is associated with swords.omwaddon.project file. %TODO where are they stored. -Project files are stored inside the user directory, in the \textbf{projects} subfolder. This is the path location for both freshly created project files, and a place where Open{CS} looks for already existing files.\\ +Project files are stored inside the user directory, in the \textbf{projects} subfolder. This is the path location for both freshly +created project files, and a place where Open{CS} looks for already existing files. \paragraph{Resources files} %textures, sounds, whatever -Unless we are talking about the fully text based game, like Zork or Rogue, you are expecting that a video game is using some media files: models with textures, pictures acting as icons, sounds and everything else. Since content files, no matter if it is esp, esm or new Open{MW} file type do not contain any of those, it's clear that they have to be deliver with a different file. It is also clear that this, let's call it ``resources file``, have to be supported by the engine. Without code handling those files, it is nothing more than a mathematical abstraction -- something, that lacks meaning for human beings\footnote{Unless we call programmers a human beings.}. Therefore this section must cover ways to add resources files to your content file, and point out what is supported. We are going to do just that. Later, you will learn how to make use of those files in your content. +Unless we are talking about the fully text based game, like Zork or Rogue, you are expecting that a video game is using some media files: +models with textures, pictures acting as icons, sounds and everything else. Since content files, no matter if it is ESP, ESM or new Open{MW} +file type do not contain any of those, it is clear that they have to be deliver with a different file. It is also clear that this, +let's call it ``resources file``, have to be supported by the engine. Without code handling those files, it is nothing more than +a mathematical abstraction -- something, that lacks meaning for human beings\footnote{Unless we call programmers a human beings.}. +Therefore this section must cover ways to add resources files to your content file, and point out what is supported. We are going +to do just that. Later, you will learn how to make use of those files in your content. \subparagraph{Audio} -Open{MW} is using {FF}mpeg for audio playback, and so we support every audio type that is supported by this library. This makes a huge list. Below is only small portion of supported file types. +Open{MW} is using {FF}mpeg for audio playback, and so we support every audio type that is supported by this library. This makes a huge list. +Below is only small portion of supported file types. \begin{description} - \item mp3 popular format and \textit{de facto} standard for storing audio. Used by the Morrowind game. - \item mp4 format with a better compression rate than mp3, but also requiring more {CPU} intensive decoding -- this makes it probably less suited for video games. - \item ogg open source, audio container file using high quality vorbis codec. Recommended. + \item mp3 (MPEG-1 Part 3 Layer 3) popular audio file format and \textit{de facto} standard for storing audio. Used by the Morrowind game. + \item ogg open source, multimedia container file using high quality vorbis audio codec. Recommended. \end{description} - \subparagraph{Video} -As in the case of audio files, we are using {FFmepg} to decode video files. The list of supported files is long, we will cover only the most significant. +As in the case of audio files, we are using {FFmepg} to decode video files. The list of supported files is long, we will cover +only the most significant. \begin{description} \item bik videos used by original Morrowind game. - \item webm is a new, shiny and open source video format with excellent compression. It needs quite a lot of processing power to be decoded, but since game logic is not running during cut scenes we can recommended it for use with Open{MW}. + \item mp4 multimedia container which use more advanced codecs (MPEG-4 Parts 2,3,10) with a better audio and video compression rate, + but also requiring more {CPU} intensive decoding -- this makes it probably less suited for video games. + \item webm is a new, shiny and open source video format with excellent compression. It needs quite a lot of processing power to be decoded, + but since game logic is not running during cut scenes we can recommended it for use with Open{MW}. \item ogv alternative, open source container using theora codec for video and vorbis for audio. \end{description} \subparagraph{Textures and images} -Original Morrowind game uses dds and tga file for all kind of two dimensional images and textures alike. In addition, engine supported bmp files for some reason (bmp is a terrible format for a video game). We also support extended set of image files -- including jpeg and png. Jpeg and png files can be useful in some cases, for instance jpeg file is a valid option for skybox texture and png can useful for masks. However please, keep in mind that jpeg can grow into large sizes quickly and are not the best option with directx rendering backend. +Original \MW game uses DDS and TGA files for all kind of two dimensional images and textures alike. In addition, engine supported BMP +files for some reason (BMP is a terrible format for a video game). We also support extended set of image files -- including JPEG and PNG. +JPEG and PNG files can be useful in some cases, for instance JPEG file is a valid option for skybox texture and PNG can useful for masks. +However please, keep in mind that JPEG can grow into large sizes quickly and are not the best option with DirectX rendering backend. -\subparagraph{Meshes} %TODO once we will support something more than just nifs \ No newline at end of file +%\subparagraph{Meshes} %TODO once we will support something more than just nifs \ No newline at end of file diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index cf0fc7202..e3781619c 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -1,25 +1,38 @@ \section{Record filters} \subsection{Introduction} -Filters are the key element of OpenCS use cases by allowing rapid and easy access to the searched records presented in all tables. Therefore: in order to use this application fully effective you should make sure that all concepts and instructions written in the this section of the manual are perfectly clear to you.\\ +Filters are the key element of OpenCS use cases by allowing rapid and easy access to the searched records presented in all tables. +Therefore: in order to use this application fully effective you should make sure that all concepts and instructions written in +the this section of the manual are perfectly clear to you. + Don't be afraid though, filters are fairly intuitive and easy to use. \subsubsection{Used Terms} \begin{description} - \item[Filter] is generally speaking a tool able to ``Filter'' (that is: select some elements, while discarding others) according to the some criteria. In case of OpenCS: records are being filtered according to the criteria of user choice. Criteria are written down in language with simple syntax. + \item[Filter] is generally speaking a tool able to ``Filter'' (that is: select some elements, while discarding others) according + to the some criteria. In case of OpenCS: records are being filtered according to the criteria of user choice. Criteria are written + down in language with simple syntax. \item[Criteria] describes condition under with any any record is being select by the filter. - \item[Syntax] as you may noticed computers (in general) are rather strict, and expect only strictly formulated orders -- that is: written with correct syntax. - \item[Expression] is way we are actually performing filtering. Filter can be treated as ``functions'': accepts arguments, and evaluates either to the true or false for every column record at the time. - \item[N-ary] is any expression that expects one or more expressions as arguments. It is useful for grouping two (or more) other expressions together in order to create filter that will check for criteria placed in two (again: or more) columns (logical ``or'', ``and''). - \item[unary] is any expression that expects one other expression. The example is ``not'' expression. In fact ``not'' is the only useful unary expression in OpenCS record filters. + \item[Syntax] as you may noticed computers (in general) are rather strict, and expect only strictly formulated orders -- that is: + written with correct syntax. + \item[Expression] is way we are actually performing filtering. Filter can be treated as ``functions'': accepts arguments, and evaluates + either to the true or false for every column record at the time. + \item[N-ary] is any expression that expects one or more expressions as arguments. It is useful for grouping two (or more) other expressions + together in order to create filter that will check for criteria placed in two (again: or more) columns (logical ``or'', ``and''). + \item[unary] is any expression that expects one other expression. The example is ``not'' expression. In fact ``not'' is the only useful + unary expression in OpenCS record filters. \item[nullary] is expression that does not accepts other expressions. It accepts arguments specified later. \end{description} \subsubsection{Basics} -In fact you don't need to learn everything about filters in order to use them. In fact all you need to know to achieve decent productivity with OpenCS is inside basics section. +In fact you don't need to learn everything about filters in order to use them. In fact all you need to know to achieve decent productivity +with OpenCS is inside basics section. \subsubsection{Interface} -Above each table there is a field that is used to enter filter: either predefined by the OpenMW developers or made by you, the user. You probably noticed it before. However there is also completely new element, although using familiar table layout. Go to the application menu view, and click filters. You should see set of default filters, made by the OpenMW team in the table with the following columns: filter, description and modified. +Above each table there is a field that is used to enter filter: either predefined by the OpenMW developers or made by you, the user. +You probably noticed it before. However there is also completely new element, although using familiar table layout. Go to the application +menu view, and click filters. You should see set of default filters, made by the OpenMW team in the table with the following columns: filter, +description and modified. \begin{description} \item[ID] contains the name of the filter. @@ -30,87 +43,161 @@ Above each table there is a field that is used to enter filter: either predefine So let's learn how to actually use those to speed up your work. \subsubsection{Using predefined filters} -Using those filters is quite easy and involves typing inside the filter field above the table. For instance, try to open referencables table and type in the filters field the following: ``project::weapons''. As soon as you complete the text, table will magicly alter and will show only the weapons. As you could noticed project::weapons is nothing else than a ID of one of the predefined filters. That's it: in order to use the filter inside the table you simply type it's name inside the filter field.\\ +Using those filters is quite easy and involves typing inside the filter field above the table. For instance, try to open referencables +table and type in the filters field the following: ``project::weapons''. As soon as you complete the text, table will magicly alter +and will show only the weapons. As you could noticed project::weapons is nothing else than a ID of one of the predefined filters. That is it: +in order to use the filter inside the table you simply type it is name inside the filter field. + To make life easier filter IDs follow simple convention. \begin{itemize} - \item Filter ID filtering a specific record type contains usually the name of a specific group. For instance project::weapons filter contains the word weapons (did you noticed?). Plural form is always used. - \item When filtering specific subgroup the ID starts just like in the case of general filter. For instance project::weaponssilver will filter only silver weapons (new mechanic introduced by the Bloodmoon, silver weapons deal double damage against werewolfs) and project::weaponsmagical will filter only magical weapons (able to hurt ghosts and other supernatural creatures). - \item There are few exceptions from the above rule. For instance there is a project::added, project::removed, project::modyfied, project::base. You would probably except something more like ``project::statusadded'' but in this case typing this few extra characters would only help to break your keyboard faster. + \item Filter ID filtering a specific record type contains usually the name of a specific group. For instance project::weapons filter + contains the word weapons (did you noticed?). Plural form is always used. + \item When filtering specific subgroup the ID starts just like in the case of general filter. For instance project::weaponssilver will + filter only silver weapons (new mechanic introduced by the Bloodmoon, silver weapons deal double damage against werewolfs) and + project::weaponsmagical will filter only magical weapons (able to hurt ghosts and other supernatural creatures). + \item There are few exceptions from the above rule. For instance there is a project::added, project::removed, project::modyfied, project::base. + You would probably except something more like ``project::statusadded'' but in this case typing this few extra characters would only + help to break your keyboard faster. \end{itemize} We strongly recommend to take a look at the filters table right now to see what you can filter with that. And try using it! It is very simple. \subsection{Advanced} -Back to the manual? Great.\\ -If you want to create your own filter you have to know exactly what do you want to get in order to translate this into the expressions. Finally, you will have to write this with correct syntax. As a result table will show only desired rows.\\ +Back to the manual? Great. + +If you want to create your own filter you have to know exactly what do you want to get in order to translate this into the expressions. +Finally, you will have to write this with correct syntax. As a result table will show only desired rows. + Advance subsection covers everything that you need to know in order to create any filter you may want to %TODO the filter part is actually wrong \subsubsection{Namespaces} -Did you noticed that every default filter has ``project::`` prefix? It is a ``namespace``, a term borrowed from the C++ language. In case of OpenCS namespace always means scope of the said object\footnote{You are not supposed to understand this at the moment.}. But what does it mean in case of filters? Well, short explanation is actually simple. +Did you noticed that every default filter has ``project::`` prefix? It is a ``namespace``, a term borrowed from the \CPP{} language. +In case of OpenCS namespace always means scope of the said object\footnote{You are not supposed to understand this at the moment.}. +But what does it mean in case of filters? Well, short explanation is actually simple. \begin{description} - \item[project::] namespace indicates that filter is used with the project, in multiple sessions. You can restart Open{CS} and filter is still there. - \item[session::] namespace indicates that filter is not stored trough multiple sessions and once you will quit Open{CS} (close session) the filter will be gone. Forever! Until then it can be found inside the filters table. + \item[project::] namespace indicates that filter is used with the project, in multiple sessions. You can restart Open{CS} and filter + is still there. + \item[session::] namespace indicates that filter is not stored trough multiple sessions and once you will quit Open{CS} (close session) + the filter will be gone. Forever! Until then it can be found inside the filters table. \end{description} -In addition to this two scopes, there is a third one; called one-shot. One-shot filters are not stored (even during single session) anywhere and as the name implies they are supposed to be created when needed only once. Good thing about the one-shot filters is that you don't need to open filters table in order to create it. Instead you just type it directly inside the filter field, starting with ``!''.\\ -Still, you may wonder how you are supposed to write expressions, what expressions you should use, and what syntax looks like. Let's start with nullary expressions that will allow you to create a basic filter. +In addition to this two scopes, there is a third one; called one-shot. One-shot filters are not stored (even during single session) +anywhere and as the name implies they are supposed to be created when needed only once. Good thing about the one-shot filters is that +you do not need to open filters table in order to create it. Instead you just type it directly inside the filter field, starting with +exclamation mark: ``!''. + +Still, you may wonder how you are supposed to write expressions, what expressions you should use, and what syntax looks like. Let's start +with nullary expressions that will allow you to create a basic filter. \subsubsection{Nullary expressions} -All nullary expressions are used in similar manner. First off: you have to write it's name (for instance: ``string'') and secondly: condition that will be checked inside brackets (for instance string(something, something)). If conditions of your expression will be meet by a record (technical speaking: expression will evaluate to true) the record will show up in the table.\\ -It is clear that you need to know what are you checking, that's is: what column of the table contains information that you are interested in and what should be inside specific cell inside this column to meet your requirements. In most cases first word inside brackets sets column you want to see, while the second one sets desired value inside of the cell. To separate column argument from the value argument use comma. +All nullary expressions are used in similar manner. First off: you have to write it is name (for instance: ``string'') and secondly: +condition that will be checked inside brackets (for instance string(something, something)). If conditions of your expression will be meet +by a record (technical speaking: expression will evaluate to true) the record will show up in the table. + +It is clear that you need to know what are you checking, that is: what column of the table contains information that you are interested +in and what should be inside specific cell inside this column to meet your requirements. In most cases first word inside brackets sets column +you want to see, while the second one sets desired value inside of the cell. To separate column argument from the value argument use comma. \paragraph{String -- string(``column'', ``value'')} -String in programmers language is often\footnote{Often, not always. There are different programming languages using slightly different terms.} just a word for anything composed of characters. In case of OpenCS this is in fact true for every value inside the column that is not composed of the pure numbers. Even columns containing only ``true`` and ``false`` values can be targeted by the string expression.\footnote{There is no Boolean (''true'' or ``false'') value in the OpenCS. You should use string for those.} String evaluates to true, when record contains in the specified column exactly the same value as specified. -\\ +String in programmers language is often\footnote{Often, not always. There are different programming languages using slightly different terms.} +just a word for anything composed of characters. In case of OpenCS this is in fact true for every value inside the column that is not composed +of the pure numbers. Even columns containing only ``true`` and ``false`` values can be targeted by the string expression. +\footnote{There is no Boolean (''true'' or ``false'') value in the OpenCS. You should use string for those.} String evaluates to true, +when record contains in the specified column exactly the same value as specified. + Since majority of the columns contain string values, string is among the most often used expressions. Examples: \begin{itemize} - \item string(``Record Type'', ``Weapon'') -- will evaluate to true for all records containing ``Weapon'' in the ``Record Type'' column cell. This group contains every weapon (including arrows and bolts) found in the game. - \item string(``Portable'', ``true'') -- will evaluate to true for all records containing word true inside ``Portable'' column cell. This group contains every portable light sources (lanterns, torches etc.). + \item string(``Record Type'', ``Weapon'') -- will evaluate to true for all records containing ``Weapon'' in the ``Record Type'' column cell. + This group contains every weapon (including arrows and bolts) found in the game. + \item string(``Portable'', ``true'') -- will evaluate to true for all records containing word true inside ``Portable'' column cell. + This group contains every portable light sources (lanterns, torches etc.). \end{itemize} -This is probably enough to create around 90\% string filters you will eventually need. However, this expression is even more powerful -- it accepts regular expressions (also called regexps). Regular expressions is a way to create string criteria that will be matched by one than just one specific value in the column. For instance, you can display both left and right gauntlets with the following expression: ``string("armor type", ".* gauntlet"))`` because ''.*'' in regexps means just: ``anything''. This filter says: please, show me ``any'' gauntlet. There are left and right gauntlets in the morrowind so this will evaluate to true for both. Simple, isn't it? -\\ -Creating regexps can be a difficult and annoying -- especially when you need complex criteria. On the other hand, We are under impression that in reality complex expressions are needed only in sporadic cases. In fact, the truth is: that most of the time only already mentioned ``.*'' is needed and therefore the following description of regexps can be skipped by vast majority of readers.\\ +This is probably enough to create around 90 string filters you will eventually need. However, this expression is even more powerful +-- it accepts regular expressions (also called regexps). Regular expressions is a way to create string criteria that will be matched +by one than just one specific value in the column. For instance, you can display both left and right gauntlets with the following expression: +``string("armor type", ".* gauntlet"))`` because ''.*'' in regexps means just: ``anything''. This filter says: please, show me ``any'' gauntlet. +There are left and right gauntlets in the morrowind so this will evaluate to true for both. Simple, isn't it? -Before working with Regular Expressions, you should understand what actually are regular expressions. Essentially, the idea is simple: when you are writing any word, you are using strictly defined letters -- that is: letters create a word. What you want to do with regular expression is to use set of rules that will match to many words. It is not that difficult to see what it's needed to do so: first, you will clearly need way to determinate what letters you want to match (word is composed by letters).\\ +Creating regexps can be a difficult and annoying -- especially when you need complex criteria. On the other hand, We are under impression +that in reality complex expressions are needed only in sporadic cases. In fact, the truth is: that most of the time only already mentioned +``.*'' is needed and therefore the following description of regexps can be skipped by vast majority of readers. -Before introducing other ways to choose between characters, I want explain anchors. Anchors allows you to decide where to ``look'' in the string. You surely should know about ``\^'' anchor and ``\textdollar''. Putting ``\^`` will tell to Open{CS} to look on the beginning of string, while ''\textdollar`` is used to mark the end of it. For instance, pattern ''\^Pink.* elephant.\textdollar`` Will match any sentence Beginning with the word ''Pink`` and ending with '' elephant.``. Pink fat elephant. Pink cute elephant. It does not matter what is in between, because ''.*`` is used.\\ +Before working with Regular Expressions, you should understand what actually are regular expressions. Essentially, the idea is simple: +when you are writing any word, you are using strictly defined letters -- that is: letters create a word. What you want to do with regular +expression is to use set of rules that will match to many words. It is not that difficult to see what it's needed to do so: first, +you will clearly need way to determinate what letters you want to match (word is composed by letters). -You have already seen the power of the simple ``.*''. But what if you want to chose between only two (or more) letters? Well, this is when ``[|]'' comes in handy. If you write something like: ``\^[a|k].*'' you are simply telling Open{CS} to filter anything that starts with either ``a'' or ``k''. Using ``\^[a|k|l].*'' will work in the same manner, but it will also cover strings starting with ``l''.\\ +Before introducing other ways to choose between characters, I want explain anchors. Anchors allows you to decide where to ``look'' in the string. +You surely should know about ``\^'' anchor and ``\textdollar''. Putting ``\^`` will tell to Open{CS} to look on the beginning of string, +while ''\textdollar`` is used to mark the end of it. For instance, pattern ''\^Pink.* elephant.\textdollar`` Will match any sentence beginning +with the word ''Pink`` and ending with '' elephant.``. Pink fat elephant. Pink cute elephant. It does not matter what is in between, +because ''.*`` is used.\\ -And What if you want to match more than just one latter? Just use ``(|)``. it is pretty similar to the above one letter as you see, but it is used to fit more than just one character. For instance: ''\^(Pink|Green).* (elephant|crocodile).\textdollar`` will be true for all sentences starting with ''Pink`` or ''Green`` and ending with either ''elephant.`` or ''crocodile.``.\\ +You have already seen the power of the simple ``.*''. But what if you want to chose between only two (or more) letters? Well, this is when +``[|]'' comes in handy. If you write something like: ``\^[a|k].*'' you are simply telling Open{CS} to filter anything that starts with either +``a'' or ``k''. Using ``\^[a|k|l].*'' will work in the same manner, but it will also cover strings starting with ``l''. -Regular expressions are not the main topic of this manual. If you wish to learn more on this subject please, read the documentation on Qt regular expressions syntax, or TRE regexp syntax (it is almost like in Qt). Above is just enough to use Open{CS} effectively to be sure.\\ +What if you want to match more than just one latter? Just use ``(|)``. it is pretty similar to the above one letter as you see, but it is +used to fit more than just one character. For instance: ''\^(Pink|Green).* (elephant|crocodile).\textdollar`` will be true for all sentences +starting with ''Pink`` or ''Green`` and ending with either ''elephant.`` or ''crocodile.``. + +Regular expressions are not the main topic of this manual. If you wish to learn more on this subject please, read the documentation on +Qt regular expressions syntax, or TRE regexp syntax (it is almost like in Qt). Above is just enough to use Open{CS} effectively to be sure. \paragraph{Value -- value(``value'', (``open'', ``close''))} -While string expression covers vast group of columns containing string values, there are in fact columns with just numerical values like ``weight``. To filter those we need a value expression. This one works in similar manner to the string filter: first token name and criteria inside brackets. Clearly, conditions should hold column to test in. However in this case wanted value is specified as a range.\\ +While string expression covers vast group of columns containing string values, there are in fact columns with just numerical values like +``weight``. To filter those we need a value expression. This one works in similar manner to the string filter: first token name and criteria +inside brackets. Clearly, conditions should hold column to test in. However in this case wanted value is specified as a range. As you would imagine the range can be specified as including a border value, or excluding. We are using two types of brackets for this: \begin{itemize} \item To include value use [] brackets. For value equal 5, expression value(something, [5, 10]) will evaluate to true. \item To exclude value use () brackets. For value equal 5, expression value(something, (5, 10)) will evaluate to false. - \item Mixing brackets is completely legal. For value equal 10, expression value(something, [5, 10) will evaluate to true. The same expression will evaluate to false for value equal 10. + \item Mixing brackets is completely legal. For value equal 10, expression value(something, [5, 10) will evaluate to true. The same expression + will evaluate to false for value equal 10. \end{itemize} \paragraph{''true`` and ''false``} -Nullary ''true`` and ''false`` do not accept any arguments, and always evaluates to true (in case of ''true``) and false (in case of ''false``) no matter what. The main usage of this expressions is the give users ability to quickly disable some part of the filter that makes heavy use of the logical expressions. +Nullary \textit{true} and \textit{false} do not accept any arguments, and always evaluates to true (in case of \textit{true}) +and false (in case of \textit{false}) no matter what. The main usage of this expressions is the give users ability to quickly +disable some part of the filter that makes heavy use of the logical expressions. \subsubsection{Logical expressions} -This subsection takes care of two remaining groups of expressions: binary and unary. The only unary expression present in the OpenCS is logical not, while the remaining binary expressions are: or, and. This clearly makes them (from the user point of view) belonging to the same group of logical expressions. +This subsection takes care of two remaining groups of expressions: binary and unary. The only unary expression present in the OpenCS is logical +\textit{not}, while the remaining binary expressions are: \textit{or}, \textit{and}. This clearly makes them (from the user point of view) +belonging to the same group of logical expressions. \paragraph{not -- not expression()} -Sometimes you may be in need of reversing the output of the expression. This is where not comes in handy. Adding not before expression will revert it: if expression was returning true, it will return false; if it was returning false, it will return true. Brackets are not needed: not will revert only the first expression following it.\\ -To show this on know example, let's consider the ''string("armor type", ".* gauntlet"))`` filter. As We mentioned earlier this will return true for every gauntlet found in game. In order to show everything, but gauntlets we simply do ''not string("armor type", ".* gauntlet"))``. This is probably not the most useful filter on earth, but this is not a surprise: real value of not expression shines when combined with or, and filter. +Sometimes you may be in need of reversing the output of the expression. This is where \textit{not} comes in handy. Adding \textit{not} before +expression will revert it: if expression was returning true, it will return false; if it was returning false, it will return true. Brackets +are not needed: \textit{not} will revert only the first expression following it. + +To show this on know example, let's consider the ''string("armor type", ".* gauntlet"))`` filter. As we mentioned earlier this will return true +for every gauntlet found in game. In order to show everything, but gauntlets we simply do ''not string("armor type", ".* gauntlet"))``. +This is probably not the most useful filter on earth, but this is not a surprise: real value of \textit{not} expression shines when combined with +\textit{or}, \textit{and} filters. \paragraph{or -- or(expression1(), expression2())} -Or is a expression that will return true if one of the arguments evaluates to true. You can use two or more arguments, separated by the comma.\\ -Or expression is useful when showing two different group of records is needed. For instance the standard actor filter is using the following ''or(string(``record type'', npc), string(``record type'', creature))`` and will show both npcs and creatures. +\textit{Or} is a expression that will return true if one of the arguments evaluates to true. You can use two or more arguments, separated by the comma. + +\textit{Or} expression is useful when showing two different group of records is needed. For instance the standard actor filter is using the following +''or(string(``record type'', npc), string(``record type'', creature))`` and will show both npcs and creatures. \paragraph{and -- and(expression1(), expression2())} -And is a expression that will return true if all arguments evaluates to true. As in the case of ''or`` you can use two or more arguments, separated by a comma.\\ -As We mentioned earlier in the ''not`` filter, combining ''not`` with ''and`` can be very useful. For instance to show all armor types, excluding gauntlets you can write the following: ''and (not string("armor type", ".* gauntlet"), string(''Record Type``, ''Armor``))''. +\textit{And} is a expression that will return true if all arguments evaluates to true. As in the case of ''or`` you can use two or more arguments, +separated by a comma. +As we mentioned earlier in the \textit{not} filter, combining \textit{not} with \textit{and} can be very useful. For instance to show all armor types, +excluding gauntlets you can write the following: ''and (not string("armor type", ".* gauntlet"), string(''Record Type``, ''Armor``))''. \subsubsection{Creating and saving filter} -In order to create and save new filter, you should go to the filters table, right click and select option ``add record'' from the context menu. A horizontal widget group at the bottom of the table should show up. From there you should select a namespace responsible for scope of the filter (described earlier) and desired ID of the filter. After pressing ok button new entry will show up in the filters table. This filter does nothing at the moment, since it still lacks expressions. In order to add your formula simply double click the filter cell of the new entry and write it down there.\\ +In order to create and save new filter, you should go to the filters table, right click and select option ``add record'' from the context menu. +A horizontal widget group at the bottom of the table should show up. From there you should select a namespace responsible for scope of +the filter (described earlier) and desired ID of the filter. After pressing OK button new entry will show up in the filters table. This filter +does nothing at the moment, since it still lacks expressions. In order to add your formula simply double click the filter cell of the new entry +and write it down there. Done! You are free to use your filter. \subsubsection{Replacing the default filters set} -{OpenCS} allows you to substitute default filters set provided by us, with your own filters. In order to do so you should create a new project, add desired filters, remove undesired and save. Rename the file to the ``defaultfilters'' (don't forget to remove .omwaddon.project extension) and place it inside your configuration directory.\\ -The file acts as template for all new project files from now. If you wish to go back to the old default set, simply rename or remove the custom file. \ No newline at end of file +{OpenCS} allows you to substitute default filters set provided by us, with your own filters. In order to do so you should create a new project, +add desired filters, remove undesired and save. Rename the file to the ``defaultfilters'' (do not forget to remove .omwaddon.project extension) +and place it inside your configuration directory. + +The file acts as template for all new project files from now. If you wish to go back to the old default set, simply rename or remove the custom file. diff --git a/manual/opencs/main.tex b/manual/opencs/main.tex index 577b7078b..3a395e277 100644 --- a/manual/opencs/main.tex +++ b/manual/opencs/main.tex @@ -1,8 +1,24 @@ \documentclass[american]{article} \usepackage[T1]{fontenc} \usepackage{babel} -\usepackage{graphicx} +\usepackage{txfonts} % Public Times New Roman text & math font +\usepackage[pdftex]{graphicx} +\usepackage[colorlinks,pdftex]{hyperref} \author{OpenMW Team} + +\def\pdfBorderAttrs{/Border [0 0 0] } % No border arround Links + +\def\CPP{{C\kern-.05em\raise.23ex\hbox{+\kern-.05em+}}} +\hypersetup{% + pdfauthor={Copyright \textcopyright{} OpenMW Team }, + pdftitle={OpenCS user manual} +} + +\def\MW{\textit{Morrowind\texttrademark{}\ }} +\def\TB{\textit{Tribunal\ }} +\def\BM{\textit{Bloodmon\ }} +\def\BS{Bethesda Softworks\ } + \begin{document} \title{OpenCS User Manual} diff --git a/manual/opencs/tables.tex b/manual/opencs/tables.tex index fb6d41ba8..cbb59b2dd 100644 --- a/manual/opencs/tables.tex +++ b/manual/opencs/tables.tex @@ -1,7 +1,11 @@ \section{Tables} \subsection{Introduction} -If you have launched OpenCS already and played around with it for a bit, you have probably gotten the impression that it contains lots of tables. You'd be spot on: OpenCS is built around using tables. This doesn't mean it works just like Excel or Calc, though. Due to the vast amounts of information involved with Morrowind, tables just made the most sense. You have to be able to spot information quickly and be able to change them on the fly. Let's browse through the various screens and see what all these tables show. +If you have launched OpenCS already and played around with it for a bit, you have probably gotten the impression that it contains lots of tables. +You'd be spot on: OpenCS is built around using tables. This does not mean it works just like Microsoft Excel or Libre Office Calc, though. +Due to the vast amounts of information involved with \MW, tables just made the most sense. You have to be able to spot information quickly +and be able to change them on the fly. +Let's browse through the various screens and see what all these tables show. \subsection{Used Terms} @@ -10,62 +14,87 @@ If you have launched OpenCS already and played around with it for a bit, you hav \begin{description} \item[Record:] An entry in OpenCS representing an item, location, sound, NPC or anything else. - \item[Reference, Referenceable:] When an item is placed in the world, it doesn't create a new record each time. For example, the game world might contain a lot of exquisite belts on different NPCs and in many crates, but they all refer to one specific record: the Exquisite Belt record. In this case, all those belts in crates and on NPCs are references. The central Exquisite Belt record is called a referenceable. This allows modders to make changes to all items of the same type. For example, if you want all exquisite belts to have 4000 enchantment points rather than 400, you will only need to change the referenceable Exquisite Belt rather than all exquisite belts references individually. + \item[Reference, Referenceable:] When an item is placed in the world, it does not create a new record each time. For example, the game world might + contain a lot of exquisite belts on different NPCs and in many crates, but they all refer to one specific record: the Exquisite Belt record. + In this case, all those belts in crates and on NPCs are references. The central Exquisite Belt record is called a referenceable. This allows modders + to make changes to all items of the same type. For example, if you want all exquisite belts to have 4000 enchantment points rather than 400, you will + only need to change the referenceable Exquisite Belt rather than all exquisite belts references individually. \end{description} \subsubsection{Recurring Terms} - Some columns are recurring throughout OpenCS. They show up in (nearly) every table in OpenCS. \begin{description} -\item[ID] Each item, location, sound, etc. gets the same unique identifier in both OpenCS and Morrowind. This is usually a very self-explanatory name. For example, the ID for the (unique) black pants of Caius Cosades is ``Caius\_pants''. This allows you to manipulate the game in many ways. For example, you could add these pants to your inventory by simply opening the console and write: ``player->addItem Caius\_pants''. Either way, in both Morrowind and OpenCS, the ID is the primary way to identify all these different parts of the game. %Wrong! Cells do not have ID, only name. +\item[ID] Each item, location, sound, etc. gets the same unique identifier in both OpenCS and Morrowind. This is usually a very self-explanatory name. +For example, the ID for the (unique) black pants of Caius Cosades is ``Caius\_pants''. This allows you to manipulate the game in many ways. For example, +you could add these pants to your inventory by simply opening the console and write: ``player->addItem Caius\_pants''. Either way, in both Morrowind +and OpenCS, the ID is the primary way to identify all these different parts of the game. %Wrong! Cells do not have ID, only name. \item[Modified] This column shows what has happened (if something has happened) to this record. There are four possible states in which it can exist. -\item[Base] means that this record is part of the base game and is in its original state. Usually, if you create a mod, the base game is Morrowind with optionally the Bloodmoon and Tribunal expansions. +\item[Base] means that this record is part of the base game and is in its original state. Usually, if you create a mod, the base game is Morrowind with +optionally the Bloodmoon and Tribunal expansions. \item[Added] means that this record was not in the base game and has been added by a modder. \item[Modified] means that the record is part of the base game, but has been changed in some way. -\item[Deleted] means that this record used to be part of the base game, but has been removed as an entry. This does not mean, however, that the occurrences in the game itself have been removed! For example, if you remove the CharGen\_Bed entry from morrowind.esm, it doesn't mean the bedroll in the basement of the Census and Excise Office in Seyda Neen is gone. You're going to have to delete that reference yourself or make sure that that object is replaced by something that still exists otherwise you'll get crashes in the worst case scenario. - \end{description} +\item[Deleted] means that this record used to be part of the base game, but has been removed as an entry. This does not mean, however, that the occurrences +in the game itself have been removed! For example, if you remove the CharGen\_Bed entry from morrowind.esm, it does not mean the bedroll in the basement +of the Census and Excise Office in Seyda Neen is gone. You're going to have to delete that reference yourself or make sure that that object is replaced +by something that still exists otherwise you will get crashes in the worst case scenario. +\end{description} \subsection{World Screens} - The contents of the game world can be changed by choosing one of the options in the appropriate menu at the top of the screen. \subsubsection{Regions} - This describes the general areas of Vvardenfell. Each of these areas has different rules about things such as encounters and weather. \begin{description} \item[Name:] This is how the game will show your location in-game. - \item[Map Colour:] This is a six-digit hexidecimal representation of the colour used to identify the region on the map available in World > Region Map. If you don't have an application with a colour picker, you can use your favourite search engine to find a colour picker online. + \item[Map Colour:] This is a six-digit hexidecimal representation of the colour used to identify the region on the map available in + World > Region Map. If you don't have an application with a colour picker, you can use your favourite search engine to find a colour picker online. \item[Sleep Encounter:] These are the rules for what kind of enemies you might encounter when you sleep outside in the wild. \end{description} \subsubsection{Cells} +Expansive worlds such as Vvardenfell, with all its items, NPCs, etc. have a lot going on simultaneously. But if you are in Balmora, +why would the computer need to keep track the exact locations of NPCs walking through the corridors in a Vivec canton? All that work would +be quite useless and bring your system to its knees! So the world has been divided up into squares we call "cells". Once your character enters a cell, +the game will load everything that is going on in that cell so you can interact with it. -Expansive worlds such as Vvardenfell, with all its items, NPCs, etc. have a lot going on simultaneously. But if you are in Balmora, why would the computer need to keep track the exact locations of NPCs walking through the corridors in a Vivec canton? All that work would be quite useless and bring your system to its knees! So the world has been divided up into squares we call "cells". Once your character enters a cell, the game will load everything that is going on in that cell so you can interact with it. - -In the original Morrowind this could be seen when you were travelling and you would see a small loading bar at the bottom of the screen; you had just entered a new cell and the game would have to load all the items and NPCs. The Cells screen in OpenCS provides you with a list of cells in the game, both the interior cells (houses, dungeons, mines, etc.) and the exterior cells (the outside world). +In the original \MW this could be seen when you were travelling and you would see a small loading bar at the bottom of the screen; +you had just entered a new cell and the game would have to load all the items and NPCs. The Cells screen in OpenCS provides you with a list of cells +in the game, both the interior cells (houses, dungeons, mines, etc.) and the exterior cells (the outside world). \begin{description} - \item[Sleep Forbidden:] Can the player sleep on the floor? In most cities it is forbidden to sleep outside. Sleeping in the wild carries its own risks of attack, though, and this entry lets you decide if a player should be allowed to sleep on the floor in this cell or not. + \item[Sleep Forbidden:] Can the player sleep on the floor? In most cities it is forbidden to sleep outside. Sleeping in the wild carries its + own risks of attack, though, and this entry lets you decide if a player should be allowed to sleep on the floor in this cell or not. - \item[Interior Water:] Should water be rendered in this interior cell? The game world consists of an endless ocean at height 0. Then the landscape is added. If part of the landscape goes below height 0, the player will see water. (See illustration.) + \item[Interior Water:] Should water be rendered in this interior cell? The game world consists of an endless ocean at height 0. Then the landscape + is added. If part of the landscape goes below height 0, the player will see water. (See illustration.) - Setting the cell's Interior Water to true tells the game that this cell is both an interior cell (inside a building, for example, rather than in the open air) but that there still needs to be water at height 0. This is useful for dungeons or mines that have water in them. + Setting the cell's Interior Water to true tells the game that this cell is both an interior cell (inside a building, for example, rather than + in the open air) but that there still needs to be water at height 0. This is useful for dungeons or mines that have water in them. - Setting the cell's Interior Water to false tells the game that the water at height 0 should not be used. Remember that cells that are in the outside world are exterior cells and should thus \textit{always} be set to false! + Setting the cell's Interior Water to false tells the game that the water at height 0 should not be used. Remember that cells that are in + the outside world are exterior cells and should thus \textit{always} be set to false! - \item[Interior Sky:] Should this interior cell have a sky? This is a rather unique case. The \textit{Tribunal} expansion took place in a city on the mainland. Normally this would require the city to be composed of exterior cells so it has a sky, weather and the like. But if the player is in an exterior cell and looks at his in-game map, he sees Vvardenfell with an overview of all exterior cells. The player would have to see the city's very own map, as if he was walking around in an interior cell. + \item[Interior Sky:] Should this interior cell have a sky? This is a rather unique case. The \TB expansion took place in a city on + the mainland. Normally this would require the city to be composed of exterior cells so it has a sky, weather and the like. But if the player is + in an exterior cell and looks at his in-game map, he sees Vvardenfell with an overview of all exterior cells. The player would have to see + the city's very own map, as if he was walking around in an interior cell. - So the developers decided to create a workaround and take a bit of both: The whole city would technically work exactly like an interior cell, but it would need a sky as if it was an exterior cell. That's what this is. This is why the vast majority of the cells you will find in this screen will have this option set to false: It's only meant for these "fake exteriors". + So the developers decided to create a workaround and take a bit of both: The whole city would technically work exactly like an interior cell, + but it would need a sky as if it was an exterior cell. That's what this is. This is why the vast majority of the cells you will find in this screen + will have this option set to false: It's only meant for these "fake exteriors". - \item[Region:] To which Region does this cell belong? This has an impact on the way the game handles weather and encounters in this area. It is also possible for a cell not to belong to any region. + \item[Region:] To which Region does this cell belong? This has an impact on the way the game handles weather and encounters in this area. + It is also possible for a cell not to belong to any region. \end{description} \subsubsection{Referenceables} - -This is a library of all the items, triggers, containers, NPCs, etc. in the game. There are several kinds of Record Types. Depending on which type a record is, it will need specific information to function. For example, an NPC needs a value attached to its aggression level. A chest, of course, does not. All Record Types contain at least a model. How else would the player see them? Usually they also have a Name, which is what you see when you hover your reticle over the object. +This is a library of all the items, triggers, containers, NPCs, etc. in the game. There are several kinds of Record Types. Depending on which type +a record is, it will need specific information to function. For example, an NPC needs a value attached to its aggression level. A chest, of course, +does not. All Record Types contain at least a model. How else would the player see them? Usually they also have a Name, which is what you see +when you hover your reticle over the object. Let's go through all Record Types and discuss what you can tell OpenCS about them. diff --git a/manual/opencs/windows.tex b/manual/opencs/windows.tex index 3bdfa6362..de377a119 100644 --- a/manual/opencs/windows.tex +++ b/manual/opencs/windows.tex @@ -1,28 +1,53 @@ \section{Windows} \subsection{Introduction} -This section describes the multiple windows interface of the OpenCS editor. This design principle was chosen in order to extend the flexibility of the editor, especially on the multiple screens setups and on environments providing advanced windows management features, like; for instance; multiple desktops found commonly on many open source desktop environments. However, it is enough to have a single large screen to see the advantages of this concept.\\ -OpenCS windows interface is easy to describe and understand. In fact We decided to minimize use of many windows concepts applied commonly in various applications. For instance dialog windows are really hard to find in the OpenCS. You are free to try, though.\\ -Because of this, and the fact that we expect that user is familiar with other applications using windows this section is mostly focused on practical ways of organizing work with the OpenCS. +This section describes the multiple windows interface of the OpenCS editor. This design principle was chosen in order +to extend the flexibility of the editor, especially on the multiple screens setups and on environments providing advanced +windows management features, like; for instance: multiple desktops found commonly on many open source desktop environments. +However, it is enough to have a single large screen to see the advantages of this concept. + +OpenCS windows interface is easy to describe and understand. In fact we decided to minimize use of many windows concepts +applied commonly in various applications. For instance dialog windows are really hard to find in the OpenCS. You are free to try, +though. + +Because of this, and the fact that we expect that user is familiar with other applications using windows this section is mostly +focused on practical ways of organizing work with the OpenCS. \subsection{Basics} -After starting Open{CS} and choosing content files to use a editor window should show up. It probably does not look surprising: there is a menubar at the top, and there is a large empty area. That's it: a brand new Open{CS} window contains only menubar and statusbar. In order to make it a little bit more useful you probably want to enable some panels\footnote{Also known as widgets.}. You are free to do so, just try to explore the menubar. \\ -You probably founded out the way to enable and disable some interesting tables, but those will be described later. For now, let's just focus on the windows itself. +After starting Open{CS} and choosing content files to use a editor window should show up. It probably does not look surprising: +there is a menubar at the top, and there is a large empty area. That is it: a brand new Open{CS} window contains only menubar +and statusbar. In order to make it a little bit more useful you probably want to enable some panels\footnote{Also known as widgets.}. +You are free to do so, just try to explore the menubar. + +You probably founded out the way to enable and disable some interesting tables, but those will be described later. For now, let's +just focus on the windows itself. \paragraph{Creating new windows} -is easy! Just visit view menu, and use the ``New View'' item. Suddenly, out of the blue a new window will show up. As you would expect, it is also blank, and you are free to add any of the Open{CS} panels. +is easy! Just visit view menu, and use the ``New View'' item. Suddenly, out of the blue a new window will show up. As you would expect, +it is also blank, and you are free to add any of the Open{CS} panels. \paragraph{Closing opened window} -is also easy! Simply close that window decoration button. We suspect that you knew that already, but better to be sure. Closing last Open{CS} window will also terminate application session. +is also easy! Simply close that window decoration button. We suspect that you knew that already, but better to be sure. +Closing last Open{CS} window will also terminate application session. \paragraph{Multi-everything} -is the main foundation of Open{CS} interface. You are free to create as many windows as you want to, free to populate it with any panels you may want to, and move everything as you wish to -- even if it makes no sense at all. If you just got crazy idea and you are wonder if you are able to have one hundred Open{CS} windows showing panels of the same type, well most likely you are able to do so.\\ +is the main foundation of Open{CS} interface. You are free to create as many windows as you want to, free to populate it with +any panels you may want to, and move everything as you wish to -- even if it makes no sense at all. If you just got crazy idea and +you are wonder if you are able to have one hundred Open{CS} windows showing panels of the same type, well most likely you are +able to do so. -The principle behind this design decision is easy to see for Bethesda made editor, but maybe not so clear for users who are just about to begin their wonderful journey of modding.\\ +The principle behind this design decision is easy to see for \BS made editor, but maybe not so clear for users who are +just about to begin their wonderful journey of modding. \subsection{Advanced} -So why? Why this is created in such manner. The answer is frankly simple: because it is effective. When creating a mod, you often have to work only with just one table. For instance you are just balancing weapons damage and other statistics. It makes sense to have all the space for just that one table. More often, you are required to work with two and switch them from time to time. All major graphical environments commonly present in operating systems comes with switcher feature, that is a key shortcut to change active window. It is very effective and fast when you have only two windows, each holding only one table. Sometimes you have to work with two at the time, and with one from time to time. Here, you can have one window holding two tables, and second holding just one.\\ +So why? Why this is created in such manner. The answer is frankly simple: because it is effective. When creating a mod, you often +have to work only with just one table. For instance you are just balancing weapons damage and other statistics. It makes sense +to have all the space for just that one table. More often, you are required to work with two and switch them from time to time. +All major graphical environments commonly present in operating systems comes with switcher feature, that is a key shortcut to change +active window. It is very effective and fast when you have only two windows, each holding only one table. Sometimes you have to work +with two at the time, and with one from time to time. Here, you can have one window holding two tables, and second holding just one. -Open{CS} is designed to simply make sense and do not slowdown users. It is as simple as possible (but not simpler), and uses one flexible approach in all cases.\\ +Open{CS} is designed to simply make sense and do not slowdown users. It is as simple as possible (but not simpler), and uses one +flexible approach in all cases. There is no point in digging deeper in the windows of Open{CS}. Let's explore panels, starting with tables. From 2e5e9e0c44c6ca16470396e6c3d0ee50b6a34e35 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 1 Dec 2013 01:15:46 +0100 Subject: [PATCH 091/889] Small official/unofficial style cleanup - missed that one. Signed-off-by: Lukasz Gromanowski --- manual/opencs/filters.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index e3781619c..4eb0985a5 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -25,7 +25,7 @@ Don't be afraid though, filters are fairly intuitive and easy to use. \end{description} \subsubsection{Basics} -In fact you don't need to learn everything about filters in order to use them. In fact all you need to know to achieve decent productivity +In fact you do not need to learn everything about filters in order to use them. In fact all you need to know to achieve decent productivity with OpenCS is inside basics section. \subsubsection{Interface} From 3b64de7441a65be7b02e4954fc8a09f1a108512b Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 1 Dec 2013 03:18:08 +0100 Subject: [PATCH 092/889] Another part of reformatting and clean up. Signed-off-by: Lukasz Gromanowski --- manual/opencs/files_and_directories.tex | 42 +++++----- manual/opencs/filters.tex | 106 ++++++++++++------------ manual/opencs/main.tex | 14 ++-- manual/opencs/tables.tex | 32 +++---- manual/opencs/windows.tex | 24 +++--- 5 files changed, 111 insertions(+), 107 deletions(-) diff --git a/manual/opencs/files_and_directories.tex b/manual/opencs/files_and_directories.tex index ce686d56c..b3283dac4 100644 --- a/manual/opencs/files_and_directories.tex +++ b/manual/opencs/files_and_directories.tex @@ -1,53 +1,53 @@ \section{Files and Directories} \subsection{Introduction} This section of the manual covers usage of files and directories by the OpenCS. Files and directories are file system concepts, -and you are probably already familiar with it. We won't try to explain this concepts, we will just focus on Open{CS}. +and you are probably already familiar with it. We won't try to explain this concepts, we will just focus on \OCS. \subsection{Used terms} %TODO \subsection{Basics} \paragraph{Directories} -Open{MW} and Open{CS} uses multiple directories on file systems. First of, there is a \textbf{user directory} that holds configuration +OpenMW and \OCS{} uses multiple directories on file systems. First of, there is a \textbf{user directory} that holds configuration files and few different folders. The location of the user directory is hard coded for each supported operating system. %TODO list paths. -In addition to this single hard coded directory, both Open{MW} and Open{CS} need a place to seek for actual data files of the game: +In addition to this single hard coded directory, both \OMW{} and \OCS{} need a~place to seek for actual data files of the game: textures, models, sounds and files that store records of objects in game; dialogues and so one -- so called content files. We support -multiple such paths (We call it \textbf{data paths}) as specified in the configuration. Usually one data path points to the directory -where original Morrowind\texttrademark is either installed or unpacked. You are free to specify as many data paths as you would like, +multiple such paths (we call it \textbf{data paths}) as specified in the configuration. Usually one data path points to the directory +where original \MW{} is either installed or unpacked. You are free to specify as many data paths as you would like, however, there is one special data path that, as described later, is used to store newly created content files. \paragraph{Content files} -\BS \MW engine is using two types of files: ESM (master) and ESP (plugin). The distinction between those +\BS{} \MW{} engine is using two types of files: ESM (master) and ESP (plugin). The distinction between those is not clear, and often confusing. You would expect the ESM (master) file is used to specify one master, that is modified by the ESPs plugins, and indeed: this is the basic idea. However, original expansions also were made as ESM files, even though they essentially could be described as a really large plugins, and therefore rather use ESP files. There were technical reasons behind this decision -- somewhat valid -in the case of original engine, but clearly it's better to create a system that can be used is more sensible way. Open{MW} achieves +in the case of original engine, but clearly it's better to create a system that can be used is more sensible way. \OMW{} achieves his with our own content file types. We support both ESM and ESP files, but in order to make use of new features of OpenMW one should consider using new file types designed with our engine in mind: game files and addon files together called ``content files``. -\subparagraph{Open{MW} content files} +\subparagraph{OpenMW content files} Game and Addon files are concept somewhat similar to the old ESM/ESP, only in the way it should be from the very beginning. Nothing easier -to describe. If you want to make new game using Open{MW} as engine (so called ``total conversion'') you should create a game file. +to describe. If you want to make new game using \OMW{} as engine (so called ``total conversion'') you should create a game file. If you want to create a addon for existing game file -- simply create addon file. Nothing else matters: The only distinction you should consider is if your project is about changing other game, or creating a new one. Simple as that. Other simple thing about content files are extensions. We are using .omwaddon for addon files and .omwgame for game files. %TODO describe what content files contains. and what not. -\subparagraph{\MW content files} -Using our content files is recommended solution for projects that are intended to used with Open{MW} engine. However some players -wish to use original Morrowind engine, even with it large flaws and lacking features\footnote{If this is actually wrong, We are very +\subparagraph{\MW{} content files} +Using our content files is recommended solution for projects that are intended to used with \OMW{} engine. However some players +wish to use original Morrowind engine, even with it large flaws and lacking features\footnote{If this is actually wrong, we are very successful project. Yay!}. Also, since 2002 thousands of ESP/ESM files were created, some with really outstanding content. -Because of this Open{CS} simply has no other choice but support ESP/ESM files. However, if you decided to choose ESP/ESM file instead +Because of this \OCS{} simply has no other choice but support ESP/ESM files. However, if you decided to choose ESP/ESM file instead using our own content file types you are most likely aim at the original engine compatibility. This subject is covered in the very last section of this manual. %not finished TODO add the said section. Most likely when more features are present. The actual creation of new files is described in the next chapter. Here we are gonna focus only on details that you need to know -in order to create your first Open{CS} file while full understanding your needs. For now let's jut remember that content files +in order to create your first \OCS{} file while full understanding your needs. For now let's jut remember that content files are created inside the user directory, in the the \textbf{data} subfolder (that is the one special data directory mentioned earlier). \subparagraph{Dependencies} @@ -65,7 +65,7 @@ for addon files -- it can not depend on addon files. %\subparagraph{Loading order} %TODO \paragraph{Project files} -Project files act as containers for data not used by the Open{MW} game engine itself, but still useful for OpenCS. The shining example +Project files act as containers for data not used by the \OMW{} game engine itself, but still useful for OpenCS. The shining example of this data category are without doubt record filters (described in the later section of the manual you are reading currently). As a mod author you probably do not need and/or want to distribute project files at all, they are meant to be used only by you. @@ -76,12 +76,12 @@ with appended extensions. For instance swords.omwaddon file is associated with s %TODO where are they stored. Project files are stored inside the user directory, in the \textbf{projects} subfolder. This is the path location for both freshly -created project files, and a place where Open{CS} looks for already existing files. +created project files, and a place where \OCS{} looks for already existing files. \paragraph{Resources files} %textures, sounds, whatever Unless we are talking about the fully text based game, like Zork or Rogue, you are expecting that a video game is using some media files: -models with textures, pictures acting as icons, sounds and everything else. Since content files, no matter if it is ESP, ESM or new Open{MW} +models with textures, pictures acting as icons, sounds and everything else. Since content files, no matter if it is ESP, ESM or new \OMW{} file type do not contain any of those, it is clear that they have to be deliver with a different file. It is also clear that this, let's call it ``resources file``, have to be supported by the engine. Without code handling those files, it is nothing more than a mathematical abstraction -- something, that lacks meaning for human beings\footnote{Unless we call programmers a human beings.}. @@ -89,7 +89,7 @@ Therefore this section must cover ways to add resources files to your content fi to do just that. Later, you will learn how to make use of those files in your content. \subparagraph{Audio} -Open{MW} is using {FF}mpeg for audio playback, and so we support every audio type that is supported by this library. This makes a huge list. +OpenMW is using {FFmpeg} for audio playback, and so we support every audio type that is supported by this library. This makes a huge list. Below is only small portion of supported file types. \begin{description} @@ -102,16 +102,16 @@ As in the case of audio files, we are using {FFmepg} to decode video files. The only the most significant. \begin{description} - \item bik videos used by original Morrowind game. + \item bik videos used by original \MW{} game. \item mp4 multimedia container which use more advanced codecs (MPEG-4 Parts 2,3,10) with a better audio and video compression rate, but also requiring more {CPU} intensive decoding -- this makes it probably less suited for video games. \item webm is a new, shiny and open source video format with excellent compression. It needs quite a lot of processing power to be decoded, - but since game logic is not running during cut scenes we can recommended it for use with Open{MW}. + but since game logic is not running during cut scenes we can recommended it for use with \OMW. \item ogv alternative, open source container using theora codec for video and vorbis for audio. \end{description} \subparagraph{Textures and images} -Original \MW game uses DDS and TGA files for all kind of two dimensional images and textures alike. In addition, engine supported BMP +Original \MW{} game uses DDS and TGA files for all kind of two dimensional images and textures alike. In addition, engine supported BMP files for some reason (BMP is a terrible format for a video game). We also support extended set of image files -- including JPEG and PNG. JPEG and PNG files can be useful in some cases, for instance JPEG file is a valid option for skybox texture and PNG can useful for masks. However please, keep in mind that JPEG can grow into large sizes quickly and are not the best option with DirectX rendering backend. diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index 4eb0985a5..802aa3ec5 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -1,16 +1,16 @@ -\section{Record filters} +d\section{Record filters} \subsection{Introduction} -Filters are the key element of OpenCS use cases by allowing rapid and easy access to the searched records presented in all tables. +Filters are the key element of \OCS{} use cases by allowing rapid and easy access to the searched records presented in all tables. Therefore: in order to use this application fully effective you should make sure that all concepts and instructions written in the this section of the manual are perfectly clear to you. -Don't be afraid though, filters are fairly intuitive and easy to use. +Do not be afraid though, filters are fairly intuitive and easy to use. \subsubsection{Used Terms} \begin{description} \item[Filter] is generally speaking a tool able to ``Filter'' (that is: select some elements, while discarding others) according - to the some criteria. In case of OpenCS: records are being filtered according to the criteria of user choice. Criteria are written + to the some criteria. In case of \OCS: records are being filtered according to the criteria of user choice. Criteria are written down in language with simple syntax. \item[Criteria] describes condition under with any any record is being select by the filter. \item[Syntax] as you may noticed computers (in general) are rather strict, and expect only strictly formulated orders -- that is: @@ -18,20 +18,20 @@ Don't be afraid though, filters are fairly intuitive and easy to use. \item[Expression] is way we are actually performing filtering. Filter can be treated as ``functions'': accepts arguments, and evaluates either to the true or false for every column record at the time. \item[N-ary] is any expression that expects one or more expressions as arguments. It is useful for grouping two (or more) other expressions - together in order to create filter that will check for criteria placed in two (again: or more) columns (logical ``or'', ``and''). - \item[unary] is any expression that expects one other expression. The example is ``not'' expression. In fact ``not'' is the only useful - unary expression in OpenCS record filters. + together in order to create filter that will check for criteria placed in two (again: or more) columns (logical \textit{or}, \textit{and}). + \item[unary] is any expression that expects one other expression. The example is \textit{not} expression. In fact \textit{not} is the only useful + unary expression in \OCS{} record filters. \item[nullary] is expression that does not accepts other expressions. It accepts arguments specified later. \end{description} \subsubsection{Basics} In fact you do not need to learn everything about filters in order to use them. In fact all you need to know to achieve decent productivity -with OpenCS is inside basics section. +with \OCS{} is inside basics section. \subsubsection{Interface} -Above each table there is a field that is used to enter filter: either predefined by the OpenMW developers or made by you, the user. +Above each table there is a field that is used to enter filter: either predefined by the \OMW{} developers or made by you, the user. You probably noticed it before. However there is also completely new element, although using familiar table layout. Go to the application -menu view, and click filters. You should see set of default filters, made by the OpenMW team in the table with the following columns: filter, +menu view, and click filters. You should see set of default filters, made by the \OMW{} team in the table with the following columns: filter, description and modified. \begin{description} @@ -44,20 +44,21 @@ description and modified. So let's learn how to actually use those to speed up your work. \subsubsection{Using predefined filters} Using those filters is quite easy and involves typing inside the filter field above the table. For instance, try to open referencables -table and type in the filters field the following: ``project::weapons''. As soon as you complete the text, table will magicly alter -and will show only the weapons. As you could noticed project::weapons is nothing else than a ID of one of the predefined filters. That is it: +table and type in the filters field the following: \mono{project::weapons}. As soon as you complete the text, table will magicly alter +and will show only the weapons. As you could noticed \mono{project::weapons} is nothing else than a~ID of one of the predefined filters. That is it: in order to use the filter inside the table you simply type it is name inside the filter field. To make life easier filter IDs follow simple convention. \begin{itemize} - \item Filter ID filtering a specific record type contains usually the name of a specific group. For instance project::weapons filter + \item Filter ID filtering a specific record type contains usually the name of a specific group. For instance \mono{project::weapons} filter contains the word weapons (did you noticed?). Plural form is always used. \item When filtering specific subgroup the ID starts just like in the case of general filter. For instance project::weaponssilver will - filter only silver weapons (new mechanic introduced by the Bloodmoon, silver weapons deal double damage against werewolfs) and - project::weaponsmagical will filter only magical weapons (able to hurt ghosts and other supernatural creatures). - \item There are few exceptions from the above rule. For instance there is a project::added, project::removed, project::modyfied, project::base. - You would probably except something more like ``project::statusadded'' but in this case typing this few extra characters would only + filter only silver weapons (new mechanic introduced by the \BM{}, silver weapons deal double damage against werewolfs) and + \mono{project::weaponsmagical} will filter only magical weapons (able to hurt ghosts and other supernatural creatures). + \item There are few exceptions from the above rule. For instance there is a \mono{project::added}, \mono{project::removed}, + \mono{project::modyfied}, \mono{project::base}. + You would probably except something more like \mono{project::statusadded} but in this case typing this few extra characters would only help to break your keyboard faster. \end{itemize} @@ -71,13 +72,13 @@ Finally, you will have to write this with correct syntax. As a result table will Advance subsection covers everything that you need to know in order to create any filter you may want to %TODO the filter part is actually wrong \subsubsection{Namespaces} -Did you noticed that every default filter has ``project::`` prefix? It is a ``namespace``, a term borrowed from the \CPP{} language. -In case of OpenCS namespace always means scope of the said object\footnote{You are not supposed to understand this at the moment.}. +Did you noticed that every default filter has \mono{project::} prefix? It is a \textit{namespace}, a~term borrowed from the \CPP{} language. +In case of \OCS{} namespace always means scope of the said object\footnote{You are not supposed to understand this at the moment.}. But what does it mean in case of filters? Well, short explanation is actually simple. \begin{description} - \item[project::] namespace indicates that filter is used with the project, in multiple sessions. You can restart Open{CS} and filter + \item[project::] namespace indicates that filter is used with the project, in multiple sessions. You can restart \OCS{} and filter is still there. - \item[session::] namespace indicates that filter is not stored trough multiple sessions and once you will quit Open{CS} (close session) + \item[session::] namespace indicates that filter is not stored trough multiple sessions and once you will quit \OCS{} (close session) the filter will be gone. Forever! Until then it can be found inside the filters table. \end{description} In addition to this two scopes, there is a third one; called one-shot. One-shot filters are not stored (even during single session) @@ -89,8 +90,8 @@ Still, you may wonder how you are supposed to write expressions, what expression with nullary expressions that will allow you to create a basic filter. \subsubsection{Nullary expressions} -All nullary expressions are used in similar manner. First off: you have to write it is name (for instance: ``string'') and secondly: -condition that will be checked inside brackets (for instance string(something, something)). If conditions of your expression will be meet +All nullary expressions are used in similar manner. First off: you have to write it is name (for instance: \mono{string}) and secondly: +condition that will be checked inside brackets (for instance \mono{string(something, something)}). If conditions of your expression will be meet by a record (technical speaking: expression will evaluate to true) the record will show up in the table. It is clear that you need to know what are you checking, that is: what column of the table contains information that you are interested @@ -99,27 +100,27 @@ you want to see, while the second one sets desired value inside of the cell. To \paragraph{String -- string(``column'', ``value'')} String in programmers language is often\footnote{Often, not always. There are different programming languages using slightly different terms.} -just a word for anything composed of characters. In case of OpenCS this is in fact true for every value inside the column that is not composed -of the pure numbers. Even columns containing only ``true`` and ``false`` values can be targeted by the string expression. -\footnote{There is no Boolean (''true'' or ``false'') value in the OpenCS. You should use string for those.} String evaluates to true, +just a word for anything composed of characters. In case of \OCS{} this is in fact true for every value inside the column that is not composed +of the pure numbers. Even columns containing only ``true`` and ``false`` values can be targeted by the string expression\footnote{There is no +Boolean (''true'' or ``false'') value in the \OCS. You should use string for those.}. String evaluates to true, when record contains in the specified column exactly the same value as specified. Since majority of the columns contain string values, string is among the most often used expressions. Examples: \begin{itemize} - \item string(``Record Type'', ``Weapon'') -- will evaluate to true for all records containing ``Weapon'' in the ``Record Type'' column cell. + \item \mono{string(``Record Type'', ``Weapon'')} -- will evaluate to true for all records containing \mono{Weapon} in the \mono{Record Type} column cell. This group contains every weapon (including arrows and bolts) found in the game. - \item string(``Portable'', ``true'') -- will evaluate to true for all records containing word true inside ``Portable'' column cell. + \item \mono{string(``Portable'', ``true'')} -- will evaluate to true for all records containing word true inside \mono{Portable} column cell. This group contains every portable light sources (lanterns, torches etc.). \end{itemize} This is probably enough to create around 90 string filters you will eventually need. However, this expression is even more powerful -- it accepts regular expressions (also called regexps). Regular expressions is a way to create string criteria that will be matched by one than just one specific value in the column. For instance, you can display both left and right gauntlets with the following expression: -``string("armor type", ".* gauntlet"))`` because ''.*'' in regexps means just: ``anything''. This filter says: please, show me ``any'' gauntlet. +\mono{string("armor type", ".* gauntlet"))} because \mono{.*} in regexps means just: ``anything''. This filter says: please, show me ``any'' gauntlet. There are left and right gauntlets in the morrowind so this will evaluate to true for both. Simple, isn't it? -Creating regexps can be a difficult and annoying -- especially when you need complex criteria. On the other hand, We are under impression +Creating regexps can be a difficult and annoying -- especially when you need complex criteria. On the other hand, we are under impression that in reality complex expressions are needed only in sporadic cases. In fact, the truth is: that most of the time only already mentioned -``.*'' is needed and therefore the following description of regexps can be skipped by vast majority of readers. +\mono{.*} is needed and therefore the following description of regexps can be skipped by vast majority of readers. Before working with Regular Expressions, you should understand what actually are regular expressions. Essentially, the idea is simple: when you are writing any word, you are using strictly defined letters -- that is: letters create a word. What you want to do with regular @@ -127,21 +128,22 @@ expression is to use set of rules that will match to many words. It is not that you will clearly need way to determinate what letters you want to match (word is composed by letters). Before introducing other ways to choose between characters, I want explain anchors. Anchors allows you to decide where to ``look'' in the string. -You surely should know about ``\^'' anchor and ``\textdollar''. Putting ``\^`` will tell to Open{CS} to look on the beginning of string, -while ''\textdollar`` is used to mark the end of it. For instance, pattern ''\^Pink.* elephant.\textdollar`` Will match any sentence beginning -with the word ''Pink`` and ending with '' elephant.``. Pink fat elephant. Pink cute elephant. It does not matter what is in between, -because ''.*`` is used.\\ +You surely should know about \mono{\textasciicircum} anchor and \mono{\textdollar}. Putting \mono{\textasciicircum} will tell to \OCS{} +to look on the beginning of string, while \mono{\textdollar} is used to mark the end of it. For instance, pattern +\mono{\textasciicircum{}Pink.* elephant.\textdollar} will match any sentence beginning with the word \mono{Pink} and ending with +\mono{ elephant.}. Pink fat elephant. Pink cute elephant. It does not matter what is in between, because \mono{.*} is used. -You have already seen the power of the simple ``.*''. But what if you want to chose between only two (or more) letters? Well, this is when -``[|]'' comes in handy. If you write something like: ``\^[a|k].*'' you are simply telling Open{CS} to filter anything that starts with either -``a'' or ``k''. Using ``\^[a|k|l].*'' will work in the same manner, but it will also cover strings starting with ``l''. +You have already seen the power of the simple \mono{.*}. But what if you want to chose between only two (or more) letters? Well, this is when +\mono{[|]} comes in handy. If you write something like: \mono{\textasciicircum[a|k].*} you are simply telling \OCS{} to filter anything that +starts with either \mono{a} or \mono{k}. Using \mono{\textasciicircum[a|k|l].*} will work in the same manner, but it will also cover +strings starting with \mono{l}. -What if you want to match more than just one latter? Just use ``(|)``. it is pretty similar to the above one letter as you see, but it is -used to fit more than just one character. For instance: ''\^(Pink|Green).* (elephant|crocodile).\textdollar`` will be true for all sentences -starting with ''Pink`` or ''Green`` and ending with either ''elephant.`` or ''crocodile.``. +What if you want to match more than just one latter? Just use \mono{(|)}. it is pretty similar to the above one letter as you see, but it is +used to fit more than just one character. For instance: \mono{\textasciicircum(Pink|Green).* (elephant|crocodile).\textdollar} will be +true for all sentences starting with \mono{Pink} or \mono{Green} and ending with either \mono{elephant.} or \mono{crocodile.}. Regular expressions are not the main topic of this manual. If you wish to learn more on this subject please, read the documentation on -Qt regular expressions syntax, or TRE regexp syntax (it is almost like in Qt). Above is just enough to use Open{CS} effectively to be sure. +Qt regular expressions syntax, or TRE regexp syntax (it is almost like in Qt). Above is just enough to use \OCS{} effectively to be sure. \paragraph{Value -- value(``value'', (``open'', ``close''))} While string expression covers vast group of columns containing string values, there are in fact columns with just numerical values like @@ -149,10 +151,10 @@ While string expression covers vast group of columns containing string values, t inside brackets. Clearly, conditions should hold column to test in. However in this case wanted value is specified as a range. As you would imagine the range can be specified as including a border value, or excluding. We are using two types of brackets for this: \begin{itemize} - \item To include value use [] brackets. For value equal 5, expression value(something, [5, 10]) will evaluate to true. - \item To exclude value use () brackets. For value equal 5, expression value(something, (5, 10)) will evaluate to false. - \item Mixing brackets is completely legal. For value equal 10, expression value(something, [5, 10) will evaluate to true. The same expression - will evaluate to false for value equal 10. + \item To include value use [] brackets. For value equal 5, expression \mono{value(something, [5, 10])} will evaluate to true. + \item To exclude value use () brackets. For value equal 5, expression \mono{value(something, (5, 10))} will evaluate to false. + \item Mixing brackets is completely legal. For value equal 10, expression \mono{value(something, [5, 10)} will evaluate to true. + The same expression will evaluate to false for value equal 10. \end{itemize} \paragraph{''true`` and ''false``} @@ -161,7 +163,7 @@ and false (in case of \textit{false}) no matter what. The main usage of this ex disable some part of the filter that makes heavy use of the logical expressions. \subsubsection{Logical expressions} -This subsection takes care of two remaining groups of expressions: binary and unary. The only unary expression present in the OpenCS is logical +This subsection takes care of two remaining groups of expressions: binary and unary. The only unary expression present in the \OCS{} is logical \textit{not}, while the remaining binary expressions are: \textit{or}, \textit{and}. This clearly makes them (from the user point of view) belonging to the same group of logical expressions. @@ -170,8 +172,8 @@ Sometimes you may be in need of reversing the output of the expression. This is expression will revert it: if expression was returning true, it will return false; if it was returning false, it will return true. Brackets are not needed: \textit{not} will revert only the first expression following it. -To show this on know example, let's consider the ''string("armor type", ".* gauntlet"))`` filter. As we mentioned earlier this will return true -for every gauntlet found in game. In order to show everything, but gauntlets we simply do ''not string("armor type", ".* gauntlet"))``. +To show this on know example, let's consider the \mono{string("armor type", ".* gauntlet"))} filter. As we mentioned earlier this will return true +for every gauntlet found in game. In order to show everything, but gauntlets we simply do \mono{not string("armor type", ".* gauntlet"))}. This is probably not the most useful filter on earth, but this is not a surprise: real value of \textit{not} expression shines when combined with \textit{or}, \textit{and} filters. @@ -179,13 +181,13 @@ This is probably not the most useful filter on earth, but this is not a surprise \textit{Or} is a expression that will return true if one of the arguments evaluates to true. You can use two or more arguments, separated by the comma. \textit{Or} expression is useful when showing two different group of records is needed. For instance the standard actor filter is using the following -''or(string(``record type'', npc), string(``record type'', creature))`` and will show both npcs and creatures. +\mono{or(string(``record type'', npc), string(``record type'', creature))} and will show both npcs and creatures. \paragraph{and -- and(expression1(), expression2())} \textit{And} is a expression that will return true if all arguments evaluates to true. As in the case of ''or`` you can use two or more arguments, separated by a comma. As we mentioned earlier in the \textit{not} filter, combining \textit{not} with \textit{and} can be very useful. For instance to show all armor types, -excluding gauntlets you can write the following: ''and (not string("armor type", ".* gauntlet"), string(''Record Type``, ''Armor``))''. +excluding gauntlets you can write the following: \mono{and (not string("armor type", ".* gauntlet"), string(''Record Type``, ''Armor``))}. \subsubsection{Creating and saving filter} In order to create and save new filter, you should go to the filters table, right click and select option ``add record'' from the context menu. @@ -196,7 +198,7 @@ and write it down there. Done! You are free to use your filter. \subsubsection{Replacing the default filters set} -{OpenCS} allows you to substitute default filters set provided by us, with your own filters. In order to do so you should create a new project, +OpenCS allows you to substitute default filters set provided by us, with your own filters. In order to do so you should create a new project, add desired filters, remove undesired and save. Rename the file to the ``defaultfilters'' (do not forget to remove .omwaddon.project extension) and place it inside your configuration directory. diff --git a/manual/opencs/main.tex b/manual/opencs/main.tex index 3a395e277..1d8aebe40 100644 --- a/manual/opencs/main.tex +++ b/manual/opencs/main.tex @@ -3,7 +3,7 @@ \usepackage{babel} \usepackage{txfonts} % Public Times New Roman text & math font \usepackage[pdftex]{graphicx} -\usepackage[colorlinks,pdftex]{hyperref} +\usepackage[final,colorlinks,pdftex,pdfpagelabels=true]{hyperref} \author{OpenMW Team} \def\pdfBorderAttrs{/Border [0 0 0] } % No border arround Links @@ -13,11 +13,13 @@ pdfauthor={Copyright \textcopyright{} OpenMW Team }, pdftitle={OpenCS user manual} } - -\def\MW{\textit{Morrowind\texttrademark{}\ }} -\def\TB{\textit{Tribunal\ }} -\def\BM{\textit{Bloodmon\ }} -\def\BS{Bethesda Softworks\ } +\def\mono{\texttt} +\def\MW{\textit{Morrowind\texttrademark{}}} +\def\TB{\textit{Tribunal}} +\def\BM{\textit{Bloodmon}} +\def\BS{Bethesda Softworks} +\def\OMW{\hbox{OpenMW}} +\def\OCS{\hbox{OpenCS}} \begin{document} diff --git a/manual/opencs/tables.tex b/manual/opencs/tables.tex index cbb59b2dd..e7cc06735 100644 --- a/manual/opencs/tables.tex +++ b/manual/opencs/tables.tex @@ -1,8 +1,8 @@ \section{Tables} \subsection{Introduction} -If you have launched OpenCS already and played around with it for a bit, you have probably gotten the impression that it contains lots of tables. -You'd be spot on: OpenCS is built around using tables. This does not mean it works just like Microsoft Excel or Libre Office Calc, though. +If you have launched \OCS{} already and played around with it for a bit, you have probably gotten the impression that it contains lots of tables. +You'd be spot on: \OCS{} is built around using tables. This does not mean it works just like Microsoft Excel or Libre Office Calc, though. Due to the vast amounts of information involved with \MW, tables just made the most sense. You have to be able to spot information quickly and be able to change them on the fly. Let's browse through the various screens and see what all these tables show. @@ -12,7 +12,7 @@ Let's browse through the various screens and see what all these tables show. \subsubsection{Glossary} \begin{description} - \item[Record:] An entry in OpenCS representing an item, location, sound, NPC or anything else. + \item[Record:] An entry in \OCS{} representing an item, location, sound, NPC or anything else. \item[Reference, Referenceable:] When an item is placed in the world, it does not create a new record each time. For example, the game world might contain a lot of exquisite belts on different NPCs and in many crates, but they all refer to one specific record: the Exquisite Belt record. @@ -22,17 +22,17 @@ Let's browse through the various screens and see what all these tables show. \end{description} \subsubsection{Recurring Terms} -Some columns are recurring throughout OpenCS. They show up in (nearly) every table in OpenCS. +Some columns are recurring throughout \OCS. They show up in (nearly) every table in \OCS. \begin{description} -\item[ID] Each item, location, sound, etc. gets the same unique identifier in both OpenCS and Morrowind. This is usually a very self-explanatory name. +\item[ID] Each item, location, sound, etc. gets the same unique identifier in both \OCS{} and \MW. This is usually a very self-explanatory name. For example, the ID for the (unique) black pants of Caius Cosades is ``Caius\_pants''. This allows you to manipulate the game in many ways. For example, you could add these pants to your inventory by simply opening the console and write: ``player->addItem Caius\_pants''. Either way, in both Morrowind -and OpenCS, the ID is the primary way to identify all these different parts of the game. %Wrong! Cells do not have ID, only name. +and \OCS, the ID is the primary way to identify all these different parts of the game. %Wrong! Cells do not have ID, only name. \item[Modified] This column shows what has happened (if something has happened) to this record. There are four possible states in which it can exist. \item[Base] means that this record is part of the base game and is in its original state. Usually, if you create a mod, the base game is Morrowind with optionally the Bloodmoon and Tribunal expansions. -\item[Added] means that this record was not in the base game and has been added by a modder. +\item[Added] means that this record was not in the base game and has been added by a~modder. \item[Modified] means that the record is part of the base game, but has been changed in some way. \item[Deleted] means that this record used to be part of the base game, but has been removed as an entry. This does not mean, however, that the occurrences in the game itself have been removed! For example, if you remove the CharGen\_Bed entry from morrowind.esm, it does not mean the bedroll in the basement @@ -49,7 +49,7 @@ This describes the general areas of Vvardenfell. Each of these areas has differe \begin{description} \item[Name:] This is how the game will show your location in-game. \item[Map Colour:] This is a six-digit hexidecimal representation of the colour used to identify the region on the map available in - World > Region Map. If you don't have an application with a colour picker, you can use your favourite search engine to find a colour picker online. + World > Region Map. If you do not have an application with a colour picker, you can use your favourite search engine to find a colour picker online. \item[Sleep Encounter:] These are the rules for what kind of enemies you might encounter when you sleep outside in the wild. \end{description} @@ -59,8 +59,8 @@ why would the computer need to keep track the exact locations of NPCs walking th be quite useless and bring your system to its knees! So the world has been divided up into squares we call "cells". Once your character enters a cell, the game will load everything that is going on in that cell so you can interact with it. -In the original \MW this could be seen when you were travelling and you would see a small loading bar at the bottom of the screen; -you had just entered a new cell and the game would have to load all the items and NPCs. The Cells screen in OpenCS provides you with a list of cells +In the original \MW{} this could be seen when you were travelling and you would see a small loading bar at the bottom of the screen; +you had just entered a new cell and the game would have to load all the items and NPCs. The Cells screen in \OCS{} provides you with a list of cells in the game, both the interior cells (houses, dungeons, mines, etc.) and the exterior cells (the outside world). \begin{description} @@ -76,14 +76,14 @@ in the game, both the interior cells (houses, dungeons, mines, etc.) and the ext Setting the cell's Interior Water to false tells the game that the water at height 0 should not be used. Remember that cells that are in the outside world are exterior cells and should thus \textit{always} be set to false! - \item[Interior Sky:] Should this interior cell have a sky? This is a rather unique case. The \TB expansion took place in a city on + \item[Interior Sky:] Should this interior cell have a sky? This is a rather unique case. The \TB{} expansion took place in a city on the mainland. Normally this would require the city to be composed of exterior cells so it has a sky, weather and the like. But if the player is in an exterior cell and looks at his in-game map, he sees Vvardenfell with an overview of all exterior cells. The player would have to see the city's very own map, as if he was walking around in an interior cell. So the developers decided to create a workaround and take a bit of both: The whole city would technically work exactly like an interior cell, - but it would need a sky as if it was an exterior cell. That's what this is. This is why the vast majority of the cells you will find in this screen - will have this option set to false: It's only meant for these "fake exteriors". + but it would need a sky as if it was an exterior cell. That is what this is. This is why the vast majority of the cells you will find in this screen + will have this option set to false: It is only meant for these "fake exteriors". \item[Region:] To which Region does this cell belong? This has an impact on the way the game handles weather and encounters in this area. It is also possible for a cell not to belong to any region. @@ -93,11 +93,11 @@ in the game, both the interior cells (houses, dungeons, mines, etc.) and the ext \subsubsection{Referenceables} This is a library of all the items, triggers, containers, NPCs, etc. in the game. There are several kinds of Record Types. Depending on which type a record is, it will need specific information to function. For example, an NPC needs a value attached to its aggression level. A chest, of course, -does not. All Record Types contain at least a model. How else would the player see them? Usually they also have a Name, which is what you see +does not. All Record Types contain at least a~model. How else would the player see them? Usually they also have a Name, which is what you see when you hover your reticle over the object. -Let's go through all Record Types and discuss what you can tell OpenCS about them. +Let's go through all Record Types and discuss what you can tell \OCS{} about them. \begin{description} - \item[Activator:] This is an item that, when activated, starts a script or even just shows a tooltip. + \item[Activator:] This is an item that, when activated, starts a script or even just shows a~tooltip. \end{description} \ No newline at end of file diff --git a/manual/opencs/windows.tex b/manual/opencs/windows.tex index de377a119..98bf369e8 100644 --- a/manual/opencs/windows.tex +++ b/manual/opencs/windows.tex @@ -1,20 +1,20 @@ \section{Windows} \subsection{Introduction} -This section describes the multiple windows interface of the OpenCS editor. This design principle was chosen in order +This section describes the multiple windows interface of the \OCS{} editor. This design principle was chosen in order to extend the flexibility of the editor, especially on the multiple screens setups and on environments providing advanced windows management features, like; for instance: multiple desktops found commonly on many open source desktop environments. However, it is enough to have a single large screen to see the advantages of this concept. OpenCS windows interface is easy to describe and understand. In fact we decided to minimize use of many windows concepts -applied commonly in various applications. For instance dialog windows are really hard to find in the OpenCS. You are free to try, +applied commonly in various applications. For instance dialog windows are really hard to find in the \OCS. You are free to try, though. Because of this, and the fact that we expect that user is familiar with other applications using windows this section is mostly -focused on practical ways of organizing work with the OpenCS. +focused on practical ways of organizing work with the \OCS. \subsection{Basics} -After starting Open{CS} and choosing content files to use a editor window should show up. It probably does not look surprising: -there is a menubar at the top, and there is a large empty area. That is it: a brand new Open{CS} window contains only menubar +After starting \OCS{} and choosing content files to use a editor window should show up. It probably does not look surprising: +there is a menubar at the top, and there is a~large empty area. That is it: a brand new \OCS{} window contains only menubar and statusbar. In order to make it a little bit more useful you probably want to enable some panels\footnote{Also known as widgets.}. You are free to do so, just try to explore the menubar. @@ -23,19 +23,19 @@ just focus on the windows itself. \paragraph{Creating new windows} is easy! Just visit view menu, and use the ``New View'' item. Suddenly, out of the blue a new window will show up. As you would expect, -it is also blank, and you are free to add any of the Open{CS} panels. +it is also blank, and you are free to add any of the \OCS{} panels. \paragraph{Closing opened window} is also easy! Simply close that window decoration button. We suspect that you knew that already, but better to be sure. -Closing last Open{CS} window will also terminate application session. +Closing last \OCS{} window will also terminate application session. \paragraph{Multi-everything} -is the main foundation of Open{CS} interface. You are free to create as many windows as you want to, free to populate it with +is the main foundation of \OCS{} interface. You are free to create as many windows as you want to, free to populate it with any panels you may want to, and move everything as you wish to -- even if it makes no sense at all. If you just got crazy idea and -you are wonder if you are able to have one hundred Open{CS} windows showing panels of the same type, well most likely you are +you are wonder if you are able to have one hundred \OCS{} windows showing panels of the same type, well most likely you are able to do so. -The principle behind this design decision is easy to see for \BS made editor, but maybe not so clear for users who are +The principle behind this design decision is easy to see for \BS{} made editor, but maybe not so clear for users who are just about to begin their wonderful journey of modding. \subsection{Advanced} @@ -46,9 +46,9 @@ All major graphical environments commonly present in operating systems comes wit active window. It is very effective and fast when you have only two windows, each holding only one table. Sometimes you have to work with two at the time, and with one from time to time. Here, you can have one window holding two tables, and second holding just one. -Open{CS} is designed to simply make sense and do not slowdown users. It is as simple as possible (but not simpler), and uses one +OpenCS is designed to simply make sense and do not slowdown users. It is as simple as possible (but not simpler), and uses one flexible approach in all cases. -There is no point in digging deeper in the windows of Open{CS}. Let's explore panels, starting with tables. +There is no point in digging deeper in the windows of \OCS. Let's explore panels, starting with tables. %We should write some tips and tricks here. \ No newline at end of file From 586990848041801ce2962bdd5f6ceb740240ab92 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 1 Dec 2013 12:03:04 +0100 Subject: [PATCH 093/889] Another small cleanup - missed that in previous commits. Signed-off-by: Lukasz Gromanowski --- manual/opencs/files_and_directories.tex | 2 +- manual/opencs/filters.tex | 4 ++-- manual/opencs/windows.tex | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/manual/opencs/files_and_directories.tex b/manual/opencs/files_and_directories.tex index b3283dac4..d13cfa727 100644 --- a/manual/opencs/files_and_directories.tex +++ b/manual/opencs/files_and_directories.tex @@ -106,7 +106,7 @@ only the most significant. \item mp4 multimedia container which use more advanced codecs (MPEG-4 Parts 2,3,10) with a better audio and video compression rate, but also requiring more {CPU} intensive decoding -- this makes it probably less suited for video games. \item webm is a new, shiny and open source video format with excellent compression. It needs quite a lot of processing power to be decoded, - but since game logic is not running during cut scenes we can recommended it for use with \OMW. + but since game logic is not running during cut scenes we can recommend it for use with \OMW. \item ogv alternative, open source container using theora codec for video and vorbis for audio. \end{description} diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index 802aa3ec5..a5f88aebb 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -1,4 +1,4 @@ -d\section{Record filters} +\section{Record filters} \subsection{Introduction} Filters are the key element of \OCS{} use cases by allowing rapid and easy access to the searched records presented in all tables. Therefore: in order to use this application fully effective you should make sure that all concepts and instructions written in @@ -53,7 +53,7 @@ To make life easier filter IDs follow simple convention. \begin{itemize} \item Filter ID filtering a specific record type contains usually the name of a specific group. For instance \mono{project::weapons} filter contains the word weapons (did you noticed?). Plural form is always used. - \item When filtering specific subgroup the ID starts just like in the case of general filter. For instance project::weaponssilver will + \item When filtering specific subgroup the ID starts just like in the case of general filter. For instance \mono{project::weaponssilver} will filter only silver weapons (new mechanic introduced by the \BM{}, silver weapons deal double damage against werewolfs) and \mono{project::weaponsmagical} will filter only magical weapons (able to hurt ghosts and other supernatural creatures). \item There are few exceptions from the above rule. For instance there is a \mono{project::added}, \mono{project::removed}, diff --git a/manual/opencs/windows.tex b/manual/opencs/windows.tex index 98bf369e8..97bb75e79 100644 --- a/manual/opencs/windows.tex +++ b/manual/opencs/windows.tex @@ -2,7 +2,7 @@ \subsection{Introduction} This section describes the multiple windows interface of the \OCS{} editor. This design principle was chosen in order to extend the flexibility of the editor, especially on the multiple screens setups and on environments providing advanced -windows management features, like; for instance: multiple desktops found commonly on many open source desktop environments. +windows management features, like for instance: multiple desktops found commonly on many open source desktop environments. However, it is enough to have a single large screen to see the advantages of this concept. OpenCS windows interface is easy to describe and understand. In fact we decided to minimize use of many windows concepts From 8b74e8cba7da123119217d88fd7d18da627de826 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 1 Dec 2013 13:00:44 +0100 Subject: [PATCH 094/889] Commiting. Not sure what since i had short blackout :/ --- manual/opencs/creating_file.tex | 25 +++++++++++++++++++++++++ manual/opencs/files_and_directories.tex | 19 ++++++++++--------- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/manual/opencs/creating_file.tex b/manual/opencs/creating_file.tex index e69de29bb..8cab72617 100644 --- a/manual/opencs/creating_file.tex +++ b/manual/opencs/creating_file.tex @@ -0,0 +1,25 @@ +\section{OpenCS starting dialog} +\subsection{Introduction} +The great day has come. Today, you shall open \OCS{} application. And when you do this, you shall see our starting dialog window that holds three buttons +that can bring both pain and happiness. So just do this, please. + +\subsection{Basics} +Back to the manual? Great! As you can see, the starting window holds just three buttons. Since you are already familiar with our files system, they come +to you with no surprise.\\ + +First, there is a \textbf{Create A New Game} button. Clearly, you should press it when you want to create a game file. Than, what \textbf{Create A New Addon} button do? +Yes! You are right! This button will create any addon content file (and new project file associated with it)! Wonderful! And what the last remaining button do? \textbf{Edit A Content File}? Well, it comes with no surprise that this should be used when you need to alter existing content file, either a game or addon.\\ + +\paragraph{Selecting Files} +As We wrote earlier, + +\subsection{Advanced} +If you are paying attention, you noticed any extra icon with wrench. This one will open small settings window. Options here are few, and easy to list. +\begin{description} + \item {Window Size} is needed to configure initial window size of the starting window. + \item {Display format} can be used to configure behavior of the \OCS{} itself, however only in the limited degree. You can decide if you want \OCS{} to display icon, text or both in some columns of tables. +\end{description} + +%TODO configuration + +And that would be it. There is no point spending more time here. We should go forward now. \ No newline at end of file diff --git a/manual/opencs/files_and_directories.tex b/manual/opencs/files_and_directories.tex index b3283dac4..bb399ce99 100644 --- a/manual/opencs/files_and_directories.tex +++ b/manual/opencs/files_and_directories.tex @@ -24,7 +24,7 @@ is not clear, and often confusing. You would expect the ESM (master) file is use and indeed: this is the basic idea. However, original expansions also were made as ESM files, even though they essentially could be described as a really large plugins, and therefore rather use ESP files. There were technical reasons behind this decision -- somewhat valid in the case of original engine, but clearly it's better to create a system that can be used is more sensible way. \OMW{} achieves -his with our own content file types. +this with our own content file types. We support both ESM and ESP files, but in order to make use of new features of OpenMW one should consider using new file types designed with our engine in mind: game files and addon files together called ``content files``. @@ -40,7 +40,7 @@ Other simple thing about content files are extensions. We are using .omwaddon fo %TODO describe what content files contains. and what not. \subparagraph{\MW{} content files} Using our content files is recommended solution for projects that are intended to used with \OMW{} engine. However some players -wish to use original Morrowind engine, even with it large flaws and lacking features\footnote{If this is actually wrong, we are very +wish to use original \MW{} engine, even with it large flaws and lacking features\footnote{If this is actually wrong, we are very successful project. Yay!}. Also, since 2002 thousands of ESP/ESM files were created, some with really outstanding content. Because of this \OCS{} simply has no other choice but support ESP/ESM files. However, if you decided to choose ESP/ESM file instead using our own content file types you are most likely aim at the original engine compatibility. This subject is covered in the very @@ -60,7 +60,7 @@ this section of the manual does not cover creating the content files -- it is on keep in mind that dependencies exist, and is up to you what to decide if your content file should depend on other content file. Game files are not intend to have any dependencies for a very simple reasons: player is using only one game file (excluding original -and dirty ESP/ESM system) at the time and therefore no game file can depend on other game file, and since game file makes the base +and dirty {ESP/ESM} system) at the time and therefore no game file can depend on other game file, and since game file makes the base for addon files -- it can not depend on addon files. %\subparagraph{Loading order} %TODO @@ -93,7 +93,7 @@ OpenMW is using {FFmpeg} for audio playback, and so we support every audio type Below is only small portion of supported file types. \begin{description} - \item mp3 (MPEG-1 Part 3 Layer 3) popular audio file format and \textit{de facto} standard for storing audio. Used by the Morrowind game. + \item mp3 ({MPEG}-1 {Part 3 Layer 3}) popular audio file format and \textit{de facto} standard for storing audio. Used by the \MW{} game. \item ogg open source, multimedia container file using high quality vorbis audio codec. Recommended. \end{description} @@ -103,17 +103,18 @@ only the most significant. \begin{description} \item bik videos used by original \MW{} game. - \item mp4 multimedia container which use more advanced codecs (MPEG-4 Parts 2,3,10) with a better audio and video compression rate, - but also requiring more {CPU} intensive decoding -- this makes it probably less suited for video games. + \item mp4 multimedia container which use more advanced codecs ({MPEG-4 Parts 2,3,10}) with a better audio and video compression rate, + but also requiring more {CPU} intensive decoding -- this makes it probably less suited for storing sounds in computer games, but good for videos. \item webm is a new, shiny and open source video format with excellent compression. It needs quite a lot of processing power to be decoded, but since game logic is not running during cut scenes we can recommended it for use with \OMW. \item ogv alternative, open source container using theora codec for video and vorbis for audio. \end{description} \subparagraph{Textures and images} -Original \MW{} game uses DDS and TGA files for all kind of two dimensional images and textures alike. In addition, engine supported BMP -files for some reason (BMP is a terrible format for a video game). We also support extended set of image files -- including JPEG and PNG. +Original \MW{} game uses {DDS} and {TGA} files for all kind of two dimensional images and textures alike. In addition, engine supported BMP +files for some reason ({BMP} is a terrible format for a video game). We also support extended set of image files -- including {JPEG} and {PNG}. JPEG and PNG files can be useful in some cases, for instance JPEG file is a valid option for skybox texture and PNG can useful for masks. -However please, keep in mind that JPEG can grow into large sizes quickly and are not the best option with DirectX rendering backend. +However please, keep in mind that JPEG can grow into large sizes quickly and are not the best option with {DirectX} rendering backend. You probabbly still want +to use {DDS} files for textures. %\subparagraph{Meshes} %TODO once we will support something more than just nifs \ No newline at end of file From 4ef2724f35abb5ebdbb902df943bc3284f4eb65f Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 1 Dec 2013 13:24:28 +0100 Subject: [PATCH 095/889] Starting dialog window finished. --- manual/opencs/creating_file.tex | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/manual/opencs/creating_file.tex b/manual/opencs/creating_file.tex index 8cab72617..34780a03e 100644 --- a/manual/opencs/creating_file.tex +++ b/manual/opencs/creating_file.tex @@ -10,8 +10,14 @@ to you with no surprise.\\ First, there is a \textbf{Create A New Game} button. Clearly, you should press it when you want to create a game file. Than, what \textbf{Create A New Addon} button do? Yes! You are right! This button will create any addon content file (and new project file associated with it)! Wonderful! And what the last remaining button do? \textbf{Edit A Content File}? Well, it comes with no surprise that this should be used when you need to alter existing content file, either a game or addon.\\ -\paragraph{Selecting Files} -As We wrote earlier, +\paragraph{Selecting Files For New Addon} +As We wrote earlier, both \OMW{} and \OCS{} are operating with dependency idea in mind. As You remember you should only depend on files you are actually using. But how?\\ +It is simple. When you click either \textbf{Create new Addon} you will be asked to choose those with a new dialog window. The window is using vertical layout, first you should consider the the top element, the one that allows you to select a game file with drop down menu. Since we are operating on the assumption that there is only one game file loaded at the time, you can depend only on one game file. Next, choose addons that you want to use in your addon with checkboxes.\\ + +The last thing to do is to name your your addon and click create. + +\paragraph{Selecting File for Editing} +Clicking \textbf{Edit A Content File} will show somewhat similar window. Here you should select your Game file with drop down menu. If you want to edit this game file, simply click \textbf{OK} button. If you want to alter addon depending on that file, mark it with checkbox and than click \textbf{Ok} button. \subsection{Advanced} If you are paying attention, you noticed any extra icon with wrench. This one will open small settings window. Options here are few, and easy to list. @@ -20,6 +26,4 @@ If you are paying attention, you noticed any extra icon with wrench. This one wi \item {Display format} can be used to configure behavior of the \OCS{} itself, however only in the limited degree. You can decide if you want \OCS{} to display icon, text or both in some columns of tables. \end{description} -%TODO configuration - And that would be it. There is no point spending more time here. We should go forward now. \ No newline at end of file From 16e2d67b1f6fb47fe0336b23e0b064a940949b23 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 1 Dec 2013 13:32:11 +0100 Subject: [PATCH 096/889] added overloaded start/endRecord functions to ESMWriter --- apps/opencs/model/doc/savingstages.cpp | 19 ++++--------------- apps/opencs/model/doc/savingstages.hpp | 4 ++-- apps/opencs/model/world/refiddata.hpp | 9 ++------- components/esm/esmwriter.cpp | 20 ++++++++++++++++++++ components/esm/esmwriter.hpp | 2 ++ 5 files changed, 30 insertions(+), 24 deletions(-) diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index 8e9bcfc0d..d7df2117d 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -133,16 +133,10 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, std::vector (&topic.mModified.sRecordId)[i]; - - mState.getWriter().startRecord (type); + mState.getWriter().startRecord (topic.mModified.sRecordId); mState.getWriter().writeHNCString ("NAME", topic.mModified.mId); topic.mModified.save (mState.getWriter()); - mState.getWriter().endRecord (type); + mState.getWriter().endRecord (topic.mModified.sRecordId); // write modified selected info records for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; @@ -178,15 +172,10 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, std::vectormModified.mId.substr (next->mModified.mId.find_last_of ('#')+1); } - std::string type; - for (int i=0; i<4; ++i) - /// \todo make endianess agnostic (change ESMWriter interface?) - type += reinterpret_cast (&info.sRecordId)[i]; - - mState.getWriter().startRecord (type); + mState.getWriter().startRecord (info.sRecordId); mState.getWriter().writeHNCString ("INAM", info.mId); info.save (mState.getWriter()); - mState.getWriter().endRecord (type); + mState.getWriter().endRecord (info.sRecordId); } } } diff --git a/apps/opencs/model/doc/savingstages.hpp b/apps/opencs/model/doc/savingstages.hpp index ca5586511..b8eb0a3b3 100644 --- a/apps/opencs/model/doc/savingstages.hpp +++ b/apps/opencs/model/doc/savingstages.hpp @@ -104,10 +104,10 @@ namespace CSMDoc /// \todo make endianess agnostic (change ESMWriter interface?) type += reinterpret_cast (&mCollection.getRecord (stage).mModified.sRecordId)[i]; - mState.getWriter().startRecord (type); + mState.getWriter().startRecord (mCollection.getRecord (stage).mModified.sRecordId); mState.getWriter().writeHNCString ("NAME", mCollection.getId (stage)); mCollection.getRecord (stage).mModified.save (mState.getWriter()); - mState.getWriter().endRecord (type); + mState.getWriter().endRecord (mCollection.getRecord (stage).mModified.sRecordId); } else if (state==CSMWorld::RecordBase::State_Deleted) { diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 9595ab23b..761c7feaa 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -136,15 +136,10 @@ namespace CSMWorld if (state==CSMWorld::RecordBase::State_Modified || state==CSMWorld::RecordBase::State_ModifiedOnly) { - std::string type; - for (int i=0; i<4; ++i) - /// \todo make endianess agnostic (change ESMWriter interface?) - type += reinterpret_cast (&mContainer.at (index).mModified.sRecordId)[i]; - - writer.startRecord (type); + writer.startRecord (mContainer.at (index).mModified.sRecordId); writer.writeHNCString ("NAME", getId (index)); mContainer.at (index).mModified.save (writer); - writer.endRecord (type); + writer.endRecord (mContainer.at (index).mModified.sRecordId); } else if (state==CSMWorld::RecordBase::State_Deleted) { diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index 069d75c7b..f38591b7b 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -88,6 +88,16 @@ namespace ESM assert(mRecords.back().size == 0); } + void ESMWriter::startRecord (uint32_t name, uint32_t flags) + { + std::string type; + for (int i=0; i<4; ++i) + /// \todo make endianess agnostic + type += reinterpret_cast (&name)[i]; + + startRecord (type, flags); + } + void ESMWriter::startSubRecord(const std::string& name) { writeName(name); @@ -117,6 +127,16 @@ namespace ESM } + void ESMWriter::endRecord (uint32_t name) + { + std::string type; + for (int i=0; i<4; ++i) + /// \todo make endianess agnostic + type += reinterpret_cast (&name)[i]; + + endRecord (type); + } + void ESMWriter::writeHNString(const std::string& name, const std::string& data) { startSubRecord(name); diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index d6646471b..94f0a1004 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -90,8 +90,10 @@ class ESMWriter } void startRecord(const std::string& name, uint32_t flags = 0); + void startRecord(uint32_t name, uint32_t flags = 0); void startSubRecord(const std::string& name); void endRecord(const std::string& name); + void endRecord(uint32_t name); void writeHString(const std::string& data); void writeHCString(const std::string& data); void writeName(const std::string& data); From b273f9e3874e5f52cc8025e76f47559bdb91e93a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 1 Dec 2013 14:44:27 +0100 Subject: [PATCH 097/889] splitting JournalEntry into Entry and JournalEntry --- apps/openmw/mwdialogue/journalentry.cpp | 20 +++++++++++++------ apps/openmw/mwdialogue/journalentry.hpp | 26 +++++++++++++++---------- apps/openmw/mwgui/journalviewmodel.cpp | 5 ++--- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwdialogue/journalentry.cpp b/apps/openmw/mwdialogue/journalentry.cpp index 20963eb79..b83d19f8c 100644 --- a/apps/openmw/mwdialogue/journalentry.cpp +++ b/apps/openmw/mwdialogue/journalentry.cpp @@ -10,13 +10,13 @@ namespace MWDialogue { - JournalEntry::JournalEntry() {} + Entry::Entry() {} - JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId) - : mTopic (topic), mInfoId (infoId) + Entry::Entry (const std::string& topic, const std::string& infoId) + : mInfoId (infoId) { const ESM::Dialogue *dialogue = - MWBase::Environment::get().getWorld()->getStore().get().find (mTopic); + MWBase::Environment::get().getWorld()->getStore().get().find (topic); for (std::vector::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) @@ -27,14 +27,21 @@ namespace MWDialogue return; } - throw std::runtime_error ("unknown info ID " + mInfoId + " for topic " + mTopic); + throw std::runtime_error ("unknown info ID " + mInfoId + " for topic " + topic); } - std::string JournalEntry::getText (const MWWorld::ESMStore& store) const + std::string Entry::getText() const { return mText; } + + JournalEntry::JournalEntry() {} + + JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId) + : Entry (topic, infoId), mTopic (topic) + {} + JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index) { return JournalEntry (topic, idFromIndex (topic, index)); @@ -55,6 +62,7 @@ namespace MWDialogue throw std::runtime_error ("unknown journal index for topic " + topic); } + StampedJournalEntry::StampedJournalEntry() : mDay (0), mMonth (0), mDayOfMonth (0) {} diff --git a/apps/openmw/mwdialogue/journalentry.hpp b/apps/openmw/mwdialogue/journalentry.hpp index ab4adece9..b51c2d2d4 100644 --- a/apps/openmw/mwdialogue/journalentry.hpp +++ b/apps/openmw/mwdialogue/journalentry.hpp @@ -3,26 +3,32 @@ #include -namespace MWWorld -{ - struct ESMStore; -} - namespace MWDialogue { - /// \brief A quest or dialogue entry - struct JournalEntry + /// \brief Basic quest/dialogue/topic entry + struct Entry { - std::string mTopic; std::string mInfoId; std::string mText; + Entry(); + + Entry (const std::string& topic, const std::string& infoId); + + std::string getText() const; + }; + + /// \brief A dialogue entry + /// + /// Same as entry, but store TopicID + struct JournalEntry : public Entry + { + std::string mTopic; + JournalEntry(); JournalEntry (const std::string& topic, const std::string& infoId); - std::string getText (const MWWorld::ESMStore& store) const; - static JournalEntry makeFromQuest (const std::string& topic, int index); static std::string idFromIndex (const std::string& topic, int index); diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index 049515ac0..7ee24a288 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -233,7 +233,7 @@ struct JournalViewModelImpl : JournalViewModel std::string getText () const { - return itr->getText(MWBase::Environment::get().getWorld()->getStore()); + return itr->getText(); } Utf8Span timestamp () const @@ -317,8 +317,7 @@ struct JournalViewModelImpl : JournalViewModel std::string getText () const { - return mTopic.getEntry (*itr).getText(MWBase::Environment::get().getWorld()->getStore()); - + return mTopic.getEntry (*itr).getText(); } Utf8Span source () const From 0f971163f709e31f98810b8ecf205faea19f96e2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 1 Dec 2013 14:50:25 +0100 Subject: [PATCH 098/889] use Entry instead of plain string for topic and quest entries --- apps/openmw/mwdialogue/quest.cpp | 4 ++-- apps/openmw/mwdialogue/topic.cpp | 6 +----- apps/openmw/mwdialogue/topic.hpp | 4 ++-- apps/openmw/mwgui/journalviewmodel.cpp | 4 ++-- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwdialogue/quest.cpp b/apps/openmw/mwdialogue/quest.cpp index 75dcaa028..12763effd 100644 --- a/apps/openmw/mwdialogue/quest.cpp +++ b/apps/openmw/mwdialogue/quest.cpp @@ -82,9 +82,9 @@ namespace MWDialogue setIndex (index); for (TEntryIter iter (mEntries.begin()); iter!=mEntries.end(); ++iter) - if (*iter==entry.mInfoId) + if (iter->mInfoId==entry.mInfoId) return; - mEntries.push_back (entry.mInfoId); + mEntries.push_back (entry); // we want slicing here } } diff --git a/apps/openmw/mwdialogue/topic.cpp b/apps/openmw/mwdialogue/topic.cpp index fc8545b5e..f40e585dc 100644 --- a/apps/openmw/mwdialogue/topic.cpp +++ b/apps/openmw/mwdialogue/topic.cpp @@ -24,11 +24,7 @@ namespace MWDialogue if (entry.mTopic!=mTopic) throw std::runtime_error ("topic does not match: " + mTopic); - for (TEntryIter iter = begin(); iter!=end(); ++iter) - if (*iter==entry.mInfoId) - return; - - mEntries.push_back (entry.mInfoId); + mEntries.push_back (entry); // we want slicing here } std::string Topic::getName() const diff --git a/apps/openmw/mwdialogue/topic.hpp b/apps/openmw/mwdialogue/topic.hpp index 17601977a..ffc5e9470 100644 --- a/apps/openmw/mwdialogue/topic.hpp +++ b/apps/openmw/mwdialogue/topic.hpp @@ -13,14 +13,14 @@ namespace MWDialogue { public: - typedef std::vector TEntryContainer; + typedef std::vector TEntryContainer; typedef TEntryContainer::const_iterator TEntryIter; protected: std::string mTopic; std::string mName; - TEntryContainer mEntries; // info-IDs + TEntryContainer mEntries; public: diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index 7ee24a288..ad0af3f05 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -268,7 +268,7 @@ struct JournalViewModelImpl : JournalViewModel { for (MWDialogue::Topic::TEntryIter j = quest->begin (); j != quest->end (); ++j) { - if (i->mInfoId == *j) + if (i->mInfoId == j->mInfoId) visitor (JournalEntryImpl (this, i)); } } @@ -317,7 +317,7 @@ struct JournalViewModelImpl : JournalViewModel std::string getText () const { - return mTopic.getEntry (*itr).getText(); + return itr->getText(); } Utf8Span source () const From 7d8e3ac6518e4bf3449a58dce83c6ee1abcf0f00 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 3 Dec 2013 13:51:44 +0100 Subject: [PATCH 099/889] fixed QuestState::load/save --- components/esm/queststate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esm/queststate.cpp b/components/esm/queststate.cpp index 5931e8b90..e93826725 100644 --- a/components/esm/queststate.cpp +++ b/components/esm/queststate.cpp @@ -7,13 +7,13 @@ void ESM::QuestState::load (ESMReader &esm) { mTopic = esm.getHNString ("YETO"); - esm.getHNOT (mState, "QSTAT"); + esm.getHNOT (mState, "QSTA"); esm.getHNOT (mFinished, "QFIN"); } void ESM::QuestState::save (ESMWriter &esm) const { esm.writeHNString ("YETO", mTopic); - esm.writeHNT ("QSTAT", mState); + esm.writeHNT ("QSTA", mState); esm.writeHNT ("QFIN", mFinished); } \ No newline at end of file From 2293b92efe5534b97c021683d444a18584b6212c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 3 Dec 2013 14:28:46 +0100 Subject: [PATCH 100/889] storing and loading the journal --- apps/openmw/mwbase/journal.hpp | 14 +++ apps/openmw/mwdialogue/journalentry.cpp | 33 ++++++ apps/openmw/mwdialogue/journalentry.hpp | 17 +++ apps/openmw/mwdialogue/journalimp.cpp | 135 ++++++++++++++++++++++-- apps/openmw/mwdialogue/journalimp.hpp | 10 ++ apps/openmw/mwdialogue/quest.cpp | 13 +++ apps/openmw/mwdialogue/quest.hpp | 9 ++ apps/openmw/mwdialogue/topic.cpp | 10 ++ apps/openmw/mwdialogue/topic.hpp | 13 ++- apps/openmw/mwstate/statemanagerimp.cpp | 42 ++++++-- 10 files changed, 278 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwbase/journal.hpp b/apps/openmw/mwbase/journal.hpp index 51e51edda..81b4ba0b4 100644 --- a/apps/openmw/mwbase/journal.hpp +++ b/apps/openmw/mwbase/journal.hpp @@ -5,10 +5,18 @@ #include #include +#include + #include "../mwdialogue/journalentry.hpp" #include "../mwdialogue/topic.hpp" #include "../mwdialogue/quest.hpp" +namespace ESM +{ + class ESMReader; + class ESMWriter; +} + namespace MWBase { /// \brief Interface for the player's journal (implemented in MWDialogue) @@ -69,6 +77,12 @@ namespace MWBase virtual TTopicIter topicEnd() const = 0; ///< Iterator pointing past the last topic. + + virtual int countSavedGameRecords() const = 0; + + virtual void write (ESM::ESMWriter& writer) const = 0; + + virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0; }; } diff --git a/apps/openmw/mwdialogue/journalentry.cpp b/apps/openmw/mwdialogue/journalentry.cpp index b83d19f8c..7828d18ad 100644 --- a/apps/openmw/mwdialogue/journalentry.cpp +++ b/apps/openmw/mwdialogue/journalentry.cpp @@ -3,6 +3,8 @@ #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -30,11 +32,19 @@ namespace MWDialogue throw std::runtime_error ("unknown info ID " + mInfoId + " for topic " + topic); } + Entry::Entry (const ESM::JournalEntry& record) : mInfoId (record.mInfo), mText (record.mText) {} + std::string Entry::getText() const { return mText; } + void Entry::write (ESM::JournalEntry& entry) const + { + entry.mInfo = mInfoId; + entry.mText = mText; + } + JournalEntry::JournalEntry() {} @@ -42,6 +52,16 @@ namespace MWDialogue : Entry (topic, infoId), mTopic (topic) {} + JournalEntry::JournalEntry (const ESM::JournalEntry& record) + : Entry (record), mTopic (record.mTopic) + {} + + void JournalEntry::write (ESM::JournalEntry& entry) const + { + Entry::write (entry); + entry.mTopic = mTopic; + } + JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index) { return JournalEntry (topic, idFromIndex (topic, index)); @@ -72,6 +92,19 @@ namespace MWDialogue : JournalEntry (topic, infoId), mDay (day), mMonth (month), mDayOfMonth (dayOfMonth) {} + StampedJournalEntry::StampedJournalEntry (const ESM::JournalEntry& record) + : JournalEntry (record), mDay (record.mDay), mMonth (record.mMonth), + mDayOfMonth (record.mDayOfMonth) + {} + + void StampedJournalEntry::write (ESM::JournalEntry& entry) const + { + JournalEntry::write (entry); + entry.mDay = mDay; + entry.mMonth = mMonth; + entry.mDayOfMonth = mDayOfMonth; + } + StampedJournalEntry StampedJournalEntry::makeFromQuest (const std::string& topic, int index) { int day = MWBase::Environment::get().getWorld()->getGlobalInt ("dayspassed"); diff --git a/apps/openmw/mwdialogue/journalentry.hpp b/apps/openmw/mwdialogue/journalentry.hpp index b51c2d2d4..18d022aab 100644 --- a/apps/openmw/mwdialogue/journalentry.hpp +++ b/apps/openmw/mwdialogue/journalentry.hpp @@ -3,6 +3,11 @@ #include +namespace ESM +{ + struct JournalEntry; +} + namespace MWDialogue { /// \brief Basic quest/dialogue/topic entry @@ -15,7 +20,11 @@ namespace MWDialogue Entry (const std::string& topic, const std::string& infoId); + Entry (const ESM::JournalEntry& record); + std::string getText() const; + + void write (ESM::JournalEntry& entry) const; }; /// \brief A dialogue entry @@ -29,6 +38,10 @@ namespace MWDialogue JournalEntry (const std::string& topic, const std::string& infoId); + JournalEntry (const ESM::JournalEntry& record); + + void write (ESM::JournalEntry& entry) const; + static JournalEntry makeFromQuest (const std::string& topic, int index); static std::string idFromIndex (const std::string& topic, int index); @@ -46,6 +59,10 @@ namespace MWDialogue StampedJournalEntry (const std::string& topic, const std::string& infoId, int day, int month, int dayOfMonth); + StampedJournalEntry (const ESM::JournalEntry& record); + + void write (ESM::JournalEntry& entry) const; + static StampedJournalEntry makeFromQuest (const std::string& topic, int index); }; } diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index 23cfb5fdd..b9359aae6 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -1,6 +1,13 @@ #include "journalimp.hpp" +#include + +#include +#include +#include +#include + #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" @@ -26,6 +33,22 @@ namespace MWDialogue return iter->second; } + Topic& Journal::getTopic (const std::string& id) + { + TTopicContainer::iterator iter = mTopics.find (id); + + if (iter==mTopics.end()) + { + std::pair result + = mTopics.insert (std::make_pair (id, Topic (id))); + + iter = result.first; + } + + return iter->second; + } + + Journal::Journal() {} @@ -66,17 +89,9 @@ namespace MWDialogue void Journal::addTopic (const std::string& topicId, const std::string& infoId) { - TTopicContainer::iterator iter = mTopics.find (topicId); + Topic& topic = getTopic (topicId); - if (iter==mTopics.end()) - { - std::pair result - = mTopics.insert (std::make_pair (topicId, Topic (topicId))); - - iter = result.first; - } - - iter->second.addEntry (JournalEntry (topicId, infoId)); + topic.addEntry (JournalEntry (topicId, infoId)); } int Journal::getJournalIndex (const std::string& id) const @@ -118,4 +133,104 @@ namespace MWDialogue { return mTopics.end(); } + + int Journal::countSavedGameRecords() const + { + int count = static_cast (mQuests.size()); + + for (TQuestIter iter (mQuests.begin()); iter!=mQuests.end(); ++iter) + count += std::distance (iter->second.begin(), iter->second.end()); + + count += std::distance (mJournal.begin(), mJournal.end()); + + for (TTopicIter iter (mTopics.begin()); iter!=mTopics.end(); ++iter) + count += std::distance (iter->second.begin(), iter->second.end()); + + return count; + } + + void Journal::write (ESM::ESMWriter& writer) const + { + for (TQuestIter iter (mQuests.begin()); iter!=mQuests.end(); ++iter) + { + const Quest& quest = iter->second; + + ESM::QuestState state; + quest.write (state); + writer.startRecord (ESM::REC_QUES); + state.save (writer); + writer.endRecord (ESM::REC_QUES); + + for (Topic::TEntryIter iter (quest.begin()); iter!=quest.end(); ++iter) + { + ESM::JournalEntry entry; + entry.mType = ESM::JournalEntry::Type_Quest; + entry.mTopic = quest.getTopic(); + iter->write (entry); + writer.startRecord (ESM::REC_JOUR); + entry.save (writer); + writer.endRecord (ESM::REC_JOUR); + } + } + + for (TEntryIter iter (mJournal.begin()); iter!=mJournal.end(); ++iter) + { + ESM::JournalEntry entry; + entry.mType = ESM::JournalEntry::Type_Journal; + iter->write (entry); + writer.startRecord (ESM::REC_JOUR); + entry.save (writer); + writer.endRecord (ESM::REC_JOUR); + } + + for (TTopicIter iter (mTopics.begin()); iter!=mTopics.end(); ++iter) + { + const Topic& topic = iter->second; + + for (Topic::TEntryIter iter (topic.begin()); iter!=topic.end(); ++iter) + { + ESM::JournalEntry entry; + entry.mType = ESM::JournalEntry::Type_Topic; + entry.mTopic = topic.getTopic(); + iter->write (entry); + writer.startRecord (ESM::REC_JOUR); + entry.save (writer); + writer.endRecord (ESM::REC_JOUR); + } + } + } + + void Journal::readRecord (ESM::ESMReader& reader, int32_t type) + { + if (type==ESM::REC_JOUR) + { + ESM::JournalEntry record; + record.load (reader); + + switch (record.mType) + { + case ESM::JournalEntry::Type_Quest: + + getQuest (record.mTopic).insertEntry (record); + break; + + case ESM::JournalEntry::Type_Journal: + + mJournal.push_back (record); + break; + + case ESM::JournalEntry::Type_Topic: + + getTopic (record.mTopic).insertEntry (record); + break; + } + } + else if (type==ESM::REC_QUES) + { + ESM::QuestState record; + record.load (reader); + + mQuests.insert (std::make_pair (record.mTopic, record)); + } + } } diff --git a/apps/openmw/mwdialogue/journalimp.hpp b/apps/openmw/mwdialogue/journalimp.hpp index f4f8eb1c2..54c49df01 100644 --- a/apps/openmw/mwdialogue/journalimp.hpp +++ b/apps/openmw/mwdialogue/journalimp.hpp @@ -15,8 +15,12 @@ namespace MWDialogue TQuestContainer mQuests; TTopicContainer mTopics; + private: + Quest& getQuest (const std::string& id); + Topic& getTopic (const std::string& id); + public: Journal(); @@ -55,6 +59,12 @@ namespace MWDialogue virtual TTopicIter topicEnd() const; ///< Iterator pointing past the last topic. + + virtual int countSavedGameRecords() const; + + virtual void write (ESM::ESMWriter& writer) const; + + virtual void readRecord (ESM::ESMReader& reader, int32_t type); }; } diff --git a/apps/openmw/mwdialogue/quest.cpp b/apps/openmw/mwdialogue/quest.cpp index 12763effd..14c5c1da9 100644 --- a/apps/openmw/mwdialogue/quest.cpp +++ b/apps/openmw/mwdialogue/quest.cpp @@ -1,6 +1,8 @@ #include "quest.hpp" +#include + #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" @@ -16,6 +18,10 @@ namespace MWDialogue : Topic (topic), mIndex (0), mFinished (false) {} + Quest::Quest (const ESM::QuestState& state) + : Topic (state.mTopic), mIndex (state.mState), mFinished (state.mFinished!=0) + {} + std::string Quest::getName() const { const ESM::Dialogue *dialogue = @@ -87,4 +93,11 @@ namespace MWDialogue mEntries.push_back (entry); // we want slicing here } + + void Quest::write (ESM::QuestState& state) const + { + state.mTopic = getTopic(); + state.mState = mIndex; + state.mFinished = mFinished; + } } diff --git a/apps/openmw/mwdialogue/quest.hpp b/apps/openmw/mwdialogue/quest.hpp index c6f0d0bec..40824f398 100644 --- a/apps/openmw/mwdialogue/quest.hpp +++ b/apps/openmw/mwdialogue/quest.hpp @@ -3,6 +3,11 @@ #include "topic.hpp" +namespace ESM +{ + struct QuestState; +} + namespace MWDialogue { /// \brief A quest in progress or a completed quest @@ -17,6 +22,8 @@ namespace MWDialogue Quest (const std::string& topic); + Quest (const ESM::QuestState& state); + virtual std::string getName() const; ///< May be an empty string @@ -31,6 +38,8 @@ namespace MWDialogue ///< Add entry and adjust index accordingly. /// /// \note Redundant entries are ignored, but the index is still adjusted. + + void write (ESM::QuestState& state) const; }; } diff --git a/apps/openmw/mwdialogue/topic.cpp b/apps/openmw/mwdialogue/topic.cpp index f40e585dc..0e546f43b 100644 --- a/apps/openmw/mwdialogue/topic.cpp +++ b/apps/openmw/mwdialogue/topic.cpp @@ -27,6 +27,16 @@ namespace MWDialogue mEntries.push_back (entry); // we want slicing here } + void Topic::insertEntry (const ESM::JournalEntry& entry) + { + mEntries.push_back (entry); + } + + std::string Topic::getTopic() const + { + return mTopic; + } + std::string Topic::getName() const { return mName; diff --git a/apps/openmw/mwdialogue/topic.hpp b/apps/openmw/mwdialogue/topic.hpp index ffc5e9470..02fa6d524 100644 --- a/apps/openmw/mwdialogue/topic.hpp +++ b/apps/openmw/mwdialogue/topic.hpp @@ -6,6 +6,11 @@ #include "journalentry.hpp" +namespace ESM +{ + struct JournalEntry; +} + namespace MWDialogue { /// \brief Collection of seen responses for a topic @@ -35,7 +40,13 @@ namespace MWDialogue /// /// \note Redundant entries are ignored. - virtual std::string getName () const; + void insertEntry (const ESM::JournalEntry& entry); + ///< Add entry without checking for redundant entries or modifying the state of the + /// topic otherwise + + std::string getTopic() const; + + virtual std::string getName() const; TEntryIter begin() const; ///< Iterator pointing to the begin of the journal for this topic. diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index b750f56fe..5bc313f37 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -99,11 +99,17 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot std::ofstream stream (slot->mPath.string().c_str()); ESM::ESMWriter writer; writer.setFormat (ESM::Header::CurrentFormat); - writer.save (stream); - writer.startRecord ("SAVE"); - slot->mProfile.save (writer); - writer.endRecord ("SAVE"); + writer.setRecordCount ( + 1+ // saved game header + MWBase::Environment::get().getJournal()->countSavedGameRecords()); + writer.save (stream); + + writer.startRecord (ESM::REC_SAVE); + slot->mProfile.save (writer); + writer.endRecord (ESM::REC_SAVE); + + MWBase::Environment::get().getJournal()->write (writer); /// \todo write saved game data writer.close(); @@ -121,10 +127,32 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl ESM::ESMReader reader; reader.open (slot->mPath.string()); - reader.getRecName(); // don't need to read that here - reader.getRecHeader(); + while (reader.hasMoreRecs()) + { + ESM::NAME n = reader.getRecName(); + reader.getRecHeader(); - /// \todo read saved game data + switch (n.val) + { + case ESM::REC_SAVE: + + // don't need to read that here + reader.skipRecord(); + break; + + case ESM::REC_JOUR: + case ESM::REC_QUES: + + MWBase::Environment::get().getJournal()->readRecord (reader, n.val); + break; + + default: + + // ignore invalid records + /// \todo log error + reader.skipRecord(); + } + } mCharacterManager.setCurrentCharacter(character); From e269c9e6898314f7c6201b30f3f439f7b7321d64 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 3 Dec 2013 14:30:18 +0100 Subject: [PATCH 101/889] changed a few sub record names to make them more unique --- components/esm/savedgame.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index 7c76f4000..55b17289c 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -9,10 +9,10 @@ unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; void ESM::SavedGame::load (ESMReader &esm) { - mPlayerName = esm.getHNString("PNAM"); - esm.getHNOT (mPlayerLevel, "PLEV"); - mPlayerClass = esm.getHNString("PCLA"); - mPlayerCell = esm.getHNString("PCEL"); + mPlayerName = esm.getHNString("PLNA"); + esm.getHNOT (mPlayerLevel, "PLLE"); + mPlayerClass = esm.getHNString("PLCL"); + mPlayerCell = esm.getHNString("PLCE"); esm.getHNT (mInGameTime, "TSTM", 16); esm.getHNT (mTimePlayed, "TIME"); mDescription = esm.getHNString ("DESC"); @@ -23,10 +23,10 @@ void ESM::SavedGame::load (ESMReader &esm) void ESM::SavedGame::save (ESMWriter &esm) const { - esm.writeHNString ("PNAM", mPlayerName); - esm.writeHNT ("PLEV", mPlayerLevel); - esm.writeHNString ("PCLA", mPlayerClass); - esm.writeHNString ("PCEL", mPlayerCell); + esm.writeHNString ("PLNA", mPlayerName); + esm.writeHNT ("PLLE", mPlayerLevel); + esm.writeHNString ("PLCL", mPlayerClass); + esm.writeHNString ("PLCE", mPlayerCell); esm.writeHNT ("TSTM", mInGameTime, 16); esm.writeHNT ("TIME", mTimePlayed); esm.writeHNString ("DESC", mDescription); From 34cdd2bb1f63a38dbe4741c817259f6cd986eea2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 3 Dec 2013 14:39:54 +0100 Subject: [PATCH 102/889] deal with dialogue/info records that don't exist anymore --- apps/openmw/mwdialogue/journalimp.cpp | 44 +++++++++++++++++++-------- apps/openmw/mwdialogue/journalimp.hpp | 2 ++ 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index b9359aae6..f24a93356 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -48,6 +48,22 @@ namespace MWDialogue return iter->second; } + bool Journal::isThere (const std::string& topicId, const std::string& infoId) const + { + if (const ESM::Dialogue *dialogue = + MWBase::Environment::get().getWorld()->getStore().get().search (topicId)) + { + if (infoId.empty()) + return true; + + for (std::vector::const_iterator iter (dialogue->mInfo.begin()); + iter!=dialogue->mInfo.end(); ++iter) + if (iter->mId == infoId) + return true; + } + + return false; + } Journal::Journal() {} @@ -207,30 +223,32 @@ namespace MWDialogue ESM::JournalEntry record; record.load (reader); - switch (record.mType) - { - case ESM::JournalEntry::Type_Quest: + if (isThere (record.mTopic, record.mInfo)) + switch (record.mType) + { + case ESM::JournalEntry::Type_Quest: - getQuest (record.mTopic).insertEntry (record); - break; + getQuest (record.mTopic).insertEntry (record); + break; - case ESM::JournalEntry::Type_Journal: + case ESM::JournalEntry::Type_Journal: - mJournal.push_back (record); - break; + mJournal.push_back (record); + break; - case ESM::JournalEntry::Type_Topic: + case ESM::JournalEntry::Type_Topic: - getTopic (record.mTopic).insertEntry (record); - break; - } + getTopic (record.mTopic).insertEntry (record); + break; + } } else if (type==ESM::REC_QUES) { ESM::QuestState record; record.load (reader); - mQuests.insert (std::make_pair (record.mTopic, record)); + if (isThere (record.mTopic)) + mQuests.insert (std::make_pair (record.mTopic, record)); } } } diff --git a/apps/openmw/mwdialogue/journalimp.hpp b/apps/openmw/mwdialogue/journalimp.hpp index 54c49df01..86091a12d 100644 --- a/apps/openmw/mwdialogue/journalimp.hpp +++ b/apps/openmw/mwdialogue/journalimp.hpp @@ -21,6 +21,8 @@ namespace MWDialogue Topic& getTopic (const std::string& id); + bool isThere (const std::string& topicId, const std::string& infoId = "") const; + public: Journal(); From 63721682f6724c84de992f15d301ff4e47a66a80 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 3 Dec 2013 15:19:13 +0100 Subject: [PATCH 103/889] GUI fix: previous character was selected when saving new character --- apps/openmw/mwgui/savegamedialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index d1e047c09..04461fef9 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -63,7 +63,7 @@ namespace MWGui mCharacterSelection->addItem (title.str()); if (mCurrentCharacter == &*it || - (!mCurrentCharacter && directory==Misc::StringUtils::lowerCase ( + (!mCurrentCharacter && !mSaving && directory==Misc::StringUtils::lowerCase ( it->begin()->mPath.parent_path().filename().string()))) { mCurrentCharacter = &*it; From 890aca720e1fc0796340547948097518b59ddee6 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 3 Dec 2013 15:35:03 +0100 Subject: [PATCH 104/889] correcting qoutes style and removing informations about settings from creating_file.tex. Thanks zini. --- manual/opencs/creating_file.tex | 6 +----- manual/opencs/filters.tex | 8 ++++---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/manual/opencs/creating_file.tex b/manual/opencs/creating_file.tex index 34780a03e..2c1377ec1 100644 --- a/manual/opencs/creating_file.tex +++ b/manual/opencs/creating_file.tex @@ -20,10 +20,6 @@ The last thing to do is to name your your addon and click create. Clicking \textbf{Edit A Content File} will show somewhat similar window. Here you should select your Game file with drop down menu. If you want to edit this game file, simply click \textbf{OK} button. If you want to alter addon depending on that file, mark it with checkbox and than click \textbf{Ok} button. \subsection{Advanced} -If you are paying attention, you noticed any extra icon with wrench. This one will open small settings window. Options here are few, and easy to list. -\begin{description} - \item {Window Size} is needed to configure initial window size of the starting window. - \item {Display format} can be used to configure behavior of the \OCS{} itself, however only in the limited degree. You can decide if you want \OCS{} to display icon, text or both in some columns of tables. -\end{description} +If you are paying attention, you noticed any extra icon with wrench. This one will open small settings window. Those are general OpenCS settings. We will cover this is separate section.\\ And that would be it. There is no point spending more time here. We should go forward now. \ No newline at end of file diff --git a/manual/opencs/filters.tex b/manual/opencs/filters.tex index a5f88aebb..36d97e0f5 100644 --- a/manual/opencs/filters.tex +++ b/manual/opencs/filters.tex @@ -102,7 +102,7 @@ you want to see, while the second one sets desired value inside of the cell. To String in programmers language is often\footnote{Often, not always. There are different programming languages using slightly different terms.} just a word for anything composed of characters. In case of \OCS{} this is in fact true for every value inside the column that is not composed of the pure numbers. Even columns containing only ``true`` and ``false`` values can be targeted by the string expression\footnote{There is no -Boolean (''true'' or ``false'') value in the \OCS. You should use string for those.}. String evaluates to true, +Boolean (``true'' or ``false'') value in the \OCS. You should use string for those.}. String evaluates to true, when record contains in the specified column exactly the same value as specified. Since majority of the columns contain string values, string is among the most often used expressions. Examples: @@ -157,7 +157,7 @@ As you would imagine the range can be specified as including a border value, or The same expression will evaluate to false for value equal 10. \end{itemize} -\paragraph{''true`` and ''false``} +\paragraph{``true'' and ``false''} Nullary \textit{true} and \textit{false} do not accept any arguments, and always evaluates to true (in case of \textit{true}) and false (in case of \textit{false}) no matter what. The main usage of this expressions is the give users ability to quickly disable some part of the filter that makes heavy use of the logical expressions. @@ -184,10 +184,10 @@ This is probably not the most useful filter on earth, but this is not a surprise \mono{or(string(``record type'', npc), string(``record type'', creature))} and will show both npcs and creatures. \paragraph{and -- and(expression1(), expression2())} -\textit{And} is a expression that will return true if all arguments evaluates to true. As in the case of ''or`` you can use two or more arguments, +\textit{And} is a expression that will return true if all arguments evaluates to true. As in the case of ``or'' you can use two or more arguments, separated by a comma. As we mentioned earlier in the \textit{not} filter, combining \textit{not} with \textit{and} can be very useful. For instance to show all armor types, -excluding gauntlets you can write the following: \mono{and (not string("armor type", ".* gauntlet"), string(''Record Type``, ''Armor``))}. +excluding gauntlets you can write the following: \mono{and (not string(``armor type'', ``.* gauntlet''), string(``Record Type'', ``Armor''))}. \subsubsection{Creating and saving filter} In order to create and save new filter, you should go to the filters table, right click and select option ``add record'' from the context menu. From 537b2efe8ebb3d4b3ee29aee5cee69cbdcd007a7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 5 Dec 2013 12:49:25 +0100 Subject: [PATCH 105/889] first round of cleaning up world cleanup --- apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwstate/statemanagerimp.cpp | 1 + apps/openmw/mwworld/refdata.cpp | 15 +++++++++-- apps/openmw/mwworld/refdata.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 36 ++++++++++++------------- apps/openmw/mwworld/worldimp.hpp | 2 ++ 6 files changed, 38 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 87a9b5bbc..837b4c23e 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -96,6 +96,8 @@ namespace MWBase virtual void startNewGame() = 0; + virtual void clear() = 0; + virtual OEngine::Render::Fader* getFader() = 0; ///< \ŧodo remove this function. Rendering details should not be exposed. diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 5bc313f37..09e574e84 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -23,6 +23,7 @@ void MWState::StateManager::cleanup() { MWBase::Environment::get().getDialogueManager()->clear(); MWBase::Environment::get().getJournal()->clear(); + MWBase::Environment::get().getWorld()->clear(); mState = State_NoGame; mCharacterManager.clearCurrentCharacter(); mTimePlayed = 0; diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index c1a3ae785..87d0efe19 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -32,6 +32,17 @@ namespace MWWorld mCustomData = 0; } + RefData::RefData() + : mBaseNode(0), mHasLocals (false), mEnabled (true), mCount (1), mCustomData (0) + { + for (int i=0; i<3; ++i) + { + mLocalRotation.rot[i] = 0; + mPosition.pos[i] = 0; + mPosition.rot[i] = 0; + } + } + RefData::RefData (const ESM::CellRef& cellRef) : mBaseNode(0), mHasLocals (false), mEnabled (true), mCount (1), mPosition (cellRef.mPos), mCustomData (0) @@ -88,7 +99,7 @@ namespace MWWorld static const std::string empty; return empty; } - + return mBaseNode->getName(); } @@ -120,7 +131,7 @@ namespace MWWorld { if(count == 0) MWBase::Environment::get().getWorld()->removeRefScript(this); - + mCount = count; } diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index d5701efc5..07841e470 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -48,6 +48,8 @@ namespace MWWorld public: + RefData(); + /// @param cellRef Used to copy constant data such as position into this class where it can /// be altered without effecting the original data. This makes it possible /// to reset the position as the orignal data is still held in the CellRef diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d7fdc3bc1..509aee983 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -258,26 +258,12 @@ namespace MWWorld void World::startNewGame() { - mWorldScene->changeToVoid(); - - mStore.clearDynamic(); - mStore.setUp(); - - mCells.clear(); - // Rebuild player setupPlayer(); - MWWorld::Ptr player = mPlayer->getPlayer(); - - // removes NpcStats, ContainerStore etc - player.getRefData().setCustomData(NULL); renderPlayer(); mRendering->resetCamera(); - // make sure to do this so that local scripts from items that were in the players inventory are removed - mLocalScripts.clear(); - MWBase::Environment::get().getWindowManager()->updatePlayer(); ESM::Position pos; @@ -290,10 +276,6 @@ namespace MWWorld pos.rot[2] = 0; mWorldScene->changeToExteriorCell(pos); - // enable collision - if(!mPhysics->toggleCollisionMode()) - mPhysics->toggleCollisionMode(); - // FIXME: should be set to 1, but the sound manager won't pause newly started sounds mPlayIntro = 2; @@ -314,6 +296,24 @@ namespace MWWorld MWBase::Environment::get().getScriptManager()->resetGlobalScripts(); } + void World::clear() + { + mLocalScripts.clear(); + + // enable collision + if (!mPhysics->toggleCollisionMode()) + mPhysics->toggleCollisionMode(); + + mWorldScene->changeToVoid(); + + if (mPlayer) + mPlayer->getPlayer().getRefData() = RefData(); + + mStore.clearDynamic(); + mStore.setUp(); + + mCells.clear(); + } void World::ensureNeededRecords() { diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 2b7d157ff..1cee7d50f 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -160,6 +160,8 @@ namespace MWWorld virtual void startNewGame(); + virtual void clear(); + virtual OEngine::Render::Fader* getFader(); ///< \ŧodo remove this function. Rendering details should not be exposed. From e818d43bc3d541b1d253e3db9800850c1dba6f98 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 5 Dec 2013 13:21:26 +0100 Subject: [PATCH 106/889] removed an outdated typedef and some dead code --- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwgui/referenceinterface.cpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- apps/openmw/mwmechanics/actors.cpp | 2 +- apps/openmw/mwmechanics/objects.cpp | 2 +- apps/openmw/mwrender/actors.cpp | 2 +- apps/openmw/mwrender/debugging.cpp | 8 ++-- apps/openmw/mwrender/localmap.cpp | 6 +-- apps/openmw/mwrender/objects.cpp | 6 +-- apps/openmw/mwrender/renderingmanager.cpp | 14 +++---- apps/openmw/mwscript/cellextensions.cpp | 6 +-- apps/openmw/mwsound/soundmanagerimp.cpp | 4 +- apps/openmw/mwworld/cells.cpp | 46 ++++++++++----------- apps/openmw/mwworld/localscripts.cpp | 14 +++---- apps/openmw/mwworld/ptr.hpp | 5 +-- apps/openmw/mwworld/scene.cpp | 10 ++--- apps/openmw/mwworld/scene.hpp | 4 +- apps/openmw/mwworld/worldimp.cpp | 50 ++++++----------------- apps/openmw/mwworld/worldimp.hpp | 6 +-- 19 files changed, 82 insertions(+), 109 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 837b4c23e..36c970839 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -351,7 +351,7 @@ namespace MWBase virtual bool isSwimming(const MWWorld::Ptr &object) const = 0; ///Is the head of the creature underwater? virtual bool isSubmerged(const MWWorld::Ptr &object) const = 0; - virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const = 0; + virtual bool isUnderwater(const MWWorld::CellStore* cell, const Ogre::Vector3 &pos) const = 0; virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0; virtual void togglePOV() = 0; diff --git a/apps/openmw/mwgui/referenceinterface.cpp b/apps/openmw/mwgui/referenceinterface.cpp index 86a85be18..9ba7154c2 100644 --- a/apps/openmw/mwgui/referenceinterface.cpp +++ b/apps/openmw/mwgui/referenceinterface.cpp @@ -18,7 +18,7 @@ namespace MWGui void ReferenceInterface::checkReferenceAvailable() { - MWWorld::Ptr::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); + MWWorld::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); // check if player has changed cell, or count of the reference has become 0 if ((playerCell != mCurrentPlayerCell && mCurrentPlayerCell != NULL) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 2be88b7f0..887bf2c68 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -750,7 +750,7 @@ namespace MWGui mCompanionWindow->onFrame(); } - void WindowManager::changeCell(MWWorld::Ptr::CellStore* cell) + void WindowManager::changeCell(MWWorld::CellStore* cell) { std::string name = MWBase::Environment::get().getWorld()->getCellName (cell); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index cc431a7b0..66d8de6f8 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -302,7 +302,7 @@ namespace MWMechanics } } - void Actors::dropActors (const MWWorld::Ptr::CellStore *cellStore) + void Actors::dropActors (const MWWorld::CellStore *cellStore) { PtrControllerMap::iterator iter = mActors.begin(); while(iter != mActors.end()) diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp index 694987855..8a1e6ee6b 100644 --- a/apps/openmw/mwmechanics/objects.cpp +++ b/apps/openmw/mwmechanics/objects.cpp @@ -45,7 +45,7 @@ void Objects::updateObject(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) } } -void Objects::dropObjects (const MWWorld::Ptr::CellStore *cellStore) +void Objects::dropObjects (const MWWorld::CellStore *cellStore) { PtrControllerMap::iterator iter = mObjects.begin(); while(iter != mObjects.end()) diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index 1bdec6e19..5eecfaf35 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -126,7 +126,7 @@ bool Actors::deleteObject (const MWWorld::Ptr& ptr) return true; } -void Actors::removeCell(MWWorld::Ptr::CellStore* store) +void Actors::removeCell(MWWorld::CellStore* store) { for(PtrAnimationMap::iterator iter = mAllActors.begin();iter != mAllActors.end();) { diff --git a/apps/openmw/mwrender/debugging.cpp b/apps/openmw/mwrender/debugging.cpp index b318c2d56..2b61e109b 100644 --- a/apps/openmw/mwrender/debugging.cpp +++ b/apps/openmw/mwrender/debugging.cpp @@ -185,14 +185,14 @@ bool Debugging::toggleRenderMode (int mode){ return false; } -void Debugging::cellAdded(MWWorld::Ptr::CellStore *store) +void Debugging::cellAdded(MWWorld::CellStore *store) { mActiveCells.push_back(store); if (mPathgridEnabled) enableCellPathgrid(store); } -void Debugging::cellRemoved(MWWorld::Ptr::CellStore *store) +void Debugging::cellRemoved(MWWorld::CellStore *store) { mActiveCells.erase(std::remove(mActiveCells.begin(), mActiveCells.end(), store), mActiveCells.end()); if (mPathgridEnabled) @@ -227,7 +227,7 @@ void Debugging::togglePathgrid() } } -void Debugging::enableCellPathgrid(MWWorld::Ptr::CellStore *store) +void Debugging::enableCellPathgrid(MWWorld::CellStore *store) { const ESM::Pathgrid *pathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*store->mCell); @@ -254,7 +254,7 @@ void Debugging::enableCellPathgrid(MWWorld::Ptr::CellStore *store) } } -void Debugging::disableCellPathgrid(MWWorld::Ptr::CellStore *store) +void Debugging::disableCellPathgrid(MWWorld::CellStore *store) { if (store->mCell->isExterior()) { diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 5f4128978..f70533d22 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -79,7 +79,7 @@ std::string LocalMap::coordStr(const int x, const int y) return StringConverter::toString(x) + "_" + StringConverter::toString(y); } -void LocalMap::saveFogOfWar(MWWorld::Ptr::CellStore* cell) +void LocalMap::saveFogOfWar(MWWorld::CellStore* cell) { if (!mInterior) { @@ -108,7 +108,7 @@ void LocalMap::saveFogOfWar(MWWorld::Ptr::CellStore* cell) } } -void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell, float zMin, float zMax) +void LocalMap::requestMap(MWWorld::CellStore* cell, float zMin, float zMax) { mInterior = false; @@ -125,7 +125,7 @@ void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell, float zMin, float zMax) render((x+0.5)*sSize, (y+0.5)*sSize, zMin, zMax, sSize, sSize, name); } -void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell, +void LocalMap::requestMap(MWWorld::CellStore* cell, AxisAlignedBox bounds) { // if we're in an empty cell, don't bother rendering anything diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index fd81baf6e..852c25044 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -172,7 +172,7 @@ bool Objects::deleteObject (const MWWorld::Ptr& ptr) } -void Objects::removeCell(MWWorld::Ptr::CellStore* store) +void Objects::removeCell(MWWorld::CellStore* store) { for(PtrAnimationMap::iterator iter = mObjects.begin();iter != mObjects.end();) { @@ -212,7 +212,7 @@ void Objects::removeCell(MWWorld::Ptr::CellStore* store) } } -void Objects::buildStaticGeometry(MWWorld::Ptr::CellStore& cell) +void Objects::buildStaticGeometry(MWWorld::CellStore& cell) { if(mStaticGeometry.find(&cell) != mStaticGeometry.end()) { @@ -226,7 +226,7 @@ void Objects::buildStaticGeometry(MWWorld::Ptr::CellStore& cell) } } -Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::Ptr::CellStore* cell) +Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::CellStore* cell) { return mBounds[cell]; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 57e00d76c..cd309df47 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -221,7 +221,7 @@ OEngine::Render::Fader* RenderingManager::getFader() return mRendering.getFader(); } -void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store) +void RenderingManager::removeCell (MWWorld::CellStore *store) { mObjects.removeCell(store); mActors.removeCell(store); @@ -238,7 +238,7 @@ void RenderingManager::toggleWater() mWater->toggle(); } -void RenderingManager::cellAdded (MWWorld::Ptr::CellStore *store) +void RenderingManager::cellAdded (MWWorld::CellStore *store) { mObjects.buildStaticGeometry (*store); sh::Factory::getInstance().unloadUnreferencedMaterials(); @@ -410,7 +410,7 @@ void RenderingManager::postRenderTargetUpdate(const RenderTargetEvent &evt) mOcclusionQuery->setActive(false); } -void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store) +void RenderingManager::waterAdded (MWWorld::CellStore *store) { const MWWorld::Store &lands = MWBase::Environment::get().getWorld()->getStore().get(); @@ -501,7 +501,7 @@ bool RenderingManager::toggleRenderMode(int mode) } } -void RenderingManager::configureFog(MWWorld::Ptr::CellStore &mCell) +void RenderingManager::configureFog(MWWorld::CellStore &mCell) { Ogre::ColourValue color; color.setAsABGR (mCell.mCell->mAmbi.mFog); @@ -554,7 +554,7 @@ void RenderingManager::setAmbientMode() } } -void RenderingManager::configureAmbient(MWWorld::Ptr::CellStore &mCell) +void RenderingManager::configureAmbient(MWWorld::CellStore &mCell) { if (mCell.mCell->mData.mFlags & ESM::Cell::Interior) mAmbientColor.setAsABGR (mCell.mCell->mAmbi.mAmbient); @@ -651,7 +651,7 @@ void RenderingManager::setGlare(bool glare) mSkyManager->setGlare(glare); } -void RenderingManager::requestMap(MWWorld::Ptr::CellStore* cell) +void RenderingManager::requestMap(MWWorld::CellStore* cell) { if (cell->mCell->isExterior()) { @@ -670,7 +670,7 @@ void RenderingManager::requestMap(MWWorld::Ptr::CellStore* cell) mLocalMap->requestMap(cell, mObjects.getDimensions(cell)); } -void RenderingManager::preCellChange(MWWorld::Ptr::CellStore* cell) +void RenderingManager::preCellChange(MWWorld::CellStore* cell) { mLocalMap->saveFogOfWar(cell); } diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index 316f912da..ce39cfa65 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -125,7 +125,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); + MWWorld::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); runtime.push (cell->mWaterLevel); } }; @@ -138,7 +138,7 @@ namespace MWScript { Interpreter::Type_Float level = runtime[0].mFloat; - MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); + MWWorld::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); if (cell->mCell->isExterior()) throw std::runtime_error("Can't set water level in exterior cell"); @@ -156,7 +156,7 @@ namespace MWScript { Interpreter::Type_Float level = runtime[0].mFloat; - MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); + MWWorld::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); if (cell->mCell->isExterior()) throw std::runtime_error("Can't set water level in exterior cell"); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 372be8393..c1c891ab4 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -399,7 +399,7 @@ namespace MWSound } } - void SoundManager::stopSound(const MWWorld::Ptr::CellStore *cell) + void SoundManager::stopSound(const MWWorld::CellStore *cell) { SoundMap::iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) @@ -595,7 +595,7 @@ namespace MWSound soundDuration=snditer->first->mFadeOutTime; snditer->first->setVolume(snditer->first->mVolume - soundDuration / snditer->first->mFadeOutTime * snditer->first->mVolume); - snditer->first->mFadeOutTime -= soundDuration; + snditer->first->mFadeOutTime -= soundDuration; } snditer->first->update(); ++snditer; diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 37c4b6a3f..2cb22cf59 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -7,28 +7,28 @@ #include "esmstore.hpp" #include "containerstore.hpp" -MWWorld::Ptr::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) +MWWorld::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) { if (cell->mData.mFlags & ESM::Cell::Interior) { - std::map::iterator result = mInteriors.find (Misc::StringUtils::lowerCase(cell->mName)); + std::map::iterator result = mInteriors.find (Misc::StringUtils::lowerCase(cell->mName)); if (result==mInteriors.end()) { - result = mInteriors.insert (std::make_pair (Misc::StringUtils::lowerCase(cell->mName), Ptr::CellStore (cell))).first; + result = mInteriors.insert (std::make_pair (Misc::StringUtils::lowerCase(cell->mName), CellStore (cell))).first; } return &result->second; } else { - std::map, Ptr::CellStore>::iterator result = + std::map, CellStore>::iterator result = mExteriors.find (std::make_pair (cell->getGridX(), cell->getGridY())); if (result==mExteriors.end()) { result = mExteriors.insert (std::make_pair ( - std::make_pair (cell->getGridX(), cell->getGridY()), Ptr::CellStore (cell))).first; + std::make_pair (cell->getGridX(), cell->getGridY()), CellStore (cell))).first; } @@ -40,11 +40,11 @@ void MWWorld::Cells::clear() { mInteriors.clear(); mExteriors.clear(); - std::fill(mIdCache.begin(), mIdCache.end(), std::make_pair("", (MWWorld::Ptr::CellStore*)0)); + std::fill(mIdCache.begin(), mIdCache.end(), std::make_pair("", (MWWorld::CellStore*)0)); mIdCacheIndex = 0; } -MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, Ptr::CellStore& cellStore) +MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, CellStore& cellStore) { Ptr ptr = getPtr (name, cellStore); @@ -61,13 +61,13 @@ MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, Ptr::CellS MWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector& reader) : mStore (store), mReader (reader), - mIdCache (40, std::pair ("", (Ptr::CellStore*)0)), /// \todo make cache size configurable + mIdCache (40, std::pair ("", (CellStore*)0)), /// \todo make cache size configurable mIdCacheIndex (0) {} -MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y) +MWWorld::CellStore *MWWorld::Cells::getExterior (int x, int y) { - std::map, Ptr::CellStore>::iterator result = + std::map, CellStore>::iterator result = mExteriors.find (std::make_pair (x, y)); if (result==mExteriors.end()) @@ -92,7 +92,7 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y) std::make_pair (x, y), CellStore (cell))).first; } - if (result->second.mState!=Ptr::CellStore::State_Loaded) + if (result->second.mState!=CellStore::State_Loaded) { // Multiple plugin support for landscape data is much easier than for references. The last plugin wins. result->second.load (mStore, mReader); @@ -101,19 +101,19 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y) return &result->second; } -MWWorld::Ptr::CellStore *MWWorld::Cells::getInterior (const std::string& name) +MWWorld::CellStore *MWWorld::Cells::getInterior (const std::string& name) { std::string lowerName = Misc::StringUtils::lowerCase(name); - std::map::iterator result = mInteriors.find (lowerName); + std::map::iterator result = mInteriors.find (lowerName); if (result==mInteriors.end()) { const ESM::Cell *cell = mStore.get().find(lowerName); - result = mInteriors.insert (std::make_pair (lowerName, Ptr::CellStore (cell))).first; + result = mInteriors.insert (std::make_pair (lowerName, CellStore (cell))).first; } - if (result->second.mState!=Ptr::CellStore::State_Loaded) + if (result->second.mState!=CellStore::State_Loaded) { result->second.load (mStore, mReader); } @@ -121,13 +121,13 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getInterior (const std::string& name) return &result->second; } -MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& cell, +MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, CellStore& cell, bool searchInContainers) { - if (cell.mState==Ptr::CellStore::State_Unloaded) + if (cell.mState==CellStore::State_Unloaded) cell.preload (mStore, mReader); - if (cell.mState==Ptr::CellStore::State_Preloaded) + if (cell.mState==CellStore::State_Preloaded) { std::string lowerCase = Misc::StringUtils::lowerCase(name); @@ -208,7 +208,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& ce MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) { // First check the cache - for (std::vector >::iterator iter (mIdCache.begin()); + for (std::vector >::iterator iter (mIdCache.begin()); iter!=mIdCache.end(); ++iter) if (iter->first==name && iter->second) { @@ -218,7 +218,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) } // Then check cells that are already listed - for (std::map, Ptr::CellStore>::iterator iter = mExteriors.begin(); + for (std::map, CellStore>::iterator iter = mExteriors.begin(); iter!=mExteriors.end(); ++iter) { Ptr ptr = getPtrAndCache (name, iter->second); @@ -226,7 +226,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) return ptr; } - for (std::map::iterator iter = mInteriors.begin(); + for (std::map::iterator iter = mInteriors.begin(); iter!=mInteriors.end(); ++iter) { Ptr ptr = getPtrAndCache (name, iter->second); @@ -240,7 +240,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) for (iter = cells.extBegin(); iter != cells.extEnd(); ++iter) { - Ptr::CellStore *cellStore = getCellStore (&(*iter)); + CellStore *cellStore = getCellStore (&(*iter)); Ptr ptr = getPtrAndCache (name, *cellStore); @@ -250,7 +250,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) for (iter = cells.intBegin(); iter != cells.intEnd(); ++iter) { - Ptr::CellStore *cellStore = getCellStore (&(*iter)); + CellStore *cellStore = getCellStore (&(*iter)); Ptr ptr = getPtrAndCache (name, *cellStore); diff --git a/apps/openmw/mwworld/localscripts.cpp b/apps/openmw/mwworld/localscripts.cpp index 5ec5ca9b5..997e9e32c 100644 --- a/apps/openmw/mwworld/localscripts.cpp +++ b/apps/openmw/mwworld/localscripts.cpp @@ -11,7 +11,7 @@ namespace { template void listCellScripts (MWWorld::LocalScripts& localScripts, - MWWorld::CellRefList& cellRefList, MWWorld::Ptr::CellStore *cell) + MWWorld::CellRefList& cellRefList, MWWorld::CellStore *cell) { for (typename MWWorld::CellRefList::List::iterator iter ( cellRefList.mList.begin()); @@ -27,15 +27,15 @@ namespace // Adds scripts for items in containers (containers/npcs/creatures) template void listCellScriptsCont (MWWorld::LocalScripts& localScripts, - MWWorld::CellRefList& cellRefList, MWWorld::Ptr::CellStore *cell) + MWWorld::CellRefList& cellRefList, MWWorld::CellStore *cell) { for (typename MWWorld::CellRefList::List::iterator iter ( cellRefList.mList.begin()); iter!=cellRefList.mList.end(); ++iter) { - - MWWorld::Ptr containerPtr (&*iter, cell); - + + MWWorld::Ptr containerPtr (&*iter, cell); + MWWorld::ContainerStore& container = MWWorld::Class::get(containerPtr).getContainerStore(containerPtr); for(MWWorld::ContainerStoreIterator it3 = container.begin(); it3 != container.end(); ++it3) { @@ -99,7 +99,7 @@ void MWWorld::LocalScripts::add (const std::string& scriptName, const Ptr& ptr) } } -void MWWorld::LocalScripts::addCell (Ptr::CellStore *cell) +void MWWorld::LocalScripts::addCell (CellStore *cell) { listCellScripts (*this, cell->mActivators, cell); listCellScripts (*this, cell->mPotions, cell); @@ -128,7 +128,7 @@ void MWWorld::LocalScripts::clear() mScripts.clear(); } -void MWWorld::LocalScripts::clearCell (Ptr::CellStore *cell) +void MWWorld::LocalScripts::clearCell (CellStore *cell) { std::list >::iterator iter = mScripts.begin(); diff --git a/apps/openmw/mwworld/ptr.hpp b/apps/openmw/mwworld/ptr.hpp index e5352da28..8b70382d0 100644 --- a/apps/openmw/mwworld/ptr.hpp +++ b/apps/openmw/mwworld/ptr.hpp @@ -14,9 +14,6 @@ namespace MWWorld { public: - typedef MWWorld::CellStore CellStore; - ///< \deprecated - MWWorld::LiveCellRefBase *mRef; CellStore *mCell; ContainerStore *mContainerStore; @@ -59,7 +56,7 @@ namespace MWWorld RefData& getRefData() const; - Ptr::CellStore *getCell() const + CellStore *getCell() const { assert(mCell); return mCell; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 254ad98cf..348f01dc5 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -116,7 +116,7 @@ namespace MWWorld mActiveCells.erase(*iter); } - void Scene::loadCell (Ptr::CellStore *cell, Loading::Listener* loadingListener) + void Scene::loadCell (CellStore *cell, Loading::Listener* loadingListener) { std::pair result = mActiveCells.insert(cell); @@ -161,7 +161,7 @@ namespace MWWorld MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell); } - void Scene::playerCellChange(MWWorld::CellStore *cell, const ESM::Position& pos, bool adjustPlayerPos) + void Scene::playerCellChange(CellStore *cell, const ESM::Position& pos, bool adjustPlayerPos) { MWBase::World *world = MWBase::Environment::get().getWorld(); world->getPlayer().setCell(cell); @@ -441,7 +441,7 @@ namespace MWWorld changeCell (x, y, position, true); } - Ptr::CellStore* Scene::getCurrentCell () + CellStore* Scene::getCurrentCell () { return mCurrentCell; } @@ -451,7 +451,7 @@ namespace MWWorld mCellChanged = false; } - int Scene::countRefs (const Ptr::CellStore& cell) + int Scene::countRefs (const CellStore& cell) { return cell.mActivators.mList.size() + cell.mPotions.mList.size() @@ -475,7 +475,7 @@ namespace MWWorld + cell.mNpcs.mList.size(); } - void Scene::insertCell (Ptr::CellStore &cell, bool rescale, Loading::Listener* loadingListener) + void Scene::insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener) { // Loop through all references in the cell insertCellRefList(mRendering, cell.mActivators, cell, *mPhysics, rescale, loadingListener); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index e3edad352..b7bb944ed 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -56,9 +56,9 @@ namespace MWWorld void playerCellChange (CellStore *cell, const ESM::Position& position, bool adjustPlayerPos = true); - void insertCell (Ptr::CellStore &cell, bool rescale, Loading::Listener* loadingListener); + void insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener); - int countRefs (const Ptr::CellStore& cell); + int countRefs (const CellStore& cell); public: diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 509aee983..b2d18a27f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -48,30 +48,6 @@ using namespace Ogre; namespace { -/* // NOTE this code is never instantiated (proper copy in localscripts.cpp), - // so this commented out to not produce syntactic errors - - template - void listCellScripts (const MWWorld::ESMStore& store, - MWWorld::CellRefList& cellRefList, MWWorld::LocalScripts& localScripts, - MWWorld::Ptr::CellStore *cell) - { - for (typename MWWorld::CellRefList::List::iterator iter ( - cellRefList.mList.begin()); - iter!=cellRefList.mList.end(); ++iter) - { - if (!iter->mBase->mScript.empty() && iter->mData.getCount()) - { - if (const ESM::Script *script = store.get().find (iter->mBase->mScript)) - { - iter->mData.setLocals (*script); - - localScripts.add (iter->mBase->mScript, MWWorld::Ptr (&*iter, cell)); - } - } - } - } -*/ template MWWorld::LiveCellRef *searchViaHandle (const std::string& handle, MWWorld::CellRefList& refList) @@ -125,7 +101,7 @@ namespace MWWorld LoadersContainer mLoaders; }; - Ptr World::getPtrViaHandle (const std::string& handle, Ptr::CellStore& cell) + Ptr World::getPtrViaHandle (const std::string& handle, CellStore& cell) { if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mActivators)) @@ -388,12 +364,12 @@ namespace MWWorld return &mFallback; } - Ptr::CellStore *World::getExterior (int x, int y) + CellStore *World::getExterior (int x, int y) { return mCells.getExterior (x, y); } - Ptr::CellStore *World::getInterior (const std::string& name) + CellStore *World::getInterior (const std::string& name) { return mCells.getInterior (name); } @@ -504,7 +480,7 @@ namespace MWWorld for (Scene::CellStoreCollection::const_iterator iter (mWorldScene->getActiveCells().begin()); iter!=mWorldScene->getActiveCells().end(); ++iter) { - Ptr::CellStore* cellstore = *iter; + CellStore* cellstore = *iter; Ptr ptr = mCells.getPtr (name, *cellstore, true); if (!ptr.isEmpty()) @@ -537,7 +513,7 @@ namespace MWWorld for (Scene::CellStoreCollection::const_iterator iter (mWorldScene->getActiveCells().begin()); iter!=mWorldScene->getActiveCells().end(); ++iter) { - Ptr::CellStore* cellstore = *iter; + CellStore* cellstore = *iter; Ptr ptr = getPtrViaHandle (handle, *cellstore); if (!ptr.isEmpty()) @@ -547,7 +523,7 @@ namespace MWWorld return MWWorld::Ptr(); } - void World::addContainerScripts(const Ptr& reference, Ptr::CellStore * cell) + void World::addContainerScripts(const Ptr& reference, CellStore * cell) { if( reference.getTypeName()==typeid (ESM::Container).name() || reference.getTypeName()==typeid (ESM::NPC).name() || @@ -1400,7 +1376,7 @@ namespace MWWorld bool World::isCellExterior() const { - Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); + CellStore *currentCell = mWorldScene->getCurrentCell(); if (currentCell) { return currentCell->mCell->isExterior(); @@ -1410,7 +1386,7 @@ namespace MWWorld bool World::isCellQuasiExterior() const { - Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); + CellStore *currentCell = mWorldScene->getCurrentCell(); if (currentCell) { if (!(currentCell->mCell->mData.mFlags & ESM::Cell::QuasiEx)) @@ -1587,7 +1563,7 @@ namespace MWWorld void World::dropObjectOnGround (const Ptr& actor, const Ptr& object, int amount) { - MWWorld::Ptr::CellStore* cell = actor.getCell(); + MWWorld::CellStore* cell = actor.getCell(); ESM::Position pos = actor.getRefData().getPosition(); @@ -1684,7 +1660,7 @@ namespace MWWorld } bool - World::isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const + World::isUnderwater(const MWWorld::CellStore* cell, const Ogre::Vector3 &pos) const { if (!(cell->mCell->mData.mFlags & ESM::Cell::HasWater)) { return false; @@ -1704,9 +1680,9 @@ namespace MWWorld return mRendering->vanityRotateCamera(rot); } - void World::setCameraDistance(float dist, bool adjust, bool override) + void World::setCameraDistance(float dist, bool adjust, bool override_) { - return mRendering->setCameraDistance(dist, adjust, override);; + return mRendering->setCameraDistance(dist, adjust, override_); } void World::setupPlayer() @@ -1734,7 +1710,7 @@ namespace MWWorld int World::canRest () { - Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); + CellStore *currentCell = mWorldScene->getCurrentCell(); Ptr player = mPlayer->getPlayer(); RefData &refdata = player.getRefData(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1cee7d50f..b8a61c471 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -79,7 +79,7 @@ namespace MWWorld World (const World&); World& operator= (const World&); - Ptr getPtrViaHandle (const std::string& handle, Ptr::CellStore& cellStore); + Ptr getPtrViaHandle (const std::string& handle, CellStore& cellStore); int mActivationDistanceOverride; std::string mFacedHandle; @@ -121,7 +121,7 @@ namespace MWWorld float getObjectActivationDistance (); void removeContainerScripts(const Ptr& reference); - void addContainerScripts(const Ptr& reference, Ptr::CellStore* cell); + void addContainerScripts(const Ptr& reference, CellStore* cell); void PCDropped (const Ptr& item); void processDoors(float duration); @@ -413,7 +413,7 @@ namespace MWWorld ///Is the head of the creature underwater? virtual bool isSubmerged(const MWWorld::Ptr &object) const; virtual bool isSwimming(const MWWorld::Ptr &object) const; - virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const; + virtual bool isUnderwater(const MWWorld::CellStore* cell, const Ogre::Vector3 &pos) const; virtual bool isOnGround(const MWWorld::Ptr &ptr) const; virtual void togglePOV() { From ce624e024b0225afe3815ef65a4f9ce3b506bd55 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 5 Dec 2013 14:03:25 +0100 Subject: [PATCH 107/889] make sure player record stays in place across cleanups --- apps/openmw/mwworld/store.hpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index c25197319..233f2f702 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -292,6 +292,20 @@ namespace MWWorld } }; + template <> + inline void Store::clearDynamic() + { + std::map::iterator iter = mDynamic.begin(); + + while (iter!=mDynamic.end()) + if (iter->first=="player") + ++iter; + else + mDynamic.erase (iter++); + + mShared.clear(); + } + template <> inline void Store::load(ESM::ESMReader &esm, const std::string &id) { std::string idLower = Misc::StringUtils::lowerCase(id); From a7b42b867bb559511886973d3b8ecfaa986588d3 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 5 Dec 2013 14:18:43 +0100 Subject: [PATCH 108/889] more cleanup --- apps/openmw/mwworld/worldimp.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b2d18a27f..539c959b3 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -238,7 +238,6 @@ namespace MWWorld setupPlayer(); renderPlayer(); - mRendering->resetCamera(); MWBase::Environment::get().getWindowManager()->updatePlayer(); @@ -289,6 +288,15 @@ namespace MWWorld mStore.setUp(); mCells.clear(); + + mProjectiles.clear(); + mDoorStates.clear(); + + mGodMode = false; + mSky = true; + mTeleportEnabled = true; + mPlayIntro = 0; + mFacedDistance = FLT_MAX; } void World::ensureNeededRecords() @@ -1701,6 +1709,7 @@ namespace MWWorld { mRendering->renderPlayer(mPlayer->getPlayer()); mPhysics->addActor(mPlayer->getPlayer()); + mRendering->resetCamera(); } void World::setupExternalRendering (MWRender::ExternalRendering& rendering) From 8b7889f8e54d1e834ee983a6ac8cb18dfced7c2c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 5 Dec 2013 14:22:08 +0100 Subject: [PATCH 109/889] setup player after loading a saved game --- apps/openmw/mwstate/statemanagerimp.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 09e574e84..9df1e2dc0 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -161,6 +161,10 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl Settings::Manager::setString ("character", "Saves", slot->mPath.parent_path().filename().string()); + + MWBase::Environment::get().getWorld()->setupPlayer(); + MWBase::Environment::get().getWorld()->renderPlayer(); + MWBase::Environment::get().getWindowManager()->updatePlayer(); } MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) From ee6ddc34045cdd8207c14971ad0b310856ea5f34 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 5 Dec 2013 14:56:30 +0100 Subject: [PATCH 110/889] block saving while chargen is in progress --- apps/openmw/mwgui/mainmenu.cpp | 3 ++- apps/openmw/mwstate/statemanagerimp.cpp | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index ff8ab8c93..da1992474 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -97,7 +97,8 @@ namespace MWGui MWBase::Environment::get().getStateManager()->characterEnd()) buttons.push_back("loadgame"); - if (state==MWBase::StateManager::State_Running) + if (state==MWBase::StateManager::State_Running && + MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1) buttons.push_back("savegame"); buttons.push_back("options"); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 9df1e2dc0..293a9e232 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -60,6 +60,8 @@ void MWState::StateManager::newGame (bool bypass) MWBase::Environment::get().getWorld()->startNewGame(); MWBase::Environment::get().getWindowManager()->setNewGame (true); } + else + MWBase::Environment::get().getWorld()->setGlobalInt ("chargenstate", -1); mState = State_Running; } From 14eff87339a27c5a30c4e3f47791eed8678d52b5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 6 Dec 2013 11:17:14 +0100 Subject: [PATCH 111/889] removed some junk from ESM store --- apps/openmw/mwworld/esmstore.hpp | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index ebb086cee..d5da5a866 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -24,10 +24,8 @@ namespace MWWorld Store mBirthSigns; Store mClasses; Store mClothes; - Store mContChange; Store mContainers; Store mCreatures; - Store mCreaChange; Store mDialogs; Store mDoors; Store mEnchants; @@ -40,7 +38,6 @@ namespace MWWorld Store mLockpicks; Store mMiscItems; Store mNpcs; - Store mNpcChange; Store mProbes; Store mRaces; Store mRegions; @@ -103,7 +100,7 @@ namespace MWWorld { // Cell store needs access to this for tracking moved references mCells.mEsmStore = this; - + mStores[ESM::REC_ACTI] = &mActivators; mStores[ESM::REC_ALCH] = &mPotions; mStores[ESM::REC_APPA] = &mAppas; @@ -114,10 +111,8 @@ namespace MWWorld mStores[ESM::REC_CELL] = &mCells; mStores[ESM::REC_CLAS] = &mClasses; mStores[ESM::REC_CLOT] = &mClothes; - mStores[ESM::REC_CNTC] = &mContChange; mStores[ESM::REC_CONT] = &mContainers; mStores[ESM::REC_CREA] = &mCreatures; - mStores[ESM::REC_CREC] = &mCreaChange; mStores[ESM::REC_DIAL] = &mDialogs; mStores[ESM::REC_DOOR] = &mDoors; mStores[ESM::REC_ENCH] = &mEnchants; @@ -133,7 +128,6 @@ namespace MWWorld mStores[ESM::REC_LTEX] = &mLandTextures; mStores[ESM::REC_MISC] = &mMiscItems; mStores[ESM::REC_NPC_] = &mNpcs; - mStores[ESM::REC_NPCC] = &mNpcChange; mStores[ESM::REC_PGRD] = &mPathgrids; mStores[ESM::REC_PROB] = &mProbes; mStores[ESM::REC_RACE] = &mRaces; @@ -287,11 +281,6 @@ namespace MWWorld return mClothes; } - template <> - inline const Store &ESMStore::get() const { - return mContChange; - } - template <> inline const Store &ESMStore::get() const { return mContainers; @@ -302,11 +291,6 @@ namespace MWWorld return mCreatures; } - template <> - inline const Store &ESMStore::get() const { - return mCreaChange; - } - template <> inline const Store &ESMStore::get() const { return mDialogs; @@ -367,11 +351,6 @@ namespace MWWorld return mNpcs; } - template <> - inline const Store &ESMStore::get() const { - return mNpcChange; - } - template <> inline const Store &ESMStore::get() const { return mProbes; From 674931a8512240e6cd080a17b1a039937370d4ad Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 6 Dec 2013 14:24:14 +0100 Subject: [PATCH 112/889] remove terminating 0 from strings read from ESM records --- components/esm/esmreader.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index f02ed2d6e..4e1860bab 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -302,6 +302,9 @@ std::string ESMReader::getString(int size) char *ptr = &mBuffer[0]; getExact(ptr, size); + if (size>0 && ptr[size-1]==0) + --size; + // Convert to UTF8 and return if (mEncoder) return mEncoder->getUtf8(ptr, size); From 1c13a9037adc13a50e40c5f66650c5fe509b7a04 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 7 Dec 2013 13:17:28 +0100 Subject: [PATCH 113/889] save and load dynamic records --- apps/openmw/mwbase/mechanicsmanager.hpp | 36 ++++++------- apps/openmw/mwbase/world.hpp | 7 +++ .../mwmechanics/mechanicsmanagerimp.cpp | 8 +++ .../mwmechanics/mechanicsmanagerimp.hpp | 20 ++++---- apps/openmw/mwstate/statemanagerimp.cpp | 29 +++++++++-- apps/openmw/mwworld/esmstore.cpp | 50 +++++++++++++++++++ apps/openmw/mwworld/esmstore.hpp | 7 +++ apps/openmw/mwworld/store.hpp | 33 ++++++++++++ apps/openmw/mwworld/worldimp.cpp | 22 ++++++++ apps/openmw/mwworld/worldimp.hpp | 6 +++ 10 files changed, 189 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 3ab234de1..eeeab77e8 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -101,28 +101,30 @@ namespace MWBase float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0; ///< Perform a persuasion action on NPC - virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0; - ///< Forces an object to refresh its animation state. + virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0; + ///< Forces an object to refresh its animation state. - virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0; - ///< Run animation for a MW-reference. Calls to this function for references that are currently not - /// in the scene should be ignored. - /// - /// \param mode 0 normal, 1 immediate start, 2 immediate loop - /// \param count How many times the animation should be run + virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0; + ///< Run animation for a MW-reference. Calls to this function for references that are currently not + /// in the scene should be ignored. + /// + /// \param mode 0 normal, 1 immediate start, 2 immediate loop + /// \param count How many times the animation should be run - virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0; - ///< Skip the animation for the given MW-reference for one frame. Calls to this function for - /// references that are currently not in the scene should be ignored. + virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0; + ///< Skip the animation for the given MW-reference for one frame. Calls to this function for + /// references that are currently not in the scene should be ignored. - virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0; + virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0; - /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently - /// paused we may want to do it manually (after equipping permanent enchantment) - virtual void updateMagicEffects (const MWWorld::Ptr& ptr) = 0; + /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently + /// paused we may want to do it manually (after equipping permanent enchantment) + virtual void updateMagicEffects (const MWWorld::Ptr& ptr) = 0; - virtual void toggleAI() = 0; - virtual bool isAIActive() = 0; + virtual void toggleAI() = 0; + virtual bool isAIActive() = 0; + + virtual void playerLoaded() = 0; }; } diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 7d1678a11..717012f72 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -30,6 +30,7 @@ namespace OEngine namespace ESM { class ESMReader; + class ESMWriter; struct Position; struct Cell; struct Class; @@ -98,6 +99,12 @@ namespace MWBase virtual void clear() = 0; + virtual int countSavedGameRecords() const = 0; + + virtual void write (ESM::ESMWriter& writer) const = 0; + + virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0; + virtual OEngine::Render::Fader* getFader() = 0; ///< \ŧodo remove this function. Rendering details should not be exposed. diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 1316baaeb..b6a7a7b75 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -701,4 +701,12 @@ namespace MWMechanics { return mAI; } + + void MechanicsManager::playerLoaded() + { + mUpdatePlayer = true; + mClassSelected = true; + mRaceSelected = true; + mAI = true; + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index ec03b457b..ebc879d26 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -98,18 +98,20 @@ namespace MWMechanics void toLower(std::string npcFaction); ///< Perform a persuasion action on NPC - virtual void forceStateUpdate(const MWWorld::Ptr &ptr); + virtual void forceStateUpdate(const MWWorld::Ptr &ptr); - virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); - virtual void skipAnimation(const MWWorld::Ptr& ptr); - virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string &groupName); + virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); + virtual void skipAnimation(const MWWorld::Ptr& ptr); + virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string &groupName); - /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently - /// paused we may want to do it manually (after equipping permanent enchantment) - virtual void updateMagicEffects (const MWWorld::Ptr& ptr); + /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently + /// paused we may want to do it manually (after equipping permanent enchantment) + virtual void updateMagicEffects (const MWWorld::Ptr& ptr); - virtual void toggleAI(); - virtual bool isAIActive(); + virtual void toggleAI(); + virtual bool isAIActive(); + + virtual void playerLoaded(); }; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 293a9e232..94219b8fc 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -11,6 +11,7 @@ #include "../mwbase/journal.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" @@ -103,8 +104,10 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot ESM::ESMWriter writer; writer.setFormat (ESM::Header::CurrentFormat); writer.setRecordCount ( - 1+ // saved game header - MWBase::Environment::get().getJournal()->countSavedGameRecords()); + 1 // saved game header + +MWBase::Environment::get().getJournal()->countSavedGameRecords() + +MWBase::Environment::get().getWorld()->countSavedGameRecords() + ); writer.save (stream); @@ -113,7 +116,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot writer.endRecord (ESM::REC_SAVE); MWBase::Environment::get().getJournal()->write (writer); - /// \todo write saved game data + MWBase::Environment::get().getWorld()->write (writer); writer.close(); @@ -149,6 +152,19 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl MWBase::Environment::get().getJournal()->readRecord (reader, n.val); break; + case ESM::REC_ALCH: + case ESM::REC_ARMO: + case ESM::REC_BOOK: + case ESM::REC_CLAS: + case ESM::REC_CLOT: + case ESM::REC_ENCH: + case ESM::REC_NPC_: + case ESM::REC_SPEL: + case ESM::REC_WEAP: + + MWBase::Environment::get().getWorld()->readRecord (reader, n.val); + break; + default: // ignore invalid records @@ -167,6 +183,13 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl MWBase::Environment::get().getWorld()->setupPlayer(); MWBase::Environment::get().getWorld()->renderPlayer(); MWBase::Environment::get().getWindowManager()->updatePlayer(); + MWBase::Environment::get().getMechanicsManager()->playerLoaded(); + + // for testing purpose only + ESM::Position pos; + pos.pos[0] = pos.pos[1] = pos.pos[2] = 0; + pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; + MWBase::Environment::get().getWorld()->changeToExteriorCell (pos); } MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index f1bff11a2..cb4e441fc 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -139,4 +139,54 @@ void ESMStore::setUp() mAttributes.setUp(); } + int ESMStore::countSavedGameRecords() const + { + return + mPotions.getDynamicSize() + +mArmors.getDynamicSize() + +mBooks.getDynamicSize() + +mClasses.getDynamicSize() + +mClothes.getDynamicSize() + +mEnchants.getDynamicSize() + +mNpcs.getDynamicSize() + +mSpells.getDynamicSize() + +mWeapons.getDynamicSize(); + } + + void ESMStore::write (ESM::ESMWriter& writer) const + { + mPotions.write (writer); + mArmors.write (writer); + mBooks.write (writer); + mClasses.write (writer); + mClothes.write (writer); + mEnchants.write (writer); + mNpcs.write (writer); + mSpells.write (writer); + mWeapons.write (writer); + } + + bool ESMStore::readRecord (ESM::ESMReader& reader, int32_t type) + { + switch (type) + { + case ESM::REC_ALCH: + case ESM::REC_ARMO: + case ESM::REC_BOOK: + case ESM::REC_CLAS: + case ESM::REC_CLOT: + case ESM::REC_ENCH: + case ESM::REC_NPC_: + case ESM::REC_SPEL: + case ESM::REC_WEAP: + + mStores[type]->read (reader); + return true; + + default: + + return false; + } + } + } // end namespace diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index d5da5a866..e6730c307 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -209,6 +209,13 @@ namespace MWWorld // This method must be called once, after loading all master/plugin files. This can only be done // from the outside, so it must be public. void setUp(); + + int countSavedGameRecords() const; + + void write (ESM::ESMWriter& writer) const; + + bool readRecord (ESM::ESMReader& reader, int32_t type); + ///< \return Known type? }; template <> diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 233f2f702..df957408d 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -6,6 +6,8 @@ #include #include +#include + #include "recordcmp.hpp" namespace MWWorld @@ -18,10 +20,16 @@ namespace MWWorld virtual void listIdentifier(std::vector &list) const {} virtual size_t getSize() const = 0; + virtual int getDynamicSize() const { return 0; } virtual void load(ESM::ESMReader &esm, const std::string &id) = 0; virtual bool eraseStatic(const std::string &id) {return false;} virtual void clearDynamic() {} + + virtual void write (ESM::ESMWriter& writer) const {} + + virtual void read (ESM::ESMReader& reader) {} + ///< Read into dynamic storage }; template @@ -212,6 +220,11 @@ namespace MWWorld return mShared.size(); } + int getDynamicSize() const + { + return mDynamic.size(); + } + void listIdentifier(std::vector &list) const { list.reserve(list.size() + getSize()); typename std::vector::const_iterator it = mShared.begin(); @@ -290,6 +303,26 @@ namespace MWWorld bool erase(const T &item) { return erase(item.mId); } + + void write (ESM::ESMWriter& writer) const + { + for (typename Dynamic::const_iterator iter (mDynamic.begin()); iter!=mDynamic.end(); + ++iter) + { + writer.startRecord (T::sRecordId); + writer.writeHNString ("NAME", iter->second.mId); + iter->second.save (writer); + writer.endRecord (T::sRecordId); + } + } + + void read (ESM::ESMReader& reader) + { + T record; + record.mId = reader.getHNString ("NAME"); + record.load (reader); + insert (record); + } }; template <> diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 82b2301db..bfcd0ae1c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -282,7 +282,10 @@ namespace MWWorld mWorldScene->changeToVoid(); if (mPlayer) + { + mPlayer->setCell (0); mPlayer->getPlayer().getRefData() = RefData(); + } mStore.clearDynamic(); mStore.setUp(); @@ -299,6 +302,25 @@ namespace MWWorld mFacedDistance = FLT_MAX; } + int World::countSavedGameRecords() const + { + return mStore.countSavedGameRecords(); + } + + void World::write (ESM::ESMWriter& writer) const + { + mStore.write (writer); + } + + void World::readRecord (ESM::ESMReader& reader, int32_t type) + { + if (!mStore.readRecord (reader, type)) + { + /// \todo handle other world state records + + } + } + void World::ensureNeededRecords() { if (!mStore.get().search("sCompanionShare")) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index b09b42e94..40245b78d 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -171,6 +171,12 @@ namespace MWWorld virtual void clear(); + virtual int countSavedGameRecords() const; + + virtual void write (ESM::ESMWriter& writer) const; + + virtual void readRecord (ESM::ESMReader& reader, int32_t type); + virtual OEngine::Render::Fader* getFader(); ///< \ŧodo remove this function. Rendering details should not be exposed. From c5e543b91b75f2131efb9816b6cb63e073f21d10 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 6 Dec 2013 17:25:32 +0100 Subject: [PATCH 114/889] Implement NiGeomMorpherController --- components/nifogre/mesh.cpp | 7 +++ components/nifogre/ogrenifloader.cpp | 86 ++++++++++++++++++++++++++-- 2 files changed, 88 insertions(+), 5 deletions(-) diff --git a/components/nifogre/mesh.cpp b/components/nifogre/mesh.cpp index ca92f62d4..ef4fbbe8d 100644 --- a/components/nifogre/mesh.cpp +++ b/components/nifogre/mesh.cpp @@ -116,6 +116,7 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape Ogre::HardwareBuffer::Usage vertUsage = Ogre::HardwareBuffer::HBU_STATIC; bool vertShadowBuffer = false; + bool geomMorpherController = false; if(!shape->controller.empty()) { Nif::ControllerPtr ctrl = shape->controller; @@ -124,6 +125,7 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape { vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY; vertShadowBuffer = true; + geomMorpherController = true; break; } } while(!(ctrl=ctrl->next).empty()); @@ -347,6 +349,11 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape if (!mesh->suggestTangentVectorBuildParams(Ogre::VES_TANGENT, src,dest)) mesh->buildTangentVectors(Ogre::VES_TANGENT, src,dest); } + + // Create a dummy vertex animation track if there's a geom morpher controller + // This is required to make Ogre create the buffers we will use for software vertex animation + if (srcVerts.size() && geomMorpherController) + mesh->createAnimation("dummy", 0)->createVertexTrack(1, sub->vertexData, Ogre::VAT_MORPH); } diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index a530d060d..ec24089b8 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -385,12 +385,35 @@ public: private: Ogre::SubEntity *mSubEntity; std::vector mMorphs; + std::vector mValues; + + std::vector mVertices; + + static float interpKey(const Nif::FloatKeyList::VecType &keys, float time) + { + if(time <= keys.front().mTime) + return keys.front().mValue; + + Nif::FloatKeyList::VecType::const_iterator iter(keys.begin()+1); + for(;iter != keys.end();iter++) + { + if(iter->mTime < time) + continue; + + Nif::FloatKeyList::VecType::const_iterator last(iter-1); + float a = (time-last->mTime) / (iter->mTime-last->mTime); + return last->mValue + ((iter->mValue - last->mValue)*a); + } + return keys.back().mValue; + } public: Value(Ogre::SubEntity *subent, const Nif::NiMorphData *data) : mSubEntity(subent) , mMorphs(data->mMorphs) - { } + { + mValues.resize(mMorphs.size()-1, 0.f); + } virtual Ogre::Real getValue() const { @@ -398,9 +421,63 @@ public: return 0.0f; } - virtual void setValue(Ogre::Real value) + virtual void setValue(Ogre::Real time) { - // TODO: Implement + if (mMorphs.size() <= 1) + return; + +#if OGRE_DOUBLE_PRECISION +#error "This code needs to be rewritten for double precision mode" +#endif + + Ogre::VertexData* data = mSubEntity->_getSoftwareVertexAnimVertexData(); + + const Ogre::VertexElement* posElem = + data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION); + + Ogre::HardwareVertexBufferSharedPtr vbuf = + data->vertexBufferBinding->getBuffer(posElem->getSource()); + + bool needToUpdate = false; + int i=0; + for (std::vector::iterator it = mMorphs.begin()+1; it != mMorphs.end(); ++it,++i) + { + float val = 0; + if (!it->mData.mKeys.empty()) + val = interpKey(it->mData.mKeys, time); + val = std::max(0.f, std::min(1.f, val)); + + if (val != mValues[i]) + needToUpdate = true; + mValues[i] = val; + } + if (!needToUpdate) + return; + + // The first morph key always contains the original positions + mVertices = mMorphs[0].mVertices; + + i = 0; + for (std::vector::iterator it = mMorphs.begin()+1; it != mMorphs.end(); ++it,++i) + { + float val = mValues[i]; + + if (it->mVertices.size() != mMorphs[0].mVertices.size()) + continue; + + if (val != 0) + { + for (unsigned int v=0; vmVertices[v] * val; + } + } + + if (mVertices.size() * sizeof(float)*3 != vbuf->getSizeInBytes()) + return; + + vbuf->writeData(0, vbuf->getSizeInBytes(), &mVertices[0]); + + mSubEntity->_markBuffersUsedForAnimation(); } }; @@ -480,11 +557,10 @@ class NIFObjectLoader { const Nif::NiGeomMorpherController *geom = static_cast(ctrl.getPtr()); - Ogre::SubEntity *subent = entity->getSubEntity(0); Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : Ogre::ControllerValueRealPtr()); - Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(subent, geom->data.getPtr())); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(entity->getSubEntity(0), geom->data.getPtr())); GeomMorpherController::Function* function = OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); objectlist.mMaxControllerLength = std::max(function->mStopTime, objectlist.mMaxControllerLength); From bb70deabb1e06606484fb7ffdec2a8452e07936f Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 7 Dec 2013 14:11:06 +0100 Subject: [PATCH 115/889] Add an incomplete implementation of SayAnimationValue (lip animation) --- apps/openmw/mwrender/npcanimation.cpp | 18 ++++++++++++++++-- apps/openmw/mwrender/npcanimation.hpp | 14 ++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 50e41062a..4ba49cf55 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -59,6 +59,15 @@ std::string getVampireHead(const std::string& race, bool female) namespace MWRender { +float SayAnimationValue::getValue() const +{ + if (MWBase::Environment::get().getSoundManager()->sayDone(mReference)) + return 0; + else + // TODO: Use the loudness of the currently playing sound + return 1; +} + static NpcAnimation::PartBoneMap createPartListMap() { NpcAnimation::PartBoneMap result; @@ -115,6 +124,8 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v { mNpc = mPtr.get()->mBase; + mSayAnimationValue = Ogre::SharedPtr(new SayAnimationValue(mPtr)); + for(size_t i = 0;i < ESM::PRT_Count;i++) { mPartslots[i] = -1; //each slot is empty @@ -558,15 +569,18 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g } // TODO: - // type == ESM::PRT_Head should get an animation source based on the current output of - // the actor's 'say' sound (0 = silent, 1 = loud?). // type == ESM::PRT_Weapon should get an animation source based on the current offset // of the weapon attack animation (from its beginning, or start marker?) std::vector >::iterator ctrl(mObjectParts[type].mControllers.begin()); for(;ctrl != mObjectParts[type].mControllers.end();ctrl++) { if(ctrl->getSource().isNull()) + { ctrl->setSource(mNullAnimationValuePtr); + + if (type == ESM::PRT_Head) + ctrl->setSource(mSayAnimationValue); + } } return true; diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index c33d511ec..4e252d9d0 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -13,6 +13,18 @@ namespace ESM namespace MWRender { +class SayAnimationValue : public Ogre::ControllerValue +{ +private: + MWWorld::Ptr mReference; +public: + SayAnimationValue(MWWorld::Ptr reference) : mReference(reference) {} + + virtual Ogre::Real getValue() const; + virtual void setValue(Ogre::Real value) + { } +}; + class NpcAnimation : public Animation, public MWWorld::InventoryStoreListener { public: @@ -50,6 +62,8 @@ private: Ogre::Vector3 mFirstPersonOffset; + Ogre::SharedPtr mSayAnimationValue; + void updateNpcBase(); NifOgre::ObjectList insertBoundedPart(const std::string &model, int group, const std::string &bonename, From 742e0e014da428c58dd1361f5702ed533f56fe93 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 7 Dec 2013 14:14:05 +0100 Subject: [PATCH 116/889] Remove more cruft in MessageBox. Fixes inconsistent sizing when close to a newline. --- apps/openmw/mwgui/messagebox.cpp | 31 +++++----------------------- apps/openmw/mwgui/messagebox.hpp | 2 -- files/mygui/openmw_messagebox.layout | 10 ++++----- 3 files changed, 10 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 8486218f0..671845552 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -141,7 +141,6 @@ namespace MWGui , mMaxTime(0) { // defines - mFixedWidth = 300; mBottomPadding = 20; mNextBoxPadding = 20; @@ -149,41 +148,21 @@ namespace MWGui mMessageWidget->setOverflowToTheLeft(true); mMessageWidget->setCaptionWithReplacing(mMessage); - - MyGUI::IntSize size; - size.width = mFixedWidth; - size.height = 100; // dummy - - mMessageWidget->setSize(size); - - MyGUI::IntSize textSize = mMessageWidget->getTextSize(); - - size.height = mHeight = textSize.height + 20; // this is the padding between the text and the box - - mMainWidget->setSize(size); - size.width -= 15; // this is to center the text (see messagebox.layout, Widget type="Edit" position="-2 -3 0 0") - mMessageWidget->setSize(size); } void MessageBox::update (int height) { MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize(); - MyGUI::IntCoord coord; - coord.left = (gameWindowSize.width - mFixedWidth)/2; - coord.top = (gameWindowSize.height - mHeight - height - mBottomPadding); + MyGUI::IntPoint pos; + pos.left = (gameWindowSize.width - mMainWidget->getWidth())/2; + pos.top = (gameWindowSize.height - mMainWidget->getHeight() - height - mBottomPadding); - MyGUI::IntSize size; - size.width = mFixedWidth; - size.height = mHeight; - - mMainWidget->setCoord(coord); - mMainWidget->setSize(size); - mMainWidget->setVisible(true); + mMainWidget->setPosition(pos); } int MessageBox::getHeight () { - return mHeight+mNextBoxPadding; // 20 is the padding between this and the next MessageBox + return mMainWidget->getHeight()+mNextBoxPadding; // 20 is the padding between this and the next MessageBox } diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index ce3a85ab3..aac4704fa 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -63,10 +63,8 @@ namespace MWGui protected: MessageBoxManager& mMessageBoxManager; - int mHeight; const std::string& mMessage; MyGUI::EditBox* mMessageWidget; - int mFixedWidth; int mBottomPadding; int mNextBoxPadding; }; diff --git a/files/mygui/openmw_messagebox.layout b/files/mygui/openmw_messagebox.layout index dfdb57648..b2d29271b 100644 --- a/files/mygui/openmw_messagebox.layout +++ b/files/mygui/openmw_messagebox.layout @@ -1,11 +1,11 @@ - - - + + + + + From 1624e0fd8a7f9604d6913f270fba6022b83ae669 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 7 Dec 2013 20:12:03 +0100 Subject: [PATCH 117/889] Rename ObjectList to ObjectScene. Wrap it in a SharedPtr so we can automate the destruction routine. --- apps/openmw/mwrender/animation.cpp | 114 ++++++++------------ apps/openmw/mwrender/animation.hpp | 10 +- apps/openmw/mwrender/npcanimation.cpp | 40 ++++--- apps/openmw/mwrender/npcanimation.hpp | 4 +- apps/openmw/mwrender/sky.cpp | 19 ++-- apps/openmw/mwrender/sky.hpp | 4 + components/nifogre/ogrenifloader.cpp | 148 +++++++++++++++----------- components/nifogre/ogrenifloader.hpp | 16 ++- 8 files changed, 178 insertions(+), 177 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index dddbc2c73..42d11aa6d 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -51,28 +51,6 @@ void Animation::EffectAnimationValue::setValue(Ogre::Real) { } - -void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects) -{ - for(size_t i = 0;i < objects.mLights.size();i++) - { - Ogre::Light *light = objects.mLights[i]; - // If parent is a scene node, it was created specifically for this light. Destroy it now. - if(light->isAttached() && !light->isParentTagPoint()) - sceneMgr->destroySceneNode(light->getParentSceneNode()); - sceneMgr->destroyLight(light); - } - for(size_t i = 0;i < objects.mParticles.size();i++) - sceneMgr->destroyParticleSystem(objects.mParticles[i]); - for(size_t i = 0;i < objects.mEntities.size();i++) - sceneMgr->destroyEntity(objects.mEntities[i]); - objects.mControllers.clear(); - objects.mLights.clear(); - objects.mParticles.clear(); - objects.mEntities.clear(); - objects.mSkelBase = NULL; -} - Animation::Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node) : mPtr(ptr) , mCamera(NULL) @@ -90,13 +68,9 @@ Animation::Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node) Animation::~Animation() { - for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ++it) - destroyObjectList(mInsert->getCreator(), it->mObjects); + mEffects.clear(); mAnimSources.clear(); - - Ogre::SceneManager *sceneMgr = mInsert->getCreator(); - destroyObjectList(sceneMgr, mObjectRoot); } @@ -105,7 +79,7 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly) OgreAssert(mAnimSources.empty(), "Setting object root while animation sources are set!"); mSkelBase = NULL; - destroyObjectList(mInsert->getCreator(), mObjectRoot); + mObjectRoot.setNull(); if(model.empty()) return; @@ -126,11 +100,11 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly) mObjectRoot = (!baseonly ? NifOgre::Loader::createObjects(mInsert, mdlname) : NifOgre::Loader::createObjectBase(mInsert, mdlname)); - if(mObjectRoot.mSkelBase) + if(mObjectRoot->mSkelBase) { - mSkelBase = mObjectRoot.mSkelBase; + mSkelBase = mObjectRoot->mSkelBase; - Ogre::AnimationStateSet *aset = mObjectRoot.mSkelBase->getAllAnimationStates(); + Ogre::AnimationStateSet *aset = mObjectRoot->mSkelBase->getAllAnimationStates(); Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); while(asiter.hasMoreElements()) { @@ -141,7 +115,7 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly) // Set the bones as manually controlled since we're applying the // transformations manually - Ogre::SkeletonInstance *skelinst = mObjectRoot.mSkelBase->getSkeleton(); + Ogre::SkeletonInstance *skelinst = mObjectRoot->mSkelBase->getSkeleton(); Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); while(boneiter.hasMoreElements()) boneiter.getNext()->setManuallyControlled(true); @@ -162,10 +136,10 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly) else mAttachedObjects.clear(); - for(size_t i = 0;i < mObjectRoot.mControllers.size();i++) + for(size_t i = 0;i < mObjectRoot->mControllers.size();i++) { - if(mObjectRoot.mControllers[i].getSource().isNull()) - mObjectRoot.mControllers[i].setSource(mAnimationValuePtr[0]); + if(mObjectRoot->mControllers[i].getSource().isNull()) + mObjectRoot->mControllers[i].setSource(mAnimationValuePtr[0]); } } @@ -233,15 +207,15 @@ public: } }; -void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue, Ogre::Real dist, bool enchantedGlow, Ogre::Vector3* glowColor) +void Animation::setRenderProperties(NifOgre::ObjectScenePtr objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue, Ogre::Real dist, bool enchantedGlow, Ogre::Vector3* glowColor) { - std::for_each(objlist.mEntities.begin(), objlist.mEntities.end(), + std::for_each(objlist->mEntities.begin(), objlist->mEntities.end(), VisQueueSet(visflags, solidqueue, transqueue, dist)); - std::for_each(objlist.mParticles.begin(), objlist.mParticles.end(), + std::for_each(objlist->mParticles.begin(), objlist->mParticles.end(), VisQueueSet(visflags, solidqueue, transqueue, dist)); if (enchantedGlow) - std::for_each(objlist.mEntities.begin(), objlist.mEntities.end(), + std::for_each(objlist->mEntities.begin(), objlist->mEntities.end(), AddGlow(glowColor)); } @@ -340,7 +314,7 @@ void Animation::clearAnimSources() } -void Animation::addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objlist, const ESM::Light *light) +void Animation::addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectScenePtr objlist, const ESM::Light *light) { const MWWorld::Fallback *fallback = MWBase::Environment::get().getWorld()->getFallback(); @@ -353,8 +327,8 @@ void Animation::addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList if((light->mData.mFlags&ESM::Light::Negative)) color *= -1; - objlist.mLights.push_back(sceneMgr->createLight()); - Ogre::Light *olight = objlist.mLights.back(); + objlist->mLights.push_back(sceneMgr->createLight()); + Ogre::Light *olight = objlist->mLights.back(); olight->setDiffuseColour(color); Ogre::ControllerValueRealPtr src(Ogre::ControllerManager::getSingleton().getFrameTimeSource()); @@ -366,7 +340,7 @@ void Animation::addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList (light->mData.mFlags&ESM::Light::PulseSlow) ? OEngine::Render::LT_PulseSlow : OEngine::Render::LT_Normal )); - objlist.mControllers.push_back(Ogre::Controller(src, dest, func)); + objlist->mControllers.push_back(Ogre::Controller(src, dest, func)); bool interior = !(mPtr.isInCell() && mPtr.getCell()->mCell->isExterior()); bool quadratic = fallback->getFallbackBool("LightAttenuation_OutQuadInLin") ? @@ -392,14 +366,14 @@ void Animation::addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList } // If there's an AttachLight bone, attach the light to that, otherwise put it in the center, - if(objlist.mSkelBase && objlist.mSkelBase->getSkeleton()->hasBone("AttachLight")) - objlist.mSkelBase->attachObjectToBone("AttachLight", olight); + if(objlist->mSkelBase && objlist->mSkelBase->getSkeleton()->hasBone("AttachLight")) + objlist->mSkelBase->attachObjectToBone("AttachLight", olight); else { Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; - for(size_t i = 0;i < objlist.mEntities.size();i++) + for(size_t i = 0;i < objlist->mEntities.size();i++) { - Ogre::Entity *ent = objlist.mEntities[i]; + Ogre::Entity *ent = objlist->mEntities[i]; bounds.merge(ent->getBoundingBox()); } @@ -942,8 +916,8 @@ Ogre::Vector3 Animation::runAnimation(float duration) ++stateiter; } - for(size_t i = 0;i < mObjectRoot.mControllers.size();i++) - mObjectRoot.mControllers[i].update(); + for(size_t i = 0;i < mObjectRoot->mControllers.size();i++) + mObjectRoot->mControllers[i].update(); // Apply group controllers for(size_t grp = 0;grp < sNumGroups;grp++) @@ -986,7 +960,7 @@ public: void Animation::enableLights(bool enable) { - std::for_each(mObjectRoot.mLights.begin(), mObjectRoot.mLights.end(), ToggleLight(enable)); + std::for_each(mObjectRoot->mLights.begin(), mObjectRoot->mLights.end(), ToggleLight(enable)); } @@ -1005,7 +979,7 @@ public: Ogre::AxisAlignedBox Animation::getWorldBounds() { Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; - std::for_each(mObjectRoot.mEntities.begin(), mObjectRoot.mEntities.end(), MergeBounds(&bounds)); + std::for_each(mObjectRoot->mEntities.begin(), mObjectRoot->mEntities.end(), MergeBounds(&bounds)); return bounds; } @@ -1055,17 +1029,17 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con params.mEffectId = effectId; params.mBoneName = bonename; - for(size_t i = 0;i < params.mObjects.mControllers.size();i++) + for(size_t i = 0;i < params.mObjects->mControllers.size();i++) { - if(params.mObjects.mControllers[i].getSource().isNull()) - params.mObjects.mControllers[i].setSource(Ogre::SharedPtr (new EffectAnimationValue())); + if(params.mObjects->mControllers[i].getSource().isNull()) + params.mObjects->mControllers[i].setSource(Ogre::SharedPtr (new EffectAnimationValue())); } if (!texture.empty()) { - for(size_t i = 0;i < params.mObjects.mParticles.size(); ++i) + for(size_t i = 0;i < params.mObjects->mParticles.size(); ++i) { - Ogre::ParticleSystem* partSys = params.mObjects.mParticles[i]; + Ogre::ParticleSystem* partSys = params.mObjects->mParticles[i]; sh::Factory::getInstance()._ensureMaterial(partSys->getMaterialName(), "Default"); Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().getByName(partSys->getMaterialName()); static int count = 0; @@ -1099,7 +1073,6 @@ void Animation::removeEffect(int effectId) { if (it->mEffectId == effectId) { - destroyObjectList(mInsert->getCreator(), it->mObjects); mEffects.erase(it); return; } @@ -1119,32 +1092,31 @@ void Animation::updateEffects(float duration) { for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ) { - NifOgre::ObjectList& objects = it->mObjects; - for(size_t i = 0; i < objects.mControllers.size() ;i++) + NifOgre::ObjectScenePtr objects = it->mObjects; + for(size_t i = 0; i < objects->mControllers.size() ;i++) { - EffectAnimationValue* value = dynamic_cast(objects.mControllers[i].getSource().get()); + EffectAnimationValue* value = dynamic_cast(objects->mControllers[i].getSource().get()); if (value) value->addTime(duration); - objects.mControllers[i].update(); + objects->mControllers[i].update(); } - if (objects.mControllers[0].getSource()->getValue() >= objects.mMaxControllerLength) + if (objects->mControllers[0].getSource()->getValue() >= objects->mMaxControllerLength) { if (it->mLoop) { // Start from the beginning again; carry over the remainder - float remainder = objects.mControllers[0].getSource()->getValue() - objects.mMaxControllerLength; - for(size_t i = 0; i < objects.mControllers.size() ;i++) + float remainder = objects->mControllers[0].getSource()->getValue() - objects->mMaxControllerLength; + for(size_t i = 0; i < objects->mControllers.size() ;i++) { - EffectAnimationValue* value = dynamic_cast(objects.mControllers[i].getSource().get()); + EffectAnimationValue* value = dynamic_cast(objects->mControllers[i].getSource().get()); if (value) value->resetTime(remainder); } } else { - destroyObjectList(mInsert->getCreator(), it->mObjects); it = mEffects.erase(it); continue; } @@ -1214,16 +1186,16 @@ public: bool ObjectAnimation::canBatch() const { - if(!mObjectRoot.mParticles.empty() || !mObjectRoot.mLights.empty() || !mObjectRoot.mControllers.empty()) + if(!mObjectRoot->mParticles.empty() || !mObjectRoot->mLights.empty() || !mObjectRoot->mControllers.empty()) return false; - return std::find_if(mObjectRoot.mEntities.begin(), mObjectRoot.mEntities.end(), - FindEntityTransparency()) == mObjectRoot.mEntities.end(); + return std::find_if(mObjectRoot->mEntities.begin(), mObjectRoot->mEntities.end(), + FindEntityTransparency()) == mObjectRoot->mEntities.end(); } void ObjectAnimation::fillBatch(Ogre::StaticGeometry *sg) { - std::vector::reverse_iterator iter = mObjectRoot.mEntities.rbegin(); - for(;iter != mObjectRoot.mEntities.rend();++iter) + std::vector::reverse_iterator iter = mObjectRoot->mEntities.rbegin(); + for(;iter != mObjectRoot->mEntities.rend();++iter) { Ogre::Node *node = (*iter)->getParentNode(); sg->addEntity(*iter, node->_getDerivedPosition(), node->_getDerivedOrientation(), node->_getDerivedScale()); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index e28aecbc1..b11b2b0a5 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -112,7 +112,7 @@ protected: struct EffectParams { std::string mModelName; // Just here so we don't add the same effect twice - NifOgre::ObjectList mObjects; + NifOgre::ObjectScenePtr mObjects; int mEffectId; bool mLoop; std::string mBoneName; @@ -125,7 +125,7 @@ protected: Ogre::SceneNode *mInsert; Ogre::Entity *mSkelBase; - NifOgre::ObjectList mObjectRoot; + NifOgre::ObjectScenePtr mObjectRoot; AnimSourceList mAnimSources; Ogre::Node *mAccumRoot; Ogre::Node *mNonAccumRoot; @@ -187,11 +187,9 @@ protected: void addAnimSource(const std::string &model); /** Adds an additional light to the given object list using the specified ESM record. */ - void addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objlist, const ESM::Light *light); + void addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectScenePtr objlist, const ESM::Light *light); - static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects); - - static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, + static void setRenderProperties(NifOgre::ObjectScenePtr objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue, Ogre::Real dist=0.0f, bool enchantedGlow=false, Ogre::Vector3* glowColor=NULL); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 4ba49cf55..a57816f34 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -106,10 +106,6 @@ NpcAnimation::~NpcAnimation() { if (!mListenerDisabled) mPtr.getClass().getInventoryStore(mPtr).setListener(NULL, mPtr); - - Ogre::SceneManager *sceneMgr = mInsert->getCreator(); - for(size_t i = 0;i < ESM::PRT_Count;i++) - destroyObjectList(sceneMgr, mObjectParts[i]); } @@ -447,18 +443,18 @@ public: } }; -NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename, bool enchantedGlow, Ogre::Vector3* glowColor) +NifOgre::ObjectScenePtr NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename, bool enchantedGlow, Ogre::Vector3* glowColor) { - NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); + NifOgre::ObjectScenePtr objects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); setRenderProperties(objects, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha, 0, enchantedGlow, glowColor); - std::for_each(objects.mEntities.begin(), objects.mEntities.end(), SetObjectGroup(group)); - std::for_each(objects.mParticles.begin(), objects.mParticles.end(), SetObjectGroup(group)); + std::for_each(objects->mEntities.begin(), objects->mEntities.end(), SetObjectGroup(group)); + std::for_each(objects->mParticles.begin(), objects->mParticles.end(), SetObjectGroup(group)); - if(objects.mSkelBase) + if(objects->mSkelBase) { - Ogre::AnimationStateSet *aset = objects.mSkelBase->getAllAnimationStates(); + Ogre::AnimationStateSet *aset = objects->mSkelBase->getAllAnimationStates(); Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); while(asiter.hasMoreElements()) { @@ -466,7 +462,7 @@ NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, in state->setEnabled(false); state->setLoop(false); } - Ogre::SkeletonInstance *skelinst = objects.mSkelBase->getSkeleton(); + Ogre::SkeletonInstance *skelinst = objects->mSkelBase->getSkeleton(); Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); while(boneiter.hasMoreElements()) boneiter.getNext()->setManuallyControlled(true); @@ -494,11 +490,13 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) for(size_t i = 0;i < ESM::PRT_Count;i++) { - std::vector >::iterator ctrl(mObjectParts[i].mControllers.begin()); - for(;ctrl != mObjectParts[i].mControllers.end();ctrl++) + if (mObjectParts[i].isNull()) + continue; + std::vector >::iterator ctrl(mObjectParts[i]->mControllers.begin()); + for(;ctrl != mObjectParts[i]->mControllers.end();ctrl++) ctrl->update(); - Ogre::Entity *ent = mObjectParts[i].mSkelBase; + Ogre::Entity *ent = mObjectParts[i]->mSkelBase; if(!ent) continue; updateSkeletonInstance(baseinst, ent->getSkeleton()); ent->getAllAnimationStates()->_notifyDirty(); @@ -512,7 +510,7 @@ void NpcAnimation::removeIndividualPart(ESM::PartReferenceType type) mPartPriorities[type] = 0; mPartslots[type] = -1; - destroyObjectList(mInsert->getCreator(), mObjectParts[type]); + mObjectParts[type].setNull(); } void NpcAnimation::reserveIndividualPart(ESM::PartReferenceType type, int group, int priority) @@ -544,12 +542,12 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g mPartPriorities[type] = priority; mObjectParts[type] = insertBoundedPart(mesh, group, sPartList.at(type), enchantedGlow, glowColor); - if(mObjectParts[type].mSkelBase) + if(mObjectParts[type]->mSkelBase) { - Ogre::SkeletonInstance *skel = mObjectParts[type].mSkelBase->getSkeleton(); - if(mObjectParts[type].mSkelBase->isParentTagPoint()) + Ogre::SkeletonInstance *skel = mObjectParts[type]->mSkelBase->getSkeleton(); + if(mObjectParts[type]->mSkelBase->isParentTagPoint()) { - Ogre::Node *root = mObjectParts[type].mSkelBase->getParentNode(); + Ogre::Node *root = mObjectParts[type]->mSkelBase->getParentNode(); if(skel->hasBone("BoneOffset")) { Ogre::Bone *offset = skel->getBone("BoneOffset"); @@ -571,8 +569,8 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g // TODO: // type == ESM::PRT_Weapon should get an animation source based on the current offset // of the weapon attack animation (from its beginning, or start marker?) - std::vector >::iterator ctrl(mObjectParts[type].mControllers.begin()); - for(;ctrl != mObjectParts[type].mControllers.end();ctrl++) + std::vector >::iterator ctrl(mObjectParts[type]->mControllers.begin()); + for(;ctrl != mObjectParts[type]->mControllers.end();ctrl++) { if(ctrl->getSource().isNull()) { diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 4e252d9d0..962663268 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -46,7 +46,7 @@ private: bool mListenerDisabled; // Bounded Parts - NifOgre::ObjectList mObjectParts[ESM::PRT_Count]; + NifOgre::ObjectScenePtr mObjectParts[ESM::PRT_Count]; const ESM::NPC *mNpc; std::string mHeadModel; @@ -66,7 +66,7 @@ private: void updateNpcBase(); - NifOgre::ObjectList insertBoundedPart(const std::string &model, int group, const std::string &bonename, + NifOgre::ObjectScenePtr insertBoundedPart(const std::string &model, int group, const std::string &bonename, bool enchantedGlow, Ogre::Vector3* glowColor=NULL); void removeIndividualPart(ESM::PartReferenceType type); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 03e14dc07..39f7ccc85 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -285,10 +285,10 @@ void SkyManager::create() // Stars mAtmosphereNight = mRootNode->createChildSceneNode(); - NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mAtmosphereNight, "meshes\\sky_night_01.nif"); - for(size_t i = 0, matidx = 0;i < objects.mEntities.size();i++) + NifOgre::ObjectScenePtr objects = NifOgre::Loader::createObjects(mAtmosphereNight, "meshes\\sky_night_01.nif"); + for(size_t i = 0, matidx = 0;i < objects->mEntities.size();i++) { - Entity* night1_ent = objects.mEntities[i]; + Entity* night1_ent = objects->mEntities[i]; night1_ent->setRenderQueueGroup(RQG_SkiesEarly+1); night1_ent->setVisibilityFlags(RV_Sky); night1_ent->setCastShadows(false); @@ -307,14 +307,14 @@ void SkyManager::create() night1_ent->getSubEntity(j)->setMaterialName(matName); } } - + mObjects.push_back(objects); // Atmosphere (day) mAtmosphereDay = mRootNode->createChildSceneNode(); objects = NifOgre::Loader::createObjects(mAtmosphereDay, "meshes\\sky_atmosphere.nif"); - for(size_t i = 0;i < objects.mEntities.size();i++) + for(size_t i = 0;i < objects->mEntities.size();i++) { - Entity* atmosphere_ent = objects.mEntities[i]; + Entity* atmosphere_ent = objects->mEntities[i]; atmosphere_ent->setCastShadows(false); atmosphere_ent->setRenderQueueGroup(RQG_SkiesEarly); atmosphere_ent->setVisibilityFlags(RV_Sky); @@ -325,14 +325,14 @@ void SkyManager::create() // Using infinite AAB here to prevent being clipped by the custom near clip plane used for reflections/refractions atmosphere_ent->getMesh()->_setBounds (aabInf); } - + mObjects.push_back(objects); // Clouds SceneNode* clouds_node = mRootNode->createChildSceneNode(); objects = NifOgre::Loader::createObjects(clouds_node, "meshes\\sky_clouds_01.nif"); - for(size_t i = 0;i < objects.mEntities.size();i++) + for(size_t i = 0;i < objects->mEntities.size();i++) { - Entity* clouds_ent = objects.mEntities[i]; + Entity* clouds_ent = objects->mEntities[i]; clouds_ent->setVisibilityFlags(RV_Sky); clouds_ent->setRenderQueueGroup(RQG_SkiesEarly+5); for(unsigned int j = 0;j < clouds_ent->getNumSubEntities();j++) @@ -341,6 +341,7 @@ void SkyManager::create() // Using infinite AAB here to prevent being clipped by the custom near clip plane used for reflections/refractions clouds_ent->getMesh()->_setBounds (aabInf); } + mObjects.push_back(objects); mCreated = true; } diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 3df8846cd..965907a97 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -11,6 +11,8 @@ #include +#include + #include "../mwworld/weather.hpp" @@ -196,6 +198,8 @@ namespace MWRender Ogre::SceneNode* mAtmosphereDay; Ogre::SceneNode* mAtmosphereNight; + std::vector mObjects; + // remember some settings so we don't have to apply them again if they didnt change Ogre::String mClouds; Ogre::String mNextClouds; diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index ec24089b8..44c3042c6 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -47,6 +47,28 @@ namespace NifOgre { + +ObjectScene::~ObjectScene() +{ + for(size_t i = 0;i < mLights.size();i++) + { + Ogre::Light *light = mLights[i]; + // If parent is a scene node, it was created specifically for this light. Destroy it now. + if(light->isAttached() && !light->isParentTagPoint()) + mSceneMgr->destroySceneNode(light->getParentSceneNode()); + mSceneMgr->destroyLight(light); + } + for(size_t i = 0;i < mParticles.size();i++) + mSceneMgr->destroyParticleSystem(mParticles[i]); + for(size_t i = 0;i < mEntities.size();i++) + mSceneMgr->destroyEntity(mEntities[i]); + mControllers.clear(); + mLights.clear(); + mParticles.clear(); + mEntities.clear(); + mSkelBase = NULL; +} + // FIXME: Should not be here. class DefaultFunction : public Ogre::ControllerFunction { @@ -504,7 +526,7 @@ class NIFObjectLoader static void createEntity(const std::string &name, const std::string &group, - Ogre::SceneManager *sceneMgr, ObjectList &objectlist, + Ogre::SceneManager *sceneMgr, ObjectScenePtr scene, const Nif::Node *node, int flags, int animflags) { const Nif::NiTriShape *shape = static_cast(node); @@ -521,16 +543,16 @@ class NIFObjectLoader Ogre::Entity *entity = sceneMgr->createEntity(fullname); entity->setVisible(!(flags&Nif::NiNode::Flag_Hidden)); - objectlist.mEntities.push_back(entity); - if(objectlist.mSkelBase) + scene->mEntities.push_back(entity); + if(scene->mSkelBase) { if(entity->hasSkeleton()) - entity->shareSkeletonInstanceWith(objectlist.mSkelBase); + entity->shareSkeletonInstanceWith(scene->mSkelBase); else { int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex); - Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); - objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), entity); + Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); + scene->mSkelBase->attachObjectToBone(trgtbone->getName(), entity); } } @@ -548,10 +570,10 @@ class NIFObjectLoader Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(material, uv->data.getPtr())); UVController::Function* function = OGRE_NEW UVController::Function(uv, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); - objectlist.mMaxControllerLength = std::max(function->mStopTime, objectlist.mMaxControllerLength); + scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); Ogre::ControllerFunctionRealPtr func(function); - objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } else if(ctrl->recType == Nif::RC_NiGeomMorpherController) { @@ -563,10 +585,10 @@ class NIFObjectLoader Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(entity->getSubEntity(0), geom->data.getPtr())); GeomMorpherController::Function* function = OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); - objectlist.mMaxControllerLength = std::max(function->mStopTime, objectlist.mMaxControllerLength); + scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); Ogre::ControllerFunctionRealPtr func(function); - objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } ctrl = ctrl->next; } @@ -647,7 +669,7 @@ class NIFObjectLoader } static void createParticleSystem(const std::string &name, const std::string &group, - Ogre::SceneNode *sceneNode, ObjectList &objectlist, + Ogre::SceneNode *sceneNode, ObjectScenePtr scene, const Nif::Node *partnode, int flags, int partflags) { const Nif::NiAutoNormalParticlesData *particledata = NULL; @@ -696,8 +718,8 @@ class NIFObjectLoader if(!partctrl->emitter.empty()) { int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex); - Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); - createParticleEmitterAffectors(partsys, partctrl, trgtbone, objectlist.mSkelBase->getName()); + Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); + createParticleEmitterAffectors(partsys, partctrl, trgtbone, scene->mSkelBase->getName()); } Ogre::ControllerValueRealPtr srcval((partflags&Nif::NiNode::ParticleFlag_AutoPlay) ? @@ -707,20 +729,20 @@ class NIFObjectLoader ParticleSystemController::Function* function = OGRE_NEW ParticleSystemController::Function(partctrl, (partflags&Nif::NiNode::ParticleFlag_AutoPlay)); - objectlist.mMaxControllerLength = std::max(function->mStopTime, objectlist.mMaxControllerLength); + scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); Ogre::ControllerFunctionRealPtr func(function); - objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } ctrl = ctrl->next; } partsys->setVisible(!(flags&Nif::NiNode::Flag_Hidden)); - objectlist.mParticles.push_back(partsys); + scene->mParticles.push_back(partsys); } - static void createNodeControllers(const std::string &name, Nif::ControllerPtr ctrl, ObjectList &objectlist, int animflags) + static void createNodeControllers(const std::string &name, Nif::ControllerPtr ctrl, ObjectScenePtr scene, int animflags) { do { if(ctrl->recType == Nif::RC_NiVisController) @@ -728,17 +750,17 @@ class NIFObjectLoader const Nif::NiVisController *vis = static_cast(ctrl.getPtr()); int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); - Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); + Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : Ogre::ControllerValueRealPtr()); Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr())); VisController::Function* function = OGRE_NEW VisController::Function(vis, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); - objectlist.mMaxControllerLength = std::max(function->mStopTime, objectlist.mMaxControllerLength); + scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); Ogre::ControllerFunctionRealPtr func(function); - objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } else if(ctrl->recType == Nif::RC_NiKeyframeController) { @@ -746,16 +768,16 @@ class NIFObjectLoader if(!key->data.empty()) { int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); - Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); + Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : Ogre::ControllerValueRealPtr()); Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr())); KeyframeController::Function* function = OGRE_NEW KeyframeController::Function(key, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); - objectlist.mMaxControllerLength = std::max(function->mStopTime, objectlist.mMaxControllerLength); + scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); Ogre::ControllerFunctionRealPtr func(function); - objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } } ctrl = ctrl->next; @@ -804,7 +826,7 @@ class NIFObjectLoader static void createObjects(const std::string &name, const std::string &group, Ogre::SceneNode *sceneNode, const Nif::Node *node, - ObjectList &objectlist, int flags, int animflags, int partflags) + ObjectScenePtr scene, int flags, int animflags, int partflags) { // Do not create objects for the collision shape (includes all children) if(node->recType == Nif::RC_RootCollisionNode) @@ -830,7 +852,7 @@ class NIFObjectLoader const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex); - extractTextKeys(tk, objectlist.mTextKeys[trgtid]); + extractTextKeys(tk, scene->mTextKeys[trgtid]); } else if(e->recType == Nif::RC_NiStringExtraData) { @@ -849,7 +871,7 @@ class NIFObjectLoader } if(!node->controller.empty() && (node->parent || node->recType != Nif::RC_NiNode)) - createNodeControllers(name, node->controller, objectlist, animflags); + createNodeControllers(name, node->controller, scene, animflags); if(node->recType == Nif::RC_NiCamera) { @@ -858,13 +880,13 @@ class NIFObjectLoader if(node->recType == Nif::RC_NiTriShape && !(flags&0x80000000)) { - createEntity(name, group, sceneNode->getCreator(), objectlist, node, flags, animflags); + createEntity(name, group, sceneNode->getCreator(), scene, node, flags, animflags); } if((node->recType == Nif::RC_NiAutoNormalParticles || node->recType == Nif::RC_NiRotatingParticles) && !(flags&0x40000000)) { - createParticleSystem(name, group, sceneNode, objectlist, node, flags, partflags); + createParticleSystem(name, group, sceneNode, scene, node, flags, partflags); } const Nif::NiNode *ninode = dynamic_cast(node); @@ -874,14 +896,14 @@ class NIFObjectLoader for(size_t i = 0;i < children.length();i++) { if(!children[i].empty()) - createObjects(name, group, sceneNode, children[i].getPtr(), objectlist, flags, animflags, partflags); + createObjects(name, group, sceneNode, children[i].getPtr(), scene, flags, animflags, partflags); } } } static void createSkelBase(const std::string &name, const std::string &group, Ogre::SceneManager *sceneMgr, const Nif::Node *node, - ObjectList &objectlist) + ObjectScenePtr scene) { /* This creates an empty mesh to which a skeleton gets attached. This * is to ensure we have an entity with a skeleton instance, even if all @@ -890,12 +912,12 @@ class NIFObjectLoader if(meshMgr.getByName(name).isNull()) NIFMeshLoader::createMesh(name, name, group, ~(size_t)0); - objectlist.mSkelBase = sceneMgr->createEntity(name); - objectlist.mEntities.push_back(objectlist.mSkelBase); + scene->mSkelBase = sceneMgr->createEntity(name); + scene->mEntities.push_back(scene->mSkelBase); } public: - static void load(Ogre::SceneNode *sceneNode, ObjectList &objectlist, const std::string &name, const std::string &group, int flags=0) + static void load(Ogre::SceneNode *sceneNode, ObjectScenePtr scene, const std::string &name, const std::string &group, int flags=0) { Nif::NIFFile::ptr nif = Nif::NIFFile::create(name); if(nif->numRoots() < 1) @@ -919,9 +941,9 @@ public: !NIFSkeletonLoader::createSkeleton(name, group, node).isNull()) { // Create a base skeleton entity if this NIF needs one - createSkelBase(name, group, sceneNode->getCreator(), node, objectlist); + createSkelBase(name, group, sceneNode->getCreator(), node, scene); } - createObjects(name, group, sceneNode, node, objectlist, flags, 0, 0); + createObjects(name, group, sceneNode, node, scene, flags, 0, 0); } static void loadKf(Ogre::Skeleton *skel, const std::string &name, @@ -984,37 +1006,37 @@ public: }; -ObjectList Loader::createObjects(Ogre::SceneNode *parentNode, std::string name, const std::string &group) +ObjectScenePtr Loader::createObjects(Ogre::SceneNode *parentNode, std::string name, const std::string &group) { - ObjectList objectlist; + ObjectScenePtr scene = ObjectScenePtr (new ObjectScene(parentNode->getCreator()));; Misc::StringUtils::toLower(name); - NIFObjectLoader::load(parentNode, objectlist, name, group); + NIFObjectLoader::load(parentNode, scene, name, group); - for(size_t i = 0;i < objectlist.mEntities.size();i++) + for(size_t i = 0;i < scene->mEntities.size();i++) { - Ogre::Entity *entity = objectlist.mEntities[i]; + Ogre::Entity *entity = scene->mEntities[i]; if(!entity->isAttached()) parentNode->attachObject(entity); } - return objectlist; + return scene; } -ObjectList Loader::createObjects(Ogre::Entity *parent, const std::string &bonename, +ObjectScenePtr Loader::createObjects(Ogre::Entity *parent, const std::string &bonename, Ogre::SceneNode *parentNode, std::string name, const std::string &group) { - ObjectList objectlist; + ObjectScenePtr scene = ObjectScenePtr (new ObjectScene(parentNode->getCreator())); Misc::StringUtils::toLower(name); - NIFObjectLoader::load(parentNode, objectlist, name, group); + NIFObjectLoader::load(parentNode, scene, name, group); bool isskinned = false; - for(size_t i = 0;i < objectlist.mEntities.size();i++) + for(size_t i = 0;i < scene->mEntities.size();i++) { - Ogre::Entity *ent = objectlist.mEntities[i]; - if(objectlist.mSkelBase != ent && ent->hasSkeleton()) + Ogre::Entity *ent = scene->mEntities[i]; + if(scene->mSkelBase != ent && ent->hasSkeleton()) { isskinned = true; break; @@ -1029,12 +1051,12 @@ ObjectList Loader::createObjects(Ogre::Entity *parent, const std::string &bonena { std::string filter = "@shape=tri "+bonename; Misc::StringUtils::toLower(filter); - for(size_t i = 0;i < objectlist.mEntities.size();i++) + for(size_t i = 0;i < scene->mEntities.size();i++) { - Ogre::Entity *entity = objectlist.mEntities[i]; + Ogre::Entity *entity = scene->mEntities[i]; if(entity->hasSkeleton()) { - if(entity == objectlist.mSkelBase || + if(entity == scene->mSkelBase || entity->getMesh()->getName().find(filter) != std::string::npos) parentNode->attachObject(entity); } @@ -1047,9 +1069,9 @@ ObjectList Loader::createObjects(Ogre::Entity *parent, const std::string &bonena } else { - for(size_t i = 0;i < objectlist.mEntities.size();i++) + for(size_t i = 0;i < scene->mEntities.size();i++) { - Ogre::Entity *entity = objectlist.mEntities[i]; + Ogre::Entity *entity = scene->mEntities[i]; if(!entity->isAttached()) { Ogre::TagPoint *tag = parent->attachObjectToBone(bonename, entity); @@ -1058,32 +1080,32 @@ ObjectList Loader::createObjects(Ogre::Entity *parent, const std::string &bonena } } - for(size_t i = 0;i < objectlist.mParticles.size();i++) + for(size_t i = 0;i < scene->mParticles.size();i++) { - Ogre::ParticleSystem *partsys = objectlist.mParticles[i]; + Ogre::ParticleSystem *partsys = scene->mParticles[i]; if(partsys->isAttached()) partsys->detachFromParent(); - Ogre::TagPoint *tag = objectlist.mSkelBase->attachObjectToBone( - objectlist.mSkelBase->getSkeleton()->getRootBone()->getName(), partsys); + Ogre::TagPoint *tag = scene->mSkelBase->attachObjectToBone( + scene->mSkelBase->getSkeleton()->getRootBone()->getName(), partsys); tag->setScale(scale); } - return objectlist; + return scene; } -ObjectList Loader::createObjectBase(Ogre::SceneNode *parentNode, std::string name, const std::string &group) +ObjectScenePtr Loader::createObjectBase(Ogre::SceneNode *parentNode, std::string name, const std::string &group) { - ObjectList objectlist; + ObjectScenePtr scene = ObjectScenePtr (new ObjectScene(parentNode->getCreator())); Misc::StringUtils::toLower(name); - NIFObjectLoader::load(parentNode, objectlist, name, group, 0xC0000000); + NIFObjectLoader::load(parentNode, scene, name, group, 0xC0000000); - if(objectlist.mSkelBase) - parentNode->attachObject(objectlist.mSkelBase); + if(scene->mSkelBase) + parentNode->attachObject(scene->mSkelBase); - return objectlist; + return scene; } diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index de06dd3d5..94d4aa674 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -39,12 +39,14 @@ namespace NifOgre typedef std::multimap TextKeyMap; static const char sTextKeyExtraDataID[] = "TextKeyExtraData"; -struct ObjectList { +struct ObjectScene { Ogre::Entity *mSkelBase; std::vector mEntities; std::vector mParticles; std::vector mLights; + Ogre::SceneManager* mSceneMgr; + // The maximum length on any of the controllers. For animations with controllers, but no text keys, consider this the animation length. float mMaxControllerLength; @@ -52,24 +54,28 @@ struct ObjectList { std::vector > mControllers; - ObjectList() : mSkelBase(0), mMaxControllerLength(0) + ObjectScene(Ogre::SceneManager* sceneMgr) : mSkelBase(0), mMaxControllerLength(0), mSceneMgr(sceneMgr) { } + + ~ObjectScene(); }; +typedef Ogre::SharedPtr ObjectScenePtr; + class Loader { public: - static ObjectList createObjects(Ogre::Entity *parent, const std::string &bonename, + static ObjectScenePtr createObjects(Ogre::Entity *parent, const std::string &bonename, Ogre::SceneNode *parentNode, std::string name, const std::string &group="General"); - static ObjectList createObjects(Ogre::SceneNode *parentNode, + static ObjectScenePtr createObjects(Ogre::SceneNode *parentNode, std::string name, const std::string &group="General"); - static ObjectList createObjectBase(Ogre::SceneNode *parentNode, + static ObjectScenePtr createObjectBase(Ogre::SceneNode *parentNode, std::string name, const std::string &group="General"); From 8d63f8eea221e2aff758673e15cb373d64b4381b Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sat, 7 Dec 2013 21:00:46 +0100 Subject: [PATCH 118/889] Fixes #998: Setting the max health should also set the current health Added setting current value of dynamic stat in OpSetDynamic class. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwscript/statsextensions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 603515ff4..6cd483ae1 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -209,6 +209,7 @@ namespace MWScript .getDynamic (mIndex)); stat.setModified (value, 0); + stat.setCurrent(value); MWWorld::Class::get (ptr).getCreatureStats (ptr).setDynamic (mIndex, stat); } From 51a9f0111cc9740ddd126c8b84d83ac7d9cac1fb Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 8 Dec 2013 16:38:13 +0100 Subject: [PATCH 119/889] Implement NiAlphaController and NiMaterialColorController --- components/CMakeLists.txt | 2 +- components/nifogre/controller.hpp | 95 ++++++++ components/nifogre/material.cpp | 3 +- components/nifogre/ogrenifloader.cpp | 313 +++++++++++++++++---------- components/nifogre/ogrenifloader.hpp | 15 ++ 5 files changed, 306 insertions(+), 122 deletions(-) create mode 100644 components/nifogre/controller.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 59fb084a8..50ac236c8 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -19,7 +19,7 @@ add_component_dir (nif ) add_component_dir (nifogre - ogrenifloader skeleton material mesh particles + ogrenifloader skeleton material mesh particles controller ) add_component_dir (nifbullet diff --git a/components/nifogre/controller.hpp b/components/nifogre/controller.hpp new file mode 100644 index 000000000..6d7f6ab3f --- /dev/null +++ b/components/nifogre/controller.hpp @@ -0,0 +1,95 @@ +#ifndef COMPONENTS_NIFOGRE_CONTROLLER_H +#define COMPONENTS_NIFOGRE_CONTROLLER_H + +#include +#include + +namespace NifOgre +{ + + class ValueInterpolator + { + protected: + float interpKey(const Nif::FloatKeyList::VecType &keys, float time, float def=0.f) const + { + if (keys.size() == 0) + return def; + + if(time <= keys.front().mTime) + return keys.front().mValue; + + Nif::FloatKeyList::VecType::const_iterator iter(keys.begin()+1); + for(;iter != keys.end();iter++) + { + if(iter->mTime < time) + continue; + + Nif::FloatKeyList::VecType::const_iterator last(iter-1); + float a = (time-last->mTime) / (iter->mTime-last->mTime); + return last->mValue + ((iter->mValue - last->mValue)*a); + } + return keys.back().mValue; + } + + Ogre::Vector3 interpKey(const Nif::Vector3KeyList::VecType &keys, float time) const + { + if(time <= keys.front().mTime) + return keys.front().mValue; + + Nif::Vector3KeyList::VecType::const_iterator iter(keys.begin()+1); + for(;iter != keys.end();iter++) + { + if(iter->mTime < time) + continue; + + Nif::Vector3KeyList::VecType::const_iterator last(iter-1); + float a = (time-last->mTime) / (iter->mTime-last->mTime); + return last->mValue + ((iter->mValue - last->mValue)*a); + } + return keys.back().mValue; + } + }; + + // FIXME: Should not be here. + class DefaultFunction : public Ogre::ControllerFunction + { + private: + float mFrequency; + float mPhase; + float mStartTime; + public: + float mStopTime; + + public: + DefaultFunction(const Nif::Controller *ctrl, bool deltaInput) + : Ogre::ControllerFunction(deltaInput) + , mFrequency(ctrl->frequency) + , mPhase(ctrl->phase) + , mStartTime(ctrl->timeStart) + , mStopTime(ctrl->timeStop) + { + if(mDeltaInput) + mDeltaCount = mPhase; + } + + virtual Ogre::Real calculate(Ogre::Real value) + { + if(mDeltaInput) + { + mDeltaCount += value*mFrequency; + if(mDeltaCount < mStartTime) + mDeltaCount = mStopTime - std::fmod(mStartTime - mDeltaCount, + mStopTime - mStartTime); + mDeltaCount = std::fmod(mDeltaCount - mStartTime, + mStopTime - mStartTime) + mStartTime; + return mDeltaCount; + } + + value = std::min(mStopTime, std::max(mStartTime, value+mPhase)); + return value; + } + }; + +} + +#endif diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index 8398dbc2e..bef0ec1d1 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -237,7 +237,8 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, Nif::ControllerPtr ctrls = matprop->controller; while(!ctrls.empty()) { - warn("Unhandled material controller "+ctrls->recName+" in "+name); + if (ctrls->recType != Nif::RC_NiAlphaController && ctrls->recType != Nif::RC_NiMaterialColorController) + warn("Unhandled material controller "+ctrls->recName+" in "+name); ctrls = ctrls->next; } } diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 44c3042c6..b71d4c4ee 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -37,16 +37,56 @@ #include #include +#include + #include #include #include "skeleton.hpp" #include "material.hpp" #include "mesh.hpp" +#include "controller.hpp" namespace NifOgre { +Ogre::MaterialPtr MaterialControllerManager::getWritableMaterial(Ogre::MovableObject *movable) +{ + if (mClonedMaterials.find(movable) != mClonedMaterials.end()) + return mClonedMaterials[movable]; + + else + { + Ogre::MaterialPtr mat; + if (Ogre::Entity* ent = dynamic_cast(movable)) + mat = ent->getSubEntity(0)->getMaterial(); + else if (Ogre::ParticleSystem* partSys = dynamic_cast(movable)) + mat = Ogre::MaterialManager::getSingleton().getByName(partSys->getMaterialName()); + + // Make sure techniques are created + sh::Factory::getInstance()._ensureMaterial(mat->getName(), "Default"); + + static int count=0; + mat = mat->clone(mat->getName() + Ogre::StringConverter::toString(count++)); + + mClonedMaterials[movable] = mat; + + if (Ogre::Entity* ent = dynamic_cast(movable)) + ent->getSubEntity(0)->setMaterial(mat); + else if (Ogre::ParticleSystem* partSys = dynamic_cast(movable)) + partSys->setMaterialName(mat->getName()); + + return mat; + } +} + +MaterialControllerManager::~MaterialControllerManager() +{ + for (std::map::iterator it = mClonedMaterials.begin(); it != mClonedMaterials.end(); ++it) + { + Ogre::MaterialManager::getSingleton().remove(it->second->getName()); + } +} ObjectScene::~ObjectScene() { @@ -69,44 +109,100 @@ ObjectScene::~ObjectScene() mSkelBase = NULL; } -// FIXME: Should not be here. -class DefaultFunction : public Ogre::ControllerFunction +class AlphaController { -private: - float mFrequency; - float mPhase; - float mStartTime; public: - float mStopTime; - -public: - DefaultFunction(const Nif::Controller *ctrl, bool deltaInput) - : Ogre::ControllerFunction(deltaInput) - , mFrequency(ctrl->frequency) - , mPhase(ctrl->phase) - , mStartTime(ctrl->timeStart) - , mStopTime(ctrl->timeStop) + class Value : public Ogre::ControllerValue, public ValueInterpolator { - if(mDeltaInput) - mDeltaCount = mPhase; - } + private: + Ogre::MovableObject* mMovable; + Nif::FloatKeyList mData; + MaterialControllerManager* mMaterialControllerMgr; - virtual Ogre::Real calculate(Ogre::Real value) - { - if(mDeltaInput) + public: + Value(Ogre::MovableObject *movable, const Nif::NiFloatData *data, MaterialControllerManager* materialControllerMgr) + : mMovable(movable) + , mData(data->mKeyList) + , mMaterialControllerMgr(materialControllerMgr) { - mDeltaCount += value*mFrequency; - if(mDeltaCount < mStartTime) - mDeltaCount = mStopTime - std::fmod(mStartTime - mDeltaCount, - mStopTime - mStartTime); - mDeltaCount = std::fmod(mDeltaCount - mStartTime, - mStopTime - mStartTime) + mStartTime; - return mDeltaCount; } - value = std::min(mStopTime, std::max(mStartTime, value+mPhase)); - return value; - } + virtual Ogre::Real getValue() const + { + // Should not be called + return 0.0f; + } + + virtual void setValue(Ogre::Real time) + { + float value = interpKey(mData.mKeys, time); + Ogre::MaterialPtr mat = mMaterialControllerMgr->getWritableMaterial(mMovable); + Ogre::Material::TechniqueIterator techs = mat->getTechniqueIterator(); + while(techs.hasMoreElements()) + { + Ogre::Technique *tech = techs.getNext(); + Ogre::Technique::PassIterator passes = tech->getPassIterator(); + while(passes.hasMoreElements()) + { + Ogre::Pass *pass = passes.getNext(); + Ogre::ColourValue diffuse = pass->getDiffuse(); + diffuse.a = value; + pass->setDiffuse(diffuse); + } + } + } + }; + + typedef DefaultFunction Function; +}; + +class MaterialColorController +{ +public: + class Value : public Ogre::ControllerValue, public ValueInterpolator + { + private: + Ogre::MovableObject* mMovable; + Nif::Vector3KeyList mData; + MaterialControllerManager* mMaterialControllerMgr; + + public: + Value(Ogre::MovableObject *movable, const Nif::NiPosData *data, MaterialControllerManager* materialControllerMgr) + : mMovable(movable) + , mData(data->mKeyList) + , mMaterialControllerMgr(materialControllerMgr) + { + } + + virtual Ogre::Real getValue() const + { + // Should not be called + return 0.0f; + } + + virtual void setValue(Ogre::Real time) + { + Ogre::Vector3 value = interpKey(mData.mKeys, time); + Ogre::MaterialPtr mat = mMaterialControllerMgr->getWritableMaterial(mMovable); + Ogre::Material::TechniqueIterator techs = mat->getTechniqueIterator(); + while(techs.hasMoreElements()) + { + Ogre::Technique *tech = techs.getNext(); + Ogre::Technique::PassIterator passes = tech->getPassIterator(); + while(passes.hasMoreElements()) + { + Ogre::Pass *pass = passes.getNext(); + Ogre::ColourValue diffuse = pass->getDiffuse(); + diffuse.r = value.x; + diffuse.g = value.y; + diffuse.b = value.z; + pass->setDiffuse(diffuse); + } + } + } + }; + + typedef DefaultFunction Function; }; class VisController @@ -185,48 +281,14 @@ public: class KeyframeController { public: - class Value : public NodeTargetValue + class Value : public NodeTargetValue, public ValueInterpolator { private: Nif::QuaternionKeyList mRotations; Nif::Vector3KeyList mTranslations; Nif::FloatKeyList mScales; - static float interpKey(const Nif::FloatKeyList::VecType &keys, float time) - { - if(time <= keys.front().mTime) - return keys.front().mValue; - - Nif::FloatKeyList::VecType::const_iterator iter(keys.begin()+1); - for(;iter != keys.end();iter++) - { - if(iter->mTime < time) - continue; - - Nif::FloatKeyList::VecType::const_iterator last(iter-1); - float a = (time-last->mTime) / (iter->mTime-last->mTime); - return last->mValue + ((iter->mValue - last->mValue)*a); - } - return keys.back().mValue; - } - - static Ogre::Vector3 interpKey(const Nif::Vector3KeyList::VecType &keys, float time) - { - if(time <= keys.front().mTime) - return keys.front().mValue; - - Nif::Vector3KeyList::VecType::const_iterator iter(keys.begin()+1); - for(;iter != keys.end();iter++) - { - if(iter->mTime < time) - continue; - - Nif::Vector3KeyList::VecType::const_iterator last(iter-1); - float a = (time-last->mTime) / (iter->mTime-last->mTime); - return last->mValue + ((iter->mValue - last->mValue)*a); - } - return keys.back().mValue; - } + using ValueInterpolator::interpKey; static Ogre::Quaternion interpKey(const Nif::QuaternionKeyList::VecType &keys, float time) { @@ -298,43 +360,24 @@ public: class UVController { public: - class Value : public Ogre::ControllerValue + class Value : public Ogre::ControllerValue, public ValueInterpolator { private: - Ogre::MaterialPtr mMaterial; + Ogre::MovableObject* mMovable; Nif::FloatKeyList mUTrans; Nif::FloatKeyList mVTrans; Nif::FloatKeyList mUScale; Nif::FloatKeyList mVScale; - - static float lookupValue(const Nif::FloatKeyList &keys, float time, float def) - { - if(keys.mKeys.size() == 0) - return def; - - if(time <= keys.mKeys.front().mTime) - return keys.mKeys.front().mValue; - - Nif::FloatKeyList::VecType::const_iterator iter(keys.mKeys.begin()+1); - for(;iter != keys.mKeys.end();iter++) - { - if(iter->mTime < time) - continue; - - Nif::FloatKeyList::VecType::const_iterator last(iter-1); - float a = (time-last->mTime) / (iter->mTime-last->mTime); - return last->mValue + ((iter->mValue - last->mValue)*a); - } - return keys.mKeys.back().mValue; - } + MaterialControllerManager* mMaterialControllerMgr; public: - Value(const Ogre::MaterialPtr &material, const Nif::NiUVData *data) - : mMaterial(material) + Value(Ogre::MovableObject* movable, const Nif::NiUVData *data, MaterialControllerManager* materialControllerMgr) + : mMovable(movable) , mUTrans(data->mKeyList[0]) , mVTrans(data->mKeyList[1]) , mUScale(data->mKeyList[2]) , mVScale(data->mKeyList[3]) + , mMaterialControllerMgr(materialControllerMgr) { } virtual Ogre::Real getValue() const @@ -345,12 +388,14 @@ public: virtual void setValue(Ogre::Real value) { - float uTrans = lookupValue(mUTrans, value, 0.0f); - float vTrans = lookupValue(mVTrans, value, 0.0f); - float uScale = lookupValue(mUScale, value, 1.0f); - float vScale = lookupValue(mVScale, value, 1.0f); + float uTrans = interpKey(mUTrans.mKeys, value, 0.0f); + float vTrans = interpKey(mVTrans.mKeys, value, 0.0f); + float uScale = interpKey(mUScale.mKeys, value, 1.0f); + float vScale = interpKey(mVScale.mKeys, value, 1.0f); - Ogre::Material::TechniqueIterator techs = mMaterial->getTechniqueIterator(); + Ogre::MaterialPtr material = mMaterialControllerMgr->getWritableMaterial(mMovable); + + Ogre::Material::TechniqueIterator techs = material->getTechniqueIterator(); while(techs.hasMoreElements()) { Ogre::Technique *tech = techs.getNext(); @@ -402,7 +447,7 @@ public: class GeomMorpherController { public: - class Value : public Ogre::ControllerValue + class Value : public Ogre::ControllerValue, public ValueInterpolator { private: Ogre::SubEntity *mSubEntity; @@ -411,24 +456,6 @@ public: std::vector mVertices; - static float interpKey(const Nif::FloatKeyList::VecType &keys, float time) - { - if(time <= keys.front().mTime) - return keys.front().mValue; - - Nif::FloatKeyList::VecType::const_iterator iter(keys.begin()+1); - for(;iter != keys.end();iter++) - { - if(iter->mTime < time) - continue; - - Nif::FloatKeyList::VecType::const_iterator last(iter-1); - float a = (time-last->mTime) / (iter->mTime-last->mTime); - return last->mValue + ((iter->mValue - last->mValue)*a); - } - return keys.back().mValue; - } - public: Value(Ogre::SubEntity *subent, const Nif::NiMorphData *data) : mSubEntity(subent) @@ -563,11 +590,10 @@ class NIFObjectLoader { const Nif::NiUVController *uv = static_cast(ctrl.getPtr()); - const Ogre::MaterialPtr &material = entity->getSubEntity(0)->getMaterial(); Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : Ogre::ControllerValueRealPtr()); - Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(material, uv->data.getPtr())); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(entity, uv->data.getPtr(), &scene->mMaterialControllerMgr)); UVController::Function* function = OGRE_NEW UVController::Function(uv, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); @@ -592,8 +618,53 @@ class NIFObjectLoader } ctrl = ctrl->next; } + + createMaterialControllers(shape, entity, animflags, scene); } + static void createMaterialControllers (const Nif::Node* node, Ogre::MovableObject* movable, int animflags, ObjectScenePtr scene) + { + const Nif::NiTexturingProperty *texprop = NULL; + const Nif::NiMaterialProperty *matprop = NULL; + const Nif::NiAlphaProperty *alphaprop = NULL; + const Nif::NiVertexColorProperty *vertprop = NULL; + const Nif::NiZBufferProperty *zprop = NULL; + const Nif::NiSpecularProperty *specprop = NULL; + const Nif::NiWireframeProperty *wireprop = NULL; + node->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop); + + if(matprop) + { + Nif::ControllerPtr ctrls = matprop->controller; + while(!ctrls.empty()) + { + Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + + if (ctrls->recType == Nif::RC_NiAlphaController) + { + const Nif::NiAlphaController *alphaCtrl = dynamic_cast(ctrls.getPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW AlphaController::Value(movable, alphaCtrl->data.getPtr(), &scene->mMaterialControllerMgr)); + AlphaController::Function* function = OGRE_NEW AlphaController::Function(alphaCtrl, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); + scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); + Ogre::ControllerFunctionRealPtr func(function); + scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } + else if (ctrls->recType == Nif::RC_NiMaterialColorController) + { + const Nif::NiMaterialColorController *matCtrl = dynamic_cast(ctrls.getPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW MaterialColorController::Value(movable, matCtrl->data.getPtr(), &scene->mMaterialControllerMgr)); + AlphaController::Function* function = OGRE_NEW AlphaController::Function(matCtrl, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); + scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); + Ogre::ControllerFunctionRealPtr func(function); + scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } + + ctrls = ctrls->next; + } + } + } static void createParticleEmitterAffectors(Ogre::ParticleSystem *partsys, const Nif::NiParticleSystemController *partctrl, Ogre::Bone* bone, @@ -670,7 +741,7 @@ class NIFObjectLoader static void createParticleSystem(const std::string &name, const std::string &group, Ogre::SceneNode *sceneNode, ObjectScenePtr scene, - const Nif::Node *partnode, int flags, int partflags) + const Nif::Node *partnode, int flags, int partflags, int animflags) { const Nif::NiAutoNormalParticlesData *particledata = NULL; if(partnode->recType == Nif::RC_NiAutoNormalParticles) @@ -739,6 +810,8 @@ class NIFObjectLoader partsys->setVisible(!(flags&Nif::NiNode::Flag_Hidden)); scene->mParticles.push_back(partsys); + + createMaterialControllers(partnode, partsys, animflags, scene); } @@ -886,7 +959,7 @@ class NIFObjectLoader if((node->recType == Nif::RC_NiAutoNormalParticles || node->recType == Nif::RC_NiRotatingParticles) && !(flags&0x40000000)) { - createParticleSystem(name, group, sceneNode, scene, node, flags, partflags); + createParticleSystem(name, group, sceneNode, scene, node, flags, partflags, animflags); } const Nif::NiNode *ninode = dynamic_cast(node); diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index 94d4aa674..0fbc5b10e 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -37,6 +37,19 @@ namespace NifOgre { +/** + * @brief Clones materials as necessary to not make controllers affect other objects (that share the original material). + */ +class MaterialControllerManager +{ +public: + ~MaterialControllerManager(); + Ogre::MaterialPtr getWritableMaterial (Ogre::MovableObject* movable); + +private: + std::map mClonedMaterials; +}; + typedef std::multimap TextKeyMap; static const char sTextKeyExtraDataID[] = "TextKeyExtraData"; struct ObjectScene { @@ -52,6 +65,8 @@ struct ObjectScene { std::map mTextKeys; + MaterialControllerManager mMaterialControllerMgr; + std::vector > mControllers; ObjectScene(Ogre::SceneManager* sceneMgr) : mSkelBase(0), mMaxControllerLength(0), mSceneMgr(sceneMgr) From 9fcb4fad5c53890f79a9b158993f8821f6a193d1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 8 Dec 2013 18:51:56 +0100 Subject: [PATCH 120/889] Implement NiFlipController --- components/nif/controller.hpp | 2 +- components/nifogre/material.hpp | 2 +- components/nifogre/ogrenifloader.cpp | 97 ++++++++++++++++++++++++++-- 3 files changed, 95 insertions(+), 6 deletions(-) diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index 011e0e445..e44f4a6f3 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -306,7 +306,7 @@ public: class NiFlipController : public Controller { public: - int mTexSlot; + int mTexSlot; // NiTexturingProperty::TextureType float mDelta; // Time between two flips. delta = (start_time - stop_time) / num_sources NiSourceTextureList mSources; diff --git a/components/nifogre/material.hpp b/components/nifogre/material.hpp index 8843ac6c6..b02c7c236 100644 --- a/components/nifogre/material.hpp +++ b/components/nifogre/material.hpp @@ -37,9 +37,9 @@ class NIFMaterialLoader { static std::map sMaterialMap; +public: static std::string findTextureName(const std::string &filename); -public: static Ogre::String getMaterial(const Nif::ShapeData *shapedata, const Ogre::String &name, const Ogre::String &group, const Nif::NiTexturingProperty *texprop, diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index b71d4c4ee..4819be4ca 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -109,6 +109,74 @@ ObjectScene::~ObjectScene() mSkelBase = NULL; } +// Animates a texture +class FlipController +{ +public: + class Value : public Ogre::ControllerValue + { + private: + Ogre::MovableObject* mMovable; + int mTexSlot; + float mDelta; + std::vector mTextures; + MaterialControllerManager* mMaterialControllerMgr; + + public: + Value(Ogre::MovableObject *movable, const Nif::NiFlipController *ctrl, MaterialControllerManager* materialControllerMgr) + : mMovable(movable) + , mMaterialControllerMgr(materialControllerMgr) + { + mTexSlot = ctrl->mTexSlot; + mDelta = ctrl->mDelta; + for (unsigned int i=0; imSources.length(); ++i) + { + const Nif::NiSourceTexture* tex = ctrl->mSources[i].getPtr(); + if (!tex->external) + std::cerr << "Warning: Found internal texture, ignoring." << std::endl; + mTextures.push_back(NIFMaterialLoader::findTextureName(tex->filename)); + } + } + + virtual Ogre::Real getValue() const + { + // Should not be called + return 0.0f; + } + + virtual void setValue(Ogre::Real time) + { + if (mDelta == 0) + return; + int curTexture = int(time / mDelta) % mTextures.size(); + + Ogre::MaterialPtr mat = mMaterialControllerMgr->getWritableMaterial(mMovable); + Ogre::Material::TechniqueIterator techs = mat->getTechniqueIterator(); + while(techs.hasMoreElements()) + { + Ogre::Technique *tech = techs.getNext(); + Ogre::Technique::PassIterator passes = tech->getPassIterator(); + while(passes.hasMoreElements()) + { + Ogre::Pass *pass = passes.getNext(); + Ogre::Pass::TextureUnitStateIterator textures = pass->getTextureUnitStateIterator(); + while (textures.hasMoreElements()) + { + Ogre::TextureUnitState *texture = textures.getNext(); + if ((texture->getName() == "diffuseMap" && mTexSlot == Nif::NiTexturingProperty::BaseTexture) + || (texture->getName() == "normalMap" && mTexSlot == Nif::NiTexturingProperty::BumpTexture) + || (texture->getName() == "detailMap" && mTexSlot == Nif::NiTexturingProperty::DetailTexture) + || (texture->getName() == "emissiveMap" && mTexSlot == Nif::NiTexturingProperty::GlowTexture)) + texture->setTextureName(mTextures[curTexture]); + } + } + } + } + }; + + typedef DefaultFunction Function; +}; + class AlphaController { public: @@ -633,15 +701,15 @@ class NIFObjectLoader const Nif::NiWireframeProperty *wireprop = NULL; node->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop); + Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + if(matprop) { Nif::ControllerPtr ctrls = matprop->controller; while(!ctrls.empty()) { - Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? - Ogre::ControllerManager::getSingleton().getFrameTimeSource() : - Ogre::ControllerValueRealPtr()); - if (ctrls->recType == Nif::RC_NiAlphaController) { const Nif::NiAlphaController *alphaCtrl = dynamic_cast(ctrls.getPtr()); @@ -661,6 +729,27 @@ class NIFObjectLoader scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } + ctrls = ctrls->next; + } + } + if (texprop) + { + Nif::ControllerPtr ctrls = texprop->controller; + while(!ctrls.empty()) + { + if (ctrls->recType == Nif::RC_NiFlipController) + { + const Nif::NiFlipController *flipCtrl = dynamic_cast(ctrls.getPtr()); + + + Ogre::ControllerValueRealPtr dstval(OGRE_NEW FlipController::Value( + movable, flipCtrl, &scene->mMaterialControllerMgr)); + FlipController::Function* function = OGRE_NEW FlipController::Function(flipCtrl, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); + scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); + Ogre::ControllerFunctionRealPtr func(function); + scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } + ctrls = ctrls->next; } } From 594cc693b27f5c4a6621ce7d8f63a9c5b27edcab Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 8 Dec 2013 21:47:43 +0100 Subject: [PATCH 121/889] Fixes #1006: Many NPCs have 0 skill Added calculation of skill values for NPC with mNpdtType set to NPC_WITH_AUTOCALCULATED_STATS (their NPDT is 12). Signed-off-by: Lukasz Gromanowski --- apps/esmtool/record.cpp | 5 +- apps/openmw/mwclass/npc.cpp | 93 ++++++++++++++++++++++++++-- apps/openmw/mwmechanics/npcstats.cpp | 4 +- components/esm/loadnpc.cpp | 12 ++-- components/esm/loadnpc.hpp | 9 ++- 5 files changed, 106 insertions(+), 17 deletions(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index e9fb1c537..cc09452c9 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -989,8 +989,7 @@ void Record::print() std::cout << " Faction: " << mData.mFaction << std::endl; std::cout << " Flags: " << npcFlags(mData.mFlags) << std::endl; - // Seriously? - if (mData.mNpdt52.mGold == -10) + 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; @@ -1022,7 +1021,7 @@ void Record::print() std::cout << " Luck: " << (int)mData.mNpdt52.mLuck << std::endl; std::cout << " Skills:" << std::endl; - for (int i = 0; i != 27; i++) + for (int i = 0; i != ESM::Skill::Length; i++) std::cout << " " << skillLabel(i) << ": " << (int)((unsigned char)mData.mNpdt52.mSkills[i]) << std::endl; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 8ff8081bc..e7c10d3c8 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -63,7 +63,6 @@ namespace bool male = (npc->mFlags & ESM::NPC::Female) == 0; int level = creatureStats.getLevel(); - for (int i=0; imData.mAttributeValues[i]; @@ -85,7 +84,7 @@ namespace } // skill bonus - for (int attribute=0; attribute (0.5 * (strength + endurance)) + multiplier * (creatureStats.getLevel() - 1)); } + + /** + * @brief autoCalculateSkills + * + * Skills are calculated with following formulae ( http://www.uesp.net/wiki/Morrowind:NPCs#Skills ): + * + * Skills: (Level - 1) × (Majority Multiplier + Specialization Multiplier) + * + * The Majority Multiplier is 1.0 for a Major or Minor Skill, or 0.1 for a Miscellaneous Skill. + * + * The Specialization Multiplier is 0.5 for a Skill in the same Specialization as the class, + * zero for other Skills. + * + * and by adding class, race, specialization bonus. + */ + void autoCalculateSkills(const ESM::NPC* npc, MWMechanics::NpcStats& npcStats) + { + const ESM::Class *class_ = + MWBase::Environment::get().getWorld()->getStore().get().find(npc->mClass); + + unsigned int level = npcStats.getLevel(); + + const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore().get().find(npc->mRace); + + + for (int i = 0; i < 2; ++i) + { + int bonus = (i==0) ? 10 : 25; + + for (int i2 = 0; i2 < 5; ++i2) + { + int index = class_->mData.mSkills[i2][i]; + if (index >= 0 && index < ESM::Skill::Length) + { + npcStats.getSkill(index).setBase (npcStats.getSkill(index).getBase() + bonus); + } + } + } + + for (int skillIndex = 0; skillIndex < ESM::Skill::Length; ++skillIndex) + { + float majorMultiplier = 0.1f; + float specMultiplier = 0.0f; + + int raceBonus = 0; + int specBonus = 0; + + for (int raceSkillIndex = 0; raceSkillIndex < 7; ++raceSkillIndex) + { + if (race->mData.mBonus[raceSkillIndex].mSkill == skillIndex) + { + raceBonus = race->mData.mBonus[raceSkillIndex].mBonus; + break; + } + } + + for (int k = 0; k < 5; ++k) + { + // is this a minor or major skill? + if ((class_->mData.mSkills[k][0] == skillIndex) || (class_->mData.mSkills[k][1] == skillIndex)) + { + majorMultiplier = 1.0f; + break; + } + } + + // is this skill in the same Specialization as the class? + const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().get().find(skillIndex); + if (skill->mData.mSpecialization == class_->mData.mSpecialization) + { + specMultiplier = 0.5f; + specBonus = 5; + } + + npcStats.getSkill(skillIndex).setBase( + std::min( + npcStats.getSkill(skillIndex).getBase() + + 5 + + raceBonus + + specBonus + + static_cast((level-1) * (majorMultiplier + specMultiplier)), 100.0f)); + } + } } namespace MWClass @@ -173,7 +255,7 @@ namespace MWClass { std::string faction = ref->mBase->mFaction; Misc::StringUtils::toLower(faction); - if(ref->mBase->mNpdt52.mGold != -10) + if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) { data->mNpcStats.getFactionRanks()[faction] = (int)ref->mBase->mNpdt52.mRank; } @@ -185,11 +267,11 @@ namespace MWClass // creature stats int gold=0; - if(ref->mBase->mNpdt52.mGold != -10) + if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) { gold = ref->mBase->mNpdt52.mGold; - for (int i=0; i<27; ++i) + for (unsigned int i=0; i< ESM::Skill::Length; ++i) data->mNpcStats.getSkill (i).setBase (ref->mBase->mNpdt52.mSkills[i]); data->mNpcStats.getAttribute(0).set (ref->mBase->mNpdt52.mStrength); @@ -220,6 +302,7 @@ namespace MWClass data->mNpcStats.setReputation(ref->mBase->mNpdt12.mReputation); autoCalculateAttributes(ref->mBase, data->mNpcStats); + autoCalculateSkills(ref->mBase, data->mNpcStats); } data->mNpcStats.getAiSequence().fill(ref->mBase->mAiPackage); diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 0b3698289..1fdefc84f 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -86,7 +86,7 @@ void MWMechanics::NpcStats::setMovementFlag (Flag flag, bool state) const MWMechanics::Stat& MWMechanics::NpcStats::getSkill (int index) const { - if (index<0 || index>=27) + if (index<0 || index>=ESM::Skill::Length) throw std::runtime_error ("skill index out of range"); return (!mIsWerewolf ? mSkill[index] : mWerewolfSkill[index]); @@ -94,7 +94,7 @@ const MWMechanics::Stat& MWMechanics::NpcStats::getSkill (int index) cons MWMechanics::Stat& MWMechanics::NpcStats::getSkill (int index) { - if (index<0 || index>=27) + if (index<0 || index>=ESM::Skill::Length) throw std::runtime_error ("skill index out of range"); return (!mIsWerewolf ? mSkill[index] : mWerewolfSkill[index]); diff --git a/components/esm/loadnpc.cpp b/components/esm/loadnpc.cpp index 9fff2d885..e5b851bf0 100644 --- a/components/esm/loadnpc.cpp +++ b/components/esm/loadnpc.cpp @@ -10,7 +10,7 @@ namespace ESM void NPC::load(ESMReader &esm) { - mNpdt52.mGold = -10; + //mNpdt52.mGold = -10; mPersistent = esm.getRecordFlags() & 0x0400; @@ -29,12 +29,12 @@ void NPC::load(ESMReader &esm) esm.getSubHeader(); if (esm.getSubSize() == 52) { - mNpdtType = 52; + mNpdtType = NPC_DEFAULT; esm.getExact(&mNpdt52, 52); } else if (esm.getSubSize() == 12) { - mNpdtType = 12; + mNpdtType = NPC_WITH_AUTOCALCULATED_STATS; esm.getExact(&mNpdt12, 12); } else @@ -76,9 +76,9 @@ void NPC::save(ESMWriter &esm) const esm.writeHNCString("KNAM", mHair); esm.writeHNOCString("SCRI", mScript); - if (mNpdtType == 52) + if (mNpdtType == NPC_DEFAULT) esm.writeHNT("NPDT", mNpdt52, 52); - else if (mNpdtType == 12) + else if (mNpdtType == NPC_WITH_AUTOCALCULATED_STATS) esm.writeHNT("NPDT", mNpdt12, 12); esm.writeHNT("FLAG", mFlags); @@ -114,7 +114,7 @@ void NPC::save(ESMWriter &esm) const mNpdt52.mLevel = 0; mNpdt52.mStrength = mNpdt52.mIntelligence = mNpdt52.mWillpower = mNpdt52.mAgility = mNpdt52.mSpeed = mNpdt52.mEndurance = mNpdt52.mPersonality = mNpdt52.mLuck = 0; - for (int i=0; i<27; ++i) mNpdt52.mSkills[i] = 0; + for (int i=0; i< Skill::Length; ++i) mNpdt52.mSkills[i] = 0; mNpdt52.mReputation = 0; mNpdt52.mHealth = mNpdt52.mMana = mNpdt52.mFatigue = 0; mNpdt52.mDisposition = 0; diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index d9e691669..1eac8d64f 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -8,6 +8,7 @@ #include "loadcont.hpp" #include "aipackage.hpp" #include "spelllist.hpp" +#include "loadskil.hpp" namespace ESM { @@ -58,6 +59,12 @@ struct NPC Metal = 0x0800 // Metal blood effect (golden?) }; + enum NpcType + { + NPC_WITH_AUTOCALCULATED_STATS = 12, + NPC_DEFAULT = 52 + }; + #pragma pack(push) #pragma pack(1) @@ -73,7 +80,7 @@ struct NPC mPersonality, mLuck; - char mSkills[27]; + char mSkills[Skill::Length]; char mReputation; short mHealth, mMana, mFatigue; char mDisposition, mFactionID, mRank; From 37a7ee8fcdd3adfc4406281ff9c2e83f3c7289b2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 8 Dec 2013 23:05:21 +0100 Subject: [PATCH 122/889] Set alpha value of character animations according to Invisibility / Chameleon effects. --- apps/openmw/mwmechanics/character.cpp | 23 +++++++++ apps/openmw/mwmechanics/character.hpp | 2 + apps/openmw/mwrender/animation.hpp | 2 + apps/openmw/mwrender/npcanimation.cpp | 59 ++++++++++++++++++++++- apps/openmw/mwrender/npcanimation.hpp | 7 +++ apps/openmw/mwrender/renderingmanager.cpp | 48 +++++++++--------- apps/openmw/mwrender/renderingmanager.hpp | 4 +- components/nifogre/ogrenifloader.cpp | 11 +++-- files/materials/objects.shader | 8 --- 9 files changed, 125 insertions(+), 39 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 7505d3405..b70fcd0cc 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -727,6 +727,8 @@ void CharacterController::update(float duration) const MWWorld::Class &cls = MWWorld::Class::get(mPtr); Ogre::Vector3 movement(0.0f); + updateVisibility(); + if(!cls.isActor()) { if(mAnimQueue.size() > 1) @@ -1130,4 +1132,25 @@ void CharacterController::updateContinuousVfx() } } +void CharacterController::updateVisibility() +{ + if (!mPtr.getClass().isActor()) + return; + float alpha = 1.f; + if (mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Invisibility).mMagnitude) + { + if (mPtr.getRefData().getHandle() == "player") + alpha = 0.4f; + else + alpha = 0.f; + } + float chameleon = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Chameleon).mMagnitude; + if (chameleon) + { + alpha *= std::max(0.2f, (100.f - chameleon)/100.f); + } + + mAnimation->setAlpha(alpha); +} + } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 0b55534a6..9e07fca7d 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -173,6 +173,8 @@ class CharacterController bool updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak); + void updateVisibility(); + public: CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim); virtual ~CharacterController(); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index b11b2b0a5..aa04e39e2 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -215,6 +215,8 @@ public: void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = "", std::string texture = ""); void removeEffect (int effectId); void getLoopingEffects (std::vector& out); + + virtual void setAlpha(float alpha) {} private: void updateEffects(float duration); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index a57816f34..eb0c5dfbc 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -5,6 +5,8 @@ #include #include +#include + #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/class.hpp" @@ -116,7 +118,8 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v mViewMode(viewMode), mShowWeapons(false), mShowShield(true), - mFirstPersonOffset(0.f, 0.f, 0.f) + mFirstPersonOffset(0.f, 0.f, 0.f), + mAlpha(1.f) { mNpc = mPtr.get()->mBase; @@ -219,6 +222,7 @@ void NpcAnimation::updateNpcBase() void NpcAnimation::updateParts() { + mAlpha = 1.f; const MWWorld::Class &cls = MWWorld::Class::get(mPtr); MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); @@ -647,6 +651,7 @@ void NpcAnimation::showWeapons(bool showWeapon) { removeIndividualPart(ESM::PRT_Weapon); } + mAlpha = 1.f; } void NpcAnimation::showShield(bool show) @@ -705,4 +710,56 @@ void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, boo } } +void NpcAnimation::setAlpha(float alpha) +{ + if (alpha == mAlpha) + return; + mAlpha = alpha; + + for (int i=0; imEntities.size(); ++j) + { + Ogre::Entity* ent = mObjectParts[i]->mEntities[j]; + if (ent != mObjectParts[i]->mSkelBase) + applyAlpha(alpha, ent, mObjectParts[i]); + } + } +} + +void NpcAnimation::applyAlpha(float alpha, Ogre::Entity *ent, NifOgre::ObjectScenePtr scene) +{ + ent->getSubEntity(0)->setRenderQueueGroup(alpha != 1.f || ent->getSubEntity(0)->getMaterial()->isTransparent() + ? RQG_Alpha : RQG_Main); + + + Ogre::MaterialPtr mat = scene->mMaterialControllerMgr.getWritableMaterial(ent); + if (mAlpha == 1.f) + { + // Don't bother remembering what the original values were. Just remove the techniques and let the factory restore them. + mat->removeAllTechniques(); + sh::Factory::getInstance()._ensureMaterial(mat->getName(), "Default"); + return; + } + + Ogre::Material::TechniqueIterator techs = mat->getTechniqueIterator(); + while(techs.hasMoreElements()) + { + Ogre::Technique *tech = techs.getNext(); + Ogre::Technique::PassIterator passes = tech->getPassIterator(); + while(passes.hasMoreElements()) + { + Ogre::Pass *pass = passes.getNext(); + pass->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA); + Ogre::ColourValue diffuse = pass->getDiffuse(); + diffuse.a = alpha; + pass->setDiffuse(diffuse); + pass->setVertexColourTracking(pass->getVertexColourTracking() &~Ogre::TVC_DIFFUSE); + } + } +} + } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 962663268..04dde87c7 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -64,6 +64,8 @@ private: Ogre::SharedPtr mSayAnimationValue; + float mAlpha; + void updateNpcBase(); NifOgre::ObjectScenePtr insertBoundedPart(const std::string &model, int group, const std::string &bonename, @@ -78,6 +80,8 @@ private: void addPartGroup(int group, int priority, const std::vector &parts, bool enchantedGlow=false, Ogre::Vector3* glowColor=NULL); + void applyAlpha(float alpha, Ogre::Entity* ent, NifOgre::ObjectScenePtr scene); + public: /** * @param ptr @@ -109,6 +113,9 @@ public: /// Rebuilds the NPC, updating their root model, animation sources, and equipment. void rebuild(); + + /// Make the NPC only partially visible + virtual void setAlpha(float alpha); }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 26e88fb0d..b216c789f 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -60,14 +60,14 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b MWWorld::Fallback* fallback) : mRendering(_rend) , mFallback(fallback) - , mObjects(mRendering) - , mActors(mRendering, this) , mPlayerAnimation(NULL) , mAmbientMode(0) , mSunEnabled(0) , mPhysicsEngine(engine) , mTerrain(NULL) { + mActors = new MWRender::Actors(mRendering, this); + mObjects = new MWRender::Objects(mRendering); // select best shader mode bool openGL = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL") != std::string::npos); bool glES = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL ES") != std::string::npos); @@ -162,8 +162,8 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b mRootNode = mRendering.getScene()->getRootSceneNode(); mRootNode->createChildSceneNode("player"); - mObjects.setRootNode(mRootNode); - mActors.setRootNode(mRootNode); + mObjects->setRootNode(mRootNode); + mActors->setRootNode(mRootNode); mCamera = new MWRender::Camera(mRendering.getCamera()); @@ -201,6 +201,8 @@ RenderingManager::~RenderingManager () delete mCompositors; delete mWater; delete mVideoPlayer; + delete mActors; + delete mObjects; delete mFactory; } @@ -210,10 +212,10 @@ MWRender::SkyManager* RenderingManager::getSkyManager() } MWRender::Objects& RenderingManager::getObjects(){ - return mObjects; + return *mObjects; } MWRender::Actors& RenderingManager::getActors(){ - return mActors; + return *mActors; } OEngine::Render::Fader* RenderingManager::getFader() @@ -223,8 +225,8 @@ OEngine::Render::Fader* RenderingManager::getFader() void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store) { - mObjects.removeCell(store); - mActors.removeCell(store); + mObjects->removeCell(store); + mActors->removeCell(store); mDebugging->cellRemoved(store); } @@ -240,7 +242,7 @@ void RenderingManager::toggleWater() void RenderingManager::cellAdded (MWWorld::Ptr::CellStore *store) { - mObjects.buildStaticGeometry (*store); + mObjects->buildStaticGeometry (*store); sh::Factory::getInstance().unloadUnreferencedMaterials(); mDebugging->cellAdded(store); waterAdded(store); @@ -254,8 +256,8 @@ void RenderingManager::addObject (const MWWorld::Ptr& ptr){ void RenderingManager::removeObject (const MWWorld::Ptr& ptr) { - if (!mObjects.deleteObject (ptr)) - mActors.deleteObject (ptr); + if (!mObjects->deleteObject (ptr)) + mActors->deleteObject (ptr); } void RenderingManager::moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position) @@ -295,9 +297,9 @@ RenderingManager::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr & parent->removeChild(child); if (MWWorld::Class::get(old).isActor()) { - mActors.updateObjectCell(old, cur); + mActors->updateObjectCell(old, cur); } else { - mObjects.updateObjectCell(old, cur); + mObjects->updateObjectCell(old, cur); } } @@ -315,7 +317,7 @@ void RenderingManager::rebuildPtr(const MWWorld::Ptr &ptr) if(ptr.getRefData().getHandle() == "player") anim = mPlayerAnimation; else if(MWWorld::Class::get(ptr).isActor()) - anim = dynamic_cast(mActors.getAnimation(ptr)); + anim = dynamic_cast(mActors->getAnimation(ptr)); if(anim) { anim->rebuild(); @@ -380,8 +382,8 @@ void RenderingManager::update (float duration, bool paused) if(paused) return; - mActors.update (duration); - mObjects.update (duration); + mActors->update (duration); + mObjects->update (duration); mSkyManager->update(duration); @@ -657,7 +659,7 @@ void RenderingManager::requestMap(MWWorld::Ptr::CellStore* cell) { assert(mTerrain); - Ogre::AxisAlignedBox dims = mObjects.getDimensions(cell); + Ogre::AxisAlignedBox dims = mObjects->getDimensions(cell); Ogre::Vector2 center(cell->mCell->getGridX() + 0.5, cell->mCell->getGridY() + 0.5); dims.merge(mTerrain->getWorldBoundingBox(center)); @@ -667,7 +669,7 @@ void RenderingManager::requestMap(MWWorld::Ptr::CellStore* cell) mLocalMap->requestMap(cell, dims.getMinimum().z, dims.getMaximum().z); } else - mLocalMap->requestMap(cell, mObjects.getDimensions(cell)); + mLocalMap->requestMap(cell, mObjects->getDimensions(cell)); } void RenderingManager::preCellChange(MWWorld::Ptr::CellStore* cell) @@ -677,13 +679,13 @@ void RenderingManager::preCellChange(MWWorld::Ptr::CellStore* cell) void RenderingManager::disableLights(bool sun) { - mObjects.disableLights(); + mObjects->disableLights(); sunDisable(sun); } void RenderingManager::enableLights(bool sun) { - mObjects.enableLights(); + mObjects->enableLights(); sunEnable(sun); } @@ -859,7 +861,7 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec if (rebuild) { - mObjects.rebuildStaticGeometry(); + mObjects->rebuildStaticGeometry(); if (mTerrain) mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"), Settings::Manager::getBool("split", "Shadows")); @@ -976,13 +978,13 @@ void RenderingManager::setupExternalRendering (MWRender::ExternalRendering& rend Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr) { - Animation *anim = mActors.getAnimation(ptr); + Animation *anim = mActors->getAnimation(ptr); if(!anim && ptr.getRefData().getHandle() == "player") anim = mPlayerAnimation; if (!anim) - anim = mObjects.getAnimation(ptr); + anim = mObjects->getAnimation(ptr); return anim; } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 2d0813912..e5dcf0aeb 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -242,8 +242,8 @@ private: OEngine::Render::OgreRenderer &mRendering; - MWRender::Objects mObjects; - MWRender::Actors mActors; + MWRender::Objects* mObjects; + MWRender::Actors* mActors; MWRender::NpcAnimation *mPlayerAnimation; diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 4819be4ca..e6c535b9b 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -63,11 +63,12 @@ Ogre::MaterialPtr MaterialControllerManager::getWritableMaterial(Ogre::MovableOb else if (Ogre::ParticleSystem* partSys = dynamic_cast(movable)) mat = Ogre::MaterialManager::getSingleton().getByName(partSys->getMaterialName()); - // Make sure techniques are created - sh::Factory::getInstance()._ensureMaterial(mat->getName(), "Default"); - static int count=0; - mat = mat->clone(mat->getName() + Ogre::StringConverter::toString(count++)); + Ogre::String newName = mat->getName() + Ogre::StringConverter::toString(count++); + sh::Factory::getInstance().createMaterialInstance(newName, mat->getName()); + // Make sure techniques are created + sh::Factory::getInstance()._ensureMaterial(newName, "Default"); + mat = Ogre::MaterialManager::getSingleton().getByName(newName); mClonedMaterials[movable] = mat; @@ -84,7 +85,7 @@ MaterialControllerManager::~MaterialControllerManager() { for (std::map::iterator it = mClonedMaterials.begin(); it != mClonedMaterials.end(); ++it) { - Ogre::MaterialManager::getSingleton().remove(it->second->getName()); + sh::Factory::getInstance().destroyMaterialInstance(it->second->getName()); } } diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 5a3d872a5..3d873f463 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -98,9 +98,7 @@ #if VERTEXCOLOR_MODE != 2 shUniform(float4, materialAmbient) @shAutoConstant(materialAmbient, surface_ambient_colour) #endif -#if VERTEXCOLOR_MODE != 2 shUniform(float4, materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) -#endif #if VERTEXCOLOR_MODE != 1 shUniform(float4, materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) #endif @@ -234,9 +232,7 @@ lightResult.xyz += lightAmbient.xyz * materialAmbient.xyz + materialEmissive.xyz; #endif -#if VERTEXCOLOR_MODE != 2 lightResult.a *= materialDiffuse.a; -#endif #endif } @@ -339,9 +335,7 @@ #if VERTEXCOLOR_MODE != 2 shUniform(float4, materialAmbient) @shAutoConstant(materialAmbient, surface_ambient_colour) #endif - #if VERTEXCOLOR_MODE != 2 shUniform(float4, materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) - #endif #if VERTEXCOLOR_MODE != 1 shUniform(float4, materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) #endif @@ -434,9 +428,7 @@ lightResult.xyz += lightAmbient.xyz * materialAmbient.xyz + materialEmissive.xyz; #endif -#if VERTEXCOLOR_MODE != 2 lightResult.a *= materialDiffuse.a; -#endif #endif // shadows only for the first (directional) light From 5fd2df5546f6f33b1db5744c5946871e94a61c51 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 8 Dec 2013 23:21:23 +0100 Subject: [PATCH 123/889] Ignore invisible targets for combat AI --- apps/openmw/mwworld/worldimp.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 87a8d7d6f..2f453f398 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1845,6 +1845,13 @@ namespace MWWorld bool World::getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc) { + // This is a placeholder! Needs to go into an NPC awareness check function (see + // https://wiki.openmw.org/index.php?title=Research:NPC_AI_Behaviour#NPC_Awareness_Check ) + if (targetNpc.getClass().getCreatureStats(targetNpc).getMagicEffects().get(ESM::MagicEffect::Invisibility).mMagnitude) + return false; + if (targetNpc.getClass().getCreatureStats(targetNpc).getMagicEffects().get(ESM::MagicEffect::Chameleon).mMagnitude > 100) + return false; + Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(npc.getRefData().getHandle())->getHalfExtents(); float* pos1 = npc.getRefData().getPosition().pos; Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetNpc.getRefData().getHandle())->getHalfExtents(); From 0bc3a13c0f8f8f3242c5a7b7a1cf23bf5235b48b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 8 Dec 2013 23:36:37 +0100 Subject: [PATCH 124/889] Break invisibility on Use or Activate --- apps/openmw/engine.cpp | 2 ++ apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwgui/statswindow.cpp | 2 +- apps/openmw/mwmechanics/character.cpp | 1 + apps/openmw/mwworld/inventorystore.cpp | 5 +++++ apps/openmw/mwworld/inventorystore.hpp | 5 ++++- apps/openmw/mwworld/worldimp.cpp | 6 ++++++ apps/openmw/mwworld/worldimp.hpp | 2 ++ 8 files changed, 23 insertions(+), 2 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 02e7a5955..f2afb3ba5 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -517,6 +517,8 @@ void OMW::Engine::activate() std::string script = MWWorld::Class::get (ptr).getScript (ptr); + MWBase::Environment::get().getWorld()->breakInvisibility(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + if (!script.empty()) { MWBase::Environment::get().getWorld()->getLocalScripts().setIgnore (ptr); diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index c092840dd..8141af712 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -419,6 +419,8 @@ namespace MWBase virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects, const MWWorld::Ptr& actor, const std::string& sourceName) = 0; + + virtual void breakInvisibility (const MWWorld::Ptr& actor) = 0; }; } diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 9facdac40..d89abb448 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -149,7 +149,7 @@ namespace MWGui // health, magicka, fatigue tooltip MyGUI::Widget* w; - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); + std::string valStr = boost::lexical_cast(int(value.getCurrent())) + "/" + boost::lexical_cast(int(value.getModified())); if (i==0) { getWidget(w, "Health"); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index b70fcd0cc..d7215ed13 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -501,6 +501,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun { if(mUpperBodyState == UpperCharState_WeapEquiped) { + MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); mAttackType.clear(); if(mWeaponType == WeapType_Spell) { diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 69e06378a..57e35adce 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -615,3 +615,8 @@ void MWWorld::InventoryStore::rechargeItems(float duration) it->second); } } + +void MWWorld::InventoryStore::purgeEffect(short effectId) +{ + mMagicEffects.add(MWMechanics::EffectKey(effectId), -mMagicEffects.get(MWMechanics::EffectKey(effectId)).mMagnitude); +} diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 58ff50ead..e764f64fb 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -179,7 +179,10 @@ namespace MWWorld void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor); void rechargeItems (float duration); - /// Restore charge on enchanted items. Note this should only be done for the player. + ///< Restore charge on enchanted items. Note this should only be done for the player. + + void purgeEffect (short effectId); + ///< Remove a magic effect }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2f453f398..0bf41c732 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2238,4 +2238,10 @@ namespace MWWorld deleteObject(movedPtr); } } + + void World::breakInvisibility(const Ptr &actor) + { + actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Invisibility); + actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 80119e014..c8133441d 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -504,6 +504,8 @@ namespace MWWorld virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects, const MWWorld::Ptr& actor, const std::string& sourceName); + + virtual void breakInvisibility (const MWWorld::Ptr& actor); }; } From 57a33c957ea7575e6d8a27b11a1a9a2dbe3c9656 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 9 Dec 2013 14:26:08 +0100 Subject: [PATCH 125/889] Add possibly missing include --- components/nifogre/ogrenifloader.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index 0fbc5b10e..976a31ccd 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -25,6 +25,7 @@ #define OPENMW_COMPONENTS_NIFOGRE_OGRENIFLOADER_HPP #include +#include #include #include From fc8bd1aacb9ae79f89fe37a7a1c7e409b7d6f3e5 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Mon, 9 Dec 2013 21:13:06 +0100 Subject: [PATCH 126/889] Allow fatigue stat to become negative when fatigue damages are taken --- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwmechanics/stat.hpp | 22 +++++++++++++++++----- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 983480782..2e7f00dd3 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -199,7 +199,7 @@ namespace MWClass else { MWMechanics::DynamicStat fatigue(getCreatureStats(ptr).getFatigue()); - fatigue.setCurrent(fatigue.getCurrent() - damage); + fatigue.setCurrent(fatigue.getCurrent() - damage, true); getCreatureStats(ptr).setFatigue(fatigue); } } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index e7c10d3c8..5f9b2de47 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -669,7 +669,7 @@ namespace MWClass else { MWMechanics::DynamicStat fatigue(getCreatureStats(ptr).getFatigue()); - fatigue.setCurrent(fatigue.getCurrent() - damage); + fatigue.setCurrent(fatigue.getCurrent() - damage, true); getCreatureStats(ptr).setFatigue(fatigue); } } diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index 65d47c9c0..cb6c09014 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -162,14 +162,26 @@ namespace MWMechanics setCurrent (getCurrent()+diff); } - void setCurrent (const T& value) + void setCurrent (const T& value, bool allowDecreaseBelowZero = false) { - mCurrent = value; + if (value > mCurrent) + { + // increase + mCurrent = value; - if (mCurrent<0) + if (mCurrent > getModified()) + mCurrent = getModified(); + } + else if (value > 0 || allowDecreaseBelowZero) + { + // allowed decrease + mCurrent = value; + } + else if (mCurrent > 0) + { + // capped decrease mCurrent = 0; - else if (mCurrent>getModified()) - mCurrent = getModified(); + } } void setModifier (const T& modifier) From 357ecd92b2c387dd7f7f93c91b0ff185df947cb3 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Tue, 10 Dec 2013 00:41:36 +0100 Subject: [PATCH 127/889] Do not display negative stat values Display zero instead of negative values. Also remove useless for loops and some unused attributes. --- apps/openmw/mwgui/hud.cpp | 56 ++++++++++++-------------- apps/openmw/mwgui/statswindow.cpp | 50 +++++++++-------------- apps/openmw/mwgui/windowmanagerimp.cpp | 27 ------------- apps/openmw/mwgui/windowmanagerimp.hpp | 2 - 4 files changed, 45 insertions(+), 90 deletions(-) diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index f0843834d..a78b1a6d1 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -174,38 +174,32 @@ namespace MWGui void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat& value) { - static const char *ids[] = - { - "HBar", "MBar", "FBar", 0 - }; + int current = std::max(0, static_cast(value.getCurrent())); + int modified = static_cast(value.getModified()); - for (int i=0; ids[i]; ++i) - if (ids[i]==id) - { - MyGUI::Widget* w; - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); - switch (i) - { - case 0: - mHealth->setProgressRange (value.getModified()); - mHealth->setProgressPosition (value.getCurrent()); - getWidget(w, "HealthFrame"); - w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); - break; - case 1: - mMagicka->setProgressRange (value.getModified()); - mMagicka->setProgressPosition (value.getCurrent()); - getWidget(w, "MagickaFrame"); - w->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); - break; - case 2: - mStamina->setProgressRange (value.getModified()); - mStamina->setProgressPosition (value.getCurrent()); - getWidget(w, "FatigueFrame"); - w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); - break; - } - } + MyGUI::Widget* w; + std::string valStr = boost::lexical_cast(current) + "/" + boost::lexical_cast(modified); + if (id == "HBar") + { + mHealth->setProgressRange(modified); + mHealth->setProgressPosition(current); + getWidget(w, "HealthFrame"); + w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); + } + else if (id == "MBar") + { + mMagicka->setProgressRange (modified); + mMagicka->setProgressPosition (current); + getWidget(w, "MagickaFrame"); + w->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); + } + else if (id == "FBar") + { + mStamina->setProgressRange (modified); + mStamina->setProgressPosition (current); + getWidget(w, "FatigueFrame"); + w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); + } } void HUD::setDrowningTimeLeft(float time) diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 136328f57..ab6096b7e 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -134,38 +134,28 @@ namespace MWGui void StatsWindow::setValue (const std::string& id, const MWMechanics::DynamicStat& value) { - static const char *ids[] = - { - "HBar", "MBar", "FBar", - 0 - }; + int current = std::max(0, static_cast(value.getCurrent())); + int modified = static_cast(value.getModified()); - for (int i=0; ids[i]; ++i) - { - if (ids[i]==id) - { - std::string id (ids[i]); - setBar (id, id + "T", static_cast(value.getCurrent()), static_cast(value.getModified())); + setBar (id, id + "T", current, modified); - // health, magicka, fatigue tooltip - MyGUI::Widget* w; - std::string valStr = boost::lexical_cast(int(value.getCurrent())) + "/" + boost::lexical_cast(int(value.getModified())); - if (i==0) - { - getWidget(w, "Health"); - w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); - } - else if (i==1) - { - getWidget(w, "Magicka"); - w->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); - } - else if (i==2) - { - getWidget(w, "Fatigue"); - w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); - } - } + // health, magicka, fatigue tooltip + MyGUI::Widget* w; + std::string valStr = boost::lexical_cast(current) + "/" + boost::lexical_cast(modified); + if (id == "HBar") + { + getWidget(w, "Health"); + w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); + } + else if (id == "MBar") + { + getWidget(w, "Magicka"); + w->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); + } + else if (id == "FBar") + { + getWidget(w, "Fatigue"); + w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); } } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 78986a052..afa020082 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -112,9 +112,6 @@ namespace MWGui , mPlayerMinorSkills() , mPlayerMajorSkills() , mPlayerSkillValues() - , mPlayerHealth() - , mPlayerMagicka() - , mPlayerFatigue() , mGui(NULL) , mGuiModes() , mCursorManager(NULL) @@ -590,32 +587,8 @@ namespace MWGui mStatsWindow->setValue (id, value); mHud->setValue (id, value); mCharGen->setValue(id, value); - if (id == "HBar") - { - mPlayerHealth = value; - } - else if (id == "MBar") - { - mPlayerMagicka = value; - } - else if (id == "FBar") - { - mPlayerFatigue = value; - } } - #if 0 - MWMechanics::DynamicStat WindowManager::getValue(const std::string& id) - { - if(id == "HBar") - return mPlayerHealth; - else if (id == "MBar") - return mPlayerMagicka; - else if (id == "FBar") - return mPlayerFatigue; - } - #endif - void WindowManager::setValue (const std::string& id, const std::string& value) { mStatsWindow->setValue (id, value); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 4f1960295..743160aa8 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -346,8 +346,6 @@ namespace MWGui std::map > mPlayerAttributes; SkillList mPlayerMajorSkills, mPlayerMinorSkills; std::map > mPlayerSkillValues; - MWMechanics::DynamicStat mPlayerHealth, mPlayerMagicka, mPlayerFatigue; - MyGUI::Gui *mGui; // Gui std::vector mGuiModes; From 1fdd43bbb75f516b990ae995df9ef0dedaa901b1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 10 Dec 2013 12:31:18 +0100 Subject: [PATCH 128/889] removed a redundant new --- apps/openmw/mwworld/worldimp.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index bfcd0ae1c..c4615c099 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -255,9 +255,7 @@ namespace MWWorld mPlayIntro = 2; // global variables - delete mGlobalVariables; - mGlobalVariables = 0; - mGlobalVariables = new Globals (mStore); + *mGlobalVariables = Globals (mStore); // set new game mark mGlobalVariables->setInt ("chargenstate", 1); From 51bfa5cde3be379ceb1ebc541bbb830dc05c8f68 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 10 Dec 2013 15:09:58 +0100 Subject: [PATCH 129/889] rewrote global variable storage (using ESM variant type now) --- apps/openmw/mwworld/globals.cpp | 132 ++++++------------------------- apps/openmw/mwworld/globals.hpp | 49 ++++-------- apps/openmw/mwworld/worldimp.cpp | 75 +++++++++--------- apps/openmw/mwworld/worldimp.hpp | 3 +- 4 files changed, 80 insertions(+), 179 deletions(-) diff --git a/apps/openmw/mwworld/globals.cpp b/apps/openmw/mwworld/globals.cpp index a905f8aae..19bfa1529 100644 --- a/apps/openmw/mwworld/globals.cpp +++ b/apps/openmw/mwworld/globals.cpp @@ -7,15 +7,14 @@ namespace MWWorld { - std::vector Globals::getGlobals () const + std::vector Globals::getGlobals() const { - std::vector retval; - Collection::const_iterator it; - for(it = mVariables.begin(); it != mVariables.end(); ++it){ - retval.push_back(it->first); - } + std::vector ids; - return retval; + for (Collection::const_iterator iter = mVariables.begin(); iter!=mVariables.end(); ++iter) + ids.push_back (iter->first); + + return ids; } Globals::Collection::const_iterator Globals::find (const std::string& name) const @@ -38,112 +37,27 @@ namespace MWWorld return iter; } - Globals::Globals (const MWWorld::ESMStore& store) + void Globals::fill (const MWWorld::ESMStore& store) { - const MWWorld::Store &globals = store.get(); - MWWorld::Store::iterator iter = globals.begin(); - for (; iter != globals.end(); ++iter) + mVariables.clear(); + + const MWWorld::Store& globals = store.get(); + + for (MWWorld::Store::iterator iter = globals.begin(); iter!=globals.end(); + ++iter) { - char type = ' '; - Data value; - - switch (iter->mValue.getType()) - { - case ESM::VT_Short: - - type = 's'; - value.mShort = iter->mValue.getInteger(); - break; - - case ESM::VT_Long: - - type = 'l'; - value.mLong = iter->mValue.getInteger(); - break; - - case ESM::VT_Float: - - type = 'f'; - value.mFloat = iter->mValue.getFloat(); - break; - - default: - - throw std::runtime_error ("unsupported global variable type"); - } - - mVariables.insert (std::make_pair (iter->mId, std::make_pair (type, value))); + mVariables.insert (std::make_pair (iter->mId, iter->mValue)); } } - const Globals::Data& Globals::operator[] (const std::string& name) const + const ESM::Variant& Globals::operator[] (const std::string& name) const { - Collection::const_iterator iter = find (name); - - return iter->second.second; + return find (name)->second; } - Globals::Data& Globals::operator[] (const std::string& name) + ESM::Variant& Globals::operator[] (const std::string& name) { - Collection::iterator iter = find (name); - - return iter->second.second; - } - - void Globals::setInt (const std::string& name, int value) - { - Collection::iterator iter = find (name); - - switch (iter->second.first) - { - case 's': iter->second.second.mShort = value; break; - case 'l': iter->second.second.mLong = value; break; - case 'f': iter->second.second.mFloat = value; break; - - default: throw std::runtime_error ("unsupported global variable type"); - } - } - - void Globals::setFloat (const std::string& name, float value) - { - Collection::iterator iter = find (name); - - switch (iter->second.first) - { - case 's': iter->second.second.mShort = value; break; - case 'l': iter->second.second.mLong = value; break; - case 'f': iter->second.second.mFloat = value; break; - - default: throw std::runtime_error ("unsupported global variable type"); - } - } - - int Globals::getInt (const std::string& name) const - { - Collection::const_iterator iter = find (name); - - switch (iter->second.first) - { - case 's': return iter->second.second.mShort; - case 'l': return iter->second.second.mLong; - case 'f': return iter->second.second.mFloat; - - default: throw std::runtime_error ("unsupported global variable type"); - } - } - - float Globals::getFloat (const std::string& name) const - { - Collection::const_iterator iter = find (name); - - switch (iter->second.first) - { - case 's': return iter->second.second.mShort; - case 'l': return iter->second.second.mLong; - case 'f': return iter->second.second.mFloat; - - default: throw std::runtime_error ("unsupported global variable type"); - } + return find (name)->second; } char Globals::getType (const std::string& name) const @@ -153,7 +67,13 @@ namespace MWWorld if (iter==mVariables.end()) return ' '; - return iter->second.first; + switch (iter->second.getType()) + { + case ESM::VT_Short: return 's'; + case ESM::VT_Long: return 'l'; + case ESM::VT_Float: return 'f'; + + default: return ' '; + } } } - diff --git a/apps/openmw/mwworld/globals.hpp b/apps/openmw/mwworld/globals.hpp index 681bd560e..587dd2092 100644 --- a/apps/openmw/mwworld/globals.hpp +++ b/apps/openmw/mwworld/globals.hpp @@ -6,6 +6,7 @@ #include #include +#include namespace MWWorld { @@ -13,49 +14,29 @@ namespace MWWorld class Globals { - public: - - union Data - { - Interpreter::Type_Float mFloat; - Interpreter::Type_Float mLong; // Why Morrowind, why? :( - Interpreter::Type_Float mShort; - }; - - typedef std::map > Collection; - private: - + + typedef std::map Collection; + Collection mVariables; // type, value - + Collection::const_iterator find (const std::string& name) const; Collection::iterator find (const std::string& name); - - public: - - Globals (const MWWorld::ESMStore& store); - - const Data& operator[] (const std::string& name) const; - Data& operator[] (const std::string& name); - - void setInt (const std::string& name, int value); - ///< Set value independently from real type. - - void setFloat (const std::string& name, float value); - ///< Set value independently from real type. - - int getInt (const std::string& name) const; - ///< Get value independently from real type. - - float getFloat (const std::string& name) const; - ///< Get value independently from real type. - + public: + + const ESM::Variant& operator[] (const std::string& name) const; + + ESM::Variant& operator[] (const std::string& name); + char getType (const std::string& name) const; ///< If there is no global variable with this name, ' ' is returned. - std::vector getGlobals () const; + std::vector getGlobals() const; + + void fill (const MWWorld::ESMStore& store); + ///< Replace variables with variables from \a store with default values. }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c4615c099..7e9647167 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -172,9 +172,9 @@ namespace MWWorld { if (mSky && (isCellExterior() || isCellQuasiExterior())) { - mRendering->skySetHour (mGlobalVariables->getFloat ("gamehour")); - mRendering->skySetDate (mGlobalVariables->getInt ("day"), - mGlobalVariables->getInt ("month")); + mRendering->skySetHour (mGlobalVariables["gamehour"].getFloat()); + mRendering->skySetDate (mGlobalVariables["day"].getInteger(), + mGlobalVariables["month"].getInteger()); mRendering->skyEnable(); } @@ -187,7 +187,7 @@ namespace MWWorld const std::vector& contentFiles, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, int mActivationDistanceOverride) - : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), + : mPlayer (0), mLocalScripts (mStore), mSky (true), mCells (mStore, mEsm), mActivationDistanceOverride (mActivationDistanceOverride), mFallback(fallbackMap), mPlayIntro(0), mTeleportEnabled(true), mLevitationEnabled(false), @@ -227,7 +227,7 @@ namespace MWWorld mStore.setUp(); mStore.movePlayerRecord(); - mGlobalVariables = new Globals (mStore); + mGlobalVariables.fill (mStore); mWorldScene = new Scene(*mRendering, mPhysics); } @@ -254,12 +254,9 @@ namespace MWWorld // FIXME: should be set to 1, but the sound manager won't pause newly started sounds mPlayIntro = 2; - // global variables - *mGlobalVariables = Globals (mStore); - // set new game mark - mGlobalVariables->setInt ("chargenstate", 1); - mGlobalVariables->setInt ("pcrace", 3); + mGlobalVariables["chargenstate"].setInteger (1); + mGlobalVariables["pcrace"].setInteger (3); // we don't want old weather to persist on a new game delete mWeatherManager; @@ -298,6 +295,8 @@ namespace MWWorld mTeleportEnabled = true; mPlayIntro = 0; mFacedDistance = FLT_MAX; + + mGlobalVariables.fill (mStore); } int World::countSavedGameRecords() const @@ -358,7 +357,6 @@ namespace MWWorld { delete mWeatherManager; delete mWorldScene; - delete mGlobalVariables; delete mRendering; delete mPhysics; @@ -436,7 +434,7 @@ namespace MWWorld else if (name=="month") setMonth (value); else - mGlobalVariables->setInt (name, value); + mGlobalVariables[name].setInteger (value); } void World::setGlobalFloat (const std::string& name, float value) @@ -448,27 +446,27 @@ namespace MWWorld else if (name=="month") setMonth (value); else - mGlobalVariables->setFloat (name, value); + mGlobalVariables[name].setFloat (value); } int World::getGlobalInt (const std::string& name) const { - return mGlobalVariables->getInt (name); + return mGlobalVariables[name].getInteger(); } float World::getGlobalFloat (const std::string& name) const { - return mGlobalVariables->getFloat (name); + return mGlobalVariables[name].getFloat(); } char World::getGlobalVariableType (const std::string& name) const { - return mGlobalVariables->getType (name); + return mGlobalVariables.getType (name); } - std::vector World::getGlobals () const + std::vector World::getGlobals() const { - return mGlobalVariables->getGlobals(); + return mGlobalVariables.getGlobals(); } std::string World::getCellName (const MWWorld::CellStore *cell) const @@ -618,14 +616,15 @@ namespace MWWorld mWeatherManager->advanceTime (hours); - hours += mGlobalVariables->getFloat ("gamehour"); + hours += mGlobalVariables["gamehour"].getFloat(); setHour (hours); int days = hours / 24; if (days>0) - mGlobalVariables->setInt ("dayspassed", days + mGlobalVariables->getInt ("dayspassed")); + mGlobalVariables["dayspassed"].setInteger ( + days + mGlobalVariables["dayspassed"].getInteger()); } void World::setHour (double hour) @@ -637,14 +636,14 @@ namespace MWWorld hour = std::fmod (hour, 24); - mGlobalVariables->setFloat ("gamehour", hour); + mGlobalVariables["gamehour"].setFloat (hour); mRendering->skySetHour (hour); mWeatherManager->setHour (hour); if (days>0) - setDay (days + mGlobalVariables->getInt ("day")); + setDay (days + mGlobalVariables["day"].getInteger()); } void World::setDay (int day) @@ -652,7 +651,7 @@ namespace MWWorld if (day<1) day = 1; - int month = mGlobalVariables->getInt ("month"); + int month = mGlobalVariables["month"].getInteger(); while (true) { @@ -667,14 +666,14 @@ namespace MWWorld else { month = 0; - mGlobalVariables->setInt ("year", mGlobalVariables->getInt ("year")+1); + mGlobalVariables["year"].setInteger (mGlobalVariables["year"].getInteger()+1); } day -= days; } - mGlobalVariables->setInt ("day", day); - mGlobalVariables->setInt ("month", month); + mGlobalVariables["day"].setInteger (day); + mGlobalVariables["month"].setInteger (month); mRendering->skySetDate (day, month); @@ -691,30 +690,30 @@ namespace MWWorld int days = getDaysPerMonth (month); - if (mGlobalVariables->getInt ("day")>days) - mGlobalVariables->setInt ("day", days); + if (mGlobalVariables["day"].getInteger()>days) + mGlobalVariables["day"].setInteger (days); - mGlobalVariables->setInt ("month", month); + mGlobalVariables["month"].setInteger (month); if (years>0) - mGlobalVariables->setInt ("year", years+mGlobalVariables->getInt ("year")); + mGlobalVariables["year"].setInteger (years+mGlobalVariables["year"].getInteger()); - mRendering->skySetDate (mGlobalVariables->getInt ("day"), month); + mRendering->skySetDate (mGlobalVariables["day"].getInteger(), month); } int World::getDay() const { - return mGlobalVariables->getInt("day"); + return mGlobalVariables["day"].getInteger(); } int World::getMonth() const { - return mGlobalVariables->getInt("month"); + return mGlobalVariables["month"].getInteger(); } int World::getYear() const { - return mGlobalVariables->getInt("year"); + return mGlobalVariables["year"].getInteger(); } std::string World::getMonthName (int month) const @@ -739,8 +738,8 @@ namespace MWWorld TimeStamp World::getTimeStamp() const { - return TimeStamp (mGlobalVariables->getFloat ("gamehour"), - mGlobalVariables->getInt ("dayspassed")); + return TimeStamp (mGlobalVariables["gamehour"].getFloat(), + mGlobalVariables["dayspassed"].getInteger()); } bool World::toggleSky() @@ -776,7 +775,7 @@ namespace MWWorld float World::getTimeScaleFactor() const { - return mGlobalVariables->getFloat ("timescale"); + return mGlobalVariables["timescale"].getFloat(); } void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position) @@ -1267,7 +1266,7 @@ namespace MWWorld if (Misc::StringUtils::ciEqual (ids[i], record.mRace)) break; - mGlobalVariables->setInt ("pcrace", (i == ids.size()) ? 0 : i+1); + mGlobalVariables["pcrace"].setInteger (i == ids.size() ? 0 : i+1); const ESM::NPC *player = mPlayer->getPlayer().get()->mBase; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 40245b78d..92c99733e 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -11,6 +11,7 @@ #include "localscripts.hpp" #include "timestamp.hpp" #include "fallback.hpp" +#include "globals.hpp" #include "../mwbase/world.hpp" @@ -64,7 +65,7 @@ namespace MWWorld std::vector mEsm; MWWorld::ESMStore mStore; LocalScripts mLocalScripts; - MWWorld::Globals *mGlobalVariables; + MWWorld::Globals mGlobalVariables; MWWorld::PhysicsSystem *mPhysics; bool mSky; From b38bfe1f214aa39cf1e7f183b3b77ff2f39c9f8c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 10 Dec 2013 15:22:38 +0100 Subject: [PATCH 130/889] removed a redundant function for listing global variables --- apps/openmw/mwbase/world.hpp | 2 -- apps/openmw/mwscript/interpretercontext.cpp | 16 +++++++++++++--- apps/openmw/mwscript/miscextensions.cpp | 5 +++-- apps/openmw/mwworld/globals.cpp | 10 ---------- apps/openmw/mwworld/globals.hpp | 2 -- apps/openmw/mwworld/worldimp.cpp | 5 ----- apps/openmw/mwworld/worldimp.hpp | 2 -- 7 files changed, 16 insertions(+), 26 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 717012f72..740114aff 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -164,8 +164,6 @@ namespace MWBase virtual char getGlobalVariableType (const std::string& name) const = 0; ///< Return ' ', if there is no global variable with this name. - virtual std::vector getGlobals () const = 0; - virtual std::string getCellName (const MWWorld::CellStore *cell = 0) const = 0; ///< Return name of the cell. /// diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index a977d3440..aedaec208 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -156,10 +156,20 @@ namespace MWScript MWBase::Environment::get().getWorld()->setGlobalFloat (name, value); } - std::vector InterpreterContext::getGlobals () const + std::vector InterpreterContext::getGlobals() const { - MWBase::World *world = MWBase::Environment::get().getWorld(); - return world->getGlobals(); + std::vector ids; + + const MWWorld::Store& globals = + MWBase::Environment::get().getWorld()->getStore().get(); + + for (MWWorld::Store::iterator iter = globals.begin(); iter!=globals.end(); + ++iter) + { + ids.push_back (iter->mId); + } + + return ids; } char InterpreterContext::getGlobalType (const std::string& name) const diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index d7b147970..8ca97d288 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -652,13 +652,14 @@ namespace MWScript void printGlobalVars(Interpreter::Runtime &runtime) { - Interpreter::Context& context = runtime.getContext(); + InterpreterContext& context = + static_cast (runtime.getContext()); std::stringstream str; str<< "Global variables:"; MWBase::World *world = MWBase::Environment::get().getWorld(); - std::vector names = world->getGlobals(); + std::vector names = context.getGlobals(); for(size_t i = 0;i < names.size();++i) { char type = world->getGlobalVariableType (names[i]); diff --git a/apps/openmw/mwworld/globals.cpp b/apps/openmw/mwworld/globals.cpp index 19bfa1529..d8e96ddc3 100644 --- a/apps/openmw/mwworld/globals.cpp +++ b/apps/openmw/mwworld/globals.cpp @@ -7,16 +7,6 @@ namespace MWWorld { - std::vector Globals::getGlobals() const - { - std::vector ids; - - for (Collection::const_iterator iter = mVariables.begin(); iter!=mVariables.end(); ++iter) - ids.push_back (iter->first); - - return ids; - } - Globals::Collection::const_iterator Globals::find (const std::string& name) const { Collection::const_iterator iter = mVariables.find (name); diff --git a/apps/openmw/mwworld/globals.hpp b/apps/openmw/mwworld/globals.hpp index 587dd2092..ad140b0c1 100644 --- a/apps/openmw/mwworld/globals.hpp +++ b/apps/openmw/mwworld/globals.hpp @@ -33,8 +33,6 @@ namespace MWWorld char getType (const std::string& name) const; ///< If there is no global variable with this name, ' ' is returned. - std::vector getGlobals() const; - void fill (const MWWorld::ESMStore& store); ///< Replace variables with variables from \a store with default values. }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 7e9647167..c4e63fad0 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -464,11 +464,6 @@ namespace MWWorld return mGlobalVariables.getType (name); } - std::vector World::getGlobals() const - { - return mGlobalVariables.getGlobals(); - } - std::string World::getCellName (const MWWorld::CellStore *cell) const { if (!cell) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 92c99733e..845668449 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -237,8 +237,6 @@ namespace MWWorld virtual char getGlobalVariableType (const std::string& name) const; ///< Return ' ', if there is no global variable with this name. - virtual std::vector getGlobals () const; - virtual std::string getCellName (const MWWorld::CellStore *cell = 0) const; ///< Return name of the cell. /// From 91a4d9a2ebf1655cd41eaba48afc6c11de07723d Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Tue, 10 Dec 2013 23:48:49 +0100 Subject: [PATCH 131/889] Fixes #845: NPCs hold torches during the day Added method in WeatherManger and World which returns true if it is night. This method is used later in character controller to show torches (or other sources of light) at night and hide them at day. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwmechanics/character.cpp | 27 +++++++++++++++------ apps/openmw/mwrender/animation.hpp | 1 + apps/openmw/mwrender/npcanimation.cpp | 34 +++++++++++++++++++-------- apps/openmw/mwrender/npcanimation.hpp | 2 ++ apps/openmw/mwworld/weather.cpp | 5 ++++ apps/openmw/mwworld/weather.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 5 ++++ apps/openmw/mwworld/worldimp.hpp | 1 + 9 files changed, 62 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 8141af712..ae1497a08 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -421,6 +421,8 @@ namespace MWBase const MWWorld::Ptr& actor, const std::string& sourceName) = 0; virtual void breakInvisibility (const MWWorld::Ptr& actor) = 0; + + virtual bool isNight() const = 0; }; } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index da3ed2523..4f7754951 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -707,17 +707,30 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun } } - - MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) + if (MWBase::Environment::get().getWorld()->isNight()) { + MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) + { + mAnimation->showLights(true); if(!mAnimation->isPlaying("torch")) - mAnimation->play("torch", Priority_Torch, - MWRender::Animation::Group_LeftArm, false, - 1.0f, "start", "stop", 0.0f, (~(size_t)0)); + mAnimation->play("torch", Priority_Torch, + MWRender::Animation::Group_LeftArm, false, + 1.0f, "start", "stop", 0.0f, (~(size_t)0)); + } + else if (mAnimation->isPlaying("torch")) + { + mAnimation->disable("torch"); + mAnimation->showLights(false); + mAnimation->showShield(true); + } } - else if(mAnimation->isPlaying("torch")) + else + { mAnimation->disable("torch"); + mAnimation->showLights(false); + mAnimation->showShield(true); + } return forcestateupdate; } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index aa04e39e2..16af6d5a6 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -275,6 +275,7 @@ public: virtual void showWeapons(bool showWeapon); virtual void showShield(bool show) {} + virtual void showLights(bool show) {} void enableLights(bool enable); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index eb0c5dfbc..6e363ab88 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -118,6 +118,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v mViewMode(viewMode), mShowWeapons(false), mShowShield(true), + mShowLights(false), mFirstPersonOffset(0.f, 0.f, 0.f), mAlpha(1.f) { @@ -320,6 +321,7 @@ void NpcAnimation::updateParts() showWeapons(mShowWeapons); showShield(mShowShield); + showLights(mShowLights); // Remember body parts so we only have to search through the store once for each race/gender/viewmode combination static std::map< std::pair,std::vector > sRaceMapping; @@ -660,21 +662,12 @@ void NpcAnimation::showShield(bool show) MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if (shield != inv.end() && shield->getTypeName() == typeid(ESM::Light).name()) - { - // ... Except for lights, which are still shown during spellcasting since they - // have their own (one-handed) casting animations - show = true; - } - if(show && shield != inv.end()) + if(show && shield != inv.end() && shield->getTypeName() != typeid(ESM::Light).name()) { Ogre::Vector3 glowColor = getEnchantmentColor(*shield); std::string mesh = MWWorld::Class::get(*shield).getModel(*shield); addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, mesh, !shield->getClass().getEnchantment(*shield).empty(), &glowColor); - - if (shield->getTypeName() == typeid(ESM::Light).name()) - addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], shield->get()->mBase); } else { @@ -682,6 +675,27 @@ void NpcAnimation::showShield(bool show) } } +void NpcAnimation::showLights(bool show) +{ + mShowLights = show; + MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); + MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + + if(show && shield != inv.end() && shield->getTypeName() == typeid(ESM::Light).name()) + { + Ogre::Vector3 glowColor = getEnchantmentColor(*shield); + std::string mesh = MWWorld::Class::get(*shield).getModel(*shield); + addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, + mesh, !shield->getClass().getEnchantment(*shield).empty(), &glowColor); + addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], shield->get()->mBase); + } + else + { + removeIndividualPart(ESM::PRT_Shield); + } +} + + void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound) { // During first auto equip, we don't play any sounds. diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 04dde87c7..0500b46c6 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -54,6 +54,7 @@ private: ViewMode mViewMode; bool mShowWeapons; bool mShowShield; + bool mShowLights; int mVisibilityFlags; @@ -101,6 +102,7 @@ public: virtual void showWeapons(bool showWeapon); virtual void showShield(bool showShield); + virtual void showLights(bool showLights); void setViewMode(ViewMode viewMode); diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 8b05d2256..c355d86a8 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -707,3 +707,8 @@ float WeatherManager::getWindSpeed() const { return mWindSpeed; } + +bool WeatherManager::isNight() const +{ + return (mHour < mSunriseTime || mHour > mNightStart - 1); +} diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 80cbe0418..4c412c449 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -152,6 +152,8 @@ namespace MWWorld void modRegion(const std::string ®ionid, const std::vector &chances); + bool isNight() const; + private: float mHour; int mDay, mMonth; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f64d22122..479feab3e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2244,4 +2244,9 @@ namespace MWWorld actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Invisibility); actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility); } + + bool World::isNight() const + { + return mWeatherManager->isNight(); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index c8133441d..2f8994c8e 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -506,6 +506,7 @@ namespace MWWorld const MWWorld::Ptr& actor, const std::string& sourceName); virtual void breakInvisibility (const MWWorld::Ptr& actor); + virtual bool isNight() const; }; } From 0c3c3ed8e95107745814f8fcfaa7b618544750ce Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 11 Dec 2013 15:15:30 +0100 Subject: [PATCH 132/889] Fix wind gravity affector --- components/nifogre/ogrenifloader.cpp | 2 +- components/nifogre/particles.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index e6c535b9b..acf8ac13a 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -724,7 +724,7 @@ class NIFObjectLoader { const Nif::NiMaterialColorController *matCtrl = dynamic_cast(ctrls.getPtr()); Ogre::ControllerValueRealPtr dstval(OGRE_NEW MaterialColorController::Value(movable, matCtrl->data.getPtr(), &scene->mMaterialControllerMgr)); - AlphaController::Function* function = OGRE_NEW AlphaController::Function(matCtrl, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); + MaterialColorController::Function* function = OGRE_NEW MaterialColorController::Function(matCtrl, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); Ogre::ControllerFunctionRealPtr func(function); scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); diff --git a/components/nifogre/particles.cpp b/components/nifogre/particles.cpp index 006a570dc..7b51f0667 100644 --- a/components/nifogre/particles.cpp +++ b/components/nifogre/particles.cpp @@ -763,7 +763,7 @@ public: protected: void applyWindForce(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed) { - const Ogre::Vector3 vec = mDirection * mForce * timeElapsed; + const Ogre::Vector3 vec = mBone->_getDerivedOrientation() * mDirection * mForce * timeElapsed; Ogre::ParticleIterator pi = psys->_getIterator(); while (!pi.end()) { From 99cfb8cda28217710369cda38bb18c0a51c3f3d7 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Thu, 12 Dec 2013 13:48:23 +0400 Subject: [PATCH 133/889] OS X: this CMake parameters can be set in CMake invocation, no need to hardcode them in CMakeLists.txt --- CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f6014dff6..2665ebdab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,10 +4,6 @@ if (APPLE) set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app") set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}") - - set(CMAKE_EXE_LINKER_FLAGS "-F /Library/Frameworks") - set(CMAKE_SHARED_LINKER_FLAGS "-F /Library/Frameworks") - set(CMAKE_MODULE_LINKER_FLAGS "-F /Library/Frameworks") endif (APPLE) # Macros From 87853f406626310554210976bfb84301c643cf30 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Thu, 12 Dec 2013 13:52:57 +0400 Subject: [PATCH 134/889] =?UTF-8?q?OS=20X:=20consider=20Ogre=20CG=20plugin?= =?UTF-8?q?=20as=20used=20only=20if=20it=E2=80=99s=20found?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2665ebdab..9f4523d4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -246,8 +246,12 @@ if (APPLE) # List used Ogre plugins SET(USED_OGRE_PLUGINS ${OGRE_RenderSystem_GL_LIBRARY_REL} ${OGRE_Plugin_OctreeSceneManager_LIBRARY_REL} - ${OGRE_Plugin_CgProgramManager_LIBRARY_REL} ${OGRE_Plugin_ParticleFX_LIBRARY_REL}) + + if (OGRE_Plugin_CgProgramManager_FOUND) + set(USED_OGRE_PLUGINS ${USED_OGRE_PLUGINS} + ${OGRE_Plugin_CgProgramManager_LIBRARY_REL}) + endif () if (${OGRE_PLUGIN_DIR_REL}}) set(OGRE_PLUGINS_REL_FOUND TRUE) From 89c60b065c1fa4ea7e728d250af1001383c7544d Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Thu, 12 Dec 2013 14:19:48 +0400 Subject: [PATCH 135/889] OS X: looks like OGRE_Plugin_CgProgramManager_FOUND is not reliable --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f4523d4f..e1a591b27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -248,7 +248,9 @@ if (APPLE) ${OGRE_Plugin_OctreeSceneManager_LIBRARY_REL} ${OGRE_Plugin_ParticleFX_LIBRARY_REL}) - if (OGRE_Plugin_CgProgramManager_FOUND) + # Actually we must use OGRE_Plugin_CgProgramManager_FOUND but it's + # not reliable and equals TRUE even if there's no Ogre Cg plugin + if (Cg_FOUND) set(USED_OGRE_PLUGINS ${USED_OGRE_PLUGINS} ${OGRE_Plugin_CgProgramManager_LIBRARY_REL}) endif () From 874ecee079d509b9d173a35c724a660b380f5642 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Thu, 12 Dec 2013 14:20:13 +0400 Subject: [PATCH 136/889] OS X: do not enforce static boost, this can be set externally anyway --- CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e1a591b27..61c6f1f58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -190,10 +190,6 @@ if (MSVC10) set(PLATFORM_INCLUDE_DIR "") endif() -if (APPLE) - set(Boost_USE_STATIC_LIBS ON) -endif (APPLE) - # Dependencies # Fix for not visible pthreads functions for linker with glibc 2.15 From fc37c77a9101972374cddbbe82d6079aa3468c2d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 12 Dec 2013 12:19:25 +0100 Subject: [PATCH 137/889] store global variables in saved game files --- apps/openmw/mwstate/statemanagerimp.cpp | 1 + apps/openmw/mwworld/globals.cpp | 40 +++++++++++++++++++++++++ apps/openmw/mwworld/globals.hpp | 18 +++++++++++ apps/openmw/mwworld/worldimp.cpp | 11 ++++--- 4 files changed, 66 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 94219b8fc..a8f5631a0 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -161,6 +161,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl case ESM::REC_NPC_: case ESM::REC_SPEL: case ESM::REC_WEAP: + case ESM::REC_GLOB: MWBase::Environment::get().getWorld()->readRecord (reader, n.val); break; diff --git a/apps/openmw/mwworld/globals.cpp b/apps/openmw/mwworld/globals.cpp index d8e96ddc3..879ffa8e3 100644 --- a/apps/openmw/mwworld/globals.cpp +++ b/apps/openmw/mwworld/globals.cpp @@ -3,6 +3,11 @@ #include +#include + +#include +#include + #include "esmstore.hpp" namespace MWWorld @@ -66,4 +71,39 @@ namespace MWWorld default: return ' '; } } + + int Globals::countSavedGameRecords() const + { + return mVariables.size(); + } + + void Globals::write (ESM::ESMWriter& writer) const + { + for (Collection::const_iterator iter (mVariables.begin()); iter!=mVariables.end(); ++iter) + { + writer.startRecord (ESM::REC_GLOB); + writer.writeHNString ("NAME", iter->first); + iter->second.write (writer, ESM::Variant::Format_Global); + writer.endRecord (ESM::REC_GLOB); + } + } + + bool Globals::readRecord (ESM::ESMReader& reader, int32_t type) + { + if (type==ESM::REC_GLOB) + { + std::string id = reader.getHNString ("NAME"); + + Collection::iterator iter = mVariables.find (Misc::StringUtils::lowerCase (id)); + + if (iter!=mVariables.end()) + iter->second.read (reader, ESM::Variant::Format_Global); + else + reader.skipHRecord(); + + return true; + } + + return false; + } } diff --git a/apps/openmw/mwworld/globals.hpp b/apps/openmw/mwworld/globals.hpp index ad140b0c1..8f521c8a6 100644 --- a/apps/openmw/mwworld/globals.hpp +++ b/apps/openmw/mwworld/globals.hpp @@ -5,9 +5,17 @@ #include #include +#include + #include #include +namespace ESM +{ + class ESMWriter; + class ESMReader; +} + namespace MWWorld { class ESMStore; @@ -35,6 +43,16 @@ namespace MWWorld void fill (const MWWorld::ESMStore& store); ///< Replace variables with variables from \a store with default values. + + int countSavedGameRecords() const; + + void write (ESM::ESMWriter& writer) const; + + bool readRecord (ESM::ESMReader& reader, int32_t type); + ///< Records for variables that do not exist are dropped silently. + /// + /// \return Known type? + }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c4e63fad0..92091097c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -301,20 +301,23 @@ namespace MWWorld int World::countSavedGameRecords() const { - return mStore.countSavedGameRecords(); + return + mStore.countSavedGameRecords() + +mGlobalVariables.countSavedGameRecords(); } void World::write (ESM::ESMWriter& writer) const { mStore.write (writer); + mGlobalVariables.write (writer); } void World::readRecord (ESM::ESMReader& reader, int32_t type) { - if (!mStore.readRecord (reader, type)) + if (!mStore.readRecord (reader, type) && + !mGlobalVariables.readRecord (reader, type)) { - /// \todo handle other world state records - + throw std::runtime_error ("unknown record in saved game"); } } From 73c6aba4d05370e6433c42ea4af534224e4e3377 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Thu, 12 Dec 2013 15:20:04 +0400 Subject: [PATCH 138/889] OS X: Ogre plugins are now installed in OpenCS bundle too. Generalized plugin install routine. --- CMakeLists.txt | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 61c6f1f58..f53d73813 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -689,9 +689,9 @@ if (APPLE) set(OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}") - set(OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/OpenCS.app") + set(OPENCS_BUNDLE_NAME "OpenCS.app") + set(OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}") - set(PLUGINS "") set(ABSOLUTE_PLUGINS "") foreach (PLUGIN ${USED_OGRE_PLUGINS}) @@ -699,12 +699,25 @@ if (APPLE) set(ABSOLUTE_PLUGINS ${PLUGIN_ABS} ${ABSOLUTE_PLUGINS}) endforeach () - set(PLUGIN_INSTALL_BASE "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}/Contents/Plugins") - install(FILES ${ABSOLUTE_PLUGINS} DESTINATION "${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}/Contents/Plugins" COMPONENT Runtime) - foreach (PLUGIN ${ABSOLUTE_PLUGINS}) - get_filename_component(PLUGIN_RELATIVE ${PLUGIN} NAME) - set(PLUGINS ${PLUGINS} "${PLUGIN_INSTALL_BASE}/${PLUGIN_RELATIVE}") - endforeach () + # installs used plugins in bundle at given path (bundle_path must be relative to ${CMAKE_INSTALL_PREFIX}) + # and returns list of install paths for all installed plugins + function (install_plugins_for_bundle bundle_path plugins_var) + set(RELATIVE_PLUGIN_INSTALL_BASE "${bundle_path}/Contents/Plugins") + install(FILES ${ABSOLUTE_PLUGINS} DESTINATION ${RELATIVE_PLUGIN_INSTALL_BASE} COMPONENT Runtime) + + set(PLUGINS "") + set(PLUGIN_INSTALL_BASE "\${CMAKE_INSTALL_PREFIX}/${RELATIVE_PLUGIN_INSTALL_BASE}") + + foreach (PLUGIN ${ABSOLUTE_PLUGINS}) + get_filename_component(PLUGIN_RELATIVE ${PLUGIN} NAME) + set(PLUGINS ${PLUGINS} "${PLUGIN_INSTALL_BASE}/${PLUGIN_RELATIVE}") + endforeach () + + set(${plugins_var} ${PLUGINS} PARENT_SCOPE) + endfunction (install_plugins_for_bundle) + + install_plugins_for_bundle("${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}" PLUGINS) + install_plugins_for_bundle("${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}" OPENCS_PLUGINS) #For now, search unresolved dependencies only in default system paths, so if you put unresolveable (i.e. with @executable_path in id name) lib or framework somewhere else, it would fail set(DIRS "") @@ -752,7 +765,7 @@ if (APPLE) set(BU_CHMOD_BUNDLE_ITEMS ON) include(BundleUtilities) fixup_bundle(\"${OPENMW_APP}\" \"${PLUGINS}\" \"${DIRS}\") - fixup_bundle(\"${OPENCS_APP}\" \"\" \"${DIRS}\") + fixup_bundle(\"${OPENCS_APP}\" \"${OPENCS_PLUGINS}\" \"${DIRS}\") " COMPONENT Runtime) include(CPack) endif (APPLE) From 74793c1c2f5d34bf6f5954d9bca8eafbf0eb5ea9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 12 Dec 2013 13:15:38 +0100 Subject: [PATCH 139/889] globals script cleanup; fixed potential case folding bug --- apps/openmw/mwbase/scriptmanager.hpp | 2 -- apps/openmw/mwscript/globalscripts.cpp | 41 +++++++++++++---------- apps/openmw/mwscript/globalscripts.hpp | 9 +++-- apps/openmw/mwscript/scriptmanagerimp.cpp | 5 --- apps/openmw/mwscript/scriptmanagerimp.hpp | 2 -- apps/openmw/mwstate/statemanagerimp.cpp | 7 ++++ apps/openmw/mwworld/worldimp.cpp | 3 -- 7 files changed, 37 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwbase/scriptmanager.hpp b/apps/openmw/mwbase/scriptmanager.hpp index 32df2bfa3..ae146e064 100644 --- a/apps/openmw/mwbase/scriptmanager.hpp +++ b/apps/openmw/mwbase/scriptmanager.hpp @@ -35,8 +35,6 @@ namespace MWBase virtual ~ScriptManager() {} - virtual void resetGlobalScripts() = 0; - virtual void run (const std::string& name, Interpreter::Context& interpreterContext) = 0; ///< Run the script with the given name (compile first, if not compiled yet) diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 608725ae6..d55ad0063 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -3,6 +3,8 @@ #include +#include + #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" @@ -15,25 +17,12 @@ namespace MWScript GlobalScripts::GlobalScripts (const MWWorld::ESMStore& store) : mStore (store) { - reset(); - } - - void GlobalScripts::reset() - { - mScripts.clear(); - addScript ("Main"); - - MWWorld::Store::iterator iter = - mStore.get().begin(); - - for (; iter != mStore.get().end(); ++iter) { - addScript (iter->mScript); - } + addStartup(); } void GlobalScripts::addScript (const std::string& name) { - if (mScripts.find (name)==mScripts.end()) + if (mScripts.find (Misc::StringUtils::lowerCase (name))==mScripts.end()) if (const ESM::Script *script = mStore.get().find (name)) { Locals locals; @@ -46,7 +35,8 @@ namespace MWScript void GlobalScripts::removeScript (const std::string& name) { - std::map >::iterator iter = mScripts.find (name); + std::map >::iterator iter = + mScripts.find (Misc::StringUtils::lowerCase (name)); if (iter!=mScripts.end()) iter->second.first = false; @@ -55,7 +45,7 @@ namespace MWScript bool GlobalScripts::isRunning (const std::string& name) const { std::map >::const_iterator iter = - mScripts.find (name); + mScripts.find (Misc::StringUtils::lowerCase (name)); if (iter==mScripts.end()) return false; @@ -76,4 +66,21 @@ namespace MWScript } } } + + void GlobalScripts::clear() + { + mScripts.clear(); + } + + void GlobalScripts::addStartup() + { + addScript ("main"); + + for (MWWorld::Store::iterator iter = + mStore.get().begin(); + iter != mStore.get().end(); ++iter) + { + addScript (iter->mScript); + } + } } diff --git a/apps/openmw/mwscript/globalscripts.hpp b/apps/openmw/mwscript/globalscripts.hpp index 628919d1d..67b619d1a 100644 --- a/apps/openmw/mwscript/globalscripts.hpp +++ b/apps/openmw/mwscript/globalscripts.hpp @@ -6,7 +6,7 @@ #include "locals.hpp" -namespace MWWorld +namespace MWWorld { struct ESMStore; } @@ -22,8 +22,6 @@ namespace MWScript GlobalScripts (const MWWorld::ESMStore& store); - void reset(); - void addScript (const std::string& name); void removeScript (const std::string& name); @@ -32,6 +30,11 @@ namespace MWScript void run(); ///< run all active global scripts + + void clear(); + + void addStartup(); + ///< Add startup script }; } diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index 14fe5b7fd..be9082eb6 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -222,9 +222,4 @@ namespace MWScript throw std::runtime_error ("unable to access local variable " + variable + " of " + scriptId); } - - void ScriptManager::resetGlobalScripts() - { - mGlobalScripts.reset(); - } } diff --git a/apps/openmw/mwscript/scriptmanagerimp.hpp b/apps/openmw/mwscript/scriptmanagerimp.hpp index 7bb98ffbd..1a856e0c5 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.hpp +++ b/apps/openmw/mwscript/scriptmanagerimp.hpp @@ -61,8 +61,6 @@ namespace MWScript ///< Compile script with the given namen /// \return Success? - virtual void resetGlobalScripts(); - virtual std::pair compileAll(); ///< Compile all scripts /// \return count, success diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index a8f5631a0..e1086f121 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -12,19 +12,24 @@ #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/scriptmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwmechanics/npcstats.hpp" +#include "../mwscript/globalscripts.hpp" + void MWState::StateManager::cleanup() { if (mState!=State_NoGame) { MWBase::Environment::get().getDialogueManager()->clear(); MWBase::Environment::get().getJournal()->clear(); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear(); MWBase::Environment::get().getWorld()->clear(); + mState = State_NoGame; mCharacterManager.clearCurrentCharacter(); mTimePlayed = 0; @@ -64,6 +69,8 @@ void MWState::StateManager::newGame (bool bypass) else MWBase::Environment::get().getWorld()->setGlobalInt ("chargenstate", -1); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); + mState = State_Running; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 92091097c..c10556fa9 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -21,7 +21,6 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwbase/scriptmanager.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/movement.hpp" @@ -262,8 +261,6 @@ namespace MWWorld delete mWeatherManager; mWeatherManager = 0; mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback); - - MWBase::Environment::get().getScriptManager()->resetGlobalScripts(); } void World::clear() From 2a35c7d33a825f293b07769f87ccf7949f2a06da Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 12 Dec 2013 13:16:32 +0100 Subject: [PATCH 140/889] fixed running global scripts a second time after they have been stopped --- apps/openmw/mwscript/globalscripts.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index d55ad0063..1400d6675 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -22,7 +22,11 @@ namespace MWScript void GlobalScripts::addScript (const std::string& name) { - if (mScripts.find (Misc::StringUtils::lowerCase (name))==mScripts.end()) + std::map >::iterator iter = + mScripts.find (Misc::StringUtils::lowerCase (name)); + + if (iter==mScripts.end()) + { if (const ESM::Script *script = mStore.get().find (name)) { Locals locals; @@ -31,6 +35,9 @@ namespace MWScript mScripts.insert (std::make_pair (name, std::make_pair (true, locals))); } + } + else + iter->second.first = true; } void GlobalScripts::removeScript (const std::string& name) From 7a9b64c6f4823f16cd2f6e6a6de6812578c106d6 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Thu, 12 Dec 2013 16:08:07 +0200 Subject: [PATCH 141/889] bug fix http://bugs.openmw.org/issues/985 --- apps/openmw/mwworld/actiontalk.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/actiontalk.cpp b/apps/openmw/mwworld/actiontalk.cpp index 905497f85..6b5e274f5 100644 --- a/apps/openmw/mwworld/actiontalk.cpp +++ b/apps/openmw/mwworld/actiontalk.cpp @@ -1,15 +1,23 @@ #include "actiontalk.hpp" +#include "class.hpp" + #include "../mwbase/environment.hpp" #include "../mwbase/dialoguemanager.hpp" +#include "../mwmechanics/creaturestats.hpp" + namespace MWWorld { ActionTalk::ActionTalk (const Ptr& actor) : Action (false, actor) {} void ActionTalk::executeImp (const Ptr& actor) { - MWBase::Environment::get().getDialogueManager()->startDialogue (getTarget()); + MWWorld::Ptr talkTo = getTarget(); //because 'actor' is always the player! + if ( MWWorld::Class::get(talkTo).getCreatureStats(talkTo).isHostile() ) + return; + + MWBase::Environment::get().getDialogueManager()->startDialogue (talkTo); } } From 39eea24dc3866760cc40b79b6d57ebbc6799fc73 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 13 Dec 2013 03:50:01 +0100 Subject: [PATCH 142/889] Don't try to show exceptions in a message box if SDL was not initialized --- apps/openmw/main.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index b1bbb14f2..13e9d9241 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -3,8 +3,7 @@ #include -#include -#include +#include #include "engine.hpp" #if defined(_WIN32) && !defined(_CONSOLE) @@ -282,7 +281,7 @@ int main(int argc, char**argv) } catch (std::exception &e) { - if (isatty(fileno(stdin))) + if (isatty(fileno(stdin)) || !SDL_WasInit(SDL_INIT_VIDEO)) std::cerr << "\nERROR: " << e.what() << std::endl; else SDL_ShowSimpleMessageBox(0, "OpenMW: Fatal error", e.what(), NULL); From c8bf69b91affbfd4082b24d70854ec15c878d260 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 13 Dec 2013 19:02:25 +0200 Subject: [PATCH 143/889] Revert "bug fix http://bugs.openmw.org/issues/985" This reverts commit 7a9b64c6f4823f16cd2f6e6a6de6812578c106d6. --- apps/openmw/mwworld/actiontalk.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/apps/openmw/mwworld/actiontalk.cpp b/apps/openmw/mwworld/actiontalk.cpp index 6b5e274f5..905497f85 100644 --- a/apps/openmw/mwworld/actiontalk.cpp +++ b/apps/openmw/mwworld/actiontalk.cpp @@ -1,23 +1,15 @@ #include "actiontalk.hpp" -#include "class.hpp" - #include "../mwbase/environment.hpp" #include "../mwbase/dialoguemanager.hpp" -#include "../mwmechanics/creaturestats.hpp" - namespace MWWorld { ActionTalk::ActionTalk (const Ptr& actor) : Action (false, actor) {} void ActionTalk::executeImp (const Ptr& actor) { - MWWorld::Ptr talkTo = getTarget(); //because 'actor' is always the player! - if ( MWWorld::Class::get(talkTo).getCreatureStats(talkTo).isHostile() ) - return; - - MWBase::Environment::get().getDialogueManager()->startDialogue (talkTo); + MWBase::Environment::get().getDialogueManager()->startDialogue (getTarget()); } } From 8b3a393a6b1b0a93cc7ac58353cbd197c3aaf346 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 13 Dec 2013 19:33:01 +0200 Subject: [PATCH 144/889] bug fix at http://bugs.openmw.org/issues/985 --- apps/openmw/mwclass/npc.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index e7c10d3c8..5e9801c49 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -711,6 +711,8 @@ namespace MWClass return boost::shared_ptr(new MWWorld::ActionOpen(ptr, true)); if(get(actor).getStance(actor, MWWorld::Class::Sneak)) return boost::shared_ptr(new MWWorld::ActionOpen(ptr)); // stealing + if(get(ptr).getCreatureStats(ptr).isHostile()) + return boost::shared_ptr(new MWWorld::FailedAction("#{sActorInCombat}")); return boost::shared_ptr(new MWWorld::ActionTalk(ptr)); } From 8dd930cf97fc4a0fee398a31209fabf24ad7fcb1 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 13 Dec 2013 22:43:58 +0200 Subject: [PATCH 145/889] bug fix http://bugs.openmw.org/issues/428 --- apps/openmw/mwbase/windowmanager.hpp | 2 ++ apps/openmw/mwbase/world.hpp | 3 ++ apps/openmw/mwgui/mainmenu.cpp | 13 +++++++- apps/openmw/mwgui/mainmenu.hpp | 4 +++ apps/openmw/mwgui/windowmanagerimp.cpp | 5 +++ apps/openmw/mwgui/windowmanagerimp.hpp | 3 ++ apps/openmw/mwinput/inputmanagerimp.cpp | 37 ++++++++++++----------- apps/openmw/mwmechanics/actors.cpp | 14 ++++----- apps/openmw/mwmechanics/character.cpp | 22 ++++++++++++-- apps/openmw/mwmechanics/character.hpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 5 +++ apps/openmw/mwrender/renderingmanager.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 9 +++++- apps/openmw/mwworld/worldimp.hpp | 2 ++ 14 files changed, 92 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index c47ad066b..4ffb44615 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -202,6 +202,8 @@ namespace MWBase virtual void setSpellVisibility(bool visible) = 0; virtual void setSneakVisibility(bool visible) = 0; + void virtual setMainMenuNoReturn(bool noreturn) = 0; + virtual void activateQuickKey (int index) = 0; virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 8141af712..b1f236bd1 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -40,6 +40,7 @@ namespace ESM namespace MWRender { + class Camera; class ExternalRendering; class Animation; } @@ -115,6 +116,8 @@ namespace MWBase virtual MWWorld::Player& getPlayer() = 0; + virtual MWRender::Camera* getCamera() const = 0; + virtual const MWWorld::ESMStore& getStore() const = 0; virtual std::vector& getEsmReader() = 0; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index fa7ed2ace..e1c72193b 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -17,6 +17,7 @@ namespace MWGui MainMenu::MainMenu(int w, int h) : OEngine::GUI::Layout("openmw_mainmenu.layout") , mButtonBox(0) + , mNoReturn(false) { onResChange(w,h); } @@ -33,7 +34,8 @@ namespace MWGui int curH = 0; std::vector buttons; - buttons.push_back("return"); + if(!mNoReturn) + buttons.push_back("return"); buttons.push_back("newgame"); //buttons.push_back("loadgame"); //buttons.push_back("savegame"); @@ -68,6 +70,15 @@ namespace MWGui mButtonBox->setCoord (w/2 - maxwidth/2, h/2 - curH/2, maxwidth, curH); } + void MainMenu::setNoReturn(bool bNoReturn) + { + if (mNoReturn != bNoReturn) + { + mNoReturn = bNoReturn; + onResChange( Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video") ); + } + } + void MainMenu::onButtonClicked(MyGUI::Widget *sender) { MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index 4e76a64df..a686cb020 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -12,9 +12,13 @@ namespace MWGui void onResChange(int w, int h); + void setNoReturn(bool bNoReturn); + private: MyGUI::Widget* mButtonBox; + bool mNoReturn; + std::map mButtons; void onButtonClicked (MyGUI::Widget* sender); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 78986a052..69ec11ccc 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -880,6 +880,11 @@ namespace MWGui mHud->setSneakVisible(visible); } + void WindowManager::setMainMenuNoReturn(bool noreturn) + { + mMenu->setNoReturn(noreturn); + } + void WindowManager::setDragDrop(bool dragDrop) { mToolTips->setEnabled(!dragDrop); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 4f1960295..3d6972718 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -198,6 +198,9 @@ namespace MWGui virtual void setSpellVisibility(bool visible); virtual void setSneakVisibility(bool visible); + //disables 'return' button in the main menu (used when player is dead) + void virtual setMainMenuNoReturn(bool noreturn); + virtual void activateQuickKey (int index); virtual void setSelectedSpell(const std::string& spellId, int successChancePercent); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index ab2569635..9b7fe98bd 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -367,24 +367,27 @@ namespace MWInput } } - if (mControlSwitch["playerviewswitch"]) { - - // work around preview mode toggle when pressing Alt+Tab - if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) { - if (mPreviewPOVDelay <= 0.5 && - (mPreviewPOVDelay += dt) > 0.5) - { - mPreviewPOVDelay = 1.f; - MWBase::Environment::get().getWorld()->togglePreviewMode(true); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + if ( !player.getClass().getCreatureStats(player).isDead() ) { + if (mControlSwitch["playerviewswitch"]) { + // work around preview mode toggle when pressing Alt+Tab + if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) { + + if (mPreviewPOVDelay <= 0.5 && + (mPreviewPOVDelay += dt) > 0.5) + { + mPreviewPOVDelay = 1.f; + MWBase::Environment::get().getWorld()->togglePreviewMode(true); + } + } else { + if (mPreviewPOVDelay > 0.5) { + //disable preview mode + MWBase::Environment::get().getWorld()->togglePreviewMode(false); + } else if (mPreviewPOVDelay > 0.f) { + MWBase::Environment::get().getWorld()->togglePOV(); + } + mPreviewPOVDelay = 0.f; } - } else { - if (mPreviewPOVDelay > 0.5) { - //disable preview mode - MWBase::Environment::get().getWorld()->togglePreviewMode(false); - } else if (mPreviewPOVDelay > 0.f) { - MWBase::Environment::get().getWorld()->togglePOV(); - } - mPreviewPOVDelay = 0.f; } } } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 22a641b34..5dd294f90 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -570,15 +570,13 @@ namespace MWMechanics continue; } - if(iter->second->isDead()) - continue; + if (iter->second->kill()) + { + ++mDeathCount[cls.getId(iter->first)]; - iter->second->kill(); - - ++mDeathCount[cls.getId(iter->first)]; - - if(cls.isEssential(iter->first)) - MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); + if(cls.isEssential(iter->first)) + MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); + } } } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index da3ed2523..c8eba20d6 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -27,6 +27,7 @@ #include "security.hpp" #include "../mwrender/animation.hpp" +#include "../mwrender/camera.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -1055,10 +1056,19 @@ void CharacterController::forceStateUpdate() } } -void CharacterController::kill() +bool CharacterController::kill() { - if(mDeathState != CharState_None) - return; + if( isDead() ) + { + //player death animation is over: toggle game menu without 'return' option + if( mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() + && !isAnimPlaying(mCurrentDeath) ) + { + MWBase::Environment::get().getWindowManager()->setMainMenuNoReturn(true); + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + } + return false; + } if(mPtr.getTypeName() == typeid(ESM::NPC).name()) { @@ -1096,6 +1106,10 @@ void CharacterController::kill() if(mAnimation) { + if (mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() + && MWBase::Environment::get().getWorld()->getCamera()->isFirstPerson() ) + MWBase::Environment::get().getWorld()->togglePOV(); + mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); mAnimation->disable(mCurrentIdle); @@ -1103,6 +1117,8 @@ void CharacterController::kill() mIdleState = CharState_None; mCurrentIdle.clear(); + + return true; } void CharacterController::resurrect() diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 9e07fca7d..2a5c7bb03 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -190,7 +190,7 @@ public: void skipAnim(); bool isAnimPlaying(const std::string &groupName); - void kill(); + bool kill(); void resurrect(); bool isDead() const { return mDeathState != CharState_None; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index b216c789f..7d5162747 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -211,6 +211,11 @@ MWRender::SkyManager* RenderingManager::getSkyManager() return mSkyManager; } +MWRender::Camera* RenderingManager::getCamera() const +{ + return mCamera; +} + MWRender::Objects& RenderingManager::getObjects(){ return *mObjects; } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index e5dcf0aeb..d6c1ef6c1 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -98,6 +98,8 @@ public: SkyManager* getSkyManager(); Compositors* getCompositors(); + MWRender::Camera* getCamera() const; + void toggleLight(); bool toggleRenderMode(int mode); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f64d22122..a900d27ab 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -312,6 +312,8 @@ namespace MWWorld mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback); MWBase::Environment::get().getScriptManager()->resetGlobalScripts(); + + MWBase::Environment::get().getWindowManager()->setMainMenuNoReturn(false); } @@ -403,6 +405,11 @@ namespace MWWorld return *mPlayer; } + MWRender::Camera* World::getCamera() const + { + return mRendering->getCamera(); + } + const MWWorld::ESMStore& World::getStore() const { return mStore; @@ -1304,7 +1311,7 @@ namespace MWWorld // inform the GUI about focused object MWWorld::Ptr object = getFacedObject (); - MWBase::Environment::get().getWindowManager()->setFocusObject(object); + MWBase::Environment::get().getWindowManager()->setFocusObject(object); // retrieve object dimensions so we know where to place the floating label if (!object.isEmpty ()) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index c8133441d..2e572d0bb 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -187,6 +187,8 @@ namespace MWWorld virtual Player& getPlayer(); + virtual MWRender::Camera* getCamera() const; + virtual const MWWorld::ESMStore& getStore() const; virtual std::vector& getEsmReader(); From 530d06ab54352c79bca6c168f270168e8bcaa29b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Dec 2013 05:07:08 +0100 Subject: [PATCH 146/889] Remove unused code --- components/CMakeLists.txt | 4 +- components/files/filelibrary.cpp | 120 ------------------------------- components/files/filelibrary.hpp | 49 ------------- components/files/fileops.cpp | 120 ------------------------------- components/files/fileops.hpp | 38 ---------- 5 files changed, 2 insertions(+), 329 deletions(-) delete mode 100644 components/files/filelibrary.cpp delete mode 100644 components/files/filelibrary.hpp delete mode 100644 components/files/fileops.cpp delete mode 100644 components/files/fileops.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 50ac236c8..a037fd5fa 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -47,8 +47,8 @@ add_component_dir (misc ) add_component_dir (files - linuxpath windowspath macospath fixedpath multidircollection collections fileops configurationmanager - filelibrary constrainedfiledatastream lowlevelfile + linuxpath windowspath macospath fixedpath multidircollection collections configurationmanager + constrainedfiledatastream lowlevelfile ) add_component_dir (compiler diff --git a/components/files/filelibrary.cpp b/components/files/filelibrary.cpp deleted file mode 100644 index ce2d95f57..000000000 --- a/components/files/filelibrary.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#include "filelibrary.hpp" - -#include - -#include -#include <../components/misc/stringops.hpp> - -namespace Files -{ - // Looks for a string in a vector of strings - bool containsVectorString(const StringVector& list, const std::string& str) - { - for (StringVector::const_iterator iter = list.begin(); - iter != list.end(); ++iter) - { - if (*iter == str) - return true; - } - return false; - } - - // Searches a path and adds the results to the library - void FileLibrary::add(const boost::filesystem::path &root, bool recursive, bool strict, - const StringVector &acceptableExtensions) - { - if (!boost::filesystem::exists(root)) - { - std::cout << "Warning " << root.string() << " does not exist.\n"; - return; - } - - std::string fileExtension; - std::string type; - - // remember the last location of the priority list when listing new items - int length = mPriorityList.size(); - - // First makes a list of all candidate files - FileLister(root, mPriorityList, recursive); - - // Then sort these files into sections according to the folder they belong to - for (PathContainer::iterator listIter = mPriorityList.begin() + length; - listIter != mPriorityList.end(); ++listIter) - { - if( !acceptableExtensions.empty() ) - { - fileExtension = boost::filesystem::path (listIter->extension()).string(); - Misc::StringUtils::toLower(fileExtension); - if(!containsVectorString(acceptableExtensions, fileExtension)) - continue; - } - - type = boost::filesystem::path (listIter->parent_path().leaf()).string(); - if (!strict) - Misc::StringUtils::toLower(type); - - mMap[type].push_back(*listIter); - // std::cout << "Added path: " << listIter->string() << " in section "<< type <second); - } - } - - // Searches the library for an item and returns a boost path to it - boost::filesystem::path FileLibrary::locate(std::string item, bool strict, bool ignoreExtensions, std::string sectionName) - { - boost::filesystem::path result(""); - if (sectionName == "") - { - return FileListLocator(mPriorityList, boost::filesystem::path(item), strict, ignoreExtensions); - } - else - { - if (!containsSection(sectionName, strict)) - { - std::cout << "Warning: There is no section named " << sectionName << "\n"; - return result; - } - result = FileListLocator(mMap[sectionName], boost::filesystem::path(item), strict, ignoreExtensions); - } - return result; - } - - // Prints all the available sections, used for debugging - void FileLibrary::printSections() - { - for(StringPathContMap::const_iterator mapIter = mMap.begin(); - mapIter != mMap.end(); ++mapIter) - { - std::cout << mapIter->first < - -namespace Files -{ - typedef std::map StringPathContMap; - typedef std::vector StringVector; - - /// Looks for a string in a vector of strings - bool containsVectorString(const StringVector& list, const std::string& str); - - /// \brief Searches directories and makes lists of files according to folder name - class FileLibrary - { - private: - StringPathContMap mMap; - PathContainer mEmptyPath; - PathContainer mPriorityList; - - public: - /// Searches a path and adds the results to the library - /// Recursive search and fs strict options are available - /// Takes a vector of acceptable files extensions, if none is given it lists everything. - void add(const boost::filesystem::path &root, bool recursive, bool strict, - const StringVector &acceptableExtensions); - - /// Returns true if the named section exists - /// You can run this check before running section() - bool containsSection(std::string sectionName, bool strict); - - /// Returns a pointer to const for a section of the library - /// which is essentially a PathContainer. - /// If the section does not exists it returns a pointer to an empty path. - const PathContainer* section(std::string sectionName, bool strict); - - /// Searches the library for an item and returns a boost path to it - /// Optionally you can provide a specific section - /// The result is the first that comes up according to alphabetical - /// section naming - boost::filesystem::path locate(std::string item, bool strict, bool ignoreExtensions, std::string sectionName=""); - - /// Prints all the available sections, used for debugging - void printSections(); - }; -} - -#endif diff --git a/components/files/fileops.cpp b/components/files/fileops.cpp deleted file mode 100644 index fbc2eef05..000000000 --- a/components/files/fileops.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#include "fileops.hpp" - -#include - -#include -#include -#include <../components/misc/stringops.hpp> - -namespace Files -{ - -bool isFile(const char *name) -{ - return boost::filesystem::exists(boost::filesystem::path(name)); -} - - // Returns true if the last part of the superset matches the subset - bool endingMatches(const std::string& superset, const std::string& subset) - { - if (subset.length() > superset.length()) - return false; - return superset.substr(superset.length() - subset.length()) == subset; - } - - // Makes a list of files from a directory - void FileLister( boost::filesystem::path currentPath, Files::PathContainer& list, bool recursive) - { - if (!boost::filesystem::exists(currentPath)) - { - std::cout << "WARNING: " << currentPath.string() << " does not exist.\n"; - return ; - } - if (recursive) - { - for ( boost::filesystem::recursive_directory_iterator end, itr(currentPath.string()); - itr != end; ++itr ) - { - if ( boost::filesystem::is_regular_file(*itr)) - list.push_back(itr->path()); - } - } - else - { - for ( boost::filesystem::directory_iterator end, itr(currentPath.string()); - itr != end; ++itr ) - { - if ( boost::filesystem::is_regular_file(*itr)) - list.push_back(itr->path()); - } - } - } - - // Locates path in path container - boost::filesystem::path FileListLocator (const Files::PathContainer& list, const boost::filesystem::path& toFind, - bool strict, bool ignoreExtensions) - { - boost::filesystem::path result(""); - if (list.empty()) - return result; - - std::string toFindStr; - if (ignoreExtensions) - toFindStr = boost::filesystem::basename(toFind); - else - toFindStr = toFind.string(); - - std::string fullPath; - - // The filesystems slash sets the default slash - std::string slash; - std::string wrongslash; - if(list[0].string().find("\\") != std::string::npos) - { - slash = "\\"; - wrongslash = "/"; - } - else - { - slash = "/"; - wrongslash = "\\"; - } - - // The file being looked for is converted to the new slash - if(toFindStr.find(wrongslash) != std::string::npos ) - { - boost::replace_all(toFindStr, wrongslash, slash); - } - - if (!strict) - { - Misc::StringUtils::toLower(toFindStr); - } - - for (Files::PathContainer::const_iterator it = list.begin(); it != list.end(); ++it) - { - fullPath = it->string(); - if (ignoreExtensions) - fullPath.erase(fullPath.length() - - boost::filesystem::path (it->extension()).string().length()); - - if (!strict) - { - Misc::StringUtils::toLower(fullPath); - } - if(endingMatches(fullPath, toFindStr)) - { - result = *it; - break; - } - } - return result; - } - - // Overloaded form of the locator that takes a string and returns a string - std::string FileListLocator (const Files::PathContainer& list,const std::string& toFind, bool strict, bool ignoreExtensions) - { - return FileListLocator(list, boost::filesystem::path(toFind), strict, ignoreExtensions).string(); - } - -} diff --git a/components/files/fileops.hpp b/components/files/fileops.hpp deleted file mode 100644 index bf1c51485..000000000 --- a/components/files/fileops.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef COMPONENTS_FILES_FILEOPS_HPP -#define COMPONENTS_FILES_FILEOPS_HPP - -#include -#include -#include - -#include - -namespace Files -{ - -///\brief Check if a given path is an existing file (not a directory) -///\param [in] name - filename -bool isFile(const char *name); - - /// A vector of Boost Paths, very handy - typedef std::vector PathContainer; - - /// Makes a list of files from a directory by taking a boost - /// path and a Path Container and adds to the Path container - /// all files in the path. It has a recursive option. - void FileLister( boost::filesystem::path currentPath, Files::PathContainer& list, bool recursive); - - /// Locates boost path in path container - /// returns the path from the container - /// that contains the searched path. - /// If it's not found it returns and empty path - /// Takes care of slashes, backslashes and it has a strict option. - boost::filesystem::path FileListLocator (const Files::PathContainer& list, const boost::filesystem::path& toFind, - bool strict, bool ignoreExtensions); - - /// Overloaded form of the locator that takes a string and returns a string - std::string FileListLocator (const Files::PathContainer& list,const std::string& toFind, bool strict, bool ignoreExtensions); - -} - -#endif /* COMPONENTS_FILES_FILEOPS_HPP */ From c0d07fbdc9023b76853e2a0b20d9649e9eca1f0a Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sat, 14 Dec 2013 15:07:00 +0200 Subject: [PATCH 147/889] improvement to bug fix http://bugs.openmw.org/issues/428 --- apps/openmw/mwinput/inputmanagerimp.cpp | 45 +++++++++++++------------ apps/openmw/mwmechanics/character.cpp | 19 ++++++----- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 9b7fe98bd..efa326a98 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -180,7 +180,9 @@ namespace MWInput switch (action) { case A_GameMenu: - toggleMainMenu (); + if(!(MWWorld::Class::get(mPlayer->getPlayer()).getCreatureStats(mPlayer->getPlayer()).isDead() + && MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_MainMenu ) ) + toggleMainMenu (); break; case A_Screenshot: screenshot(); @@ -300,7 +302,9 @@ namespace MWInput return; // Disable movement in Gui mode - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; + if (MWBase::Environment::get().getWindowManager()->isGuiMode() + || MWWorld::Class::get(mPlayer->getPlayer()).getCreatureStats(mPlayer->getPlayer()).isDead() ) + return; // Configure player movement according to keyboard input. Actual movement will @@ -367,29 +371,28 @@ namespace MWInput } } - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); - if ( !player.getClass().getCreatureStats(player).isDead() ) { - if (mControlSwitch["playerviewswitch"]) { - // work around preview mode toggle when pressing Alt+Tab - if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) { + + if (mControlSwitch["playerviewswitch"]) { + // work around preview mode toggle when pressing Alt+Tab + if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) { - if (mPreviewPOVDelay <= 0.5 && - (mPreviewPOVDelay += dt) > 0.5) - { - mPreviewPOVDelay = 1.f; - MWBase::Environment::get().getWorld()->togglePreviewMode(true); - } - } else { - if (mPreviewPOVDelay > 0.5) { - //disable preview mode - MWBase::Environment::get().getWorld()->togglePreviewMode(false); - } else if (mPreviewPOVDelay > 0.f) { - MWBase::Environment::get().getWorld()->togglePOV(); - } - mPreviewPOVDelay = 0.f; + if (mPreviewPOVDelay <= 0.5 && + (mPreviewPOVDelay += dt) > 0.5) + { + mPreviewPOVDelay = 1.f; + MWBase::Environment::get().getWorld()->togglePreviewMode(true); } + } else { + if (mPreviewPOVDelay > 0.5) { + //disable preview mode + MWBase::Environment::get().getWorld()->togglePreviewMode(false); + } else if (mPreviewPOVDelay > 0.f) { + MWBase::Environment::get().getWorld()->togglePOV(); + } + mPreviewPOVDelay = 0.f; } } + } if (actionIsActive(A_MoveForward) || actionIsActive(A_MoveBackward) || diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c8eba20d6..1c4979e37 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1061,11 +1061,11 @@ bool CharacterController::kill() if( isDead() ) { //player death animation is over: toggle game menu without 'return' option - if( mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() - && !isAnimPlaying(mCurrentDeath) ) + if( mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() + && !isAnimPlaying(mCurrentDeath) && MWBase::Environment::get().getWindowManager()->getMode () != MWGui::GM_MainMenu ) { - MWBase::Environment::get().getWindowManager()->setMainMenuNoReturn(true); - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setHealth(0); + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); } return false; } @@ -1106,10 +1106,13 @@ bool CharacterController::kill() if(mAnimation) { - if (mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() - && MWBase::Environment::get().getWorld()->getCamera()->isFirstPerson() ) - MWBase::Environment::get().getWorld()->togglePOV(); - + if (mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) + { + MWBase::Environment::get().getWindowManager()->setMainMenuNoReturn(true); + if (MWBase::Environment::get().getWorld()->getCamera()->isFirstPerson() ) + MWBase::Environment::get().getWorld()->togglePOV(); + } + mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); mAnimation->disable(mCurrentIdle); From fc21dd1f3afc44ba13a3c868690964b6b636daf9 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 14 Dec 2013 17:21:20 +0400 Subject: [PATCH 148/889] Music playback on OS X >= 10.9 works again. Fixes bug #1041. --- apps/openmw/mwsound/openal_output.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 4ee754b35..868b4bdbd 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -316,8 +316,23 @@ void OpenAL_SoundStream::play() throwALerror(); mSamplesQueued = 0; - for(ALuint i = 0;i < sNumBuffers;i++) - alBufferData(mBuffers[i], mFormat, this, 0, mSampleRate); + int srate; + ChannelConfig chans; + SampleType sampleType; + + mDecoder->getInfo(&srate, &chans, &sampleType); + + // Use exactly one sample of silence. + // This is required for OpenAL implementations that don't accept empty buffer data. + // (like one in OS X 10.9) + ALuint sampleSize = framesToBytes(1, chans, sampleType); + std::vector silenceSample(sampleSize); + + if (sampleType == SampleType_UInt8) + std::fill(silenceSample.begin(), silenceSample.end(), 0x80); + + for(ALuint i = 0;i < sNumBuffers;i++) + alBufferData(mBuffers[i], mFormat, &silenceSample[0], sampleSize, mSampleRate); throwALerror(); alSourceQueueBuffers(mSource, sNumBuffers, mBuffers); From 4bc4af6bf0116ef1df832cca56395ea6023d7905 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Dec 2013 22:01:24 +0100 Subject: [PATCH 149/889] Enable microcode caching for Ogre 1.9+ --- extern/shiny/Platforms/Ogre/OgrePlatform.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/extern/shiny/Platforms/Ogre/OgrePlatform.cpp b/extern/shiny/Platforms/Ogre/OgrePlatform.cpp index 3725d5f35..9f309fbcd 100644 --- a/extern/shiny/Platforms/Ogre/OgrePlatform.cpp +++ b/extern/shiny/Platforms/Ogre/OgrePlatform.cpp @@ -64,8 +64,11 @@ namespace sh bool OgrePlatform::supportsShaderSerialization () { - // Not very reliable in OpenGL mode (requires extension), and somehow doesn't work on linux even if the extension is present + #if OGRE_VERSION >= (1 << 16 | 9 << 8 | 0) + return true; + #else return Ogre::Root::getSingleton ().getRenderSystem ()->getName ().find("OpenGL") == std::string::npos; + #endif } bool OgrePlatform::supportsMaterialQueuedListener () @@ -110,10 +113,15 @@ namespace sh void OgrePlatform::serializeShaders (const std::string& file) { - std::fstream output; - output.open(file.c_str(), std::ios::out | std::ios::binary); - Ogre::DataStreamPtr shaderCache (OGRE_NEW Ogre::FileStreamDataStream(file, &output, false)); - Ogre::GpuProgramManager::getSingleton().saveMicrocodeCache(shaderCache); + #if OGRE_VERSION >= (1 << 16 | 9 << 8 | 0) + if (Ogre::GpuProgramManager::getSingleton().isCacheDirty()) + #endif + { + std::fstream output; + output.open(file.c_str(), std::ios::out | std::ios::binary); + Ogre::DataStreamPtr shaderCache (OGRE_NEW Ogre::FileStreamDataStream(file, &output, false)); + Ogre::GpuProgramManager::getSingleton().saveMicrocodeCache(shaderCache); + } } void OgrePlatform::deserializeShaders (const std::string& file) @@ -143,7 +151,7 @@ namespace sh else if (typeid(*value) == typeid(IntValue)) type = Ogre::GCT_INT1; else - assert(0); + throw std::runtime_error("unexpected type"); params->addConstantDefinition(name, type); mSharedParameters[name] = params; } From 3590fa40bd13c3e0b95ee8783b685266e2a261c1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 15 Dec 2013 16:16:50 +0100 Subject: [PATCH 150/889] store global script state in saved game files --- apps/openmw/mwscript/globalscripts.cpp | 58 ++++++++++++++++++++++ apps/openmw/mwscript/globalscripts.hpp | 17 +++++++ apps/openmw/mwscript/locals.cpp | 66 +++++++++++++++++++++++++ apps/openmw/mwscript/locals.hpp | 4 ++ apps/openmw/mwstate/statemanagerimp.cpp | 7 +++ components/CMakeLists.txt | 2 +- components/compiler/locals.cpp | 42 ++++++++-------- components/compiler/locals.hpp | 20 ++++---- components/esm/defs.hpp | 1 + components/esm/globalscript.cpp | 25 ++++++++++ components/esm/globalscript.hpp | 24 +++++++++ components/esm/locals.cpp | 28 +++++++++++ components/esm/locals.hpp | 27 ++++++++++ components/esm/variant.hpp | 2 +- 14 files changed, 290 insertions(+), 33 deletions(-) create mode 100644 components/esm/globalscript.cpp create mode 100644 components/esm/globalscript.hpp create mode 100644 components/esm/locals.cpp create mode 100644 components/esm/locals.hpp diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 1400d6675..8f269a015 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "../mwworld/esmstore.hpp" @@ -90,4 +91,61 @@ namespace MWScript addScript (iter->mScript); } } + + int GlobalScripts::countSavedGameRecords() const + { + return mScripts.size(); + } + + void GlobalScripts::write (ESM::ESMWriter& writer) const + { + for (std::map >::const_iterator iter (mScripts.begin()); + iter!=mScripts.end(); ++iter) + { + ESM::GlobalScript script; + + script.mId = iter->first; + + iter->second.second.write (script.mLocals, iter->first); + + script.mRunning = iter->second.first ? 1 : 0; + + writer.startRecord (ESM::REC_GSCR); + script.save (writer); + writer.endRecord (ESM::REC_GSCR); + } + } + + bool GlobalScripts::readRecord (ESM::ESMReader& reader, int32_t type) + { + if (type==ESM::REC_GSCR) + { + ESM::GlobalScript script; + script.load (reader); + + std::map >::iterator iter = + mScripts.find (script.mId); + + if (iter==mScripts.end()) + { + if (const ESM::Script *scriptRecord = mStore.get().search (script.mId)) + { + std::pair data (false, Locals()); + + data.second.configure (*scriptRecord); + + iter = mScripts.insert (std::make_pair (script.mId, data)).first; + } + else // script does not exist anymore + return true; + } + + iter->second.first = script.mRunning!=0; + iter->second.second.read (script.mLocals, script.mId); + + return true; + } + + return false; + } } diff --git a/apps/openmw/mwscript/globalscripts.hpp b/apps/openmw/mwscript/globalscripts.hpp index 67b619d1a..cf716c8e4 100644 --- a/apps/openmw/mwscript/globalscripts.hpp +++ b/apps/openmw/mwscript/globalscripts.hpp @@ -4,8 +4,16 @@ #include #include +#include + #include "locals.hpp" +namespace ESM +{ + class ESMWriter; + class ESMReader; +} + namespace MWWorld { struct ESMStore; @@ -35,6 +43,15 @@ namespace MWScript void addStartup(); ///< Add startup script + + int countSavedGameRecords() const; + + void write (ESM::ESMWriter& writer) const; + + bool readRecord (ESM::ESMReader& reader, int32_t type); + ///< Records for variables that do not exist are dropped silently. + /// + /// \return Known type? }; } diff --git a/apps/openmw/mwscript/locals.cpp b/apps/openmw/mwscript/locals.cpp index 180a2791b..094fe085a 100644 --- a/apps/openmw/mwscript/locals.cpp +++ b/apps/openmw/mwscript/locals.cpp @@ -1,6 +1,8 @@ #include "locals.hpp" #include +#include +#include #include @@ -65,4 +67,68 @@ namespace MWScript } return false; } + + void Locals::write (ESM::Locals& locals, const std::string& script) const + { + const Compiler::Locals& declarations = + MWBase::Environment::get().getScriptManager()->getLocals(script); + + for (int i=0; i<3; ++i) + { + char type = 0; + + switch (i) + { + case 0: type = 's'; break; + case 1: type = 'l'; break; + case 2: type = 'f'; break; + } + + const std::vector& names = declarations.get (type); + + for (int i2=0; i2 (names.size()); ++i2) + { + ESM::Variant value; + + switch (i) + { + case 0: value.setType (ESM::VT_Int); value.setInteger (mShorts.at (i2)); break; + case 1: value.setType (ESM::VT_Int); value.setInteger (mLongs.at (i2)); break; + case 2: value.setType (ESM::VT_Float); value.setFloat (mFloats.at (i2)); break; + } + + locals.mVariables.push_back (std::make_pair (names[i2], value)); + } + } + } + + void Locals::read (const ESM::Locals& locals, const std::string& script) + { + const Compiler::Locals& declarations = + MWBase::Environment::get().getScriptManager()->getLocals(script); + + for (std::vector >::const_iterator iter + = locals.mVariables.begin(); iter!=locals.mVariables.end(); ++iter) + { + char type = declarations.getType (iter->first); + char index = declarations.getIndex (iter->first); + + try + { + switch (type) + { + case 's': mShorts.at (index) = iter->second.getInteger(); break; + case 'l': mLongs.at (index) = iter->second.getInteger(); break; + case 'f': mFloats.at (index) = iter->second.getFloat(); break; + + // silently ignore locals that don't exist anymore + } + } + catch (...) + { + // ignore type changes + /// \todo write to log + } + } + } } diff --git a/apps/openmw/mwscript/locals.hpp b/apps/openmw/mwscript/locals.hpp index deae0d44e..d17d1be2d 100644 --- a/apps/openmw/mwscript/locals.hpp +++ b/apps/openmw/mwscript/locals.hpp @@ -8,6 +8,7 @@ namespace ESM { struct Script; + struct Locals; } namespace MWScript @@ -23,6 +24,9 @@ namespace MWScript bool setVarByInt(const std::string& script, const std::string& var, int val); int getIntVar (const std::string& script, const std::string& var); ///< if var does not exist, returns 0 + void write (ESM::Locals& locals, const std::string& script) const; + + void read (const ESM::Locals& locals, const std::string& script); }; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index e1086f121..93650fc44 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -114,6 +114,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot 1 // saved game header +MWBase::Environment::get().getJournal()->countSavedGameRecords() +MWBase::Environment::get().getWorld()->countSavedGameRecords() + +MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords() ); writer.save (stream); @@ -124,6 +125,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot MWBase::Environment::get().getJournal()->write (writer); MWBase::Environment::get().getWorld()->write (writer); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer); writer.close(); @@ -173,6 +175,11 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl MWBase::Environment::get().getWorld()->readRecord (reader, n.val); break; + case ESM::REC_GSCR: + + MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val); + break; + default: // ignore invalid records diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 3223ab1a7..6f01216f1 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -40,7 +40,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame journalentry queststate + savedgame journalentry queststate locals globalscript ) add_component_dir (misc diff --git a/components/compiler/locals.cpp b/components/compiler/locals.cpp index d93e73849..e2b1c5c96 100644 --- a/components/compiler/locals.cpp +++ b/components/compiler/locals.cpp @@ -15,27 +15,27 @@ namespace Compiler { case 's': return mShorts; case 'l': return mLongs; - case 'f': return mFloats; + case 'f': return mFloats; } - + throw std::logic_error ("unknown variable type"); } - + int Locals::searchIndex (char type, const std::string& name) const { const std::vector& collection = get (type); - + std::vector::const_iterator iter = std::find (collection.begin(), collection.end(), name); - + if (iter==collection.end()) return -1; - + return iter-collection.begin(); } - + bool Locals::search (char type, const std::string& name) const - { + { return searchIndex (type, name)!=-1; } @@ -45,10 +45,10 @@ namespace Compiler { case 's': return mShorts; case 'l': return mLongs; - case 'f': return mFloats; + case 'f': return mFloats; } - - throw std::logic_error ("unknown variable type"); + + throw std::logic_error ("unknown variable type"); } char Locals::getType (const std::string& name) const @@ -58,35 +58,35 @@ namespace Compiler if (search ('l', name)) return 'l'; - + if (search ('f', name)) return 'f'; - + return ' '; } - + int Locals::getIndex (const std::string& name) const { int index = searchIndex ('s', name); - + if (index!=-1) return index; - + index = searchIndex ('l', name); if (index!=-1) return index; - return searchIndex ('f', name); + return searchIndex ('f', name); } - + void Locals::write (std::ostream& localFile) const { localFile << get ('s').size() << ' ' << get ('l').size() << ' ' << get ('f').size() << std::endl; - + std::copy (get ('s').begin(), get ('s').end(), std::ostream_iterator (localFile, " ")); std::copy (get ('l').begin(), get ('l').end(), @@ -94,12 +94,12 @@ namespace Compiler std::copy (get ('f').begin(), get ('f').end(), std::ostream_iterator (localFile, " ")); } - + void Locals::declare (char type, const std::string& name) { get (type).push_back (name); } - + void Locals::clear() { get ('s').clear(); diff --git a/components/compiler/locals.hpp b/components/compiler/locals.hpp index e54b7798c..d5bf05253 100644 --- a/components/compiler/locals.hpp +++ b/components/compiler/locals.hpp @@ -8,35 +8,35 @@ namespace Compiler { /// \brief Local variable declarations - + class Locals { std::vector mShorts; std::vector mLongs; std::vector mFloats; - + int searchIndex (char type, const std::string& name) const; bool search (char type, const std::string& name) const; - - std::vector& get (char type); - + + std::vector& get (char type); + public: - + char getType (const std::string& name) const; ///< 's': short, 'l': long, 'f': float, ' ': does not exist. - + int getIndex (const std::string& name) const; ///< return index for local variable \a name (-1: does not exist). - + const std::vector& get (char type) const; void write (std::ostream& localFile) const; ///< write declarations to file. - + void declare (char type, const std::string& name); ///< declares a variable. - + void clear(); ///< remove all declarations. }; diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 03091d9d8..4cf0b1dac 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -87,6 +87,7 @@ enum RecNameInts REC_SAVE = 0x45564153, REC_JOUR = 0x524f55a4, REC_QUES = 0x53455551, + REC_GSCR = 0x52435347, // format 1 REC_FILT = 0x544C4946 diff --git a/components/esm/globalscript.cpp b/components/esm/globalscript.cpp new file mode 100644 index 000000000..dcbd91140 --- /dev/null +++ b/components/esm/globalscript.cpp @@ -0,0 +1,25 @@ + +#include "globalscript.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::GlobalScript::load (ESMReader &esm) +{ + mId = esm.getHNString ("NAME"); + + mLocals.load (esm); + + mRunning = 0; + esm.getHNOT (mRunning, "RUN_"); +} + +void ESM::GlobalScript::save (ESMWriter &esm) const +{ + esm.writeHNString ("NAME", mId); + + mLocals.save (esm); + + if (mRunning) + esm.writeHNT ("RUN_", mRunning); +} \ No newline at end of file diff --git a/components/esm/globalscript.hpp b/components/esm/globalscript.hpp new file mode 100644 index 000000000..4fb8b7c48 --- /dev/null +++ b/components/esm/globalscript.hpp @@ -0,0 +1,24 @@ +#ifndef OPENMW_ESM_GLOBALSCRIPT_H +#define OPENMW_ESM_GLOBALSCRIPT_H + +#include "locals.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + /// \brief Storage structure for global script state (only used in saved games) + + struct GlobalScript + { + std::string mId; + Locals mLocals; + int mRunning; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif diff --git a/components/esm/locals.cpp b/components/esm/locals.cpp new file mode 100644 index 000000000..9c470a025 --- /dev/null +++ b/components/esm/locals.cpp @@ -0,0 +1,28 @@ + +#include "locals.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::Locals::load (ESMReader &esm) +{ + while (esm.isNextSub ("LOCA")) + { + std::string id = esm.getHString(); + + Variant value; + value.read (esm, Variant::Format_Info); + + mVariables.push_back (std::make_pair (id, value)); + } +} + +void ESM::Locals::save (ESMWriter &esm) const +{ + for (std::vector >::const_iterator iter (mVariables.begin()); + iter!=mVariables.end(); ++iter) + { + esm.writeHNString ("LOCA", iter->first); + iter->second.write (esm, Variant::Format_Info); + } +} \ No newline at end of file diff --git a/components/esm/locals.hpp b/components/esm/locals.hpp new file mode 100644 index 000000000..af5afb23b --- /dev/null +++ b/components/esm/locals.hpp @@ -0,0 +1,27 @@ +#ifndef OPENMW_ESM_LOCALS_H +#define OPENMW_ESM_LOCALS_H + +#include +#include + +#include "variant.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + /// \brief Storage structure for local variables (only used in saved games) + /// + /// \note This is not a top-level record. + + struct Locals + { + std::vector > mVariables; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif diff --git a/components/esm/variant.hpp b/components/esm/variant.hpp index 2bba60a15..8ba9bb34f 100644 --- a/components/esm/variant.hpp +++ b/components/esm/variant.hpp @@ -33,7 +33,7 @@ namespace ESM { Format_Global, Format_Gmst, - Format_Info + Format_Info // also used for local variables in saved game files }; Variant(); From bf4ffe94dc4a6cb27f71c16911037094fe446bfa Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 15 Dec 2013 16:19:45 +0100 Subject: [PATCH 151/889] fixed a memory leak in the script record --- components/esm/loadscpt.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index 30460c17a..de679e815 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -26,24 +26,24 @@ void Script::load(ESMReader &esm) if (esm.isNextSub("SCVR")) { int s = mData.mStringTableSize; - char* tmp = new char[s]; - esm.getHExact(tmp, s); + + std::vector tmp (s); + esm.getHExact (&tmp[0], s); // Set up the list of variable names mVarNames.resize(mData.mNumShorts + mData.mNumLongs + mData.mNumFloats); // The tmp buffer is a null-byte separated string list, we // just have to pick out one string at a time. - char* str = tmp; + char* str = &tmp[0]; for (size_t i = 0; i < mVarNames.size(); i++) { mVarNames[i] = std::string(str); str += mVarNames[i].size() + 1; - if (str - tmp > s) + if (str - &tmp[0] > s) esm.fail("String table overflow"); } - delete[] tmp; } // Script mData From fd9f8c34f6065d2645db9bd1443df00939fa020a Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sun, 15 Dec 2013 18:50:25 +0200 Subject: [PATCH 152/889] bug fix http://bugs.openmw.org/issues/428 --- apps/openmw/mwbase/world.hpp | 3 +++ apps/openmw/mwinput/inputmanagerimp.cpp | 7 +++-- apps/openmw/mwmechanics/actors.cpp | 16 +++++------- apps/openmw/mwmechanics/character.cpp | 32 ++++++++++++++++++++--- apps/openmw/mwmechanics/character.hpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 5 ++++ apps/openmw/mwrender/renderingmanager.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 5 ++++ apps/openmw/mwworld/worldimp.hpp | 2 ++ 9 files changed, 58 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 740114aff..ea8619b3a 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -43,6 +43,7 @@ namespace MWRender { class ExternalRendering; class Animation; + class Camera; } namespace MWMechanics @@ -112,6 +113,8 @@ namespace MWBase virtual MWWorld::CellStore *getInterior (const std::string& name) = 0; + virtual MWRender::Camera* getCamera() const = 0; + virtual void setWaterHeight(const float height) = 0; virtual void toggleWater() = 0; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 850a62bec..47fb979d7 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -181,7 +181,8 @@ namespace MWInput switch (action) { case A_GameMenu: - toggleMainMenu (); + if(MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_Running) + toggleMainMenu (); break; case A_Screenshot: screenshot(); @@ -301,7 +302,9 @@ namespace MWInput return; // Disable movement in Gui mode - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; + if (MWBase::Environment::get().getWindowManager()->isGuiMode() + || MWWorld::Class::get(mPlayer->getPlayer()).getCreatureStats(mPlayer->getPlayer()).isDead() ) + return; // Configure player movement according to keyboard input. Actual movement will diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 19c2dac0c..c7ab0322d 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -571,19 +571,15 @@ namespace MWMechanics stats.resurrect(); continue; } - - MWBase::Environment::get().getStateManager()->endGame(); } - if(iter->second->isDead()) - continue; + if (iter->second->kill()) + { + ++mDeathCount[cls.getId(iter->first)]; - iter->second->kill(); - - ++mDeathCount[cls.getId(iter->first)]; - - if(cls.isEssential(iter->first)) - MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); + if(cls.isEssential(iter->first)) + MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); + } } } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 68f87ef4c..7c7f40e97 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -26,12 +26,14 @@ #include "creaturestats.hpp" #include "security.hpp" +#include "../mwrender/camera.hpp" #include "../mwrender/animation.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/statemanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" @@ -1052,10 +1054,20 @@ void CharacterController::forceStateUpdate() } } -void CharacterController::kill() +bool CharacterController::kill() { - if(mDeathState != CharState_None) - return; + if( isDead() ) + { + //state=end game only when player's death animation is over + if( mPtr.getRefData().getHandle()=="player" && !isAnimPlaying(mCurrentDeath) + && MWBase::Environment::get().getWindowManager()->getMode () != MWGui::GM_MainMenu ) + { + MWBase::Environment::get().getStateManager()->endGame(); + MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setHealth(0); + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + } + return false; + } if(mPtr.getTypeName() == typeid(ESM::NPC).name()) { @@ -1093,6 +1105,18 @@ void CharacterController::kill() if(mAnimation) { + //switch to 3rd person before player's death animation + if (mPtr.getRefData().getHandle()=="player") + { + if(MWBase::Environment::get().getWorld()->getCamera()->isVanityOrPreviewModeEnabled() ) + { + MWBase::Environment::get().getWorld()->getCamera()->togglePreviewMode(false); + MWBase::Environment::get().getWorld()->getCamera()->toggleVanityMode(false); + } + if(MWBase::Environment::get().getWorld()->getCamera()->isFirstPerson()) + MWBase::Environment::get().getWorld()->togglePOV(); + } + mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); mAnimation->disable(mCurrentIdle); @@ -1100,6 +1124,8 @@ void CharacterController::kill() mIdleState = CharState_None; mCurrentIdle.clear(); + + return true; } void CharacterController::resurrect() diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 0b55534a6..5e7a7c6d4 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -188,7 +188,7 @@ public: void skipAnim(); bool isAnimPlaying(const std::string &groupName); - void kill(); + bool kill(); void resurrect(); bool isDead() const { return mDeathState != CharState_None; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 2eb2b1523..8aca3b489 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -221,6 +221,11 @@ OEngine::Render::Fader* RenderingManager::getFader() return mRendering.getFader(); } + MWRender::Camera* RenderingManager::getCamera() const +{ + return mCamera; +} + void RenderingManager::removeCell (MWWorld::CellStore *store) { mObjects.removeCell(store); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 2d0813912..5bbc3055d 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -98,6 +98,8 @@ public: SkyManager* getSkyManager(); Compositors* getCompositors(); + MWRender::Camera* getCamera() const; + void toggleLight(); bool toggleRenderMode(int mode); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c4e63fad0..2676be4b7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -400,6 +400,11 @@ namespace MWWorld return mCells.getInterior (name); } + MWRender::Camera* World::getCamera() const + { + return mRendering->getCamera(); + } + MWWorld::Player& World::getPlayer() { return *mPlayer; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 845668449..7c24adfa8 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -185,6 +185,8 @@ namespace MWWorld virtual CellStore *getInterior (const std::string& name); + virtual MWRender::Camera* getCamera() const; + virtual void setWaterHeight(const float height); virtual void toggleWater(); From 1cf1d49bc43d717db5b3d2f4ca6334cb721a423f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 16 Dec 2013 11:39:24 +0100 Subject: [PATCH 153/889] fix to content file reading error reporting in case of missing dependency --- apps/openmw/mwworld/esmstore.cpp | 2 +- components/esm/esmreader.cpp | 5 +++++ components/esm/esmreader.hpp | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index cb4e441fc..62b91c2e4 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -52,7 +52,7 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) if (index == (int)~0) { // Tried to load a parent file that has not been loaded yet. This is bad, // the launcher should have taken care of this. - std::string fstring = "File " + fname + " asks for parent file " + masters[j].name + std::string fstring = "File " + esm.getName() + " asks for parent file " + masters[j].name + ", but it has not been loaded yet. Please check your load order."; esm.fail(fstring); } diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 4e1860bab..ebdb1e41f 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -8,6 +8,11 @@ namespace ESM using namespace Misc; + std::string ESMReader::getName() const + { + return mCtx.filename; + } + ESM_Context ESMReader::getContext() { // Update the file position before returning diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 3bf194c4e..897c8fe73 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -38,6 +38,7 @@ public: int getFormat() const; const NAME &retSubName() const { return mCtx.subName; } uint32_t getSubSize() const { return mCtx.leftSub; } + std::string getName() const; /************************************************************************* * From cd756a8a398d83034517cd1f5ae9d0ce0bb828fe Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Dec 2013 13:22:27 +0100 Subject: [PATCH 154/889] Fix incorrect value for partially used items (missing float casts). Make sure the correct value is displayed in tooltips. --- apps/openmw/mwclass/apparatus.cpp | 2 +- apps/openmw/mwclass/armor.cpp | 4 ++-- apps/openmw/mwclass/book.cpp | 2 +- apps/openmw/mwclass/clothing.cpp | 2 +- apps/openmw/mwclass/ingredient.cpp | 2 +- apps/openmw/mwclass/light.cpp | 2 +- apps/openmw/mwclass/lockpick.cpp | 4 ++-- apps/openmw/mwclass/potion.cpp | 2 +- apps/openmw/mwclass/probe.cpp | 4 ++-- apps/openmw/mwclass/repair.cpp | 4 ++-- apps/openmw/mwclass/weapon.cpp | 4 ++-- 11 files changed, 16 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 697b75579..53c62273d 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -124,7 +124,7 @@ namespace MWClass std::string text; text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); + text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index f3f36542a..5b2b7caa3 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -172,7 +172,7 @@ namespace MWClass if (ptr.getCellRef().mCharge == -1) return ref->mBase->mData.mValue; else - return ref->mBase->mData.mValue * (ptr.getCellRef().mCharge / getItemMaxHealth(ptr)); + return ref->mBase->mData.mValue * (static_cast(ptr.getCellRef().mCharge) / getItemMaxHealth(ptr)); } void Armor::registerSelf() @@ -248,7 +248,7 @@ namespace MWClass + MWGui::ToolTips::toString(ref->mBase->mData.mHealth); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight) + " (" + typeText + ")"; - text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); + text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index b22cbc31f..1da920970 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -136,7 +136,7 @@ namespace MWClass std::string text; text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); + text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 8941f3627..c162bbe9d 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -191,7 +191,7 @@ namespace MWClass std::string text; text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); + text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 06d9d5d23..4296d4e1b 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -145,7 +145,7 @@ namespace MWClass std::string text; text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); + text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index a031d2556..6a6133cb9 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -183,7 +183,7 @@ namespace MWClass std::string text; text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); + text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 73b47d6af..e1dc5b2e1 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -89,7 +89,7 @@ namespace MWClass if (ptr.getCellRef().mCharge == -1) return ref->mBase->mData.mValue; else - return ref->mBase->mData.mValue * (ptr.getCellRef().mCharge / getItemMaxHealth(ptr)); + return ref->mBase->mData.mValue * (static_cast(ptr.getCellRef().mCharge) / getItemMaxHealth(ptr)); } void Lockpick::registerSelf() @@ -141,7 +141,7 @@ namespace MWClass text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses); text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); + text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 883473eb3..e276c58aa 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -128,7 +128,7 @@ namespace MWClass std::string text; text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); + text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); info.effects = MWGui::Widgets::MWEffectList::effectListFromESM(&ref->mBase->mEffects); diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 845c2a0d0..b54464acd 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -88,7 +88,7 @@ namespace MWClass if (ptr.getCellRef().mCharge == -1) return ref->mBase->mData.mValue; else - return ref->mBase->mData.mValue * (ptr.getCellRef().mCharge / getItemMaxHealth(ptr)); + return ref->mBase->mData.mValue * (static_cast(ptr.getCellRef().mCharge) / getItemMaxHealth(ptr)); } void Probe::registerSelf() @@ -140,7 +140,7 @@ namespace MWClass text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses); text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); + text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index dbfa9f0f6..ce2b4ff10 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -79,7 +79,7 @@ namespace MWClass if (ptr.getCellRef().mCharge == -1) return ref->mBase->mData.mValue; else - return ref->mBase->mData.mValue * (ptr.getCellRef().mCharge / getItemMaxHealth(ptr)); + return ref->mBase->mData.mValue * (static_cast(ptr.getCellRef().mCharge) / getItemMaxHealth(ptr)); } void Repair::registerSelf() @@ -144,7 +144,7 @@ namespace MWClass text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses); text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); + text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index b1bf2b0b7..a09e83380 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -157,7 +157,7 @@ namespace MWClass if (ptr.getCellRef().mCharge == -1) return ref->mBase->mData.mValue; else - return ref->mBase->mData.mValue * (ptr.getCellRef().mCharge / getItemMaxHealth(ptr)); + return ref->mBase->mData.mValue * (static_cast(ptr.getCellRef().mCharge) / getItemMaxHealth(ptr)); } void Weapon::registerSelf() @@ -346,7 +346,7 @@ namespace MWClass } text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); + text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); info.enchant = ref->mBase->mEnchant; From 56893a097def1184301af4c635a119856f25850e Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Dec 2013 13:31:03 +0100 Subject: [PATCH 155/889] Don't stack used torches --- apps/openmw/mwworld/class.cpp | 2 +- apps/openmw/mwworld/class.hpp | 2 +- apps/openmw/mwworld/containerstore.cpp | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index d3d1aff49..ffe81a4ac 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -139,7 +139,7 @@ namespace MWWorld float Class::getRemainingUsageTime (const Ptr& ptr) const { - throw std::runtime_error ("class does not support time-based uses"); + return -1; } std::string Class::getScript (const Ptr& ptr) const diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 2db293e68..cb6690a46 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -162,7 +162,7 @@ namespace MWWorld virtual float getRemainingUsageTime (const Ptr& ptr) const; ///< Returns the remaining duration of the object, such as an equippable light - /// source. (default implementation: throw an exception) + /// source. (default implementation: -1, i.e. infinite) virtual std::string getScript (const Ptr& ptr) const; ///< Return name of the script attached to ptr (default implementation: return an empty diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index d1d16ee01..3797e6922 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -109,6 +109,8 @@ bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& ptr2) && ptr1.getCellRef().mOwner == ptr2.getCellRef().mOwner && ptr1.getCellRef().mSoul == ptr2.getCellRef().mSoul + && ptr1.getClass().getRemainingUsageTime(ptr1) == ptr2.getClass().getRemainingUsageTime(ptr2) + && cls1.getScript(ptr1) == cls2.getScript(ptr2) // item that is already partly used up never stacks From f50ff0b1c4df394bc451e1fbd137e846cf81e39c Mon Sep 17 00:00:00 2001 From: mrcheko Date: Mon, 16 Dec 2013 15:40:47 +0200 Subject: [PATCH 156/889] reworked http://bugs.openmw.org/issues/428 --- apps/openmw/engine.cpp | 54 +++++++++++++------------ apps/openmw/mwbase/world.hpp | 3 +- apps/openmw/mwinput/inputmanagerimp.cpp | 9 +++-- apps/openmw/mwmechanics/actors.cpp | 1 - apps/openmw/mwmechanics/character.cpp | 19 +-------- apps/openmw/mwstate/statemanagerimp.cpp | 1 + apps/openmw/mwworld/worldimp.cpp | 10 ++++- apps/openmw/mwworld/worldimp.hpp | 4 +- 8 files changed, 49 insertions(+), 52 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 8fc36a5d5..97c2844eb 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -94,35 +94,39 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) MWBase::Environment::get().getSoundManager()->update(frametime); // global scripts + MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); + + bool changed = MWBase::Environment::get().getWorld()->hasCellChanged(); + + // local scripts + executeLocalScripts(); // This does not handle the case where a global script causes a + // cell change, followed by a cell change in a local script during + // the same frame. + + if (changed) // keep change flag for another frame, if cell changed happened in local script + MWBase::Environment::get().getWorld()->markCellAsUnchanged(); + + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) + MWBase::Environment::get().getWorld()->advanceTime( + frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); + + // update actors + MWBase::Environment::get().getMechanicsManager()->update(frametime, + MWBase::Environment::get().getWindowManager()->isGuiMode()); + if (MWBase::Environment::get().getStateManager()->getState()== MWBase::StateManager::State_Running) { - MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); - - bool changed = MWBase::Environment::get().getWorld()->hasCellChanged(); - - // local scripts - executeLocalScripts(); // This does not handle the case where a global script causes a - // cell change, followed by a cell change in a local script during - // the same frame. - - if (changed) // keep change flag for another frame, if cell changed happened in local script - MWBase::Environment::get().getWorld()->markCellAsUnchanged(); - - if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) - MWBase::Environment::get().getWorld()->advanceTime( - frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); - - // update actors - MWBase::Environment::get().getMechanicsManager()->update(frametime, - MWBase::Environment::get().getWindowManager()->isGuiMode()); - - // update world - MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode()); - - // update game state - MWBase::Environment::get().getStateManager()->update (frametime); + MWWorld::Ptr player = mEnvironment.getWorld()->getPlayer().getPlayer(); + if(MWWorld::Class::get(player).getCreatureStats(player).isDead()) + MWBase::Environment::get().getStateManager()->endGame(); } + + // update world + MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode()); + + // update game state + MWBase::Environment::get().getStateManager()->update (frametime); // update GUI Ogre::RenderWindow* window = mOgre->getWindow(); diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index ea8619b3a..f7041a47b 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -43,7 +43,6 @@ namespace MWRender { class ExternalRendering; class Animation; - class Camera; } namespace MWMechanics @@ -113,7 +112,7 @@ namespace MWBase virtual MWWorld::CellStore *getInterior (const std::string& name) = 0; - virtual MWRender::Camera* getCamera() const = 0; + virtual void useDeathCamera() = 0; virtual void setWaterHeight(const float height) = 0; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 47fb979d7..e6e349c4d 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -181,8 +181,9 @@ namespace MWInput switch (action) { case A_GameMenu: - if(MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_Running) - toggleMainMenu (); + if(!(MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running + && MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu)) + toggleMainMenu (); break; case A_Screenshot: screenshot(); @@ -303,8 +304,8 @@ namespace MWInput // Disable movement in Gui mode if (MWBase::Environment::get().getWindowManager()->isGuiMode() - || MWWorld::Class::get(mPlayer->getPlayer()).getCreatureStats(mPlayer->getPlayer()).isDead() ) - return; + || MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running) + return; // Configure player movement according to keyboard input. Actual movement will diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index c7ab0322d..7180eb883 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -19,7 +19,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" - #include "../mwbase/statemanager.hpp" #include "npcstats.hpp" #include "creaturestats.hpp" diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 7c7f40e97..9a19ac7fa 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -26,14 +26,12 @@ #include "creaturestats.hpp" #include "security.hpp" -#include "../mwrender/camera.hpp" #include "../mwrender/animation.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwbase/statemanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" @@ -1058,11 +1056,10 @@ bool CharacterController::kill() { if( isDead() ) { - //state=end game only when player's death animation is over + //player's death animation is over if( mPtr.getRefData().getHandle()=="player" && !isAnimPlaying(mCurrentDeath) - && MWBase::Environment::get().getWindowManager()->getMode () != MWGui::GM_MainMenu ) + && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_MainMenu ) { - MWBase::Environment::get().getStateManager()->endGame(); MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setHealth(0); MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); } @@ -1105,18 +1102,6 @@ bool CharacterController::kill() if(mAnimation) { - //switch to 3rd person before player's death animation - if (mPtr.getRefData().getHandle()=="player") - { - if(MWBase::Environment::get().getWorld()->getCamera()->isVanityOrPreviewModeEnabled() ) - { - MWBase::Environment::get().getWorld()->getCamera()->togglePreviewMode(false); - MWBase::Environment::get().getWorld()->getCamera()->toggleVanityMode(false); - } - if(MWBase::Environment::get().getWorld()->getCamera()->isFirstPerson()) - MWBase::Environment::get().getWorld()->togglePOV(); - } - mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); mAnimation->disable(mCurrentIdle); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 94219b8fc..bad4de03d 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -70,6 +70,7 @@ void MWState::StateManager::newGame (bool bypass) void MWState::StateManager::endGame() { mState = State_Ended; + MWBase::Environment::get().getWorld()->useDeathCamera(); } void MWState::StateManager::saveGame (const std::string& description, const Slot *slot) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2676be4b7..357101e45 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -400,9 +400,15 @@ namespace MWWorld return mCells.getInterior (name); } - MWRender::Camera* World::getCamera() const + void World::useDeathCamera() { - return mRendering->getCamera(); + if(mRendering->getCamera()->isVanityOrPreviewModeEnabled() ) + { + mRendering->getCamera()->togglePreviewMode(false); + mRendering->getCamera()->toggleVanityMode(false); + } + if(mRendering->getCamera()->isFirstPerson()) + togglePOV(); } MWWorld::Player& World::getPlayer() diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7c24adfa8..fb9c4cb68 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -42,6 +42,7 @@ namespace MWRender class SkyManager; class CellRender; class Animation; + class Camera; } struct ContentLoader; @@ -185,7 +186,8 @@ namespace MWWorld virtual CellStore *getInterior (const std::string& name); - virtual MWRender::Camera* getCamera() const; + //switch to POV before showing player's death animation + virtual void useDeathCamera(); virtual void setWaterHeight(const float height); From eb5e4ecec2f15dcc6ec22b12159d49b686c59427 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Dec 2013 15:35:06 +0100 Subject: [PATCH 157/889] Remove more unused code --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/world.hpp | 1 - apps/openmw/mwgui/loadingscreen.cpp | 36 +------- apps/openmw/mwrender/compositors.cpp | 108 ---------------------- apps/openmw/mwrender/compositors.hpp | 64 ------------- apps/openmw/mwrender/renderingmanager.cpp | 42 +-------- apps/openmw/mwrender/renderingmanager.hpp | 6 -- apps/openmw/mwrender/water.hpp | 2 - 8 files changed, 7 insertions(+), 254 deletions(-) delete mode 100644 apps/openmw/mwrender/compositors.cpp delete mode 100644 apps/openmw/mwrender/compositors.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 90fdd11ba..5a062575c 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -19,7 +19,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation actors objects renderinginterface localmap occlusionquery water shadows - compositors characterpreview externalrendering globalmap videoplayer ripplesimulation refraction + characterpreview externalrendering globalmap videoplayer ripplesimulation refraction terrainstorage ) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 8141af712..961d3d958 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -80,7 +80,6 @@ namespace MWBase Render_CollisionDebug, Render_Wireframe, Render_Pathgrid, - Render_Compositors, Render_BoundingBoxes }; diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 4bd383c2f..868b58209 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -1,8 +1,6 @@ #include "loadingscreen.hpp" #include -#include -#include #include @@ -199,28 +197,7 @@ namespace MWGui MWBase::Environment::get().getInputManager()->update(0, true); - Ogre::CompositorChain* chain = Ogre::CompositorManager::getSingleton().getCompositorChain(mWindow->getViewport(0)); - - bool hasCompositor = chain->getCompositor ("gbufferFinalizer"); - - - if (!hasCompositor) - { - mWindow->getViewport(0)->setClearEveryFrame(false); - } - else - { - if (!mFirstLoad) - { - mBackgroundMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(chain->getCompositor ("gbufferFinalizer")->getTextureInstance ("no_mrt_output", 0)->getName()); - mRectangle->setVisible(true); - } - - for (unsigned int i = 0; igetNumCompositors(); ++i) - { - Ogre::CompositorManager::getSingleton().setCompositorEnabled(mWindow->getViewport(0), chain->getCompositor(i)->getCompositor()->getName(), false); - } - } + mWindow->getViewport(0)->setClearEveryFrame(false); // First, swap buffers from last draw, then, queue an update of the // window contents, but don't swap buffers (which would have @@ -231,15 +208,8 @@ namespace MWGui mWindow->update(false); - if (!hasCompositor) - mWindow->getViewport(0)->setClearEveryFrame(true); - else - { - for (unsigned int i = 0; igetNumCompositors(); ++i) - { - Ogre::CompositorManager::getSingleton().setCompositorEnabled(mWindow->getViewport(0), chain->getCompositor(i)->getCompositor()->getName(), true); - } - } + mWindow->getViewport(0)->setClearEveryFrame(true); + mRectangle->setVisible(false); diff --git a/apps/openmw/mwrender/compositors.cpp b/apps/openmw/mwrender/compositors.cpp deleted file mode 100644 index b1c98a306..000000000 --- a/apps/openmw/mwrender/compositors.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include "compositors.hpp" - -#include -#include -#include -#include - -using namespace MWRender; - -Compositors::Compositors(Ogre::Viewport* vp) : - mViewport(vp) - , mEnabled(true) -{ -} - -Compositors::~Compositors() -{ - Ogre::CompositorManager::getSingleton().removeCompositorChain(mViewport); -} - -void Compositors::setEnabled (const bool enabled) -{ - for (CompositorMap::iterator it=mCompositors.begin(); - it != mCompositors.end(); ++it) - { - Ogre::CompositorManager::getSingleton().setCompositorEnabled(mViewport, it->first, enabled && it->second.first); - } - mEnabled = enabled; -} - -void Compositors::recreate() -{ - Ogre::CompositorManager::getSingleton().removeCompositorChain(mViewport); - - CompositorMap temp = mCompositors; - mCompositors.clear(); - - for (CompositorMap::iterator it=temp.begin(); - it != temp.end(); ++it) - { - addCompositor(it->first, it->second.second); - setCompositorEnabled(it->first, mEnabled && it->second.first); - } -} - -void Compositors::addCompositor (const std::string& name, const int priority) -{ - int id = 0; - - for (CompositorMap::iterator it=mCompositors.begin(); - it != mCompositors.end(); ++it) - { - if (it->second.second > priority) - break; - ++id; - } - Ogre::CompositorManager::getSingleton().addCompositor (mViewport, name, id); - - mCompositors[name] = std::make_pair(false, priority); -} - -void Compositors::setCompositorEnabled (const std::string& name, const bool enabled) -{ - mCompositors[name].first = enabled; - Ogre::CompositorManager::getSingleton().setCompositorEnabled (mViewport, name, enabled && mEnabled); -} - -void Compositors::removeAll() -{ - Ogre::CompositorManager::getSingleton().removeCompositorChain(mViewport); - - mCompositors.clear(); -} - -bool Compositors::anyCompositorEnabled() -{ - for (CompositorMap::iterator it=mCompositors.begin(); - it != mCompositors.end(); ++it) - { - if (it->second.first && mEnabled) - return true; - } - return false; -} - -void Compositors::countTrianglesBatches(unsigned int &triangles, unsigned int &batches) -{ - triangles = 0; - batches = 0; - - Ogre::CompositorInstance* c = NULL; - Ogre::CompositorChain* chain = Ogre::CompositorManager::getSingleton().getCompositorChain (mViewport); - // accumulate tris & batches from all compositors with all their render targets - for (unsigned int i=0; i < chain->getNumCompositors(); ++i) - { - if (chain->getCompositor(i)->getEnabled()) - { - c = chain->getCompositor(i); - for (unsigned int j = 0; j < c->getTechnique()->getNumTargetPasses(); ++j) - { - std::string textureName = c->getTechnique()->getTargetPass(j)->getOutputName(); - Ogre::RenderTarget* rt = c->getRenderTarget(textureName); - triangles += rt->getTriangleCount(); - batches += rt->getBatchCount(); - } - } - } -} diff --git a/apps/openmw/mwrender/compositors.hpp b/apps/openmw/mwrender/compositors.hpp deleted file mode 100644 index e5dd7503c..000000000 --- a/apps/openmw/mwrender/compositors.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef GAME_MWRENDER_COMPOSITORS_H -#define GAME_MWRENDER_COMPOSITORS_H - -#include -#include - -namespace Ogre -{ - class Viewport; -} - -namespace MWRender -{ - typedef std::map < std::string, std::pair > CompositorMap; - - /// \brief Manages a set of compositors for one viewport - class Compositors - { - public: - Compositors(Ogre::Viewport* vp); - virtual ~Compositors(); - - /** - * enable or disable all compositors globally - */ - void setEnabled (const bool enabled); - - void setViewport(Ogre::Viewport* vp) { mViewport = vp; } - - /// recreate compositors (call this after viewport size changes) - void recreate(); - - bool toggle() { setEnabled(!mEnabled); return mEnabled; } - - /** - * enable or disable a specific compositor - * @note enable has no effect if all compositors are globally disabled - */ - void setCompositorEnabled (const std::string& name, const bool enabled); - - /** - * @param name of compositor - * @param priority, lower number will be first in the chain - */ - void addCompositor (const std::string& name, const int priority); - - bool anyCompositorEnabled(); - - void countTrianglesBatches(unsigned int &triangles, unsigned int &batches); - - void removeAll (); - - protected: - /// maps compositor name to its "enabled" state - CompositorMap mCompositors; - - bool mEnabled; - - Ogre::Viewport* mViewport; - }; - -} - -#endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index b216c789f..0b10791b8 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -8,10 +8,6 @@ #include #include #include -#include -#include -#include -#include #include #include #include @@ -43,7 +39,6 @@ #include "shadows.hpp" #include "localmap.hpp" #include "water.hpp" -#include "compositors.hpp" #include "npcanimation.hpp" #include "externalrendering.hpp" #include "globalmap.hpp" @@ -87,8 +82,6 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b mRendering.getWindow()->addListener(this); mRendering.setWindowListener(this); - mCompositors = new Compositors(mRendering.getViewport()); - mWater = 0; // material system @@ -157,8 +150,6 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b sh::Factory::getInstance ().setGlobalSetting ("viewproj_fix", "false"); sh::Factory::getInstance ().setSharedParameter ("vpRow2Fix", sh::makeProperty (new sh::Vector4(0,0,0,0))); - applyCompositors(); - mRootNode = mRendering.getScene()->getRootSceneNode(); mRootNode->createChildSceneNode("player"); @@ -198,7 +189,6 @@ RenderingManager::~RenderingManager () delete mTerrain; delete mLocalMap; delete mOcclusionQuery; - delete mCompositors; delete mWater; delete mVideoPlayer; delete mActors; @@ -478,29 +468,21 @@ bool RenderingManager::toggleRenderMode(int mode) { if (mRendering.getCamera()->getPolygonMode() == PM_SOLID) { - mCompositors->setEnabled(false); - mRendering.getCamera()->setPolygonMode(PM_WIREFRAME); return true; } else { - mCompositors->setEnabled(true); - mRendering.getCamera()->setPolygonMode(PM_SOLID); return false; } } - else if (mode == MWBase::World::Render_BoundingBoxes) + else //if (mode == MWBase::World::Render_BoundingBoxes) { bool show = !mRendering.getScene()->getShowBoundingBoxes(); mRendering.getScene()->showBoundingBoxes(show); return show; } - else //if (mode == MWBase::World::Render_Compositors) - { - return mCompositors->toggle(); - } } void RenderingManager::configureFog(MWWorld::Ptr::CellStore &mCell) @@ -745,11 +727,6 @@ Ogre::Vector4 RenderingManager::boundingBoxToScreen(Ogre::AxisAlignedBox bounds) return Vector4(min_x, min_y, max_x, max_y); } -Compositors* RenderingManager::getCompositors() -{ - return mCompositors; -} - void RenderingManager::processChangedSettings(const Settings::CategorySettingVector& settings) { bool changeRes = false; @@ -795,7 +772,6 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec } else if (it->second == "shader" && it->first == "Water") { - applyCompositors(); sh::Factory::getInstance ().setGlobalSetting ("simple_water", Settings::Manager::getBool("shader", "Water") ? "false" : "true"); rebuild = true; mRendering.getViewport ()->setClearEveryFrame (true); @@ -883,28 +859,16 @@ void RenderingManager::windowResized(int x, int y) Settings::Manager::setInt("resolution x", "Video", x); Settings::Manager::setInt("resolution y", "Video", y); mRendering.adjustViewport(); - mCompositors->recreate(); mVideoPlayer->setResolution (x, y); MWBase::Environment::get().getWindowManager()->windowResized(x,y); } -void RenderingManager::applyCompositors() -{ -} - void RenderingManager::getTriangleBatchCount(unsigned int &triangles, unsigned int &batches) { - if (mCompositors->anyCompositorEnabled()) - { - mCompositors->countTrianglesBatches(triangles, batches); - } - else - { - triangles = mRendering.getWindow()->getTriangleCount(); - batches = mRendering.getWindow()->getBatchCount(); - } + batches = mRendering.getWindow()->getBatchCount(); + triangles = mRendering.getWindow()->getTriangleCount(); } void RenderingManager::setupPlayer(const MWWorld::Ptr &ptr) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index e5dcf0aeb..abc8fd71a 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -48,7 +48,6 @@ namespace MWRender class Shadows; class LocalMap; class Water; - class Compositors; class ExternalRendering; class GlobalMap; class VideoPlayer; @@ -96,7 +95,6 @@ public: void renderPlayer(const MWWorld::Ptr &ptr); SkyManager* getSkyManager(); - Compositors* getCompositors(); void toggleLight(); bool toggleRenderMode(int mode); @@ -224,8 +222,6 @@ private: void setMenuTransparency(float val); - void applyCompositors(); - bool mSunEnabled; MWWorld::Fallback* mFallback; @@ -269,8 +265,6 @@ private: MWRender::Shadows* mShadows; - MWRender::Compositors* mCompositors; - VideoPlayer* mVideoPlayer; }; diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index bc15b4980..481a41297 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -133,8 +133,6 @@ namespace MWRender { RenderingManager* mRendering; SkyManager* mSky; - std::string mCompositorName; - Ogre::MaterialPtr mMaterial; bool mUnderwaterEffect; From a854eb73db8e0629cf5c707b69ccbb7b2a1d4943 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Mon, 16 Dec 2013 21:02:21 +0200 Subject: [PATCH 158/889] StateRunning check returns --- apps/openmw/engine.cpp | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 97c2844eb..5b8872b0f 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -93,22 +93,30 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) if (mUseSound) MWBase::Environment::get().getSoundManager()->update(frametime); - // global scripts - MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); + if (MWBase::Environment::get().getStateManager()->getState()== + MWBase::StateManager::State_Running) + { + // global scripts + MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); - bool changed = MWBase::Environment::get().getWorld()->hasCellChanged(); + bool changed = MWBase::Environment::get().getWorld()->hasCellChanged(); - // local scripts - executeLocalScripts(); // This does not handle the case where a global script causes a - // cell change, followed by a cell change in a local script during - // the same frame. + // local scripts + executeLocalScripts(); // This does not handle the case where a global script causes a + // cell change, followed by a cell change in a local script during + // the same frame. - if (changed) // keep change flag for another frame, if cell changed happened in local script - MWBase::Environment::get().getWorld()->markCellAsUnchanged(); + if (changed) // keep change flag for another frame, if cell changed happened in local script + MWBase::Environment::get().getWorld()->markCellAsUnchanged(); + + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) + MWBase::Environment::get().getWorld()->advanceTime( + frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); + + // update game state + MWBase::Environment::get().getStateManager()->update (frametime); + } - if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) - MWBase::Environment::get().getWorld()->advanceTime( - frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); // update actors MWBase::Environment::get().getMechanicsManager()->update(frametime, @@ -125,9 +133,6 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) // update world MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode()); - // update game state - MWBase::Environment::get().getStateManager()->update (frametime); - // update GUI Ogre::RenderWindow* window = mOgre->getWindow(); unsigned int tri, batch; From da9b67b6d22b1e34ae7f3895f424a75fae04bd34 Mon Sep 17 00:00:00 2001 From: pvdk Date: Mon, 16 Dec 2013 20:40:58 +0100 Subject: [PATCH 159/889] Fix for Bug #922: Launcher writing merged openmw.cfg files --- apps/launcher/maindialog.cpp | 26 ++++++++++++++++ apps/launcher/settings/gamesettings.cpp | 41 ++++++++++++++++--------- apps/launcher/settings/gamesettings.hpp | 11 +++++++ 3 files changed, 63 insertions(+), 15 deletions(-) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 4012a1fbd..a6ac3d78d 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -443,6 +443,32 @@ bool Launcher::MainDialog::setupGameSettings() QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string()); + // Load the user config file first, separately + // So we can write it properly, uncontaminated + QString path = userPath + QLatin1String("openmw.cfg"); + QFile file(path); + + qDebug() << "Loading config file:" << qPrintable(path); + + if (file.exists()) { + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + QMessageBox msgBox; + msgBox.setWindowTitle(tr("Error opening OpenMW configuration file")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(QObject::tr("
Could not open %0 for reading

\ + Please make sure you have the right permissions \ + and try again.
").arg(file.fileName())); + msgBox.exec(); + return false; + } + QTextStream stream(&file); + stream.setCodec(QTextCodec::codecForName("UTF-8")); + + mGameSettings.readUserFile(stream); + } + + // Now the rest QStringList paths; paths.append(userPath + QString("openmw.cfg")); paths.append(QString("openmw.cfg")); diff --git a/apps/launcher/settings/gamesettings.cpp b/apps/launcher/settings/gamesettings.cpp index 41113c35a..e7e5cf1ea 100644 --- a/apps/launcher/settings/gamesettings.cpp +++ b/apps/launcher/settings/gamesettings.cpp @@ -90,6 +90,16 @@ QStringList Launcher::GameSettings::values(const QString &key, const QStringList } bool Launcher::GameSettings::readFile(QTextStream &stream) +{ + return readFile(stream, mSettings); +} + +bool Launcher::GameSettings::readUserFile(QTextStream &stream) +{ + return readFile(stream, mUserSettings); +} + +bool Launcher::GameSettings::readFile(QTextStream &stream, QMap &settings) { QMap cache; QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); @@ -107,10 +117,10 @@ bool Launcher::GameSettings::readFile(QTextStream &stream) // Don't remove existing data entries if (key != QLatin1String("data")) - mSettings.remove(key); + settings.remove(key); QStringList values = cache.values(key); - values.append(mSettings.values(key)); + values.append(settings.values(key)); if (!values.contains(value)) { cache.insertMulti(key, value); @@ -118,23 +128,24 @@ bool Launcher::GameSettings::readFile(QTextStream &stream) } } - if (mSettings.isEmpty()) { - mSettings = cache; // This is the first time we read a file + if (settings.isEmpty()) { + settings = cache; // This is the first time we read a file validatePaths(); return true; } // Merge the changed keys with those which didn't - mSettings.unite(cache); + settings.unite(cache); validatePaths(); return true; } + bool Launcher::GameSettings::writeFile(QTextStream &stream) { // Iterate in reverse order to preserve insertion order - QMapIterator i(mSettings); + QMapIterator i(mUserSettings); i.toBack(); while (i.hasPrevious()) { @@ -162,7 +173,7 @@ bool Launcher::GameSettings::writeFile(QTextStream &stream) } - QStringList content = mSettings.values(QString("content")); + QStringList content = mUserSettings.values(QString("content")); for (int i = content.count(); i--;) { stream << "content=" << content.at(i) << "\n"; } @@ -172,14 +183,14 @@ bool Launcher::GameSettings::writeFile(QTextStream &stream) bool Launcher::GameSettings::hasMaster() { - bool result = false; - QStringList content = mSettings.values(QString("content")); - for (int i = 0; i < content.count(); ++i) { - if (content.at(i).contains(".omwgame") || content.at(i).contains(".esm")) { - result = true; - break; + bool result = false; + QStringList content = mSettings.values(QString("content")); + for (int i = 0; i < content.count(); ++i) { + if (content.at(i).contains(".omwgame") || content.at(i).contains(".esm")) { + result = true; + break; + } } - } - return result; + return result; } diff --git a/apps/launcher/settings/gamesettings.hpp b/apps/launcher/settings/gamesettings.hpp index 60236200a..df8215074 100644 --- a/apps/launcher/settings/gamesettings.hpp +++ b/apps/launcher/settings/gamesettings.hpp @@ -31,6 +31,7 @@ namespace Launcher inline void setValue(const QString &key, const QString &value) { mSettings.insert(key, value); + mUserSettings.insert(key, value); } inline void setMultiValue(const QString &key, const QString &value) @@ -38,11 +39,16 @@ namespace Launcher QStringList values = mSettings.values(key); if (!values.contains(value)) mSettings.insertMulti(key, value); + + values = mUserSettings.values(key); + if (!values.contains(value)) + mUserSettings.insertMulti(key, value); } inline void remove(const QString &key) { mSettings.remove(key); + mUserSettings.remove(key); } inline QStringList getDataDirs() { return mDataDirs; } @@ -52,7 +58,11 @@ namespace Launcher bool hasMaster(); QStringList values(const QString &key, const QStringList &defaultValues = QStringList()); + bool readFile(QTextStream &stream); + bool readFile(QTextStream &stream, QMap &settings); + bool readUserFile(QTextStream &stream); + bool writeFile(QTextStream &stream); private: @@ -60,6 +70,7 @@ namespace Launcher void validatePaths(); QMap mSettings; + QMap mUserSettings; QStringList mDataDirs; QString mDataLocal; From c22e38f825827d408833de11b19bcf5a8210206f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 17 Dec 2013 21:19:05 +0100 Subject: [PATCH 160/889] removing 255 content file limitation --- apps/esmtool/esmtool.cpp | 2 +- apps/openmw/mwworld/cellstore.cpp | 5 +- apps/openmw/mwworld/livecellref.hpp | 7 ++- apps/openmw/mwworld/manualref.hpp | 5 +- apps/openmw/mwworld/store.cpp | 8 +-- components/esm/cellref.cpp | 11 +++- components/esm/cellref.hpp | 10 +++- components/esm/loadcell.cpp | 86 ++++++++++++----------------- components/esm/loadcell.hpp | 8 +-- 9 files changed, 74 insertions(+), 68 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 27980096e..3bbdaef35 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -244,7 +244,7 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info) if(quiet) continue; - std::cout << " Refnum: " << ref.mRefnum << std::endl; + std::cout << " Refnum: " << ref.mRefNum.mIndex << std::endl; std::cout << " ID: '" << ref.mRefID << "'\n"; std::cout << " Owner: '" << ref.mOwner << "'\n"; std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n"; diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 0c145ab60..06ae083ce 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -38,7 +38,7 @@ namespace MWWorld void CellRefList::load(ESM::CellRef &ref, const MWWorld::ESMStore &esmStore) { // Get existing reference, in case we need to overwrite it. - typename std::list::iterator iter = std::find(mList.begin(), mList.end(), ref.mRefnum); + typename std::list::iterator iter = std::find(mList.begin(), mList.end(), ref.mRefNum); // Skip this when reference was deleted. // TODO: Support respawning references, in this case, we need to track it somehow. @@ -148,13 +148,14 @@ namespace MWWorld mCell->restore (esm[index], i); ESM::CellRef ref; + ref.mRefNum.mContentFile = -1; // Get each reference in turn while(mCell->getNextRef(esm[index], ref)) { // Don't load reference if it was moved to a different cell. std::string lowerCase = Misc::StringUtils::lowerCase(ref.mRefID); - ESM::MovedCellRefTracker::const_iterator iter = std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefnum); + ESM::MovedCellRefTracker::const_iterator iter = std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefNum); if (iter != mCell->mMovedRefs.end()) { continue; } diff --git a/apps/openmw/mwworld/livecellref.hpp b/apps/openmw/mwworld/livecellref.hpp index 415351e78..558639a3b 100644 --- a/apps/openmw/mwworld/livecellref.hpp +++ b/apps/openmw/mwworld/livecellref.hpp @@ -31,6 +31,11 @@ namespace MWWorld virtual ~LiveCellRefBase() { } }; + inline bool operator== (const LiveCellRefBase& cellRef, const ESM::CellRef::RefNum refNum) + { + return cellRef.mRef.mRefNum==refNum; + } + /// A reference to one object (of any type) in a cell. /// /// Constructing this with a CellRef instance in the constructor means that @@ -51,8 +56,6 @@ namespace MWWorld // The object that this instance is based on. const X* mBase; }; - -// template bool operator==(const LiveCellRef& ref, int pRefnum); } #endif diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index 1cdcd8484..f138e3732 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -25,6 +25,8 @@ namespace MWWorld { LiveCellRef ref; ref.mBase = instance; + ref.mRef.mRefNum.mIndex = 0; + ref.mRef.mRefNum.mContentFile = -1; mRef = ref; mPtr = Ptr (&boost::any_cast&> (mRef), 0); @@ -65,7 +67,8 @@ namespace MWWorld // initialise ESM::CellRef& cellRef = mPtr.getCellRef(); cellRef.mRefID = name; - cellRef.mRefnum = -1; + cellRef.mRefNum.mIndex = 0; + cellRef.mRefNum.mContentFile = -1; cellRef.mScale = 1; cellRef.mFactIndex = 0; cellRef.mCharge = -1; diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 512883f1a..9ba2d8133 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -10,7 +10,7 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) // and we merge all this data into one Cell object. However, we can't simply search for the cell id, // as many exterior cells do not have a name. Instead, we need to search by (x,y) coordinates - and they // are not available until both cells have been loaded! So first, proceed as usual. - + // All cells have a name record, even nameless exterior cells. std::string idLower = Misc::StringUtils::lowerCase(id); ESM::Cell *cell = new ESM::Cell; @@ -40,7 +40,7 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) // We should not need to test for duplicates, as this part of the code is pre-cell merge. cell->mMovedRefs.push_back(cMRef); // But there may be duplicates here! - ESM::CellRefTracker::iterator iter = std::find(cellAlt->mLeasedRefs.begin(), cellAlt->mLeasedRefs.end(), ref.mRefnum); + ESM::CellRefTracker::iterator iter = std::find(cellAlt->mLeasedRefs.begin(), cellAlt->mLeasedRefs.end(), ref.mRefNum); if (iter == cellAlt->mLeasedRefs.end()) cellAlt->mLeasedRefs.push_back(ref); else @@ -76,11 +76,11 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) // merge lists of leased references, use newer data in case of conflict for (ESM::MovedCellRefTracker::const_iterator it = cell->mMovedRefs.begin(); it != cell->mMovedRefs.end(); ++it) { // remove reference from current leased ref tracker and add it to new cell - ESM::MovedCellRefTracker::iterator itold = std::find(oldcell->mMovedRefs.begin(), oldcell->mMovedRefs.end(), it->mRefnum); + ESM::MovedCellRefTracker::iterator itold = std::find(oldcell->mMovedRefs.begin(), oldcell->mMovedRefs.end(), it->mRefNum); if (itold != oldcell->mMovedRefs.end()) { ESM::MovedCellRef target0 = *itold; ESM::Cell *wipecell = const_cast(search(target0.mTarget[0], target0.mTarget[1])); - ESM::CellRefTracker::iterator it_lease = std::find(wipecell->mLeasedRefs.begin(), wipecell->mLeasedRefs.end(), it->mRefnum); + ESM::CellRefTracker::iterator it_lease = std::find(wipecell->mLeasedRefs.begin(), wipecell->mLeasedRefs.end(), it->mRefNum); wipecell->mLeasedRefs.erase(it_lease); *itold = *it; } diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index e91059b26..23a95a4ab 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -5,7 +5,8 @@ void ESM::CellRef::save(ESMWriter &esm) const { - esm.writeHNT("FRMR", mRefnum); + esm.writeHNT("FRMR", mRefNum.mIndex); + /// \todo read content file index (if present) esm.writeHNCString("NAME", mRefID); if (mScale != 1.0) { @@ -58,7 +59,8 @@ void ESM::CellRef::save(ESMWriter &esm) const void ESM::CellRef::blank() { - mRefnum = 0; + mRefNum.mIndex = 0; + mRefNum.mContentFile = -1; mRefID.clear(); mScale = 1; mOwner.clear(); @@ -84,4 +86,9 @@ void ESM::CellRef::blank() mPos.pos[i] = 0; mPos.rot[i] = 0; } +} + +bool ESM::operator== (const CellRef::RefNum& left, const CellRef::RefNum& right) +{ + return left.mIndex==right.mIndex && left.mContentFile==right.mContentFile; } \ No newline at end of file diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 47cb0b99e..ef2523869 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -19,7 +19,13 @@ namespace ESM { public: - int mRefnum; // Reference number + struct RefNum + { + int mIndex; + int mContentFile; // -1 no content file + }; + + RefNum mRefNum; // Reference number std::string mRefID; // ID of object being referenced float mScale; // Scale applied to mesh @@ -87,6 +93,8 @@ namespace ESM void blank(); }; + + bool operator== (const CellRef::RefNum& left, const CellRef::RefNum& right); } #endif \ No newline at end of file diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index c22c1b22b..ba4195370 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -9,20 +9,42 @@ #include "esmwriter.hpp" #include "defs.hpp" +namespace +{ + ///< Translate 8bit/24bit code (stored in refNum.mIndex) into a proper refNum + void adjustRefNum (ESM::CellRef::RefNum& refNum, ESM::ESMReader& reader) + { + int local = (refNum.mIndex & 0xff000000) >> 24; + + if (local) + { + // If the most significant 8 bits are used, then this reference already exists. + // In this case, do not spawn a new reference, but overwrite the old one. + refNum.mIndex &= 0x00ffffff; // delete old plugin ID + refNum.mContentFile = reader.getGameFiles()[local-1].index; + } + else + { + // This is an addition by the present plugin. Set the corresponding plugin index. + refNum.mContentFile = reader.getIndex(); + } + } +} + namespace ESM { unsigned int Cell::sRecordId = REC_CELL; -/// Some overloaded compare operators. -bool operator==(const MovedCellRef& ref, int pRefnum) -{ - return (ref.mRefnum == pRefnum); -} + // Some overloaded compare operators. + bool operator== (const MovedCellRef& ref, const CellRef::RefNum& refNum) + { + return ref.mRefNum == refNum; + } -bool operator==(const CellRef& ref, int pRefnum) -{ - return (ref.mRefnum == pRefnum); -} + bool operator== (const CellRef& ref, const CellRef::RefNum& refNum) + { + return ref.mRefNum == refNum; + } void Cell::load(ESMReader &esm, bool saveContext) @@ -163,49 +185,17 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) //esm.getHNOT(NAM0, "NAM0"); } - esm.getHNT(ref.mRefnum, "FRMR"); + esm.getHNT (ref.mRefNum.mIndex, "FRMR"); ref.mRefID = esm.getHNString("NAME"); // Identify references belonging to a parent file and adapt the ID accordingly. - int local = (ref.mRefnum & 0xff000000) >> 24; - size_t global = esm.getIndex() + 1; - if (local) - { - // If the most significant 8 bits are used, then this reference already exists. - // In this case, do not spawn a new reference, but overwrite the old one. - ref.mRefnum &= 0x00ffffff; // delete old plugin ID - const std::vector &masters = esm.getGameFiles(); - global = masters[local-1].index + 1; - ref.mRefnum |= global << 24; // insert global plugin ID - } - else - { - // This is an addition by the present plugin. Set the corresponding plugin index. - ref.mRefnum |= global << 24; // insert global plugin ID - } + adjustRefNum (ref.mRefNum, esm); // getHNOT will not change the existing value if the subrecord is // missing ref.mScale = 1.0; esm.getHNOT(ref.mScale, "XSCL"); - // TODO: support loading references from saves, there are tons of keys not recognized yet. - // The following is just an incomplete list. - if (esm.isNextSub("ACTN")) - esm.skipHSub(); - if (esm.isNextSub("STPR")) - esm.skipHSub(); - if (esm.isNextSub("ACDT")) - esm.skipHSub(); - if (esm.isNextSub("ACSC")) - esm.skipHSub(); - if (esm.isNextSub("ACSL")) - esm.skipHSub(); - if (esm.isNextSub("CHRD")) - esm.skipHSub(); - else if (esm.isNextSub("CRED")) // ??? - esm.skipHSub(); - ref.mOwner = esm.getHNOString("ANAM"); ref.mGlob = esm.getHNOString("BNAM"); ref.mSoul = esm.getHNOString("XSOL"); @@ -271,16 +261,10 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) { - esm.getHT(mref.mRefnum); + esm.getHT(mref.mRefNum.mIndex); esm.getHNOT(mref.mTarget, "CNDT"); - // Identify references belonging to a parent file and adapt the ID accordingly. - int local = (mref.mRefnum & 0xff000000) >> 24; - size_t global = esm.getIndex() + 1; - mref.mRefnum &= 0x00ffffff; // delete old plugin ID - const std::vector &masters = esm.getGameFiles(); - global = masters[local-1].index + 1; - mref.mRefnum |= global << 24; // insert global plugin ID + adjustRefNum (mref.mRefNum, esm); return true; } diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 61d586b9d..b0340e945 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -27,7 +27,7 @@ class ESMWriter; class MovedCellRef { public: - int mRefnum; + CellRef::RefNum mRefNum; // Target cell (if exterior) int mTarget[2]; @@ -37,9 +37,9 @@ public: // introduces a henchman (which no one uses), so we may need this as well. }; -/// Overloaded copare operator used to search inside a list of cell refs. -bool operator==(const MovedCellRef& ref, int pRefnum); -bool operator==(const CellRef& ref, int pRefnum); +/// Overloaded compare operator used to search inside a list of cell refs. +bool operator==(const MovedCellRef& ref, const CellRef::RefNum& refNum); +bool operator==(const CellRef& ref, const CellRef::RefNum& refNum); typedef std::list MovedCellRefTracker; typedef std::list CellRefTracker; From 876fb9a899cf19a045b0b9a63be6144f1f1c0daa Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Dec 2013 00:11:14 +0100 Subject: [PATCH 161/889] Addition to bb4bd999ba706e : adjust position for objects placed from inventory --- apps/openmw/mwworld/worldimp.cpp | 8 ++++---- apps/openmw/mwworld/worldimp.hpp | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f64d22122..448211bc2 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1095,7 +1095,7 @@ namespace MWWorld MWWorld::Ptr World::safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos) { - return copyObjectToCell(ptr,Cell,pos); + return copyObjectToCell(ptr,Cell,pos,false); } void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const @@ -1516,7 +1516,7 @@ namespace MWWorld // copy the object and set its count int origCount = object.getRefData().getCount(); object.getRefData().setCount(amount); - Ptr dropped = copyObjectToCell(object, *cell, pos); + Ptr dropped = copyObjectToCell(object, *cell, pos, true); object.getRefData().setCount(origCount); // only the player place items in the world, so no need to check actor @@ -1537,13 +1537,13 @@ namespace MWWorld } - Ptr World::copyObjectToCell(const Ptr &object, CellStore &cell, const ESM::Position &pos) + Ptr World::copyObjectToCell(const Ptr &object, CellStore &cell, const ESM::Position &pos, bool adjustPos) { /// \todo add searching correct cell for position specified MWWorld::Ptr dropped = MWWorld::Class::get(object).copyToCell(object, cell, pos); - if (object.getClass().isActor()) + if (object.getClass().isActor() || adjustPos) { Ogre::Vector3 min, max; if (mPhysics->getObjectAABB(object, min, max)) { diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index c8133441d..5a51cb773 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -114,8 +114,7 @@ namespace MWWorld bool moveObjectImp (const Ptr& ptr, float x, float y, float z); ///< @return true if the active cell (cell player is in) changed - - Ptr copyObjectToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos); + Ptr copyObjectToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos, bool adjustPos=true); void updateWindowManager (); void performUpdateSceneQueries (); From a0d38dfb6371d3eeb2da5aa1e82a781ec5d48022 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Dec 2013 00:26:08 +0100 Subject: [PATCH 162/889] Fix highlighted topics being selectable when in a choice --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 6 ++---- apps/openmw/mwgui/dialogue.cpp | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 52493bf76..3951cedcb 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -413,10 +413,6 @@ namespace MWDialogue void DialogueManager::goodbyeSelected() { - // Do not close the dialogue window if the player has to answer a question - if (mIsInChoice) - return; - MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue); // Apply disposition change to NPC's base disposition @@ -474,6 +470,8 @@ namespace MWDialogue void DialogueManager::goodbye() { + mIsInChoice = true; + MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); win->goodbye(); diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 71995f97f..914302d84 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -302,6 +302,8 @@ namespace MWGui void DialogueWindow::onByeClicked(MyGUI::Widget* _sender) { + if (!mEnabled || MWBase::Environment::get().getDialogueManager()->isInChoice()) + return; MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); } From 9afdf71af30c42e6162532fa1bb9ee20868b459a Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Dec 2013 00:37:57 +0100 Subject: [PATCH 163/889] Fix crash with player->position command --- apps/openmw/mwworld/scene.cpp | 3 +-- apps/openmw/mwworld/scene.hpp | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 25ce038f3..dab272f7c 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -203,6 +203,7 @@ namespace MWWorld void Scene::changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos) { + mRendering.enableTerrain(true); Nif::NIFFile::CacheLock cachelock; Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); @@ -436,8 +437,6 @@ namespace MWWorld MWBase::Environment::get().getWorld()->positionToIndex (position.pos[0], position.pos[1], x, y); - mRendering.enableTerrain(true); - changeCell (x, y, position, true); } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index e3edad352..73c3c4b12 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -71,8 +71,6 @@ namespace MWWorld void loadCell (CellStore *cell, Loading::Listener* loadingListener); void changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos); - ///< Move from exterior to interior or from interior cell to a different - /// interior cell. CellStore* getCurrentCell (); From 6a3eb3b355ecff7983ad61eeaad01fb576568e3f Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Dec 2013 01:31:12 +0100 Subject: [PATCH 164/889] Better way of reversing layer UV. --- files/materials/terrain.shader | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index eda80c9e3..7903292d3 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -335,7 +335,7 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5; float4 albedo = float4(0,0,0,1); - float2 layerUV = UV * 16; + float2 layerUV = float2(UV.x, 1.f-UV.y) * 16; // Reverse Y, required to get proper tangents float2 thisLayerUV; float4 normalTex; @@ -355,8 +355,6 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5; #endif thisLayerUV = layerUV; - // required to play nicely with the tangents - thisLayerUV.y *= -1; #if @shPropertyBool(use_parallax_@shIterator) thisLayerUV += TSeyeDir.xy * ( normalTex.a * PARALLAX_SCALE + PARALLAX_BIAS ); #endif From 18c002a21d9cb87ac33db74108ae4d1e91e3e52f Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Dec 2013 01:31:42 +0100 Subject: [PATCH 165/889] Fix an awful typo. --- components/terrain/material.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index b421de5a2..511d45f41 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -220,9 +220,8 @@ namespace Terrain ++neededTextureUnits; // layer texture // Check if this layer has a normal map - if (mNormalMapping && !mLayerList[layerOffset].mNormalMap.empty()) + if (mNormalMapping && !mLayerList[layerIndex].mNormalMap.empty() && !renderCompositeMap) ++neededTextureUnits; // normal map - if (neededTextureUnits <= remainingTextureUnits) { // We can fit another! From 5fd98d7c3ad9edf31868953287881ce2bc3d8c7a Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Dec 2013 01:41:36 +0100 Subject: [PATCH 166/889] Add an assertion --- components/terrain/material.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index 511d45f41..5dcdb7fed 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -334,6 +334,8 @@ namespace Terrain // Make sure the pass index is fed to the permutation handler, because blendmap components may be different p->mShaderProperties.setProperty ("pass_index", sh::makeProperty(new sh::IntValue(layerOffset))); + assert ((int)p->mTexUnits.size() == OGRE_MAX_TEXTURE_LAYERS - remainingTextureUnits); + layerOffset += numLayersInThisPass; } } From 3816a09c6fd98d4d0b4ede58b8891c9ee845df26 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Thu, 19 Dec 2013 22:08:34 +0200 Subject: [PATCH 167/889] bug 428 ask to load recent saved game --- apps/openmw/mwbase/statemanager.hpp | 2 ++ apps/openmw/mwmechanics/character.cpp | 6 ++-- apps/openmw/mwstate/statemanagerimp.cpp | 44 ++++++++++++++++++++++++- apps/openmw/mwstate/statemanagerimp.hpp | 3 ++ 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index 8548a74f7..cd907408a 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -44,6 +44,8 @@ namespace MWBase virtual bool hasQuitRequest() const = 0; + virtual void askLoadRecent() = 0; + virtual State getState() const = 0; virtual void newGame (bool bypass = false) = 0; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 9a19ac7fa..614d697ec 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -32,6 +32,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/statemanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" @@ -1057,11 +1058,10 @@ bool CharacterController::kill() if( isDead() ) { //player's death animation is over - if( mPtr.getRefData().getHandle()=="player" && !isAnimPlaying(mCurrentDeath) - && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_MainMenu ) + if( mPtr.getRefData().getHandle()=="player" && !isAnimPlaying(mCurrentDeath) ) { MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setHealth(0); - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + MWBase::Environment::get().getStateManager()->askLoadRecent(); } return false; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index bad4de03d..9e6395812 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -32,7 +32,7 @@ void MWState::StateManager::cleanup() } MWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::string& game) -: mQuitRequest (false), mState (State_NoGame), mCharacterManager (saves, game), mTimePlayed (0) +: mQuitRequest (false), mAskLoadRecent(false), mState (State_NoGame), mCharacterManager (saves, game), mTimePlayed (0) { } @@ -47,6 +47,48 @@ bool MWState::StateManager::hasQuitRequest() const return mQuitRequest; } +void MWState::StateManager::askLoadRecent() +{ + if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu) + return; + + if( !mAskLoadRecent ) + { + if(MWBase::Environment::get().getStateManager()->getCurrentCharacter()->begin() + == MWBase::Environment::get().getStateManager()->getCurrentCharacter()->end() )//no saves + { + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + } + else + { + MWState::Slot lastSave = *getCurrentCharacter()->begin(); + std::vector buttons; + buttons.push_back("Yes"); + buttons.push_back("No"); + std::string message = "The most recent Save Game is '" + lastSave.mProfile.mDescription + "'.\n Would you like to load it?"; + MWBase::Environment::get().getWindowManager()->messageBox(message, buttons); + mAskLoadRecent = true; + } + } + else + { + int iButton = MWBase::Environment::get().getWindowManager()->readPressedButton(); + if(iButton==0) + { + mAskLoadRecent = false; + //Load last saved game for current character + MWState::Character *curCharacter = getCurrentCharacter(); + MWState::Slot lastSave = *curCharacter->begin(); + loadGame(curCharacter, &lastSave); + } + else if(iButton==1) + { + mAskLoadRecent = false; + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + } + } +} + MWState::StateManager::State MWState::StateManager::getState() const { return mState; diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 78b578766..a2abcfd1b 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -12,6 +12,7 @@ namespace MWState class StateManager : public MWBase::StateManager { bool mQuitRequest; + bool mAskLoadRecent; State mState; CharacterManager mCharacterManager; double mTimePlayed; @@ -28,6 +29,8 @@ namespace MWState virtual bool hasQuitRequest() const; + virtual void askLoadRecent(); + virtual State getState() const; virtual void newGame (bool bypass = false); From 92072d968b65ec6fef32d0211aad45ff08dbef30 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Thu, 19 Dec 2013 21:11:07 +0100 Subject: [PATCH 168/889] Fixes #845: NPCs hold torches during the day Simplified a bit code which shows and hides light. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwmechanics/character.cpp | 27 +++++++++++++-------------- apps/openmw/mwrender/npcanimation.cpp | 14 +++++++------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 4f7754951..398eadf86 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -36,6 +36,8 @@ #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwworld/actionequip.hpp" +#include "../mwworld/actiontake.hpp" namespace { @@ -709,27 +711,24 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun if (MWBase::Environment::get().getWorld()->isNight()) { - MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) + MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if (item != inv.end() && item->getTypeName() == typeid(ESM::Light).name()) { mAnimation->showLights(true); - if(!mAnimation->isPlaying("torch")) - mAnimation->play("torch", Priority_Torch, - MWRender::Animation::Group_LeftArm, false, - 1.0f, "start", "stop", 0.0f, (~(size_t)0)); - } - else if (mAnimation->isPlaying("torch")) - { - mAnimation->disable("torch"); - mAnimation->showLights(false); - mAnimation->showShield(true); + if (!mAnimation->isPlaying("torch")) + { + mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, + false, 1.0f, "start", "stop", 0.0f, (~(size_t)0)); + } } } else { + if (mAnimation->isPlaying("torch")) + { mAnimation->disable("torch"); - mAnimation->showLights(false); - mAnimation->showShield(true); + } + mAnimation->showLights(false); } return forcestateupdate; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 6e363ab88..2da45e8a1 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -677,17 +677,17 @@ void NpcAnimation::showShield(bool show) void NpcAnimation::showLights(bool show) { - mShowLights = show; MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); - MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + MWWorld::ContainerStoreIterator light = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if(show && shield != inv.end() && shield->getTypeName() == typeid(ESM::Light).name()) + if(show && light != inv.end() && light->getTypeName() == typeid(ESM::Light).name()) { - Ogre::Vector3 glowColor = getEnchantmentColor(*shield); - std::string mesh = MWWorld::Class::get(*shield).getModel(*shield); + mShowLights = show; + Ogre::Vector3 glowColor = getEnchantmentColor(*light); + std::string mesh = MWWorld::Class::get(*light).getModel(*light); addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, - mesh, !shield->getClass().getEnchantment(*shield).empty(), &glowColor); - addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], shield->get()->mBase); + mesh, !light->getClass().getEnchantment(*light).empty(), &glowColor); + addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], light->get()->mBase); } else { From 6eb674e4e5e899b906df25490877c65641e8b67a Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Fri, 20 Dec 2013 00:18:34 +0100 Subject: [PATCH 169/889] Fixes #845: NPCs hold torches during the day Added equipping/unequipping torches. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwmechanics/character.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 398eadf86..9a4f65c82 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -712,6 +712,20 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun if (MWBase::Environment::get().getWorld()->isNight()) { MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if (item != inv.end() && item->getTypeName() != typeid(ESM::Light).name()) + { + inv.unequipItem(*item, mPtr); + } + else if (item == inv.end()) + { + MWWorld::Ptr itemPtr = inv.search("torch_infinite_time"); + if (!itemPtr.isEmpty()) + { + item = inv.add(itemPtr, mPtr); + inv.equip(MWWorld::InventoryStore::Slot_CarriedLeft, item, mPtr); + } + } + if (item != inv.end() && item->getTypeName() == typeid(ESM::Light).name()) { mAnimation->showLights(true); @@ -727,8 +741,14 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun if (mAnimation->isPlaying("torch")) { mAnimation->disable("torch"); + mAnimation->showLights(false); + MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if (item != inv.end() && item->getTypeName() == typeid(ESM::Light).name()) + { + inv.unequipItem(*item, mPtr); + inv.add(*item, mPtr); + } } - mAnimation->showLights(false); } return forcestateupdate; From abc126e2af89ced763abf33fde1e2c00850161ac Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Fri, 20 Dec 2013 01:01:30 +0100 Subject: [PATCH 170/889] Fixes #845: NPCs hold torches during the day Added check for Player character so it won't be affected by showing, or hidding torches. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwmechanics/character.cpp | 70 ++++++++++++++------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 9a4f65c82..f2b353297 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -709,48 +709,50 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun } } - if (MWBase::Environment::get().getWorld()->isNight()) + if (mPtr.getRefData().getHandle() != "player") { - MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if (item != inv.end() && item->getTypeName() != typeid(ESM::Light).name()) + if (MWBase::Environment::get().getWorld()->isNight()) { - inv.unequipItem(*item, mPtr); - } - else if (item == inv.end()) - { - MWWorld::Ptr itemPtr = inv.search("torch_infinite_time"); - if (!itemPtr.isEmpty()) - { - item = inv.add(itemPtr, mPtr); - inv.equip(MWWorld::InventoryStore::Slot_CarriedLeft, item, mPtr); - } - } - - if (item != inv.end() && item->getTypeName() == typeid(ESM::Light).name()) - { - mAnimation->showLights(true); - if (!mAnimation->isPlaying("torch")) - { - mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, - false, 1.0f, "start", "stop", 0.0f, (~(size_t)0)); - } - } - } - else - { - if (mAnimation->isPlaying("torch")) - { - mAnimation->disable("torch"); - mAnimation->showLights(false); MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if (item != inv.end() && item->getTypeName() == typeid(ESM::Light).name()) + if (item != inv.end() && item->getTypeName() != typeid(ESM::Light).name()) { inv.unequipItem(*item, mPtr); - inv.add(*item, mPtr); + } + else if (item == inv.end()) + { + MWWorld::Ptr itemPtr = inv.search("torch_infinite_time"); + if (!itemPtr.isEmpty()) + { + item = inv.add(itemPtr, mPtr); + inv.equip(MWWorld::InventoryStore::Slot_CarriedLeft, item, mPtr); + } + } + + if (item != inv.end() && item->getTypeName() == typeid(ESM::Light).name()) + { + mAnimation->showLights(true); + if (!mAnimation->isPlaying("torch")) + { + mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, + false, 1.0f, "start", "stop", 0.0f, (~(size_t)0)); + } + } + } + else + { + if (mAnimation->isPlaying("torch")) + { + mAnimation->disable("torch"); + mAnimation->showLights(false); + MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if (item != inv.end() && item->getTypeName() == typeid(ESM::Light).name()) + { + inv.unequipItem(*item, mPtr); + inv.add(*item, mPtr); + } } } } - return forcestateupdate; } From 8eb2696f6ccf690a23f927dbe031e2c1e3f2d9b0 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 20 Dec 2013 14:04:59 +0200 Subject: [PATCH 171/889] using gmst string --- apps/openmw/mwstate/statemanagerimp.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 9e6395812..a408a9fb7 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -54,8 +54,7 @@ void MWState::StateManager::askLoadRecent() if( !mAskLoadRecent ) { - if(MWBase::Environment::get().getStateManager()->getCurrentCharacter()->begin() - == MWBase::Environment::get().getStateManager()->getCurrentCharacter()->end() )//no saves + if(getCurrentCharacter()->begin() == getCurrentCharacter()->end() )//no saves { MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); } @@ -65,7 +64,10 @@ void MWState::StateManager::askLoadRecent() std::vector buttons; buttons.push_back("Yes"); buttons.push_back("No"); - std::string message = "The most recent Save Game is '" + lastSave.mProfile.mDescription + "'.\n Would you like to load it?"; + std::string tag("%s"); + std::string message = MWBase::Environment::get().getWindowManager()->getGameSettingString("sLoadLastSaveMsg", tag); + size_t pos = message.find(tag); + message.replace(pos, tag.length(), lastSave.mProfile.mDescription); MWBase::Environment::get().getWindowManager()->messageBox(message, buttons); mAskLoadRecent = true; } From 8085fcc792a72087d9402ae570623d2842d16b1b Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 20 Dec 2013 20:02:42 +0100 Subject: [PATCH 172/889] Added Referencable checks class, added method to get refidcontainer, added method to get mBooks. Currently only books are checked, and only if name is present. --- apps/opencs/CMakeLists.txt | 2 +- .../opencs/model/tools/referenceablecheck.cpp | 63 +++++++++++++++++++ .../opencs/model/tools/referenceablecheck.hpp | 26 ++++++++ apps/opencs/model/tools/tools.cpp | 5 +- apps/opencs/model/world/refidcollection.cpp | 6 ++ apps/opencs/model/world/refidcollection.hpp | 2 + apps/opencs/model/world/refiddata.cpp | 7 ++- apps/opencs/model/world/refiddata.hpp | 3 + 8 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 apps/opencs/model/tools/referenceablecheck.cpp create mode 100644 apps/opencs/model/tools/referenceablecheck.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 1197e2014..baf677f8d 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -38,7 +38,7 @@ opencs_units (model/tools opencs_units_noqt (model/tools mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck - birthsigncheck spellcheck + birthsigncheck spellcheck referenceablecheck ) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp new file mode 100644 index 000000000..01c8d30a7 --- /dev/null +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -0,0 +1,63 @@ +#include "referenceablecheck.hpp" + +#include +#include +#include + +#include +#include "../world/record.hpp" + +#include "../world/universalid.hpp" + +CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable) : + mReferencables(referenceable), + mBooksSize(0) +{ + setSizeVariables(); +} + +void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::string >& messages) +{ + //Checks for books, than, when stage is above mBooksSize goes to other checks, with stage - minus prev sizes as stage. + bool CheckPerformed = false; + + if (stage <= mBooksSize) + { + bookCheck(stage, mReferencables.getBooks(), messages); + CheckPerformed = true; + } + + if (CheckPerformed) + { + return; + } +} + +int CSMTools::ReferenceableCheckStage::setup() +{ + return mReferencables.getSize(); +} + +void CSMTools::ReferenceableCheckStage::bookCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Book >& records, std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + + if (baserecord.isDeleted()) + { + return; + } + + const ESM::Book& Book = (static_cast& >(baserecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Book, Book.mId); + + //Checking for name + if (Book.mName.empty()) + { + messages.push_back(id.toString() + "|" + Book.mId + " has an empty name"); + } +} + +void CSMTools::ReferenceableCheckStage::setSizeVariables() +{ + mBooksSize = mReferencables.getBooks().getSize(); +} diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp new file mode 100644 index 000000000..509e8d8c0 --- /dev/null +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -0,0 +1,26 @@ +#ifndef REFERENCEABLECHECKSTAGE_H +#define REFERENCEABLECHECKSTAGE_H + +#include "../world/universalid.hpp" +#include "../doc/stage.hpp" +#include "../world/data.hpp" +#include "../world/refiddata.hpp" + +namespace CSMTools +{ + class ReferenceableCheckStage : public CSMDoc::Stage + { + public: + ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable); + virtual void perform(int stage, std::vector< std::string >& messages); + virtual int setup(); + + private: + void bookCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Book >& records, std::vector< std::string >& messages); + void setSizeVariables(); + + const CSMWorld::RefIdData mReferencables; + int mBooksSize; + }; +} +#endif // REFERENCEABLECHECKSTAGE_H diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index cd4653280..bca9f6fb7 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -19,6 +19,7 @@ #include "regioncheck.hpp" #include "birthsigncheck.hpp" #include "spellcheck.hpp" +#include "referenceablecheck.hpp" CSMDoc::Operation *CSMTools::Tools::get (int type) { @@ -74,6 +75,8 @@ CSMDoc::Operation *CSMTools::Tools::getVerifier() mVerifier->appendStage (new BirthsignCheckStage (mData.getBirthsigns())); mVerifier->appendStage (new SpellCheckStage (mData.getSpells())); + + mVerifier->appendStage( new ReferenceableCheckStage (mData.getReferenceables().getDataSet())); } return mVerifier; @@ -138,4 +141,4 @@ void CSMTools::Tools::verifierMessage (const QString& message, int type) if (iter!=mActiveReports.end()) mReports[iter->second]->add (message.toStdString()); -} \ No newline at end of file +} diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 86a542c5c..9ed526818 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -549,3 +549,9 @@ void CSMWorld::RefIdCollection::save (int index, ESM::ESMWriter& writer) const { mData.save (index, writer); } + +const CSMWorld::RefIdData& CSMWorld::RefIdCollection::getDataSet() const +{ + return mData; +} + diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index 5ff4a70bf..70cae53c8 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -107,6 +107,8 @@ namespace CSMWorld /// \return Success? void save (int index, ESM::ESMWriter& writer) const; + + const RefIdData& getDataSet() const; //I can't figure out a better name for this one :( }; } diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 8f59b0fe7..0fbfef4f1 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -230,4 +230,9 @@ void CSMWorld::RefIdData::save (int index, ESM::ESMWriter& writer) const throw std::logic_error ("invalid local index type"); iter->second->save (localIndex.first, writer); -} \ No newline at end of file +} + +const CSMWorld::RefIdDataContainer< ESM::Book >& CSMWorld::RefIdData::getBooks() const +{ + return mBooks; +} diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 9595ab23b..1590b58df 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -219,6 +219,9 @@ namespace CSMWorld /// \param listDeleted include deleted record in the list void save (int index, ESM::ESMWriter& writer) const; + + //RECORD CONTAINERS ACCESS METHODS + const RefIdDataContainer& getBooks() const; }; } From e480e31d1ebcc02771eea694098477511069893f Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 20 Dec 2013 22:37:18 +0200 Subject: [PATCH 173/889] Revert "improvement to bug fix http://bugs.openmw.org/issues/428" This reverts commit c0d07fbdc9023b76853e2a0b20d9649e9eca1f0a. --- apps/openmw/mwinput/inputmanagerimp.cpp | 45 ++++++++++++------------- apps/openmw/mwmechanics/character.cpp | 19 +++++------ 2 files changed, 29 insertions(+), 35 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index efa326a98..9b7fe98bd 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -180,9 +180,7 @@ namespace MWInput switch (action) { case A_GameMenu: - if(!(MWWorld::Class::get(mPlayer->getPlayer()).getCreatureStats(mPlayer->getPlayer()).isDead() - && MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_MainMenu ) ) - toggleMainMenu (); + toggleMainMenu (); break; case A_Screenshot: screenshot(); @@ -302,9 +300,7 @@ namespace MWInput return; // Disable movement in Gui mode - if (MWBase::Environment::get().getWindowManager()->isGuiMode() - || MWWorld::Class::get(mPlayer->getPlayer()).getCreatureStats(mPlayer->getPlayer()).isDead() ) - return; + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; // Configure player movement according to keyboard input. Actual movement will @@ -371,28 +367,29 @@ namespace MWInput } } - - if (mControlSwitch["playerviewswitch"]) { - // work around preview mode toggle when pressing Alt+Tab - if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) { + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + if ( !player.getClass().getCreatureStats(player).isDead() ) { + if (mControlSwitch["playerviewswitch"]) { + // work around preview mode toggle when pressing Alt+Tab + if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) { - if (mPreviewPOVDelay <= 0.5 && - (mPreviewPOVDelay += dt) > 0.5) - { - mPreviewPOVDelay = 1.f; - MWBase::Environment::get().getWorld()->togglePreviewMode(true); + if (mPreviewPOVDelay <= 0.5 && + (mPreviewPOVDelay += dt) > 0.5) + { + mPreviewPOVDelay = 1.f; + MWBase::Environment::get().getWorld()->togglePreviewMode(true); + } + } else { + if (mPreviewPOVDelay > 0.5) { + //disable preview mode + MWBase::Environment::get().getWorld()->togglePreviewMode(false); + } else if (mPreviewPOVDelay > 0.f) { + MWBase::Environment::get().getWorld()->togglePOV(); + } + mPreviewPOVDelay = 0.f; } - } else { - if (mPreviewPOVDelay > 0.5) { - //disable preview mode - MWBase::Environment::get().getWorld()->togglePreviewMode(false); - } else if (mPreviewPOVDelay > 0.f) { - MWBase::Environment::get().getWorld()->togglePOV(); - } - mPreviewPOVDelay = 0.f; } } - } if (actionIsActive(A_MoveForward) || actionIsActive(A_MoveBackward) || diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 1c4979e37..c8eba20d6 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1061,11 +1061,11 @@ bool CharacterController::kill() if( isDead() ) { //player death animation is over: toggle game menu without 'return' option - if( mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() - && !isAnimPlaying(mCurrentDeath) && MWBase::Environment::get().getWindowManager()->getMode () != MWGui::GM_MainMenu ) + if( mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() + && !isAnimPlaying(mCurrentDeath) ) { - MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setHealth(0); - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + MWBase::Environment::get().getWindowManager()->setMainMenuNoReturn(true); + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); } return false; } @@ -1106,13 +1106,10 @@ bool CharacterController::kill() if(mAnimation) { - if (mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) - { - MWBase::Environment::get().getWindowManager()->setMainMenuNoReturn(true); - if (MWBase::Environment::get().getWorld()->getCamera()->isFirstPerson() ) - MWBase::Environment::get().getWorld()->togglePOV(); - } - + if (mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() + && MWBase::Environment::get().getWorld()->getCamera()->isFirstPerson() ) + MWBase::Environment::get().getWorld()->togglePOV(); + mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); mAnimation->disable(mCurrentIdle); From b98cfe2d1b887a8c305945fd40e2d0ee7b34bebf Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 20 Dec 2013 22:39:02 +0200 Subject: [PATCH 174/889] Revert "bug fix http://bugs.openmw.org/issues/428" This reverts commit 8dd930cf97fc4a0fee398a31209fabf24ad7fcb1. --- apps/openmw/mwbase/windowmanager.hpp | 2 -- apps/openmw/mwbase/world.hpp | 3 -- apps/openmw/mwgui/mainmenu.cpp | 13 +------- apps/openmw/mwgui/mainmenu.hpp | 4 --- apps/openmw/mwgui/windowmanagerimp.cpp | 5 --- apps/openmw/mwgui/windowmanagerimp.hpp | 3 -- apps/openmw/mwinput/inputmanagerimp.cpp | 37 +++++++++++------------ apps/openmw/mwmechanics/actors.cpp | 14 +++++---- apps/openmw/mwmechanics/character.cpp | 22 ++------------ apps/openmw/mwmechanics/character.hpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 5 --- apps/openmw/mwrender/renderingmanager.hpp | 2 -- apps/openmw/mwworld/worldimp.cpp | 9 +----- apps/openmw/mwworld/worldimp.hpp | 2 -- 14 files changed, 31 insertions(+), 92 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 4ffb44615..c47ad066b 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -202,8 +202,6 @@ namespace MWBase virtual void setSpellVisibility(bool visible) = 0; virtual void setSneakVisibility(bool visible) = 0; - void virtual setMainMenuNoReturn(bool noreturn) = 0; - virtual void activateQuickKey (int index) = 0; virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index b1f236bd1..8141af712 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -40,7 +40,6 @@ namespace ESM namespace MWRender { - class Camera; class ExternalRendering; class Animation; } @@ -116,8 +115,6 @@ namespace MWBase virtual MWWorld::Player& getPlayer() = 0; - virtual MWRender::Camera* getCamera() const = 0; - virtual const MWWorld::ESMStore& getStore() const = 0; virtual std::vector& getEsmReader() = 0; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index e1c72193b..fa7ed2ace 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -17,7 +17,6 @@ namespace MWGui MainMenu::MainMenu(int w, int h) : OEngine::GUI::Layout("openmw_mainmenu.layout") , mButtonBox(0) - , mNoReturn(false) { onResChange(w,h); } @@ -34,8 +33,7 @@ namespace MWGui int curH = 0; std::vector buttons; - if(!mNoReturn) - buttons.push_back("return"); + buttons.push_back("return"); buttons.push_back("newgame"); //buttons.push_back("loadgame"); //buttons.push_back("savegame"); @@ -70,15 +68,6 @@ namespace MWGui mButtonBox->setCoord (w/2 - maxwidth/2, h/2 - curH/2, maxwidth, curH); } - void MainMenu::setNoReturn(bool bNoReturn) - { - if (mNoReturn != bNoReturn) - { - mNoReturn = bNoReturn; - onResChange( Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video") ); - } - } - void MainMenu::onButtonClicked(MyGUI::Widget *sender) { MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index a686cb020..4e76a64df 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -12,13 +12,9 @@ namespace MWGui void onResChange(int w, int h); - void setNoReturn(bool bNoReturn); - private: MyGUI::Widget* mButtonBox; - bool mNoReturn; - std::map mButtons; void onButtonClicked (MyGUI::Widget* sender); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 69ec11ccc..78986a052 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -880,11 +880,6 @@ namespace MWGui mHud->setSneakVisible(visible); } - void WindowManager::setMainMenuNoReturn(bool noreturn) - { - mMenu->setNoReturn(noreturn); - } - void WindowManager::setDragDrop(bool dragDrop) { mToolTips->setEnabled(!dragDrop); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 3d6972718..4f1960295 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -198,9 +198,6 @@ namespace MWGui virtual void setSpellVisibility(bool visible); virtual void setSneakVisibility(bool visible); - //disables 'return' button in the main menu (used when player is dead) - void virtual setMainMenuNoReturn(bool noreturn); - virtual void activateQuickKey (int index); virtual void setSelectedSpell(const std::string& spellId, int successChancePercent); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 9b7fe98bd..ab2569635 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -367,27 +367,24 @@ namespace MWInput } } - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); - if ( !player.getClass().getCreatureStats(player).isDead() ) { - if (mControlSwitch["playerviewswitch"]) { - // work around preview mode toggle when pressing Alt+Tab - if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) { - - if (mPreviewPOVDelay <= 0.5 && - (mPreviewPOVDelay += dt) > 0.5) - { - mPreviewPOVDelay = 1.f; - MWBase::Environment::get().getWorld()->togglePreviewMode(true); - } - } else { - if (mPreviewPOVDelay > 0.5) { - //disable preview mode - MWBase::Environment::get().getWorld()->togglePreviewMode(false); - } else if (mPreviewPOVDelay > 0.f) { - MWBase::Environment::get().getWorld()->togglePOV(); - } - mPreviewPOVDelay = 0.f; + if (mControlSwitch["playerviewswitch"]) { + + // work around preview mode toggle when pressing Alt+Tab + if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) { + if (mPreviewPOVDelay <= 0.5 && + (mPreviewPOVDelay += dt) > 0.5) + { + mPreviewPOVDelay = 1.f; + MWBase::Environment::get().getWorld()->togglePreviewMode(true); } + } else { + if (mPreviewPOVDelay > 0.5) { + //disable preview mode + MWBase::Environment::get().getWorld()->togglePreviewMode(false); + } else if (mPreviewPOVDelay > 0.f) { + MWBase::Environment::get().getWorld()->togglePOV(); + } + mPreviewPOVDelay = 0.f; } } } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 5dd294f90..22a641b34 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -570,13 +570,15 @@ namespace MWMechanics continue; } - if (iter->second->kill()) - { - ++mDeathCount[cls.getId(iter->first)]; + if(iter->second->isDead()) + continue; - if(cls.isEssential(iter->first)) - MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); - } + iter->second->kill(); + + ++mDeathCount[cls.getId(iter->first)]; + + if(cls.isEssential(iter->first)) + MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); } } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c8eba20d6..da3ed2523 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -27,7 +27,6 @@ #include "security.hpp" #include "../mwrender/animation.hpp" -#include "../mwrender/camera.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -1056,19 +1055,10 @@ void CharacterController::forceStateUpdate() } } -bool CharacterController::kill() +void CharacterController::kill() { - if( isDead() ) - { - //player death animation is over: toggle game menu without 'return' option - if( mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() - && !isAnimPlaying(mCurrentDeath) ) - { - MWBase::Environment::get().getWindowManager()->setMainMenuNoReturn(true); - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); - } - return false; - } + if(mDeathState != CharState_None) + return; if(mPtr.getTypeName() == typeid(ESM::NPC).name()) { @@ -1106,10 +1096,6 @@ bool CharacterController::kill() if(mAnimation) { - if (mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() - && MWBase::Environment::get().getWorld()->getCamera()->isFirstPerson() ) - MWBase::Environment::get().getWorld()->togglePOV(); - mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); mAnimation->disable(mCurrentIdle); @@ -1117,8 +1103,6 @@ bool CharacterController::kill() mIdleState = CharState_None; mCurrentIdle.clear(); - - return true; } void CharacterController::resurrect() diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 2a5c7bb03..9e07fca7d 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -190,7 +190,7 @@ public: void skipAnim(); bool isAnimPlaying(const std::string &groupName); - bool kill(); + void kill(); void resurrect(); bool isDead() const { return mDeathState != CharState_None; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 7d5162747..b216c789f 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -211,11 +211,6 @@ MWRender::SkyManager* RenderingManager::getSkyManager() return mSkyManager; } -MWRender::Camera* RenderingManager::getCamera() const -{ - return mCamera; -} - MWRender::Objects& RenderingManager::getObjects(){ return *mObjects; } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index d6c1ef6c1..e5dcf0aeb 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -98,8 +98,6 @@ public: SkyManager* getSkyManager(); Compositors* getCompositors(); - MWRender::Camera* getCamera() const; - void toggleLight(); bool toggleRenderMode(int mode); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index a900d27ab..f64d22122 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -312,8 +312,6 @@ namespace MWWorld mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback); MWBase::Environment::get().getScriptManager()->resetGlobalScripts(); - - MWBase::Environment::get().getWindowManager()->setMainMenuNoReturn(false); } @@ -405,11 +403,6 @@ namespace MWWorld return *mPlayer; } - MWRender::Camera* World::getCamera() const - { - return mRendering->getCamera(); - } - const MWWorld::ESMStore& World::getStore() const { return mStore; @@ -1311,7 +1304,7 @@ namespace MWWorld // inform the GUI about focused object MWWorld::Ptr object = getFacedObject (); - MWBase::Environment::get().getWindowManager()->setFocusObject(object); + MWBase::Environment::get().getWindowManager()->setFocusObject(object); // retrieve object dimensions so we know where to place the floating label if (!object.isEmpty ()) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 2e572d0bb..c8133441d 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -187,8 +187,6 @@ namespace MWWorld virtual Player& getPlayer(); - virtual MWRender::Camera* getCamera() const; - virtual const MWWorld::ESMStore& getStore() const; virtual std::vector& getEsmReader(); From f69465d7e0ca6a24f445825d6e0768463699fc1f Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 20 Dec 2013 22:31:17 +0100 Subject: [PATCH 175/889] Added aditional checks for books. Activator check. --- .../opencs/model/tools/referenceablecheck.cpp | 72 +++++++++++++++++-- .../opencs/model/tools/referenceablecheck.hpp | 2 + apps/opencs/model/world/refiddata.cpp | 5 ++ apps/opencs/model/world/refiddata.hpp | 1 + 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 01c8d30a7..f8dcd86a4 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -4,26 +4,27 @@ #include #include -#include #include "../world/record.hpp" #include "../world/universalid.hpp" CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable) : mReferencables(referenceable), - mBooksSize(0) + mBooksSize(0), + mActivatorsSize(0) { setSizeVariables(); } void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::string >& messages) { - //Checks for books, than, when stage is above mBooksSize goes to other checks, with stage - minus prev sizes as stage. + //Checks for books, than, when stage is above mBooksSize goes to other checks, with (stage - PrevSum) as stage. bool CheckPerformed = false; - + int PrevSum(0); + if (stage <= mBooksSize) - { - bookCheck(stage, mReferencables.getBooks(), messages); + { + bookCheck(stage, mReferencables.getBooks(), messages); CheckPerformed = true; } @@ -31,6 +32,21 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str { return; } + + PrevSum += mBooksSize; + + if ((stage - PrevSum) <= mActivatorsSize) + { + activatorCheck(stage - PrevSum, mReferencables.getActivator(), messages); + CheckPerformed = true; + } + + if (CheckPerformed) + { + return; + } + + PrevSum += mActivatorsSize; } int CSMTools::ReferenceableCheckStage::setup() @@ -55,9 +71,53 @@ void CSMTools::ReferenceableCheckStage::bookCheck(int stage, const CSMWorld::Ref { messages.push_back(id.toString() + "|" + Book.mId + " has an empty name"); } + + //Checking for weight + if (Book.mData.mWeight < 0) + { + messages.push_back(id.toString() + "|" + Book.mId + " has a negative weight"); + } + + //Checking for value + if (Book.mData.mValue < 0) + { + messages.push_back(id.toString() + "|" + Book.mId + " has a negative value"); + } + +//checking for model + if (Book.mModel.empty()) + { + messages.push_back(id.toString() + "|" + Book.mId + " has no model"); + } + + //checking for icon + if (Book.mIcon.empty()) + { + messages.push_back(id.toString() + "|" + Book.mId + " has no icon"); + } +} + +void CSMTools::ReferenceableCheckStage::activatorCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Activator >& records, std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + + if (baserecord.isDeleted()) + { + return; + } + + const ESM::Activator& Activator = (static_cast& >(baserecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Activator, Activator.mId); + + //Checking for model, IIRC all activators should have a model + if (Activator.mModel.empty()) + { + messages.push_back(id.toString() + "|" + Activator.mId + " has no model"); + } } void CSMTools::ReferenceableCheckStage::setSizeVariables() { mBooksSize = mReferencables.getBooks().getSize(); + mActivatorsSize = mReferencables.getActivator().getSize(); } diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index 509e8d8c0..dc2913c22 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -17,10 +17,12 @@ namespace CSMTools private: void bookCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Book >& records, std::vector< std::string >& messages); + void activatorCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Activator >& records, std::vector< std::string >& messages); void setSizeVariables(); const CSMWorld::RefIdData mReferencables; int mBooksSize; + int mActivatorsSize; }; } #endif // REFERENCEABLECHECKSTAGE_H diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 0fbfef4f1..2285790be 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -236,3 +236,8 @@ const CSMWorld::RefIdDataContainer< ESM::Book >& CSMWorld::RefIdData::getBooks() { return mBooks; } + +const CSMWorld::RefIdDataContainer< ESM::Activator >& CSMWorld::RefIdData::getActivator() const +{ + return mActivators; +} diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 1590b58df..4a8e1e6f5 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -222,6 +222,7 @@ namespace CSMWorld //RECORD CONTAINERS ACCESS METHODS const RefIdDataContainer& getBooks() const; + const RefIdDataContainer& getActivator() const; }; } From 900bc06d2c236b80fe6c422ba335aefb897b37a7 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Fri, 20 Dec 2013 22:38:23 +0100 Subject: [PATCH 176/889] Fixes #845: NPCs hold torches during the day Moved 'equipping torches at night and unequipping at day' code from Character to Actors class. Removed unneeded showLights method (introduced in previous commits) from animation/npcanimation classes. Since this commit autoEquip() method doesn't automatically equip lights. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwmechanics/actors.cpp | 37 ++++++++++++++++++- apps/openmw/mwmechanics/actors.hpp | 11 +++--- apps/openmw/mwmechanics/character.cpp | 51 +++++--------------------- apps/openmw/mwrender/animation.hpp | 1 - apps/openmw/mwrender/npcanimation.cpp | 34 +++++------------ apps/openmw/mwrender/npcanimation.hpp | 2 - apps/openmw/mwworld/inventorystore.cpp | 7 ++++ 7 files changed, 66 insertions(+), 77 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 22a641b34..1561c9d5b 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -439,15 +439,48 @@ namespace MWMechanics void Actors::updateEquippedLight (const MWWorld::Ptr& ptr, float duration) { - //If holding a light... + bool isPlayer = ptr.getRefData().getHandle()=="player"; + MWWorld::InventoryStore &inventoryStore = MWWorld::Class::get(ptr).getInventoryStore(ptr); MWWorld::ContainerStoreIterator heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + /** + * Automatically equip NPCs torches at night and unequip them at day + */ + if (!isPlayer && !MWWorld::Class::get (ptr).getCreatureStats (ptr).isHostile()) + { + if (mTorchPtr.isEmpty()) + { + mTorchPtr = inventoryStore.search("torch_infinite_time"); + } + if (MWBase::Environment::get().getWorld()->isNight()) + { + if (heldIter != inventoryStore.end() && heldIter->getTypeName() != typeid(ESM::Light).name()) + { + inventoryStore.unequipItem(*heldIter, ptr); + } + else if (heldIter == inventoryStore.end() && !mTorchPtr.isEmpty()) + { + heldIter = inventoryStore.add(mTorchPtr, ptr); + inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, heldIter, ptr); + } + } + else + { + if (heldIter != inventoryStore.end() && heldIter->getTypeName() == typeid(ESM::Light).name()) + { + inventoryStore.unequipItem(*heldIter, ptr); + inventoryStore.add(*heldIter, ptr); + inventoryStore.autoEquip(ptr); + } + } + } + + //If holding a light... if(heldIter.getType() == MWWorld::ContainerStore::Type_Light) { // Use time from the player's light - bool isPlayer = ptr.getRefData().getHandle()=="player"; if(isPlayer) { float timeRemaining = heldIter->getClass().getRemainingUsageTime(*heldIter); diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 6afdefdbd..411ac54ca 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -25,14 +25,13 @@ namespace MWMechanics { class Actors { - typedef std::map PtrControllerMap; - PtrControllerMap mActors; - - std::map mDeathCount; - - void updateNpc(const MWWorld::Ptr &ptr, float duration, bool paused); + typedef std::map PtrControllerMap; + PtrControllerMap mActors; + std::map mDeathCount; + MWWorld::Ptr mTorchPtr; + void updateNpc(const MWWorld::Ptr &ptr, float duration, bool paused); void adjustMagicEffects (const MWWorld::Ptr& creature); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index f2b353297..e3373e7f3 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -709,50 +709,17 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun } } - if (mPtr.getRefData().getHandle() != "player") + MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if (item != inv.end() && item->getTypeName() == typeid(ESM::Light).name()) { - if (MWBase::Environment::get().getWorld()->isNight()) - { - MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if (item != inv.end() && item->getTypeName() != typeid(ESM::Light).name()) - { - inv.unequipItem(*item, mPtr); - } - else if (item == inv.end()) - { - MWWorld::Ptr itemPtr = inv.search("torch_infinite_time"); - if (!itemPtr.isEmpty()) - { - item = inv.add(itemPtr, mPtr); - inv.equip(MWWorld::InventoryStore::Slot_CarriedLeft, item, mPtr); - } - } - - if (item != inv.end() && item->getTypeName() == typeid(ESM::Light).name()) - { - mAnimation->showLights(true); - if (!mAnimation->isPlaying("torch")) - { - mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, - false, 1.0f, "start", "stop", 0.0f, (~(size_t)0)); - } - } - } - else - { - if (mAnimation->isPlaying("torch")) - { - mAnimation->disable("torch"); - mAnimation->showLights(false); - MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if (item != inv.end() && item->getTypeName() == typeid(ESM::Light).name()) - { - inv.unequipItem(*item, mPtr); - inv.add(*item, mPtr); - } - } - } + mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, + false, 1.0f, "start", "stop", 0.0f, (~(size_t)0)); } + else if (mAnimation->isPlaying("torch")) + { + mAnimation->disable("torch"); + } + return forcestateupdate; } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 16af6d5a6..aa04e39e2 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -275,7 +275,6 @@ public: virtual void showWeapons(bool showWeapon); virtual void showShield(bool show) {} - virtual void showLights(bool show) {} void enableLights(bool enable); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 2da45e8a1..eb0c5dfbc 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -118,7 +118,6 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v mViewMode(viewMode), mShowWeapons(false), mShowShield(true), - mShowLights(false), mFirstPersonOffset(0.f, 0.f, 0.f), mAlpha(1.f) { @@ -321,7 +320,6 @@ void NpcAnimation::updateParts() showWeapons(mShowWeapons); showShield(mShowShield); - showLights(mShowLights); // Remember body parts so we only have to search through the store once for each race/gender/viewmode combination static std::map< std::pair,std::vector > sRaceMapping; @@ -662,12 +660,21 @@ void NpcAnimation::showShield(bool show) MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if(show && shield != inv.end() && shield->getTypeName() != typeid(ESM::Light).name()) + if (shield != inv.end() && shield->getTypeName() == typeid(ESM::Light).name()) + { + // ... Except for lights, which are still shown during spellcasting since they + // have their own (one-handed) casting animations + show = true; + } + if(show && shield != inv.end()) { Ogre::Vector3 glowColor = getEnchantmentColor(*shield); std::string mesh = MWWorld::Class::get(*shield).getModel(*shield); addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, mesh, !shield->getClass().getEnchantment(*shield).empty(), &glowColor); + + if (shield->getTypeName() == typeid(ESM::Light).name()) + addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], shield->get()->mBase); } else { @@ -675,27 +682,6 @@ void NpcAnimation::showShield(bool show) } } -void NpcAnimation::showLights(bool show) -{ - MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); - MWWorld::ContainerStoreIterator light = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - - if(show && light != inv.end() && light->getTypeName() == typeid(ESM::Light).name()) - { - mShowLights = show; - Ogre::Vector3 glowColor = getEnchantmentColor(*light); - std::string mesh = MWWorld::Class::get(*light).getModel(*light); - addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, - mesh, !light->getClass().getEnchantment(*light).empty(), &glowColor); - addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], light->get()->mBase); - } - else - { - removeIndividualPart(ESM::PRT_Shield); - } -} - - void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound) { // During first auto equip, we don't play any sounds. diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 0500b46c6..04dde87c7 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -54,7 +54,6 @@ private: ViewMode mViewMode; bool mShowWeapons; bool mShowShield; - bool mShowLights; int mVisibilityFlags; @@ -102,7 +101,6 @@ public: virtual void showWeapons(bool showWeapon); virtual void showShield(bool showShield); - virtual void showLights(bool showLights); void setViewMode(ViewMode viewMode); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 57e35adce..856697b8e 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -175,6 +175,13 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) for (ContainerStoreIterator iter (begin()); iter!=end(); ++iter) { Ptr test = *iter; + + // Don't autoEquip lights + if (test.getTypeName() == typeid(ESM::Light).name()) + { + continue; + } + int testSkill = MWWorld::Class::get (test).getEquipmentSkill (test); std::pair, bool> itemsSlots = From 18a9878bdd82cd144f7bbb3efaa6a806a4169841 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sat, 21 Dec 2013 09:33:05 +0100 Subject: [PATCH 177/889] Fixes #1042: TES3 header data wrong encoding Changed loading of HEDR structure from all-in-once to field-by-field so author and descryption could be converted to UTF-8. Signed-off-by: Lukasz Gromanowski --- components/esm/loadtes3.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/components/esm/loadtes3.cpp b/components/esm/loadtes3.cpp index 87a8d1d57..262d4f6fa 100644 --- a/components/esm/loadtes3.cpp +++ b/components/esm/loadtes3.cpp @@ -19,7 +19,15 @@ void ESM::Header::blank() void ESM::Header::load (ESMReader &esm) { - esm.getHNT (mData, "HEDR", 300); + if (esm.isNextSub("HEDR")) + { + esm.getSubHeader(); + esm.getT(mData.version); + esm.getT(mData.type); + mData.author.assign(esm.getString(sizeof(mData.author.name))); + mData.desc.assign(esm.getString(sizeof(mData.desc.name))); + esm.getT(mData.records); + } if (esm.isNextSub ("FORM")) { @@ -52,4 +60,4 @@ void ESM::Header::save (ESMWriter &esm) esm.writeHNCString ("MAST", iter->name); esm.writeHNT ("DATA", iter->size); } -} \ No newline at end of file +} From 4842c56cb5849f6634a29f065064cd9704d70f37 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 21 Dec 2013 10:58:05 +0100 Subject: [PATCH 178/889] added getpotions --- apps/opencs/model/world/refiddata.cpp | 5 +++++ apps/opencs/model/world/refiddata.hpp | 1 + 2 files changed, 6 insertions(+) diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 2285790be..ddbde099e 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -241,3 +241,8 @@ const CSMWorld::RefIdDataContainer< ESM::Activator >& CSMWorld::RefIdData::getAc { return mActivators; } + +const CSMWorld::RefIdDataContainer< ESM::Potion >& CSMWorld::RefIdData::getPotions() const +{ + return mPotions; +} diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 4a8e1e6f5..9f5cf9f9a 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -223,6 +223,7 @@ namespace CSMWorld //RECORD CONTAINERS ACCESS METHODS const RefIdDataContainer& getBooks() const; const RefIdDataContainer& getActivator() const; + const RefIdDataContainer& getPotions() const; }; } From 385824aee0194d9ecf04c74303e7b225b880e687 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 21 Dec 2013 10:59:13 +0100 Subject: [PATCH 179/889] Renamed getActivator to getActivators. --- apps/opencs/model/tools/referenceablecheck.cpp | 2 +- apps/opencs/model/world/refiddata.cpp | 2 +- apps/opencs/model/world/refiddata.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index f8dcd86a4..eceaf999b 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -37,7 +37,7 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str if ((stage - PrevSum) <= mActivatorsSize) { - activatorCheck(stage - PrevSum, mReferencables.getActivator(), messages); + activatorCheck(stage - PrevSum, mReferencables.getActivators(), messages); CheckPerformed = true; } diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index ddbde099e..3a9d82630 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -237,7 +237,7 @@ const CSMWorld::RefIdDataContainer< ESM::Book >& CSMWorld::RefIdData::getBooks() return mBooks; } -const CSMWorld::RefIdDataContainer< ESM::Activator >& CSMWorld::RefIdData::getActivator() const +const CSMWorld::RefIdDataContainer< ESM::Activator >& CSMWorld::RefIdData::getActivators() const { return mActivators; } diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 9f5cf9f9a..1a4a41638 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -222,7 +222,7 @@ namespace CSMWorld //RECORD CONTAINERS ACCESS METHODS const RefIdDataContainer& getBooks() const; - const RefIdDataContainer& getActivator() const; + const RefIdDataContainer& getActivators() const; const RefIdDataContainer& getPotions() const; }; } From a6c36e23779251ad652312fe06742fa122880380 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 21 Dec 2013 11:01:40 +0100 Subject: [PATCH 180/889] Small optimization. --- apps/opencs/model/tools/referenceablecheck.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index eceaf999b..8eff6ed0b 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -20,7 +20,6 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str { //Checks for books, than, when stage is above mBooksSize goes to other checks, with (stage - PrevSum) as stage. bool CheckPerformed = false; - int PrevSum(0); if (stage <= mBooksSize) { @@ -32,12 +31,11 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str { return; } - - PrevSum += mBooksSize; - - if ((stage - PrevSum) <= mActivatorsSize) + stage -= mBooksSize; + + if ((stage) <= mActivatorsSize) { - activatorCheck(stage - PrevSum, mReferencables.getActivators(), messages); + activatorCheck(stage, mReferencables.getActivators(), messages); CheckPerformed = true; } @@ -45,8 +43,7 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str { return; } - - PrevSum += mActivatorsSize; + stage -= mActivatorsSize; } int CSMTools::ReferenceableCheckStage::setup() From 21ee2a6d2e6384b794210b67215ae71dad51c62b Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 21 Dec 2013 11:02:58 +0100 Subject: [PATCH 181/889] Getting size of potions. --- apps/opencs/model/tools/referenceablecheck.cpp | 6 ++++-- apps/opencs/model/tools/referenceablecheck.hpp | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 8eff6ed0b..ff2d6e713 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -11,7 +11,8 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable) : mReferencables(referenceable), mBooksSize(0), - mActivatorsSize(0) + mActivatorsSize(0), + mPotionsSize(0) { setSizeVariables(); } @@ -116,5 +117,6 @@ void CSMTools::ReferenceableCheckStage::activatorCheck(int stage, const CSMWorld void CSMTools::ReferenceableCheckStage::setSizeVariables() { mBooksSize = mReferencables.getBooks().getSize(); - mActivatorsSize = mReferencables.getActivator().getSize(); + mActivatorsSize = mReferencables.getActivators().getSize(); + mPotionsSize = mReferencables.getPotions().getSize(); } diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index dc2913c22..fed662e27 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -23,6 +23,7 @@ namespace CSMTools const CSMWorld::RefIdData mReferencables; int mBooksSize; int mActivatorsSize; + int mPotionsSize; }; } #endif // REFERENCEABLECHECKSTAGE_H From ee5dfd1cc8d0b40b806391ced7b36641ade4241f Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 21 Dec 2013 11:15:46 +0100 Subject: [PATCH 182/889] Adde potionCheck method --- .../opencs/model/tools/referenceablecheck.cpp | 69 +++++++++++++++---- .../opencs/model/tools/referenceablecheck.hpp | 10 +-- 2 files changed, 62 insertions(+), 17 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index ff2d6e713..59591ca02 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -20,31 +20,30 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefId void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::string >& messages) { //Checks for books, than, when stage is above mBooksSize goes to other checks, with (stage - PrevSum) as stage. - bool CheckPerformed = false; if (stage <= mBooksSize) { bookCheck(stage, mReferencables.getBooks(), messages); - CheckPerformed = true; - } - - if (CheckPerformed) - { return; } + stage -= mBooksSize; - - if ((stage) <= mActivatorsSize) + + if (stage <= mActivatorsSize) { activatorCheck(stage, mReferencables.getActivators(), messages); - CheckPerformed = true; - } - - if (CheckPerformed) - { return; } + stage -= mActivatorsSize; + + if (stage <= mPotionsSize) + { + potionsCheck(stage, mReferencables.getPotions(), messages); + return; + } + + stage -= mPotionsSize; } int CSMTools::ReferenceableCheckStage::setup() @@ -114,6 +113,50 @@ void CSMTools::ReferenceableCheckStage::activatorCheck(int stage, const CSMWorld } } +void CSMTools::ReferenceableCheckStage::potionsCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Potion >& records, std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + + if (baserecord.isDeleted()) + { + return; + } + + const ESM::Potion& Potion = (static_cast& >(baserecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, Potion.mId); + + //Checking for name + if (Potion.mName.empty()) + { + messages.push_back(id.toString() + "|" + Potion.mId + " has an empty name"); + } + + //Checking for weight + if (Potion.mData.mWeight < 0) + { + messages.push_back(id.toString() + "|" + Potion.mId + " has a negative weight"); + } + + //Checking for value + if (Potion.mData.mValue < 0) + { + messages.push_back(id.toString() + "|" + Potion.mId + " has a negative value"); + } + +//checking for model + if (Potion.mModel.empty()) + { + messages.push_back(id.toString() + "|" + Potion.mId + " has no model"); + } + + //checking for icon + if (Potion.mIcon.empty()) + { + messages.push_back(id.toString() + "|" + Potion.mId + " has no icon"); + } + //IIRC potion can have empty effects list just fine. +} + void CSMTools::ReferenceableCheckStage::setSizeVariables() { mBooksSize = mReferencables.getBooks().getSize(); diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index fed662e27..801a2232b 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -18,12 +18,14 @@ namespace CSMTools private: void bookCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Book >& records, std::vector< std::string >& messages); void activatorCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Activator >& records, std::vector< std::string >& messages); - void setSizeVariables(); + void potionsCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + + void setSizeVariables(); const CSMWorld::RefIdData mReferencables; - int mBooksSize; - int mActivatorsSize; - int mPotionsSize; + int mBooksSize; + int mActivatorsSize; + int mPotionsSize; }; } #endif // REFERENCEABLECHECKSTAGE_H From a27441720e20a119914d3ea512c1ff9298fac405 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 21 Dec 2013 12:07:40 +0100 Subject: [PATCH 183/889] Added enchantmnet check for books. --- .../opencs/model/tools/referenceablecheck.cpp | 85 ++++++++++++++++--- .../opencs/model/tools/referenceablecheck.hpp | 7 +- apps/opencs/model/world/refiddata.cpp | 5 ++ apps/opencs/model/world/refiddata.hpp | 1 + 4 files changed, 85 insertions(+), 13 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 59591ca02..b299744d6 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -12,11 +12,20 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefId mReferencables(referenceable), mBooksSize(0), mActivatorsSize(0), - mPotionsSize(0) + mPotionsSize(0), + mApparatiSize(0) { setSizeVariables(); } +void CSMTools::ReferenceableCheckStage::setSizeVariables() +{ + mBooksSize = mReferencables.getBooks().getSize(); + mActivatorsSize = mReferencables.getActivators().getSize(); + mPotionsSize = mReferencables.getPotions().getSize(); + mApparatiSize = mReferencables.getApparati().getSize(); +} + void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::string >& messages) { //Checks for books, than, when stage is above mBooksSize goes to other checks, with (stage - PrevSum) as stage. @@ -24,6 +33,7 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str if (stage <= mBooksSize) { bookCheck(stage, mReferencables.getBooks(), messages); + std::cout<<"Book checking \n"; return; } @@ -39,11 +49,19 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str if (stage <= mPotionsSize) { - potionsCheck(stage, mReferencables.getPotions(), messages); + potionCheck(stage, mReferencables.getPotions(), messages); return; } stage -= mPotionsSize; + + if (stage <= mApparatiSize) + { + apparatusCheck(stage, mReferencables.getApparati(), messages); + return; + } + + stage -= mApparatiSize; } int CSMTools::ReferenceableCheckStage::setup() @@ -72,13 +90,13 @@ void CSMTools::ReferenceableCheckStage::bookCheck(int stage, const CSMWorld::Ref //Checking for weight if (Book.mData.mWeight < 0) { - messages.push_back(id.toString() + "|" + Book.mId + " has a negative weight"); + messages.push_back(id.toString() + "|" + Book.mId + " has negative weight"); } //Checking for value if (Book.mData.mValue < 0) { - messages.push_back(id.toString() + "|" + Book.mId + " has a negative value"); + messages.push_back(id.toString() + "|" + Book.mId + " has negative value"); } //checking for model @@ -92,6 +110,12 @@ void CSMTools::ReferenceableCheckStage::bookCheck(int stage, const CSMWorld::Ref { messages.push_back(id.toString() + "|" + Book.mId + " has no icon"); } + + //checking for enchantment points + if (Book.mData.mEnchant < 0) + { + messages.push_back(id.toString() + "|" + Book.mId + " has negative enchantment"); + } } void CSMTools::ReferenceableCheckStage::activatorCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Activator >& records, std::vector< std::string >& messages) @@ -113,7 +137,7 @@ void CSMTools::ReferenceableCheckStage::activatorCheck(int stage, const CSMWorld } } -void CSMTools::ReferenceableCheckStage::potionsCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Potion >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::potionCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Potion >& records, std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -134,13 +158,13 @@ void CSMTools::ReferenceableCheckStage::potionsCheck(int stage, const CSMWorld:: //Checking for weight if (Potion.mData.mWeight < 0) { - messages.push_back(id.toString() + "|" + Potion.mId + " has a negative weight"); + messages.push_back(id.toString() + "|" + Potion.mId + " has negative weight"); } //Checking for value if (Potion.mData.mValue < 0) { - messages.push_back(id.toString() + "|" + Potion.mId + " has a negative value"); + messages.push_back(id.toString() + "|" + Potion.mId + " has negative value"); } //checking for model @@ -157,9 +181,46 @@ void CSMTools::ReferenceableCheckStage::potionsCheck(int stage, const CSMWorld:: //IIRC potion can have empty effects list just fine. } -void CSMTools::ReferenceableCheckStage::setSizeVariables() + +void CSMTools::ReferenceableCheckStage::apparatusCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Apparatus >& records, std::vector< std::string >& messages) { - mBooksSize = mReferencables.getBooks().getSize(); - mActivatorsSize = mReferencables.getActivators().getSize(); - mPotionsSize = mReferencables.getPotions().getSize(); -} + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + + if (baserecord.isDeleted()) + { + return; + } + + const ESM::Apparatus& Apparatus = (static_cast& >(baserecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Apparatus, Apparatus.mId); + + //Checking for name + if (Apparatus.mName.empty()) + { + messages.push_back(id.toString() + "|" + Apparatus.mId + " has an empty name"); + } + + //Checking for weight + if (Apparatus.mData.mWeight < 0) + { + messages.push_back(id.toString() + "|" + Apparatus.mId + " has negative weight"); + } + + //Checking for value + if (Apparatus.mData.mValue < 0) + { + messages.push_back(id.toString() + "|" + Apparatus.mId + " has negative value"); + } + +//checking for model + if (Apparatus.mModel.empty()) + { + messages.push_back(id.toString() + "|" + Apparatus.mId + " has no model"); + } + + //checking for icon + if (Apparatus.mIcon.empty()) + { + messages.push_back(id.toString() + "|" + Apparatus.mId + " has no icon"); + } +} \ No newline at end of file diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index 801a2232b..a40ca16a4 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -16,16 +16,21 @@ namespace CSMTools virtual int setup(); private: + //CONCRETE CHECKS void bookCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Book >& records, std::vector< std::string >& messages); void activatorCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Activator >& records, std::vector< std::string >& messages); - void potionsCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void potionCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void apparatusCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void setSizeVariables(); const CSMWorld::RefIdData mReferencables; + + //SIZES OF CONCRETE TYPES int mBooksSize; int mActivatorsSize; int mPotionsSize; + int mApparatiSize; }; } #endif // REFERENCEABLECHECKSTAGE_H diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 3a9d82630..6f278cc02 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -246,3 +246,8 @@ const CSMWorld::RefIdDataContainer< ESM::Potion >& CSMWorld::RefIdData::getPotio { return mPotions; } + +const CSMWorld::RefIdDataContainer< ESM::Apparatus >& CSMWorld::RefIdData::getApparati() const +{ + return mApparati; +} diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 1a4a41638..b32b0347a 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -224,6 +224,7 @@ namespace CSMWorld const RefIdDataContainer& getBooks() const; const RefIdDataContainer& getActivators() const; const RefIdDataContainer& getPotions() const; + const RefIdDataContainer& getApparati() const; }; } From 1811f0a71b8b0695f7b510dd36288133c9876fc8 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 21 Dec 2013 15:40:39 +0100 Subject: [PATCH 184/889] Works, but it seems that I miss something in referencables. I will write post on the forum. --- apps/opencs/model/tools/referenceablecheck.cpp | 11 +++++------ apps/opencs/model/tools/reportmodel.cpp | 2 +- apps/opencs/model/tools/tools.cpp | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index b299744d6..e86e8db54 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -30,16 +30,15 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str { //Checks for books, than, when stage is above mBooksSize goes to other checks, with (stage - PrevSum) as stage. - if (stage <= mBooksSize) + if (stage < mBooksSize) { bookCheck(stage, mReferencables.getBooks(), messages); - std::cout<<"Book checking \n"; return; } stage -= mBooksSize; - if (stage <= mActivatorsSize) + if (stage < mActivatorsSize) { activatorCheck(stage, mReferencables.getActivators(), messages); return; @@ -47,7 +46,7 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str stage -= mActivatorsSize; - if (stage <= mPotionsSize) + if (stage < mPotionsSize) { potionCheck(stage, mReferencables.getPotions(), messages); return; @@ -55,7 +54,7 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str stage -= mPotionsSize; - if (stage <= mApparatiSize) + if (stage < mApparatiSize) { apparatusCheck(stage, mReferencables.getApparati(), messages); return; @@ -223,4 +222,4 @@ void CSMTools::ReferenceableCheckStage::apparatusCheck(int stage, const CSMWorld { messages.push_back(id.toString() + "|" + Apparatus.mId + " has no icon"); } -} \ No newline at end of file +} diff --git a/apps/opencs/model/tools/reportmodel.cpp b/apps/opencs/model/tools/reportmodel.cpp index b12531875..d88361746 100644 --- a/apps/opencs/model/tools/reportmodel.cpp +++ b/apps/opencs/model/tools/reportmodel.cpp @@ -68,4 +68,4 @@ void CSMTools::ReportModel::add (const std::string& row) const CSMWorld::UniversalId& CSMTools::ReportModel::getUniversalId (int row) const { return mRows.at (row).first; -} \ No newline at end of file +} diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index bca9f6fb7..d8cbb2fc1 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -76,7 +76,7 @@ CSMDoc::Operation *CSMTools::Tools::getVerifier() mVerifier->appendStage (new SpellCheckStage (mData.getSpells())); - mVerifier->appendStage( new ReferenceableCheckStage (mData.getReferenceables().getDataSet())); + mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet())); } return mVerifier; From 955fe3e8cf627977477542e96c25199c1844b3ec Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 22 Dec 2013 10:00:04 +0100 Subject: [PATCH 185/889] Added check for non-positive quality of alchemical apparatus --- apps/opencs/model/tools/referenceablecheck.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index e86e8db54..da63dce9f 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -29,7 +29,6 @@ void CSMTools::ReferenceableCheckStage::setSizeVariables() void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::string >& messages) { //Checks for books, than, when stage is above mBooksSize goes to other checks, with (stage - PrevSum) as stage. - if (stage < mBooksSize) { bookCheck(stage, mReferencables.getBooks(), messages); @@ -222,4 +221,10 @@ void CSMTools::ReferenceableCheckStage::apparatusCheck(int stage, const CSMWorld { messages.push_back(id.toString() + "|" + Apparatus.mId + " has no icon"); } + + //checking for quality, 0 → apparatus is basicly useless, any negative → apparatus is harmfull instead of helpfull + if (Apparatus.mData.mQuality <= 0) + { + messages.push_back(id.toString() + "|" + Apparatus.mId + " has non-positive quality"); + } } From 97fc8acbdb207cdfb301df7c1f32a52aa04e840d Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 22 Dec 2013 14:22:03 +0100 Subject: [PATCH 186/889] Added armor check --- .../opencs/model/tools/referenceablecheck.cpp | 90 +++++++++++++++++-- .../opencs/model/tools/referenceablecheck.hpp | 2 + apps/opencs/model/world/refiddata.cpp | 5 ++ apps/opencs/model/world/refiddata.hpp | 1 + 4 files changed, 89 insertions(+), 9 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index da63dce9f..b6c99960d 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -13,7 +13,8 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefId mBooksSize(0), mActivatorsSize(0), mPotionsSize(0), - mApparatiSize(0) + mApparatiSize(0), + mArmorsSzie(0) { setSizeVariables(); } @@ -24,6 +25,7 @@ void CSMTools::ReferenceableCheckStage::setSizeVariables() mActivatorsSize = mReferencables.getActivators().getSize(); mPotionsSize = mReferencables.getPotions().getSize(); mApparatiSize = mReferencables.getApparati().getSize(); + mArmorsSzie = mReferencables.getArmors().getSize(); } void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::string >& messages) @@ -52,14 +54,22 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str } stage -= mPotionsSize; - + if (stage < mApparatiSize) { - apparatusCheck(stage, mReferencables.getApparati(), messages); - return; + apparatusCheck(stage, mReferencables.getApparati(), messages); + return; + } + + stage -= mApparatiSize; + + if (stage < mArmorsSzie) + { + armorCheck(stage, mReferencables.getArmors(), messages); + return; } - stage -= mApparatiSize; + stage -= mArmorsSzie; } int CSMTools::ReferenceableCheckStage::setup() @@ -108,11 +118,11 @@ void CSMTools::ReferenceableCheckStage::bookCheck(int stage, const CSMWorld::Ref { messages.push_back(id.toString() + "|" + Book.mId + " has no icon"); } - + //checking for enchantment points if (Book.mData.mEnchant < 0) { - messages.push_back(id.toString() + "|" + Book.mId + " has negative enchantment"); + messages.push_back(id.toString() + "|" + Book.mId + " has negative enchantment"); } } @@ -176,6 +186,7 @@ void CSMTools::ReferenceableCheckStage::potionCheck(int stage, const CSMWorld::R { messages.push_back(id.toString() + "|" + Potion.mId + " has no icon"); } + //IIRC potion can have empty effects list just fine. } @@ -221,10 +232,71 @@ void CSMTools::ReferenceableCheckStage::apparatusCheck(int stage, const CSMWorld { messages.push_back(id.toString() + "|" + Apparatus.mId + " has no icon"); } - + //checking for quality, 0 → apparatus is basicly useless, any negative → apparatus is harmfull instead of helpfull if (Apparatus.mData.mQuality <= 0) { - messages.push_back(id.toString() + "|" + Apparatus.mId + " has non-positive quality"); + messages.push_back(id.toString() + "|" + Apparatus.mId + " has non-positive quality"); + } +} + +void CSMTools::ReferenceableCheckStage::armorCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Armor >& records, std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + + if (baserecord.isDeleted()) + { + return; + } + + const ESM::Armor& Armor = (static_cast& >(baserecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Armor, Armor.mId); + + //Checking for name + if (Armor.mName.empty()) + { + messages.push_back(id.toString() + "|" + Armor.mId + " has an empty name"); + } + + //Checking for weight + if (Armor.mData.mWeight < 0) + { + messages.push_back(id.toString() + "|" + Armor.mId + " has negative weight"); + } + + //Checking for value + if (Armor.mData.mValue < 0) + { + messages.push_back(id.toString() + "|" + Armor.mId + " has negative value"); + } + +//checking for model + if (Armor.mModel.empty()) + { + messages.push_back(id.toString() + "|" + Armor.mId + " has no model"); + } + + //checking for icon + if (Armor.mIcon.empty()) + { + messages.push_back(id.toString() + "|" + Armor.mId + " has no icon"); + } + + //checking for enchantment points + if (Armor.mData.mEnchant < 0) + { + messages.push_back(id.toString() + "|" + Armor.mId + " has negative enchantment"); + } + + //checking for armor class, armor should have poistive armor class, but 0 is considered legal + if (Armor.mData.mArmor < 0) + { + messages.push_back(id.toString() + "|" + Armor.mId + " has negative armor class"); + } + + //checking for health. Only positive numbers are allowed, and 0 is illegal + if (Armor.mData.mHealth <= 0) + { + messages.push_back(id.toString() + "|" + Armor.mId + " has non positive health"); } } diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index a40ca16a4..7e5b761af 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -21,6 +21,7 @@ namespace CSMTools void activatorCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Activator >& records, std::vector< std::string >& messages); void potionCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void apparatusCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void armorCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void setSizeVariables(); @@ -31,6 +32,7 @@ namespace CSMTools int mActivatorsSize; int mPotionsSize; int mApparatiSize; + int mArmorsSzie; }; } #endif // REFERENCEABLECHECKSTAGE_H diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 6f278cc02..98a81c784 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -251,3 +251,8 @@ const CSMWorld::RefIdDataContainer< ESM::Apparatus >& CSMWorld::RefIdData::getAp { return mApparati; } + +const CSMWorld::RefIdDataContainer< ESM::Armor >& CSMWorld::RefIdData::getArmors() const +{ + return mArmors; +} diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index b32b0347a..f03da9588 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -225,6 +225,7 @@ namespace CSMWorld const RefIdDataContainer& getActivators() const; const RefIdDataContainer& getPotions() const; const RefIdDataContainer& getApparati() const; + const RefIdDataContainer& getArmors() const; }; } From bbcaef8e42be9decd6d082fd3981e1b4e2da6b27 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 22 Dec 2013 14:32:42 +0100 Subject: [PATCH 187/889] Added armor check and related stuff. --- .../opencs/model/tools/referenceablecheck.cpp | 75 +++++++++++++++++-- .../opencs/model/tools/referenceablecheck.hpp | 2 + apps/opencs/model/world/refiddata.cpp | 5 ++ apps/opencs/model/world/refiddata.hpp | 1 + 4 files changed, 75 insertions(+), 8 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index b6c99960d..8f871fca2 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -14,7 +14,8 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefId mActivatorsSize(0), mPotionsSize(0), mApparatiSize(0), - mArmorsSzie(0) + mArmorsSzie(0), + mClothingSize(0) { setSizeVariables(); } @@ -26,6 +27,7 @@ void CSMTools::ReferenceableCheckStage::setSizeVariables() mPotionsSize = mReferencables.getPotions().getSize(); mApparatiSize = mReferencables.getApparati().getSize(); mArmorsSzie = mReferencables.getArmors().getSize(); + mClothingSize = mReferencables.getClothing().getSize(); } void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::string >& messages) @@ -68,8 +70,16 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str armorCheck(stage, mReferencables.getArmors(), messages); return; } - + stage -= mArmorsSzie; + + if (stage < mClothingSize) + { + clothingCheck(stage, mReferencables.getClothing(), messages); + return; + } + + stage -= mClothingSize; } int CSMTools::ReferenceableCheckStage::setup() @@ -281,22 +291,71 @@ void CSMTools::ReferenceableCheckStage::armorCheck(int stage, const CSMWorld::Re { messages.push_back(id.toString() + "|" + Armor.mId + " has no icon"); } - - //checking for enchantment points + + //checking for enchantment points if (Armor.mData.mEnchant < 0) { messages.push_back(id.toString() + "|" + Armor.mId + " has negative enchantment"); } - + //checking for armor class, armor should have poistive armor class, but 0 is considered legal if (Armor.mData.mArmor < 0) { - messages.push_back(id.toString() + "|" + Armor.mId + " has negative armor class"); + messages.push_back(id.toString() + "|" + Armor.mId + " has negative armor class"); } - + //checking for health. Only positive numbers are allowed, and 0 is illegal if (Armor.mData.mHealth <= 0) { - messages.push_back(id.toString() + "|" + Armor.mId + " has non positive health"); + messages.push_back(id.toString() + "|" + Armor.mId + " has non positive health"); + } +} + +void CSMTools::ReferenceableCheckStage::clothingCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Clothing >& records, std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + + if (baserecord.isDeleted()) + { + return; + } + + const ESM::Clothing& Clothing = (static_cast& >(baserecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Clothing, Clothing.mId); + + //Checking for name + if (Clothing.mName.empty()) + { + messages.push_back(id.toString() + "|" + Clothing.mId + " has an empty name"); + } + + //Checking for weight + if (Clothing.mData.mWeight < 0) + { + messages.push_back(id.toString() + "|" + Clothing.mId + " has negative weight"); + } + + //Checking for value + if (Clothing.mData.mValue < 0) + { + messages.push_back(id.toString() + "|" + Clothing.mId + " has negative value"); + } + +//checking for model + if (Clothing.mModel.empty()) + { + messages.push_back(id.toString() + "|" + Clothing.mId + " has no model"); + } + + //checking for icon + if (Clothing.mIcon.empty()) + { + messages.push_back(id.toString() + "|" + Clothing.mId + " has no icon"); + } + + //checking for enchantment points + if (Clothing.mData.mEnchant < 0) + { + messages.push_back(id.toString() + "|" + Clothing.mId + " has negative enchantment"); } } diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index 7e5b761af..a4146c4d2 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -22,6 +22,7 @@ namespace CSMTools void potionCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void apparatusCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void armorCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void clothingCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void setSizeVariables(); @@ -33,6 +34,7 @@ namespace CSMTools int mPotionsSize; int mApparatiSize; int mArmorsSzie; + int mClothingSize; }; } #endif // REFERENCEABLECHECKSTAGE_H diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 98a81c784..28cf28565 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -256,3 +256,8 @@ const CSMWorld::RefIdDataContainer< ESM::Armor >& CSMWorld::RefIdData::getArmors { return mArmors; } + +const CSMWorld::RefIdDataContainer< ESM::Clothing >& CSMWorld::RefIdData::getClothing() const +{ + return mClothing; +} diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index f03da9588..237c2f30b 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -226,6 +226,7 @@ namespace CSMWorld const RefIdDataContainer& getPotions() const; const RefIdDataContainer& getApparati() const; const RefIdDataContainer& getArmors() const; + const RefIdDataContainer& getClothing() const; }; } From dc594da0ded4049c2d84e93c33f827c5894624a4 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 22 Dec 2013 14:34:29 +0100 Subject: [PATCH 188/889] added getContainers --- apps/opencs/model/world/refiddata.cpp | 5 +++++ apps/opencs/model/world/refiddata.hpp | 1 + 2 files changed, 6 insertions(+) diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 28cf28565..0a93d93ec 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -261,3 +261,8 @@ const CSMWorld::RefIdDataContainer< ESM::Clothing >& CSMWorld::RefIdData::getClo { return mClothing; } + +const CSMWorld::RefIdDataContainer< ESM::Container >& CSMWorld::RefIdData::getContainers() const +{ + return mContainers; +} diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 237c2f30b..2f6ea187e 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -227,6 +227,7 @@ namespace CSMWorld const RefIdDataContainer& getApparati() const; const RefIdDataContainer& getArmors() const; const RefIdDataContainer& getClothing() const; + const RefIdDataContainer& getContainers() const; }; } From 1a7f023237b8210b257dd2a1d0cc14bc091af7b4 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 22 Dec 2013 14:35:40 +0100 Subject: [PATCH 189/889] added mContainersSize. --- apps/opencs/model/tools/referenceablecheck.cpp | 4 +++- apps/opencs/model/tools/referenceablecheck.hpp | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 8f871fca2..055244cab 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -15,7 +15,8 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefId mPotionsSize(0), mApparatiSize(0), mArmorsSzie(0), - mClothingSize(0) + mClothingSize(0), + mContainersSize(0) { setSizeVariables(); } @@ -28,6 +29,7 @@ void CSMTools::ReferenceableCheckStage::setSizeVariables() mApparatiSize = mReferencables.getApparati().getSize(); mArmorsSzie = mReferencables.getArmors().getSize(); mClothingSize = mReferencables.getClothing().getSize(); + mContainersSize = mReferencables.getContainers().getSize(); } void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::string >& messages) diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index a4146c4d2..1c88c8cfe 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -35,6 +35,7 @@ namespace CSMTools int mApparatiSize; int mArmorsSzie; int mClothingSize; + int mContainersSize; }; } #endif // REFERENCEABLECHECKSTAGE_H From 16af9e69865632a88036ab32c70e6a6e3387ce40 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 22 Dec 2013 14:42:17 +0100 Subject: [PATCH 190/889] added container check --- .../opencs/model/tools/referenceablecheck.cpp | 39 +++++++++++++++++++ .../opencs/model/tools/referenceablecheck.hpp | 1 + 2 files changed, 40 insertions(+) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 055244cab..f08aadf86 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -82,6 +82,14 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str } stage -= mClothingSize; + + if (stage < mContainersSize) + { + containerCheck(stage, mReferencables.getContainers(), messages); + return; + } + + stage -= mContainersSize; } int CSMTools::ReferenceableCheckStage::setup() @@ -361,3 +369,34 @@ void CSMTools::ReferenceableCheckStage::clothingCheck(int stage, const CSMWorld: messages.push_back(id.toString() + "|" + Clothing.mId + " has negative enchantment"); } } + +void CSMTools::ReferenceableCheckStage::containerCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Container >& records, std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + + if (baserecord.isDeleted()) + { + return; + } + + const ESM::Container& Container = (static_cast& >(baserecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Container, Container.mId); + + //Checking for model, IIRC all containers should have a model + if (Container.mModel.empty()) + { + messages.push_back(id.toString() + "|" + Container.mId + " has no model"); + } + + //Checking for capacity (weight) + if (Container.mWeight < 0) //0 is allowed + { + messages.push_back(id.toString() + "|" + Container.mId + " has negative weight (capacity)"); + } + + //checking for name + if (Container.mName.empty()) + { + messages.push_back(id.toString() + "|" + Container.mId + " has an empty name"); + } +} diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index 1c88c8cfe..b55db38c6 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -23,6 +23,7 @@ namespace CSMTools void apparatusCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void armorCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void clothingCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void containerCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void setSizeVariables(); From 32046070d54d8231681c0e4b6efb1985c3ac6ba8 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 22 Dec 2013 14:44:22 +0100 Subject: [PATCH 191/889] Added getCreatures --- apps/opencs/model/world/refiddata.cpp | 5 +++++ apps/opencs/model/world/refiddata.hpp | 1 + 2 files changed, 6 insertions(+) diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 0a93d93ec..266741155 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -266,3 +266,8 @@ const CSMWorld::RefIdDataContainer< ESM::Container >& CSMWorld::RefIdData::getCo { return mContainers; } + +const CSMWorld::RefIdDataContainer< ESM::Creature >& CSMWorld::RefIdData::getCreatures() const +{ + return mCreatures; +} diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 2f6ea187e..6af433be2 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -228,6 +228,7 @@ namespace CSMWorld const RefIdDataContainer& getArmors() const; const RefIdDataContainer& getClothing() const; const RefIdDataContainer& getContainers() const; + const RefIdDataContainer& getCreatures() const; }; } From 94fcea4d4df7cea66629fefdea5b368269697e97 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 22 Dec 2013 14:46:07 +0100 Subject: [PATCH 192/889] added mCreaturesSize --- apps/opencs/model/tools/referenceablecheck.cpp | 4 +++- apps/opencs/model/tools/referenceablecheck.hpp | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index f08aadf86..750ef6448 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -16,7 +16,8 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefId mApparatiSize(0), mArmorsSzie(0), mClothingSize(0), - mContainersSize(0) + mContainersSize(0), + mCreaturesSize(0) { setSizeVariables(); } @@ -30,6 +31,7 @@ void CSMTools::ReferenceableCheckStage::setSizeVariables() mArmorsSzie = mReferencables.getArmors().getSize(); mClothingSize = mReferencables.getClothing().getSize(); mContainersSize = mReferencables.getContainers().getSize(); + mCreaturesSize = mReferencables.getCreatures().getSize(); } void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::string >& messages) diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index b55db38c6..17897fe34 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -37,6 +37,7 @@ namespace CSMTools int mArmorsSzie; int mClothingSize; int mContainersSize; + int mCreaturesSize; }; } #endif // REFERENCEABLECHECKSTAGE_H From e4e7d5062369570077172f1545c37e0c026253f7 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 23 Dec 2013 12:32:35 +0100 Subject: [PATCH 193/889] Added creatureCheck. I don't know meaning of all data fields here. --- .../opencs/model/tools/referenceablecheck.cpp | 104 +++++++++++++++++- .../opencs/model/tools/referenceablecheck.hpp | 1 + components/esm/loadcrea.hpp | 2 +- 3 files changed, 102 insertions(+), 5 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 750ef6448..c8a310e9f 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -84,13 +84,13 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str } stage -= mClothingSize; - + if (stage < mContainersSize) { - containerCheck(stage, mReferencables.getContainers(), messages); - return; + containerCheck(stage, mReferencables.getContainers(), messages); + return; } - + stage -= mContainersSize; } @@ -402,3 +402,99 @@ void CSMTools::ReferenceableCheckStage::containerCheck(int stage, const CSMWorld messages.push_back(id.toString() + "|" + Container.mId + " has an empty name"); } } + +void CSMTools::ReferenceableCheckStage::creatureCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Creature >& records, std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + + if (baserecord.isDeleted()) + { + return; + } + + const ESM::Creature& Creature = (static_cast&>(baserecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Creature, Creature.mId); + + if (Creature.mModel.empty()) + { + messages.push_back(id.toString() + "|" + Creature.mId + " has no model"); + } + + if (Creature.mName.empty()) + { + messages.push_back(id.toString() + "|" + Creature.mId + " has an empty name"); + } + + //stats checks + if (Creature.mData.mLevel < 1) + { + messages.push_back(id.toString() + "|" + Creature.mId + " has non-postive level"); + } + + if (Creature.mData.mStrength < 0) + { + messages.push_back(id.toString() + "|" + Creature.mId + " has negative strength"); + } + + if (Creature.mData.mIntelligence < 0) + { + messages.push_back(id.toString() + "|" + Creature.mId + " has negative intelligence"); + } + + if (Creature.mData.mWillpower < 0) + { + messages.push_back(id.toString() + "|" + Creature.mId + " has negative willpower"); + } + + if (Creature.mData.mAgility < 0) + { + messages.push_back(id.toString() + "|" + Creature.mId + " has negative agility"); + } + + if (Creature.mData.mSpeed < 0) + { + messages.push_back(id.toString() + "|" + Creature.mId + " has negative speed"); + } + + if (Creature.mData.mEndurance < 0) + { + messages.push_back(id.toString() + "|" + Creature.mId + " has negative endurance"); + } + + if (Creature.mData.mPersonality < 0) + { + messages.push_back(id.toString() + "|" + Creature.mId + " has negative personality"); + } + + if (Creature.mData.mLuck < 0) + { + messages.push_back(id.toString() + "|" + Creature.mId + " has negative luck"); + } + + if (Creature.mData.mHealth < 0) + { + messages.push_back(id.toString() + "|" + Creature.mId + " has negative health"); + } + + if (Creature.mData.mSoul < 0) + { + messages.push_back(id.toString() + "|" + Creature.mId + " has negative soul value"); + } + + for (int i = 0; i < 6; ++i) + { + if (Creature.mData.mAttack[i] < 0) + { + messages.push_back(id.toString() + "|" + Creature.mId + " has negative attack strength"); + break; + } + } + + //TODO, find meaning of other values + /* + if (Creature.mData.mGold < 0) + { + messages.push_back(id.toString() + "|" + Creature.mId + " has negative gold "); + } + */ +} diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index 17897fe34..72af4e0ff 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -24,6 +24,7 @@ namespace CSMTools void armorCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void clothingCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void containerCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void creatureCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void setSizeVariables(); diff --git a/components/esm/loadcrea.hpp b/components/esm/loadcrea.hpp index 99c4f5225..a39a7ebe1 100644 --- a/components/esm/loadcrea.hpp +++ b/components/esm/loadcrea.hpp @@ -66,7 +66,7 @@ struct Creature int mCombat, mMagic, mStealth; // Don't know yet. int mAttack[6]; // AttackMin1, AttackMax1, ditto2, ditto3 int mGold; - }; // 96 bytes + }; // 96 byte NPDTstruct mData; From 892ff93489fc21ab731e1fe5663f2db8c3babf03 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Tue, 24 Dec 2013 23:28:12 +0400 Subject: [PATCH 194/889] OS X: attempt to fix #1045 --- CMakeLists.txt | 3 ++- components/ogreinit/ogreinit.cpp | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f53d73813..04e75e94e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -288,7 +288,8 @@ endif() add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}") add_definitions(-DOGRE_PLUGIN_DIR_DBG="${OGRE_PLUGIN_DIR_DBG}") if (APPLE AND OPENMW_OSX_DEPLOYMENT) - add_definitions(-DOGRE_PLUGIN_DIR="${APP_BUNDLE_NAME}/Contents/Plugins") + # make it empty so plugin loading code can check this and try to find plugins inside app bundle + add_definitions(-DOGRE_PLUGIN_DIR="") else() add_definitions(-DOGRE_PLUGIN_DIR="${OGRE_PLUGIN_DIR}") endif() diff --git a/components/ogreinit/ogreinit.cpp b/components/ogreinit/ogreinit.cpp index c8fc621c7..46424a29a 100644 --- a/components/ogreinit/ogreinit.cpp +++ b/components/ogreinit/ogreinit.cpp @@ -6,6 +6,10 @@ #include #include +#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE +#include +#endif + #include #include "ogreplugin.hpp" @@ -127,6 +131,9 @@ namespace OgreInit #endif #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE pluginDir = OGRE_PLUGIN_DIR; + // if path is not specified try to find plugins inside the app bundle + if (pluginDir.empty()) + pluginDir = Ogre::macPluginPath(); #endif #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX pluginDir = OGRE_PLUGIN_DIR_REL; From 9fabae51981ca43eca252336ab9d3bdcf5ef8190 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Tue, 24 Dec 2013 23:59:59 +0400 Subject: [PATCH 195/889] OS X: force Cocoa API for Ogre windows in OpenCS --- apps/opencs/editor.cpp | 3 +++ apps/opencs/view/render/scenewidget.cpp | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 1c1e37c2d..a1de75053 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -225,6 +225,9 @@ int CS::Editor::run() params.insert(std::make_pair("FSAA", "0")); params.insert(std::make_pair("vsync", "false")); params.insert(std::make_pair("hidden", "true")); +#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE + params.insert(std::make_pair("macAPI", "cocoa")); +#endif Ogre::RenderWindow* hiddenWindow = Ogre::Root::getSingleton().createRenderWindow("InactiveHidden", 1, 1, false, ¶ms); hiddenWindow->setActive(false); diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index c8b37e9bb..8cbfc72c6 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -66,6 +66,10 @@ namespace CSVRender params.insert(std::make_pair("title", windowTitle.str())); params.insert(std::make_pair("FSAA", "0")); // TODO setting params.insert(std::make_pair("vsync", "false")); // TODO setting +#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE + params.insert(std::make_pair("macAPI", "cocoa")); + params.insert(std::make_pair("macAPICocoaUseNSView", "true")); +#endif mWindow = Ogre::Root::getSingleton().createRenderWindow(windowTitle.str(), this->width(), this->height(), false, ¶ms); mWindow->addViewport(mCamera)->setBackgroundColour(Ogre::ColourValue(0.3,0.3,0.3,1)); From c1715779fa626c2d28a944019c14df690128b22f Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 26 Dec 2013 12:59:43 +0100 Subject: [PATCH 196/889] Added door check with related methods. --- .../opencs/model/tools/referenceablecheck.cpp | 40 +++++++++++++++++-- .../opencs/model/tools/referenceablecheck.hpp | 2 + apps/opencs/model/world/refiddata.cpp | 6 +++ apps/opencs/model/world/refiddata.hpp | 1 + 4 files changed, 45 insertions(+), 4 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index c8a310e9f..03efb59cc 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -17,7 +17,8 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefId mArmorsSzie(0), mClothingSize(0), mContainersSize(0), - mCreaturesSize(0) + mCreaturesSize(0), + mDoorsSize(0) { setSizeVariables(); } @@ -32,6 +33,7 @@ void CSMTools::ReferenceableCheckStage::setSizeVariables() mClothingSize = mReferencables.getClothing().getSize(); mContainersSize = mReferencables.getContainers().getSize(); mCreaturesSize = mReferencables.getCreatures().getSize(); + mDoorsSize = mReferencables.getDoors().getSize(); } void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::string >& messages) @@ -92,6 +94,14 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str } stage -= mContainersSize; + + if (stage < mDoorsSize) + { + doorCheck(stage, mReferencables.getDoors(), messages); + return; + } + + stage -= mDoorsSize; } int CSMTools::ReferenceableCheckStage::setup() @@ -491,10 +501,32 @@ void CSMTools::ReferenceableCheckStage::creatureCheck(int stage, const CSMWorld: } //TODO, find meaning of other values - /* - if (Creature.mData.mGold < 0) + if (Creature.mData.mGold < 0) //It seems that this is for gold in merchant creatures { messages.push_back(id.toString() + "|" + Creature.mId + " has negative gold "); } - */ +} + +void CSMTools::ReferenceableCheckStage::doorCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Door >& records, std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + + if (baserecord.isDeleted()) + { + return; + } + + const ESM::Door& Door= (static_cast&>(baserecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Door, Door.mId); + + //usual, name and model + if (Door.mName.empty()) + { + messages.push_back(id.toString() + "|" + Door.mId + " has an empty name"); + } + + if (Door.mModel.empty()) + { + messages.push_back(id.toString() + "|" + Door.mId + " has no model"); + } } diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index 72af4e0ff..3a0b2a4b8 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -25,6 +25,7 @@ namespace CSMTools void clothingCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void containerCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void creatureCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void doorCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void setSizeVariables(); @@ -39,6 +40,7 @@ namespace CSMTools int mClothingSize; int mContainersSize; int mCreaturesSize; + int mDoorsSize; }; } #endif // REFERENCEABLECHECKSTAGE_H diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 266741155..83ff79cc8 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -271,3 +271,9 @@ const CSMWorld::RefIdDataContainer< ESM::Creature >& CSMWorld::RefIdData::getCre { return mCreatures; } + +const CSMWorld::RefIdDataContainer< ESM::Door >& CSMWorld::RefIdData::getDoors() const +{ + return mDoors; +} + diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 6af433be2..87818c9c9 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -229,6 +229,7 @@ namespace CSMWorld const RefIdDataContainer& getClothing() const; const RefIdDataContainer& getContainers() const; const RefIdDataContainer& getCreatures() const; + const RefIdDataContainer& getDoors() const; }; } From 6c95cea4c48c1e2dcde5819481715070b5289a65 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 26 Dec 2013 15:24:52 +0100 Subject: [PATCH 197/889] Added ingredient check --- .../opencs/model/tools/referenceablecheck.cpp | 61 ++++++++++++++++--- .../opencs/model/tools/referenceablecheck.hpp | 2 + apps/opencs/model/world/refiddata.cpp | 4 ++ apps/opencs/model/world/refiddata.hpp | 1 + 4 files changed, 61 insertions(+), 7 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 03efb59cc..589372f8a 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -18,7 +18,8 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefId mClothingSize(0), mContainersSize(0), mCreaturesSize(0), - mDoorsSize(0) + mDoorsSize(0), + mIngredientsSize(0) { setSizeVariables(); } @@ -34,6 +35,7 @@ void CSMTools::ReferenceableCheckStage::setSizeVariables() mContainersSize = mReferencables.getContainers().getSize(); mCreaturesSize = mReferencables.getCreatures().getSize(); mDoorsSize = mReferencables.getDoors().getSize(); + mIngredientsSize = mReferencables.getIngredients().getSize(); } void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::string >& messages) @@ -94,13 +96,13 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str } stage -= mContainersSize; - + if (stage < mDoorsSize) { - doorCheck(stage, mReferencables.getDoors(), messages); - return; + doorCheck(stage, mReferencables.getDoors(), messages); + return; } - + stage -= mDoorsSize; } @@ -516,9 +518,9 @@ void CSMTools::ReferenceableCheckStage::doorCheck(int stage, const CSMWorld::Ref return; } - const ESM::Door& Door= (static_cast&>(baserecord)).get(); + const ESM::Door& Door = (static_cast&>(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Door, Door.mId); - + //usual, name and model if (Door.mName.empty()) { @@ -529,4 +531,49 @@ void CSMTools::ReferenceableCheckStage::doorCheck(int stage, const CSMWorld::Ref { messages.push_back(id.toString() + "|" + Door.mId + " has no model"); } + + //TODO, check what static unsigned int sRecordId; is for +} + +void CSMTools::ReferenceableCheckStage::ingredientCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Ingredient >& records, std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + + if (baserecord.isDeleted()) + { + return; + } + + const ESM::Ingredient& Ingredient = (static_cast& >(baserecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Ingredient, Ingredient.mId); + + //Checking for name + if (Ingredient.mName.empty()) + { + messages.push_back(id.toString() + "|" + Ingredient.mId + " has an empty name"); + } + + //Checking for weight + if (Ingredient.mData.mWeight < 0) + { + messages.push_back(id.toString() + "|" + Ingredient.mId + " has negative weight"); + } + + //Checking for value + if (Ingredient.mData.mValue < 0) + { + messages.push_back(id.toString() + "|" + Ingredient.mId + " has negative value"); + } + +//checking for model + if (Ingredient.mModel.empty()) + { + messages.push_back(id.toString() + "|" + Ingredient.mId + " has no model"); + } + + //checking for icon + if (Ingredient.mIcon.empty()) + { + messages.push_back(id.toString() + "|" + Ingredient.mId + " has no icon"); + } } diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index 3a0b2a4b8..c7c418ff6 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -26,6 +26,7 @@ namespace CSMTools void containerCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void creatureCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void doorCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void ingredientCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void setSizeVariables(); @@ -41,6 +42,7 @@ namespace CSMTools int mContainersSize; int mCreaturesSize; int mDoorsSize; + int mIngredientsSize; }; } #endif // REFERENCEABLECHECKSTAGE_H diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 83ff79cc8..8af42703e 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -277,3 +277,7 @@ const CSMWorld::RefIdDataContainer< ESM::Door >& CSMWorld::RefIdData::getDoors() return mDoors; } +const CSMWorld::RefIdDataContainer< ESM::Ingredient >& CSMWorld::RefIdData::getIngredients() const +{ + return mIngredients; +} diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 87818c9c9..6a67ed1c8 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -230,6 +230,7 @@ namespace CSMWorld const RefIdDataContainer& getContainers() const; const RefIdDataContainer& getCreatures() const; const RefIdDataContainer& getDoors() const; + const RefIdDataContainer& getIngredients() const; }; } From 360a939aa0f224c7870b1193f6916f75c0eb253e Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 26 Dec 2013 15:35:58 +0100 Subject: [PATCH 198/889] Working on leveled lists now. --- .../opencs/model/tools/referenceablecheck.cpp | 27 ++++++++++++++++++- .../opencs/model/tools/referenceablecheck.hpp | 2 ++ apps/opencs/model/world/refiddata.cpp | 5 ++++ apps/opencs/model/world/refiddata.hpp | 1 + 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 589372f8a..12727d49c 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -19,7 +19,8 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefId mContainersSize(0), mCreaturesSize(0), mDoorsSize(0), - mIngredientsSize(0) + mIngredientsSize(0), + mCreaturesLevListsSize(0) { setSizeVariables(); } @@ -36,6 +37,7 @@ void CSMTools::ReferenceableCheckStage::setSizeVariables() mCreaturesSize = mReferencables.getCreatures().getSize(); mDoorsSize = mReferencables.getDoors().getSize(); mIngredientsSize = mReferencables.getIngredients().getSize(); + mCreaturesLevListsSize = mReferencables.getCreatureLevelledLists().getSize(); } void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::string >& messages) @@ -104,6 +106,14 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str } stage -= mDoorsSize; + + if (stage < mIngredientsSize) + { + ingredientCheck(stage, mReferencables.getIngredients(), messages); + return; + } + + stage -= mIngredientsSize; } int CSMTools::ReferenceableCheckStage::setup() @@ -577,3 +587,18 @@ void CSMTools::ReferenceableCheckStage::ingredientCheck(int stage, const CSMWorl messages.push_back(id.toString() + "|" + Ingredient.mId + " has no icon"); } } + +void CSMTools::ReferenceableCheckStage::creaturesLevListCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::CreatureLevList >& records, std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + + if (baserecord.isDeleted()) + { + return; + } + + const ESM::CreatureLevList& CreatureLevList = (static_cast& >(baserecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_CreatureLevList, CreatureLevList.mId); + + //TODO(!) +} \ No newline at end of file diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index c7c418ff6..8fd123b06 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -27,6 +27,7 @@ namespace CSMTools void creatureCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void doorCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void ingredientCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void creaturesLevListCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void setSizeVariables(); @@ -43,6 +44,7 @@ namespace CSMTools int mCreaturesSize; int mDoorsSize; int mIngredientsSize; + int mCreaturesLevListsSize; }; } #endif // REFERENCEABLECHECKSTAGE_H diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 8af42703e..aec20b188 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -281,3 +281,8 @@ const CSMWorld::RefIdDataContainer< ESM::Ingredient >& CSMWorld::RefIdData::getI { return mIngredients; } + +const CSMWorld::RefIdDataContainer< ESM::CreatureLevList >& CSMWorld::RefIdData::getCreatureLevelledLists() const +{ + return mCreatureLevelledLists; +} diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 6a67ed1c8..02ca53953 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -231,6 +231,7 @@ namespace CSMWorld const RefIdDataContainer& getCreatures() const; const RefIdDataContainer& getDoors() const; const RefIdDataContainer& getIngredients() const; + const RefIdDataContainer& getCreatureLevelledLists() const; }; } From c2993909cf0efce9850557d6b9519cefe0c533a0 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 26 Dec 2013 18:16:54 +0100 Subject: [PATCH 199/889] added creature leveled list check --- .../opencs/model/tools/referenceablecheck.cpp | 42 +++++++++++++++---- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 12727d49c..0abebc709 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -106,14 +106,22 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str } stage -= mDoorsSize; - + if (stage < mIngredientsSize) { - ingredientCheck(stage, mReferencables.getIngredients(), messages); - return; + ingredientCheck(stage, mReferencables.getIngredients(), messages); + return; } - + stage -= mIngredientsSize; + + if (stage < mCreaturesLevListsSize) + { + creaturesLevListCheck(stage, mReferencables.getCreatureLevelledLists(), messages); + return; + } + + stage -= mCreaturesLevListsSize; } int CSMTools::ReferenceableCheckStage::setup() @@ -590,7 +598,7 @@ void CSMTools::ReferenceableCheckStage::ingredientCheck(int stage, const CSMWorl void CSMTools::ReferenceableCheckStage::creaturesLevListCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::CreatureLevList >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); if (baserecord.isDeleted()) { @@ -598,7 +606,25 @@ void CSMTools::ReferenceableCheckStage::creaturesLevListCheck(int stage, const C } const ESM::CreatureLevList& CreatureLevList = (static_cast& >(baserecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_CreatureLevList, CreatureLevList.mId); - + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_CreatureLevelledList, CreatureLevList.mId); //CreatureLevList but Type_CreatureLevelledList :/ + + for (int i = 0; i < CreatureLevList.mList.size(); ++i) + { + if (CreatureLevList.mList[i].mId.empty()) + { + messages.push_back(id.toString() + "|" + CreatureLevList.mId + " contains item with empty Id"); + } + + if (CreatureLevList.mList[i].mLevel < 1) + { + messages.push_back(id.toString() + "|" + CreatureLevList.mId + " contains item with non-positive level"); + } + } + + if (CreatureLevList.mChanceNone < 0 or CreatureLevList.mChanceNone > 100) + { + messages.push_back(id.toString() + "|" + CreatureLevList.mId + " chance to be empty is not beetween 0 and 100"); + } + //TODO(!) -} \ No newline at end of file +} From 1da56e1790f084d267f802e32a746d4f61c1f2a7 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 26 Dec 2013 18:28:24 +0100 Subject: [PATCH 200/889] checkin leveled items lists --- .../opencs/model/tools/referenceablecheck.cpp | 45 +++++++++++++++++-- .../opencs/model/tools/referenceablecheck.hpp | 2 + apps/opencs/model/world/refiddata.cpp | 5 +++ apps/opencs/model/world/refiddata.hpp | 1 + 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 0abebc709..e2091de2e 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -20,7 +20,8 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefId mCreaturesSize(0), mDoorsSize(0), mIngredientsSize(0), - mCreaturesLevListsSize(0) + mCreaturesLevListsSize(0), + mItemLevelledListsSize(0) { setSizeVariables(); } @@ -38,6 +39,7 @@ void CSMTools::ReferenceableCheckStage::setSizeVariables() mDoorsSize = mReferencables.getDoors().getSize(); mIngredientsSize = mReferencables.getIngredients().getSize(); mCreaturesLevListsSize = mReferencables.getCreatureLevelledLists().getSize(); + mItemLevelledListsSize = mReferencables.getItemLevelledList().getSize(); } void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::string >& messages) @@ -122,6 +124,14 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str } stage -= mCreaturesLevListsSize; + + if (stage < mItemLevelledListsSize) + { + mItemLevelledListCheck(stage, mReferencables.getItemLevelledList(), messages); + return; + } + + stage -= mItemLevelledListsSize; } int CSMTools::ReferenceableCheckStage::setup() @@ -625,6 +635,35 @@ void CSMTools::ReferenceableCheckStage::creaturesLevListCheck(int stage, const C { messages.push_back(id.toString() + "|" + CreatureLevList.mId + " chance to be empty is not beetween 0 and 100"); } - - //TODO(!) +} + +void CSMTools::ReferenceableCheckStage::mItemLevelledListCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::ItemLevList >& records, std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + + if (baserecord.isDeleted()) + { + return; + } + + const ESM::ItemLevList& ItemLevList = (static_cast& >(baserecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_ItemLevelledList, CreatureLevList.mId); + + for (int i = 0; i < ItemLevList.mList.size(); ++i) + { + if (ItemLevList.mList[i].mId.empty()) + { + messages.push_back(id.toString() + "|" + ItemLevList.mId + " contains item with empty Id"); + } + + if (ItemLevList.mList[i].mLevel < 1) + { + messages.push_back(id.toString() + "|" + ItemLevList.mId + " contains item with non-positive level"); + } + } + + if (ItemLevList.mChanceNone < 0 or ItemLevList.mChanceNone > 100) + { + messages.push_back(id.toString() + "|" + ItemLevList.mId + " chance to be empty is not beetween 0 and 100"); + } } diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index 8fd123b06..5a0d39ec5 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -28,6 +28,7 @@ namespace CSMTools void doorCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void ingredientCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void creaturesLevListCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void mItemLevelledListCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void setSizeVariables(); @@ -45,6 +46,7 @@ namespace CSMTools int mDoorsSize; int mIngredientsSize; int mCreaturesLevListsSize; + int mItemLevelledListsSize; }; } #endif // REFERENCEABLECHECKSTAGE_H diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index aec20b188..7f3e6506b 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -286,3 +286,8 @@ const CSMWorld::RefIdDataContainer< ESM::CreatureLevList >& CSMWorld::RefIdData: { return mCreatureLevelledLists; } + +const CSMWorld::RefIdDataContainer< ESM::ItemLevList >& CSMWorld::RefIdData::getItemLevelledList() const +{ + return mItemLevelledLists; +} diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 02ca53953..6e7231a24 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -232,6 +232,7 @@ namespace CSMWorld const RefIdDataContainer& getDoors() const; const RefIdDataContainer& getIngredients() const; const RefIdDataContainer& getCreatureLevelledLists() const; + const RefIdDataContainer& getItemLevelledList() const; }; } From f04a727af67cd8bb1240f201a42fbb68837a7160 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 26 Dec 2013 18:40:47 +0100 Subject: [PATCH 201/889] removed set sizes, consted sizes, added itemleveledlist check --- .../opencs/model/tools/referenceablecheck.cpp | 52 +++++++------------ .../opencs/model/tools/referenceablecheck.hpp | 27 +++++----- apps/opencs/model/world/refiddata.cpp | 5 ++ apps/opencs/model/world/refiddata.hpp | 1 + 4 files changed, 37 insertions(+), 48 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index e2091de2e..d37808810 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -10,36 +10,20 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable) : mReferencables(referenceable), - mBooksSize(0), - mActivatorsSize(0), - mPotionsSize(0), - mApparatiSize(0), - mArmorsSzie(0), - mClothingSize(0), - mContainersSize(0), - mCreaturesSize(0), - mDoorsSize(0), - mIngredientsSize(0), - mCreaturesLevListsSize(0), - mItemLevelledListsSize(0) + mBooksSize(mReferencables.getBooks().getSize()), + mActivatorsSize(mReferencables.getActivators().getSize()), + mPotionsSize(mReferencables.getPotions().getSize()), + mApparatiSize(mReferencables.getApparati().getSize()), + mArmorsSzie(mReferencables.getArmors().getSize()), + mClothingSize(mReferencables.getClothing().getSize()), + mContainersSize(mReferencables.getContainers().getSize()), + mCreaturesSize(mReferencables.getCreatures().getSize()), + mDoorsSize(mReferencables.getDoors().getSize()), + mIngredientsSize(mReferencables.getIngredients().getSize()), + mCreaturesLevListsSize(mReferencables.getCreatureLevelledLists().getSize()), + mItemLevelledListsSize(mReferencables.getItemLevelledList().getSize()), + mLightsSize(mReferencables.getLights().getSize()) { - setSizeVariables(); -} - -void CSMTools::ReferenceableCheckStage::setSizeVariables() -{ - mBooksSize = mReferencables.getBooks().getSize(); - mActivatorsSize = mReferencables.getActivators().getSize(); - mPotionsSize = mReferencables.getPotions().getSize(); - mApparatiSize = mReferencables.getApparati().getSize(); - mArmorsSzie = mReferencables.getArmors().getSize(); - mClothingSize = mReferencables.getClothing().getSize(); - mContainersSize = mReferencables.getContainers().getSize(); - mCreaturesSize = mReferencables.getCreatures().getSize(); - mDoorsSize = mReferencables.getDoors().getSize(); - mIngredientsSize = mReferencables.getIngredients().getSize(); - mCreaturesLevListsSize = mReferencables.getCreatureLevelledLists().getSize(); - mItemLevelledListsSize = mReferencables.getItemLevelledList().getSize(); } void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::string >& messages) @@ -124,13 +108,13 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str } stage -= mCreaturesLevListsSize; - + if (stage < mItemLevelledListsSize) { - mItemLevelledListCheck(stage, mReferencables.getItemLevelledList(), messages); - return; + mItemLevelledListCheck(stage, mReferencables.getItemLevelledList(), messages); + return; } - + stage -= mItemLevelledListsSize; } @@ -647,7 +631,7 @@ void CSMTools::ReferenceableCheckStage::mItemLevelledListCheck(int stage, const } const ESM::ItemLevList& ItemLevList = (static_cast& >(baserecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_ItemLevelledList, CreatureLevList.mId); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_ItemLevelledList, ItemLevList.mId); for (int i = 0; i < ItemLevList.mList.size(); ++i) { diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index 5a0d39ec5..6bffc2b25 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -30,23 +30,22 @@ namespace CSMTools void creaturesLevListCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void mItemLevelledListCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); - void setSizeVariables(); - const CSMWorld::RefIdData mReferencables; //SIZES OF CONCRETE TYPES - int mBooksSize; - int mActivatorsSize; - int mPotionsSize; - int mApparatiSize; - int mArmorsSzie; - int mClothingSize; - int mContainersSize; - int mCreaturesSize; - int mDoorsSize; - int mIngredientsSize; - int mCreaturesLevListsSize; - int mItemLevelledListsSize; + const int mBooksSize; + const int mActivatorsSize; + const int mPotionsSize; + const int mApparatiSize; + const int mArmorsSzie; + const int mClothingSize; + const int mContainersSize; + const int mCreaturesSize; + const int mDoorsSize; + const int mIngredientsSize; + const int mCreaturesLevListsSize; + const int mItemLevelledListsSize; + const int mLightsSize; }; } #endif // REFERENCEABLECHECKSTAGE_H diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 7f3e6506b..54e2cd12f 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -291,3 +291,8 @@ const CSMWorld::RefIdDataContainer< ESM::ItemLevList >& CSMWorld::RefIdData::get { return mItemLevelledLists; } + +const CSMWorld::RefIdDataContainer< ESM::Light >& CSMWorld::RefIdData::getLights() const +{ + return mLights; +} diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 6e7231a24..6b6e01e01 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -233,6 +233,7 @@ namespace CSMWorld const RefIdDataContainer& getIngredients() const; const RefIdDataContainer& getCreatureLevelledLists() const; const RefIdDataContainer& getItemLevelledList() const; + const RefIdDataContainer& getLights() const; }; } From 6d27ebabb61d97414c91ec9732ab6a0644c7072a Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Dec 2013 15:06:15 +0100 Subject: [PATCH 202/889] Integrate AddGlow with material controllers --- apps/openmw/mwbase/environment.cpp | 6 ++--- apps/openmw/mwrender/animation.cpp | 34 +++++++++++----------------- components/nifogre/material.cpp | 1 - components/nifogre/ogrenifloader.hpp | 2 ++ extern/shiny/Main/Factory.hpp | 3 +-- 5 files changed, 19 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 6b309025c..4db0b45b9 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -141,15 +141,15 @@ void MWBase::Environment::cleanup() delete mScriptManager; mScriptManager = 0; + delete mWindowManager; + mWindowManager = 0; + delete mWorld; mWorld = 0; delete mSoundManager; mSoundManager = 0; - delete mWindowManager; - mWindowManager = 0; - delete mInputManager; mInputManager = 0; } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 42d11aa6d..3b04457b6 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -146,29 +146,21 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly) struct AddGlow { Ogre::Vector3* mColor; - AddGlow(Ogre::Vector3* col) : mColor(col) {} + NifOgre::MaterialControllerManager* mMaterialControllerMgr; + AddGlow(Ogre::Vector3* col, NifOgre::MaterialControllerManager* materialControllerMgr) + : mColor(col) + , mMaterialControllerMgr(materialControllerMgr) + {} - // TODO: integrate this with material controllers? void operator()(Ogre::Entity* entity) const { - unsigned int numsubs = entity->getNumSubEntities(); - for(unsigned int i = 0;i < numsubs;++i) - { - unsigned int numsubs = entity->getNumSubEntities(); - for(unsigned int i = 0;i < numsubs;++i) - { - Ogre::SubEntity* subEnt = entity->getSubEntity(i); - std::string newName = subEnt->getMaterialName() + "@fx"; - if (sh::Factory::getInstance().searchInstance(newName) == NULL) - { - sh::MaterialInstance* instance = - sh::Factory::getInstance().createMaterialInstance(newName, subEnt->getMaterialName()); - instance->setProperty("env_map", sh::makeProperty(new sh::BooleanValue(true))); - instance->setProperty("env_map_color", sh::makeProperty(new sh::Vector3(mColor->x, mColor->y, mColor->z))); - } - subEnt->setMaterialName(newName); - } - } + if (!entity->getNumSubEntities()) + return; + Ogre::MaterialPtr writableMaterial = mMaterialControllerMgr->getWritableMaterial(entity); + sh::MaterialInstance* instance = sh::Factory::getInstance().getMaterialInstance(writableMaterial->getName()); + + instance->setProperty("env_map", sh::makeProperty(new sh::BooleanValue(true))); + instance->setProperty("env_map_color", sh::makeProperty(new sh::Vector3(mColor->x, mColor->y, mColor->z))); } }; @@ -216,7 +208,7 @@ void Animation::setRenderProperties(NifOgre::ObjectScenePtr objlist, Ogre::uint3 if (enchantedGlow) std::for_each(objlist->mEntities.begin(), objlist->mEntities.end(), - AddGlow(glowColor)); + AddGlow(glowColor, &objlist->mMaterialControllerMgr)); } diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index bef0ec1d1..d529fb109 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -390,7 +390,6 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, instance->setProperty("depth_write", sh::makeProperty(new sh::StringValue(((depthFlags>>1)&1) ? "on" : "off"))); // depth_func??? - sh::Factory::getInstance()._ensureMaterial(name, "Default"); return name; } diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index 976a31ccd..5858aef39 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -45,6 +45,8 @@ class MaterialControllerManager { public: ~MaterialControllerManager(); + + /// @attention if \a movable is an Entity, it needs to have *one* SubEntity Ogre::MaterialPtr getWritableMaterial (Ogre::MovableObject* movable); private: diff --git a/extern/shiny/Main/Factory.hpp b/extern/shiny/Main/Factory.hpp index 7d52266b5..15c859958 100644 --- a/extern/shiny/Main/Factory.hpp +++ b/extern/shiny/Main/Factory.hpp @@ -259,9 +259,8 @@ namespace sh Platform* mPlatform; MaterialInstance* findInstance (const std::string& name); - public: - MaterialInstance* searchInstance (const std::string& name); private: + MaterialInstance* searchInstance (const std::string& name); /// @return was anything removed? bool removeCache (const std::string& pattern); From c5c3248376cac94a163a90416b8f32556455753e Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 22 Dec 2013 16:16:08 +0100 Subject: [PATCH 203/889] Compile fixes for Ogre 1.10 --- components/nifogre/particles.cpp | 56 +++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/components/nifogre/particles.cpp b/components/nifogre/particles.cpp index e54a77885..d306f4944 100644 --- a/components/nifogre/particles.cpp +++ b/components/nifogre/particles.cpp @@ -189,23 +189,36 @@ public: xOff = Ogre::Math::SymmetricRandom() * mXRange; yOff = Ogre::Math::SymmetricRandom() * mYRange; zOff = Ogre::Math::SymmetricRandom() * mZRange; - - particle->position = mBone->_getDerivedPosition() + xOff + yOff + zOff; + +#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0) + Ogre::Vector3& position = particle->mPosition; + Ogre::Vector3& direction = particle->mDirection; + Ogre::ColourValue& colour = particle->mColour; + Ogre::Real& totalTimeToLive = particle->mTotalTimeToLive; + Ogre::Real& timeToLive = particle->mTimeToLive; +#else + Ogre::Vector3& position = particle->position; + Ogre::Vector3& direction = particle->direction; + Ogre::ColourValue& colour = particle->colour; + Ogre::Real& totalTimeToLive = particle->totalTimeToLive; + Ogre::Real& timeToLive = particle->timeToLive; +#endif + position = mBone->_getDerivedPosition() + xOff + yOff + zOff; // Generate complex data by reference - genEmissionColour(particle->colour); + genEmissionColour(colour); // NOTE: We do not use mDirection/mAngle for the initial direction. Ogre::Radian hdir = mHorizontalDir + mHorizontalAngle*Ogre::Math::SymmetricRandom(); Ogre::Radian vdir = mVerticalDir + mVerticalAngle*Ogre::Math::SymmetricRandom(); - particle->direction = (mBone->_getDerivedOrientation() * Ogre::Quaternion(hdir, Ogre::Vector3::UNIT_Z) * + direction = (mBone->_getDerivedOrientation() * Ogre::Quaternion(hdir, Ogre::Vector3::UNIT_Z) * Ogre::Quaternion(vdir, Ogre::Vector3::UNIT_X)) * Ogre::Vector3::UNIT_Z; - genEmissionVelocity(particle->direction); + genEmissionVelocity(direction); // Generate simpler data - particle->timeToLive = particle->totalTimeToLive = genEmissionTTL(); + timeToLive = totalTimeToLive = genEmissionTTL(); } /** Overloaded to update the trans. matrix */ @@ -466,9 +479,13 @@ public: /** See Ogre::ParticleAffector. */ void _initParticle(Ogre::Particle *particle) { +#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0) + const Ogre::Real life_time = particle->mTotalTimeToLive; + Ogre::Real particle_time = particle->mTimeToLive; +#else const Ogre::Real life_time = particle->totalTimeToLive; Ogre::Real particle_time = particle->timeToLive; - +#endif Ogre::Real width = mParent->getDefaultWidth(); Ogre::Real height = mParent->getDefaultHeight(); if(life_time-particle_time < mGrowTime) @@ -493,9 +510,13 @@ public: while (!pi.end()) { Ogre::Particle *p = pi.getNext(); - const Ogre::Real life_time = p->totalTimeToLive; - Ogre::Real particle_time = p->timeToLive; - +#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0) + const Ogre::Real life_time = p->mTotalTimeToLive; + Ogre::Real particle_time = p->mTimeToLive; +#else + const Ogre::Real life_time = p->totalTimeToLive; + Ogre::Real particle_time = p->timeToLive; +#endif Ogre::Real width = mParent->getDefaultWidth(); Ogre::Real height = mParent->getDefaultHeight(); if(life_time-particle_time < mGrowTime) @@ -772,7 +793,11 @@ protected: while (!pi.end()) { Ogre::Particle *p = pi.getNext(); +#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0) + p->mDirection += vec; +#else p->direction += vec; +#endif } } @@ -783,9 +808,18 @@ protected: while (!pi.end()) { Ogre::Particle *p = pi.getNext(); +#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0) + Ogre::Vector3 position = p->mPosition; +#else + Ogre::Vector3 position = p->position; +#endif const Ogre::Vector3 vec = ( - (mBone->_getDerivedOrientation() * mPosition + mBone->_getDerivedPosition()) - p->position).normalisedCopy() * force; + (mBone->_getDerivedOrientation() * mPosition + mBone->_getDerivedPosition()) - position).normalisedCopy() * force; +#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0) + p->mDirection += vec; +#else p->direction += vec; +#endif } } From 74e902330fb91e0cc2757bf27e1f65c274aac7d0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Dec 2013 13:40:09 +0100 Subject: [PATCH 204/889] Fix a terrain shader issue --- files/materials/terrain.shader | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 7903292d3..3b80c3aec 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -345,6 +345,7 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5; #endif @shForeach(@shPropertyString(num_layers)) + thisLayerUV = layerUV; #if @shPropertyBool(use_normal_map_@shIterator) normalTex = shSample(normalMap@shIterator, thisLayerUV); #if @shIterator == 0 && IS_FIRST_PASS @@ -354,7 +355,6 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5; #endif #endif - thisLayerUV = layerUV; #if @shPropertyBool(use_parallax_@shIterator) thisLayerUV += TSeyeDir.xy * ( normalTex.a * PARALLAX_SCALE + PARALLAX_BIAS ); #endif From 81ec8c2f5565aa91be7a043808dde68deceb9422 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Dec 2013 23:55:40 +0100 Subject: [PATCH 205/889] Handle --version and --help before reading configuration - putting these options into openmw.cfg makes no sense --- apps/openmw/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 51281e213..e3158d268 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -168,8 +168,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat bpo::store(valid_opts, variables); bpo::notify(variables); - cfgMgr.readConfiguration(variables, desc); - bool run = true; if (variables.count ("help")) @@ -187,6 +185,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat if (!run) return false; + cfgMgr.readConfiguration(variables, desc); + engine.setGrabMouse(!variables.count("no-grab")); // Font encoding settings From b6bad969a063dc48d2d6ee8ca8f5a8dda94b5159 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Dec 2013 23:57:42 +0100 Subject: [PATCH 206/889] Fix an issue with items that have no UI icon --- apps/openmw/mwgui/container.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 19ed4dbc0..b7c6e3367 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -61,8 +61,9 @@ namespace MWGui mDraggedWidget = baseWidget; MyGUI::ImageBox* image = baseWidget->createWidget("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); - int pos = path.rfind("."); - path.erase(pos); + size_t pos = path.rfind("."); + if (pos != std::string::npos) + path.erase(pos); path.append(".dds"); image->setImageTexture(path); image->setNeedMouseFocus(false); From 31c1f484eda7d40ff7c11f415e45fd53ea5d736a Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Dec 2013 00:20:01 +0100 Subject: [PATCH 207/889] Slight performance improvement for WindowManager::updateVisible --- apps/openmw/mwgui/inventorywindow.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 56c474c89..ffd81e98b 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -120,10 +120,13 @@ namespace MWGui Settings::Manager::getFloat(setting + " y", "Windows") * viewSize.height); MyGUI::IntSize size (Settings::Manager::getFloat(setting + " w", "Windows") * viewSize.width, Settings::Manager::getFloat(setting + " h", "Windows") * viewSize.height); + + if (size.width != mMainWidget->getWidth() || size.height != mMainWidget->getHeight()) + mPreviewDirty = true; + mMainWidget->setPosition(pos); mMainWidget->setSize(size); adjustPanes(); - mPreviewDirty = true; } TradeItemModel* InventoryWindow::getTradeModel() From a9e1e89bbc32fecb16efb746e31254def19347b5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Dec 2013 00:29:16 +0100 Subject: [PATCH 208/889] Bug #1007: Fix the console getting key focus when a reference becomes unavailable, even if the console is not visible --- apps/openmw/mwgui/console.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index 96bc204c1..b8d20709d 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -406,13 +406,14 @@ namespace MWGui setTitle("#{sConsoleTitle} (" + object.getCellRef().mRefID + ")"); mPtr = object; } + // User clicked on an object. Restore focus to the console command line. + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine); } else { setTitle("#{sConsoleTitle}"); mPtr = MWWorld::Ptr(); } - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine); } void Console::onReferenceUnavailable() From 0050e6e67b2e32ce1ba9e75ffbdec5dcec6d19fc Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Dec 2013 01:29:42 +0100 Subject: [PATCH 209/889] Support materials with no base (diffuse) texture (should be white). Support alternate UV set for diffuse texture. --- components/nifogre/material.cpp | 7 ++++++- files/materials/objects.mat | 8 ++++++-- files/materials/objects.shader | 13 ++++++++++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index d529fb109..a18b29544 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -324,6 +324,11 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, instance->setProperty("normalMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BumpTexture])); instance->setProperty("detailMap", sh::makeProperty(texName[Nif::NiTexturingProperty::DetailTexture])); instance->setProperty("emissiveMap", sh::makeProperty(texName[Nif::NiTexturingProperty::GlowTexture])); + if (!texName[Nif::NiTexturingProperty::BaseTexture].empty()) + { + instance->setProperty("use_diffuse_map", sh::makeProperty(new sh::BooleanValue(true))); + instance->setProperty("diffuseMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::BaseTexture].uvSet))); + } if (!texName[Nif::NiTexturingProperty::GlowTexture].empty()) { instance->setProperty("use_emissive_map", sh::makeProperty(new sh::BooleanValue(true))); @@ -347,7 +352,7 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, i == Nif::NiTexturingProperty::GlowTexture) continue; if(!texName[i].empty()) - warn("Ignored texture "+texName[i]+" on layer "+Ogre::StringConverter::toString(i)); + warn("Ignored texture "+texName[i]+" on layer "+Ogre::StringConverter::toString(i) + " in " + name); } if (vertexColour) diff --git a/files/materials/objects.mat b/files/materials/objects.mat index 32787e159..2cced6091 100644 --- a/files/materials/objects.mat +++ b/files/materials/objects.mat @@ -10,8 +10,10 @@ material openmw_objects_base emissiveMap use_emissive_map false use_detail_map false + use_diffuse_map false emissiveMapUVSet 0 detailMapUVSet 0 + diffuseMapUVSet 0 use_parallax false scene_blend default @@ -34,8 +36,10 @@ material openmw_objects_base normalMap $normalMap emissiveMapUVSet $emissiveMapUVSet detailMapUVSet $detailMapUVSet + diffuseMapUVSet $diffuseMapUVSet emissiveMap $emissiveMap detailMap $detailMap + diffuseMap $diffuseMap env_map $env_map env_map_color $env_map_color use_parallax $use_parallax @@ -55,8 +59,8 @@ material openmw_objects_base texture_unit diffuseMap { direct_texture $diffuseMap - create_in_ffp true - tex_coord_set $emissiveMapUVSet + create_in_ffp $use_diffuse_map + tex_coord_set $diffuseMapUVSet } texture_unit normalMap diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 3d873f463..e50c97a6a 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -17,13 +17,14 @@ #define NORMAL_MAP @shPropertyHasValue(normalMap) #define EMISSIVE_MAP @shPropertyHasValue(emissiveMap) #define DETAIL_MAP @shPropertyHasValue(detailMap) +#define DIFFUSE_MAP @shPropertyHasValue(diffuseMap) #define PARALLAX @shPropertyBool(use_parallax) #define PARALLAX_SCALE 0.04 #define PARALLAX_BIAS -0.02 // right now we support 2 UV sets max. implementing them is tedious, and we're probably not going to need more -#define SECOND_UV_SET (@shPropertyString(emissiveMapUVSet) || @shPropertyString(detailMapUVSet)) +#define SECOND_UV_SET (@shPropertyString(emissiveMapUVSet) || @shPropertyString(detailMapUVSet) || @shPropertyString(diffuseMapUVSet)) // if normal mapping is enabled, we force pixel lighting #define VERTEX_LIGHTING (!@shPropertyHasValue(normalMap)) @@ -246,7 +247,9 @@ #endif SH_BEGIN_PROGRAM +#if DIFFUSE_MAP shSampler2D(diffuseMap) +#endif #if NORMAL_MAP shSampler2D(normalMap) @@ -376,7 +379,15 @@ newUV += (TSeyeDir.xyxy * ( normalTex.a * PARALLAX_SCALE + PARALLAX_BIAS )).xyxy; #endif +#if DIFFUSE_MAP + #if @shPropertyString(diffuseMapUVSet) + float4 diffuse = shSample(diffuseMap, newUV.zw); + #else float4 diffuse = shSample(diffuseMap, newUV.xy); + #endif +#else + float4 diffuse = float4(1,1,1,1); +#endif shOutputColour(0) = diffuse; #if DETAIL_MAP From e68e2f82a2db9288bc3babb6202c7a53fdc83991 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Dec 2013 02:40:55 +0100 Subject: [PATCH 210/889] Implement DarkTexture slot. Fix an issue with incorrect transparency override when base texture is empty. --- components/nifogre/material.cpp | 23 +++++++++++++++++------ files/materials/objects.mat | 24 +++++++++++++++++++----- files/materials/objects.shader | 25 +++++++++++++++++++------ 3 files changed, 55 insertions(+), 17 deletions(-) diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index a18b29544..c7c9abfc1 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -324,6 +324,7 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, instance->setProperty("normalMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BumpTexture])); instance->setProperty("detailMap", sh::makeProperty(texName[Nif::NiTexturingProperty::DetailTexture])); instance->setProperty("emissiveMap", sh::makeProperty(texName[Nif::NiTexturingProperty::GlowTexture])); + instance->setProperty("darkMap", sh::makeProperty(texName[Nif::NiTexturingProperty::DarkTexture])); if (!texName[Nif::NiTexturingProperty::BaseTexture].empty()) { instance->setProperty("use_diffuse_map", sh::makeProperty(new sh::BooleanValue(true))); @@ -339,6 +340,11 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, instance->setProperty("use_detail_map", sh::makeProperty(new sh::BooleanValue(true))); instance->setProperty("detailMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::DetailTexture].uvSet))); } + if (!texName[Nif::NiTexturingProperty::DarkTexture].empty()) + { + instance->setProperty("use_dark_map", sh::makeProperty(new sh::BooleanValue(true))); + instance->setProperty("darkMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::DarkTexture].uvSet))); + } bool useParallax = !texName[Nif::NiTexturingProperty::BumpTexture].empty() && texName[Nif::NiTexturingProperty::BumpTexture].find("_nh.") != std::string::npos; @@ -348,6 +354,7 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, { if(i == Nif::NiTexturingProperty::BaseTexture || i == Nif::NiTexturingProperty::DetailTexture || + i == Nif::NiTexturingProperty::DarkTexture || i == Nif::NiTexturingProperty::BumpTexture || i == Nif::NiTexturingProperty::GlowTexture) continue; @@ -358,15 +365,19 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, if (vertexColour) instance->setProperty("has_vertex_colour", sh::makeProperty(new sh::BooleanValue(true))); - // Add transparency if NiAlphaProperty was present - NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName[0]); - if (result.first) + // Override alpha flags based on our override list (transparency-overrides.cfg) + if (!texName[0].empty()) { - alphaFlags = (1<<9) | (6<<10); /* alpha_rejection enabled, greater_equal */ - alphaTest = result.second; - depthFlags = (1<<0) | (1<<1); // depth_write on, depth_check on + NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName[0]); + if (result.first) + { + alphaFlags = (1<<9) | (6<<10); /* alpha_rejection enabled, greater_equal */ + alphaTest = result.second; + depthFlags = (1<<0) | (1<<1); // depth_write on, depth_check on + } } + // Add transparency if NiAlphaProperty was present if((alphaFlags&1)) { std::string blend_mode; diff --git a/files/materials/objects.mat b/files/materials/objects.mat index 2cced6091..751b51243 100644 --- a/files/materials/objects.mat +++ b/files/materials/objects.mat @@ -8,12 +8,15 @@ material openmw_objects_base diffuseMap black.png normalMap emissiveMap + darkMap use_emissive_map false use_detail_map false use_diffuse_map false + use_dark_map false emissiveMapUVSet 0 detailMapUVSet 0 diffuseMapUVSet 0 + darkMapUVSet 0 use_parallax false scene_blend default @@ -37,9 +40,11 @@ material openmw_objects_base emissiveMapUVSet $emissiveMapUVSet detailMapUVSet $detailMapUVSet diffuseMapUVSet $diffuseMapUVSet + darkMapUVSet $darkMapUVSet emissiveMap $emissiveMap detailMap $detailMap diffuseMap $diffuseMap + darkMap $darkMap env_map $env_map env_map_color $env_map_color use_parallax $use_parallax @@ -70,12 +75,13 @@ material openmw_objects_base num_mipmaps 4 } - texture_unit emissiveMap + texture_unit darkMap { - create_in_ffp $use_emissive_map - colour_op add - direct_texture $emissiveMap - tex_coord_set $emissiveMapUVSet + create_in_ffp $use_dark_map + colour_op_ex modulate src_current src_texture + alpha_op_ex modulate src_current src_texture + direct_texture $darkMap + tex_coord_set $darkMapUVSet } texture_unit detailMap @@ -86,6 +92,14 @@ material openmw_objects_base tex_coord_set $detailMapUVSet } + texture_unit emissiveMap + { + create_in_ffp $use_emissive_map + colour_op add + direct_texture $emissiveMap + tex_coord_set $emissiveMapUVSet + } + texture_unit envMap { create_in_ffp $env_map diff --git a/files/materials/objects.shader b/files/materials/objects.shader index e50c97a6a..93368f1f6 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -18,13 +18,14 @@ #define EMISSIVE_MAP @shPropertyHasValue(emissiveMap) #define DETAIL_MAP @shPropertyHasValue(detailMap) #define DIFFUSE_MAP @shPropertyHasValue(diffuseMap) +#define DARK_MAP @shPropertyHasValue(darkMap) #define PARALLAX @shPropertyBool(use_parallax) #define PARALLAX_SCALE 0.04 #define PARALLAX_BIAS -0.02 // right now we support 2 UV sets max. implementing them is tedious, and we're probably not going to need more -#define SECOND_UV_SET (@shPropertyString(emissiveMapUVSet) || @shPropertyString(detailMapUVSet) || @shPropertyString(diffuseMapUVSet)) +#define SECOND_UV_SET (@shPropertyString(emissiveMapUVSet) || @shPropertyString(detailMapUVSet) || @shPropertyString(diffuseMapUVSet) || @shPropertyString(darkMapUVSet)) // if normal mapping is enabled, we force pixel lighting #define VERTEX_LIGHTING (!@shPropertyHasValue(normalMap)) @@ -255,14 +256,18 @@ shSampler2D(normalMap) #endif -#if EMISSIVE_MAP - shSampler2D(emissiveMap) +#if DARK_MAP + shSampler2D(darkMap) #endif #if DETAIL_MAP shSampler2D(detailMap) #endif +#if EMISSIVE_MAP + shSampler2D(emissiveMap) +#endif + #if ENV_MAP shSampler2D(envMap) shUniform(float3, env_map_color) @shUniformProperty3f(env_map_color, env_map_color) @@ -388,16 +393,24 @@ #else float4 diffuse = float4(1,1,1,1); #endif - shOutputColour(0) = diffuse; #if DETAIL_MAP #if @shPropertyString(detailMapUVSet) - shOutputColour(0) *= shSample(detailMap, newUV.zw)*2; + diffuse *= shSample(detailMap, newUV.zw)*2; #else - shOutputColour(0) *= shSample(detailMap, newUV.xy)*2; + diffuse *= shSample(detailMap, newUV.xy)*2; #endif #endif +#if DARK_MAP +#if @shPropertyString(darkMapUVSet) + diffuse *= shSample(darkMap, newUV.zw); +#else + diffuse *= shSample(darkMap, newUV.xy); +#endif +#endif + + shOutputColour(0) = diffuse; #if !VERTEX_LIGHTING float3 viewPos = shMatrixMult(worldView, float4(objSpacePositionPassthrough.xyz,1)).xyz; From aef0fd146085fd61782cbe7e89e173000c3aeb68 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 00:28:19 +0100 Subject: [PATCH 211/889] Rename some path methods --- components/files/configurationmanager.cpp | 14 +++++++------- components/files/fixedpath.hpp | 12 ++++++------ components/files/linuxpath.cpp | 4 ++-- components/files/linuxpath.hpp | 20 ++++---------------- components/files/macospath.cpp | 4 ++-- components/files/macospath.hpp | 4 ++-- components/files/windowspath.cpp | 4 ++-- components/files/windowspath.hpp | 4 ++-- 8 files changed, 27 insertions(+), 39 deletions(-) diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 75c877dc5..056adb8ce 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -26,9 +26,9 @@ ConfigurationManager::ConfigurationManager() { setupTokensMapping(); - boost::filesystem::create_directories(mFixedPath.getUserPath()); + boost::filesystem::create_directories(mFixedPath.getUserConfigPath()); - mLogPath = mFixedPath.getUserPath(); + mLogPath = mFixedPath.getUserConfigPath(); } ConfigurationManager::~ConfigurationManager() @@ -39,19 +39,19 @@ void ConfigurationManager::setupTokensMapping() { mTokensMapping.insert(std::make_pair(mwToken, &FixedPath<>::getInstallPath)); mTokensMapping.insert(std::make_pair(localToken, &FixedPath<>::getLocalPath)); - mTokensMapping.insert(std::make_pair(userToken, &FixedPath<>::getUserPath)); + mTokensMapping.insert(std::make_pair(userToken, &FixedPath<>::getUserConfigPath)); mTokensMapping.insert(std::make_pair(globalToken, &FixedPath<>::getGlobalDataPath)); } void ConfigurationManager::readConfiguration(boost::program_options::variables_map& variables, boost::program_options::options_description& description) { - loadConfig(mFixedPath.getUserPath(), variables, description); + loadConfig(mFixedPath.getUserConfigPath(), variables, description); boost::program_options::notify(variables); loadConfig(mFixedPath.getLocalPath(), variables, description); boost::program_options::notify(variables); - loadConfig(mFixedPath.getGlobalPath(), variables, description); + loadConfig(mFixedPath.getGlobalConfigPath(), variables, description); boost::program_options::notify(variables); } @@ -141,12 +141,12 @@ void ConfigurationManager::loadConfig(const boost::filesystem::path& path, const boost::filesystem::path& ConfigurationManager::getGlobalPath() const { - return mFixedPath.getGlobalPath(); + return mFixedPath.getGlobalConfigPath(); } const boost::filesystem::path& ConfigurationManager::getUserPath() const { - return mFixedPath.getUserPath(); + return mFixedPath.getUserConfigPath(); } const boost::filesystem::path& ConfigurationManager::getLocalPath() const diff --git a/components/files/fixedpath.hpp b/components/files/fixedpath.hpp index a309dc9fb..b3708a477 100644 --- a/components/files/fixedpath.hpp +++ b/components/files/fixedpath.hpp @@ -48,8 +48,8 @@ struct FixedPath */ FixedPath(const std::string& application_name) : mPath(application_name + "/") - , mUserPath(mPath.getUserPath()) - , mGlobalPath(mPath.getGlobalPath()) + , mUserPath(mPath.getUserConfigPath()) + , mGlobalConfigPath(mPath.getGlobalConfigPath()) , mLocalPath(mPath.getLocalPath()) , mGlobalDataPath(mPath.getGlobalDataPath()) , mInstallPath(mPath.getInstallPath()) @@ -62,7 +62,7 @@ struct FixedPath * * \return boost::filesystem::path */ - const boost::filesystem::path& getUserPath() const + const boost::filesystem::path& getUserConfigPath() const { return mUserPath; } @@ -72,9 +72,9 @@ struct FixedPath * * \return boost::filesystem::path */ - const boost::filesystem::path& getGlobalPath() const + const boost::filesystem::path& getGlobalConfigPath() const { - return mGlobalPath; + return mGlobalConfigPath; } /** @@ -106,7 +106,7 @@ struct FixedPath PathType mPath; boost::filesystem::path mUserPath; /**< User path */ - boost::filesystem::path mGlobalPath; /**< Global path */ + boost::filesystem::path mGlobalConfigPath; /**< Global path */ boost::filesystem::path mLocalPath; /**< It is the same directory where application was run */ boost::filesystem::path mGlobalDataPath; /**< Global application data path */ diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index c974a91d3..0dcc10595 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -19,7 +19,7 @@ LinuxPath::LinuxPath(const std::string& application_name) { } -boost::filesystem::path LinuxPath::getUserPath() const +boost::filesystem::path LinuxPath::getUserConfigPath() const { boost::filesystem::path userPath("."); @@ -63,7 +63,7 @@ boost::filesystem::path LinuxPath::getCachePath() const return userPath / ".cache" / mName; } -boost::filesystem::path LinuxPath::getGlobalPath() const +boost::filesystem::path LinuxPath::getGlobalConfigPath() const { boost::filesystem::path globalPath("/etc/"); return globalPath / mName; diff --git a/components/files/linuxpath.hpp b/components/files/linuxpath.hpp index 6acf2a2d5..191f779b6 100644 --- a/components/files/linuxpath.hpp +++ b/components/files/linuxpath.hpp @@ -20,44 +20,32 @@ struct LinuxPath /** * \brief Return path to the user directory. - * - * \return boost::filesystem::path */ - boost::filesystem::path getUserPath() const; + boost::filesystem::path getUserConfigPath() const; /** - * \brief Return path to the global (system) directory where game files could be placed. - * - * \return boost::filesystem::path + * \brief Return path to the global (system) directory where config files can be placed. */ - boost::filesystem::path getGlobalPath() const; + boost::filesystem::path getGlobalConfigPath() const; /** * \brief Return path to the runtime configuration directory which is the * place where an application was started. - * - * \return boost::filesystem::path */ boost::filesystem::path getLocalPath() const; /** - * \brief - * - * \return boost::filesystem::path + * \brief Return path to the global (system) directory where game files can be placed. */ boost::filesystem::path getGlobalDataPath() const; /** * \brief - * - * \return boost::filesystem::path */ boost::filesystem::path getCachePath() const; /** * \brief Gets the path of the installed Morrowind version if there is one. - * - * \return boost::filesystem::path */ boost::filesystem::path getInstallPath() const; diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index 9edcd6ef2..f9f988995 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -22,7 +22,7 @@ MacOsPath::MacOsPath(const std::string& application_name) { } -boost::filesystem::path MacOsPath::getUserPath() const +boost::filesystem::path MacOsPath::getUserConfigPath() const { boost::filesystem::path userPath("."); @@ -43,7 +43,7 @@ boost::filesystem::path MacOsPath::getUserPath() const return userPath / mName; } -boost::filesystem::path MacOsPath::getGlobalPath() const +boost::filesystem::path MacOsPath::getGlobalConfigPath() const { boost::filesystem::path globalPath("/Library/Preferences/"); return globalPath / mName; diff --git a/components/files/macospath.hpp b/components/files/macospath.hpp index 576ec1681..e9307c464 100644 --- a/components/files/macospath.hpp +++ b/components/files/macospath.hpp @@ -23,14 +23,14 @@ struct MacOsPath * * \return boost::filesystem::path */ - boost::filesystem::path getUserPath() const; + boost::filesystem::path getUserConfigPath() const; /** * \brief Return path to the global (system) directory. * * \return boost::filesystem::path */ - boost::filesystem::path getGlobalPath() const; + boost::filesystem::path getGlobalConfigPath() const; /** * \brief Return path to the runtime directory which is the diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index e8f1a2b08..001611f99 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -25,7 +25,7 @@ WindowsPath::WindowsPath(const std::string& application_name) { } -boost::filesystem::path WindowsPath::getUserPath() const +boost::filesystem::path WindowsPath::getUserConfigPath() const { boost::filesystem::path userPath("."); @@ -41,7 +41,7 @@ boost::filesystem::path WindowsPath::getUserPath() const return userPath / mName; } -boost::filesystem::path WindowsPath::getGlobalPath() const +boost::filesystem::path WindowsPath::getGlobalConfigPath() const { boost::filesystem::path globalPath("."); diff --git a/components/files/windowspath.hpp b/components/files/windowspath.hpp index 6044b67c2..dc3b71d91 100644 --- a/components/files/windowspath.hpp +++ b/components/files/windowspath.hpp @@ -29,14 +29,14 @@ struct WindowsPath * * \return boost::filesystem::path */ - boost::filesystem::path getUserPath() const; + boost::filesystem::path getUserConfigPath() const; /** * \brief Returns "X:\Program Files\" * * \return boost::filesystem::path */ - boost::filesystem::path getGlobalPath() const; + boost::filesystem::path getGlobalConfigPath() const; /** * \brief Return local path which is a location where From 33389b9b63aaea8b998e0e6bdd240769dd90562c Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 01:24:43 +0100 Subject: [PATCH 212/889] XDG compliant paths --- apps/launcher/maindialog.cpp | 10 +-- apps/opencs/editor.cpp | 4 - apps/opencs/model/doc/document.cpp | 4 +- apps/opencs/model/doc/documentmanager.cpp | 4 +- apps/opencs/model/settings/usersettings.cpp | 2 +- apps/openmw/engine.cpp | 6 +- components/files/configurationmanager.cpp | 8 +- components/files/configurationmanager.hpp | 2 +- components/files/fixedpath.hpp | 19 ++--- components/files/linuxpath.cpp | 86 ++++++++++----------- components/files/linuxpath.hpp | 2 + components/files/macospath.cpp | 79 +++++++++---------- components/files/macospath.hpp | 2 + components/files/windowspath.cpp | 6 ++ components/files/windowspath.hpp | 2 + 15 files changed, 119 insertions(+), 117 deletions(-) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index a6ac3d78d..9b3c4e1b0 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -219,7 +219,7 @@ bool Launcher::MainDialog::showFirstRunDialog() } // Create the file if it doesn't already exist, else the importer will fail - QString path = QString::fromStdString(mCfgMgr.getUserPath().string()) + QString("openmw.cfg"); + QString path = QString::fromStdString(mCfgMgr.getUserConfigPath().string()) + QString("openmw.cfg"); QFile file(path); if (!file.exists()) { @@ -334,7 +334,7 @@ bool Launcher::MainDialog::setupLauncherSettings() { mLauncherSettings.setMultiValueEnabled(true); - QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); + QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string()); QStringList paths; paths.append(QString("launcher.cfg")); @@ -440,7 +440,7 @@ bool Launcher::expansions(Launcher::UnshieldThread& cd) bool Launcher::MainDialog::setupGameSettings() { - QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); + QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string()); QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string()); // Load the user config file first, separately @@ -591,7 +591,7 @@ bool Launcher::MainDialog::setupGraphicsSettings() { mGraphicsSettings.setMultiValueEnabled(false); - QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); + QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string()); QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string()); QFile localDefault(QString("settings-default.cfg")); @@ -678,7 +678,7 @@ bool Launcher::MainDialog::writeSettings() mGraphicsPage->saveSettings(); mDataFilesPage->saveSettings(); - QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); + QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string()); QDir dir(userPath); if (!dir.exists()) { diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 1c1e37c2d..44926610b 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -86,10 +86,6 @@ void CS::Editor::setupDataFiles() return; } - // Set the charset for reading the esm/esp files - // QString encoding = QString::fromStdString(variables["encoding"].as()); - //mFileDialog.setEncoding(encoding); - dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); mDocumentManager.setResourceDir (variables["resources"].as()); diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 27f4f498a..3ef14ee7e 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2221,7 +2221,7 @@ void CSMDoc::Document::createBase() CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_) : mSavePath (savePath), mContentFiles (files), mTools (mData), mResDir(resDir), - mProjectPath ((configuration.getUserPath() / "projects") / + mProjectPath ((configuration.getUserDataPath() / "projects") / (savePath.filename().string() + ".project")), mSaving (*this, mProjectPath) { @@ -2254,7 +2254,7 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, co } else { - boost::filesystem::path locCustomFiltersPath (configuration.getUserPath()); + boost::filesystem::path locCustomFiltersPath (configuration.getUserDataPath()); locCustomFiltersPath /= "defaultfilters"; if (boost::filesystem::exists(locCustomFiltersPath)) diff --git a/apps/opencs/model/doc/documentmanager.cpp b/apps/opencs/model/doc/documentmanager.cpp index 024c46bea..3ff75c9c1 100644 --- a/apps/opencs/model/doc/documentmanager.cpp +++ b/apps/opencs/model/doc/documentmanager.cpp @@ -15,7 +15,7 @@ CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration) : mConfiguration (configuration) { - boost::filesystem::path projectPath = configuration.getUserPath() / "projects"; + boost::filesystem::path projectPath = configuration.getUserDataPath() / "projects"; if (!boost::filesystem::is_directory (projectPath)) boost::filesystem::create_directories (projectPath); @@ -53,4 +53,4 @@ bool CSMDoc::DocumentManager::removeDocument (Document *document) void CSMDoc::DocumentManager::setResourceDir (const boost::filesystem::path& parResDir) { mResDir = boost::filesystem::system_complete(parResDir); -} \ No newline at end of file +} diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp index 1ce28ed75..94cee8a43 100644 --- a/apps/opencs/model/settings/usersettings.cpp +++ b/apps/opencs/model/settings/usersettings.cpp @@ -251,7 +251,7 @@ void CSMSettings::UserSettings::loadSettings (const QString &fileName) bool localOk = loadFromFile(localFilePath); //user - mUserFilePath = QString::fromStdString(mCfgMgr.getUserPath().string()) + fileName; + mUserFilePath = QString::fromStdString(mCfgMgr.getUserConfigPath().string()) + fileName; loadFromFile(mUserFilePath); if (!(localOk || globalOk)) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index f2afb3ba5..01cca1b78 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -301,7 +301,7 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings) throw std::runtime_error ("No default settings file found! Make sure the file \"settings-default.cfg\" was properly installed."); // load user settings if they exist, otherwise just load the default settings as user settings - const std::string settingspath = mCfgMgr.getUserPath().string() + "/settings.cfg"; + const std::string settingspath = mCfgMgr.getUserConfigPath().string() + "/settings.cfg"; if (boost::filesystem::exists(settingspath)) settings.loadUser(settingspath); else if (boost::filesystem::exists(localdefault)) @@ -373,7 +373,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) // Create input and UI first to set up a bootstrapping environment for // showing a loading screen and keeping the window responsive while doing so - std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string(); + std::string keybinderUser = (mCfgMgr.getUserConfigPath() / "input.xml").string(); bool keybinderUserExists = boost::filesystem::exists(keybinderUser); MWInput::InputManager* input = new MWInput::InputManager (*mOgre, *this, keybinderUser, keybinderUserExists, mGrab); mEnvironment.setInputManager (input); @@ -536,7 +536,7 @@ void OMW::Engine::screenshot() // Count screenshots. int shotCount = 0; - const std::string screenshotPath = mCfgMgr.getUserPath().string(); + const std::string& screenshotPath = mCfgMgr.getUserDataPath().string(); // Find the first unused filename with a do-while std::ostringstream stream; diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 056adb8ce..761b7ca5a 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -27,6 +27,7 @@ ConfigurationManager::ConfigurationManager() setupTokensMapping(); boost::filesystem::create_directories(mFixedPath.getUserConfigPath()); + boost::filesystem::create_directories(mFixedPath.getUserDataPath()); mLogPath = mFixedPath.getUserConfigPath(); } @@ -144,11 +145,16 @@ const boost::filesystem::path& ConfigurationManager::getGlobalPath() const return mFixedPath.getGlobalConfigPath(); } -const boost::filesystem::path& ConfigurationManager::getUserPath() const +const boost::filesystem::path& ConfigurationManager::getUserConfigPath() const { return mFixedPath.getUserConfigPath(); } +const boost::filesystem::path& ConfigurationManager::getUserDataPath() const +{ + return mFixedPath.getUserDataPath(); +} + const boost::filesystem::path& ConfigurationManager::getLocalPath() const { return mFixedPath.getLocalPath(); diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index 4df871664..35144fe04 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -36,7 +36,7 @@ struct ConfigurationManager /**< Fixed paths */ const boost::filesystem::path& getGlobalPath() const; - const boost::filesystem::path& getUserPath() const; + const boost::filesystem::path& getUserConfigPath() const; const boost::filesystem::path& getLocalPath() const; const boost::filesystem::path& getGlobalDataPath() const; diff --git a/components/files/fixedpath.hpp b/components/files/fixedpath.hpp index b3708a477..cfd3458ce 100644 --- a/components/files/fixedpath.hpp +++ b/components/files/fixedpath.hpp @@ -48,7 +48,8 @@ struct FixedPath */ FixedPath(const std::string& application_name) : mPath(application_name + "/") - , mUserPath(mPath.getUserConfigPath()) + , mUserConfigPath(mPath.getUserConfigPath()) + , mUserDataPath(mPath.getUserDataPath()) , mGlobalConfigPath(mPath.getGlobalConfigPath()) , mLocalPath(mPath.getLocalPath()) , mGlobalDataPath(mPath.getGlobalDataPath()) @@ -59,18 +60,19 @@ struct FixedPath /** * \brief Return path pointing to the user local configuration directory. - * - * \return boost::filesystem::path */ const boost::filesystem::path& getUserConfigPath() const { - return mUserPath; + return mUserConfigPath; + } + + const boost::filesystem::path& getUserDataPath() const + { + return mUserDataPath; } /** * \brief Return path pointing to the global (system) configuration directory. - * - * \return boost::filesystem::path */ const boost::filesystem::path& getGlobalConfigPath() const { @@ -79,8 +81,6 @@ struct FixedPath /** * \brief Return path pointing to the directory where application was started. - * - * \return boost::filesystem::path */ const boost::filesystem::path& getLocalPath() const { @@ -105,7 +105,8 @@ struct FixedPath private: PathType mPath; - boost::filesystem::path mUserPath; /**< User path */ + boost::filesystem::path mUserConfigPath; /**< User path */ + boost::filesystem::path mUserDataPath; boost::filesystem::path mGlobalConfigPath; /**< Global path */ boost::filesystem::path mLocalPath; /**< It is the same directory where application was run */ diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index 0dcc10595..d285f4229 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -8,6 +8,39 @@ #include #include + +namespace +{ + boost::filesystem::path getUserHome() + { + const char* dir = getenv("HOME"); + if (dir == NULL) + { + struct passwd* pwd = getpwuid(getuid()); + if (pwd != NULL) + { + dir = pwd->pw_dir; + } + } + if (dir == NULL) + return boost::filesystem::path(); + else + return boost::filesystem::path(dir); + } + + boost::filesystem::path getEnv(const std::string& envVariable, const boost::filesystem::path& fallback) + { + const char* result = getenv(envVariable.c_str()); + if (!result) + return fallback; + boost::filesystem::path dir(result); + if (dir.empty()) + return fallback; + else + return dir; + } +} + /** * \namespace Files */ @@ -21,46 +54,17 @@ LinuxPath::LinuxPath(const std::string& application_name) boost::filesystem::path LinuxPath::getUserConfigPath() const { - boost::filesystem::path userPath("."); + return getEnv("XDG_CONFIG_HOME", getUserHome() / ".config") / mName; +} - const char* theDir = getenv("HOME"); - if (theDir == NULL) - { - struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) - { - theDir = pwd->pw_dir; - } - } - - if (theDir != NULL) - { - userPath = boost::filesystem::path(theDir); - } - - return userPath / ".config" / mName; +boost::filesystem::path LinuxPath::getUserDataPath() const +{ + return getEnv("XDG_DATA_HOME", getUserHome() / ".local/share") / mName; } boost::filesystem::path LinuxPath::getCachePath() const { - boost::filesystem::path userPath("."); - - const char* theDir = getenv("HOME"); - if (theDir == NULL) - { - struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) - { - theDir = pwd->pw_dir; - } - } - - if (theDir != NULL) - { - userPath = boost::filesystem::path(theDir); - } - - return userPath / ".cache" / mName; + return getEnv("XDG_CACHE_HOME", getUserHome() / ".cache") / mName; } boost::filesystem::path LinuxPath::getGlobalConfigPath() const @@ -84,17 +88,9 @@ boost::filesystem::path LinuxPath::getInstallPath() const { boost::filesystem::path installPath; - char *homePath = getenv("HOME"); - if (homePath == NULL) - { - struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) - { - homePath = pwd->pw_dir; - } - } + boost::filesystem::path homePath = getUserHome(); - if (homePath != NULL) + if (!homePath.empty()) { boost::filesystem::path wineDefaultRegistry(homePath); wineDefaultRegistry /= ".wine/system.reg"; diff --git a/components/files/linuxpath.hpp b/components/files/linuxpath.hpp index 191f779b6..b710165b4 100644 --- a/components/files/linuxpath.hpp +++ b/components/files/linuxpath.hpp @@ -23,6 +23,8 @@ struct LinuxPath */ boost::filesystem::path getUserConfigPath() const; + boost::filesystem::path getUserDataPath() const; + /** * \brief Return path to the global (system) directory where config files can be placed. */ diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index f9f988995..3e53f5306 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -11,9 +11,26 @@ * FIXME: Someone with MacOS system should check this and correct if necessary */ -/** - * \namespace Files - */ +namespace +{ + boost::filesystem::path getUserHome() + { + const char* dir = getenv("HOME"); + if (dir == NULL) + { + struct passwd* pwd = getpwuid(getuid()); + if (pwd != NULL) + { + dir = pwd->pw_dir; + } + } + if (dir == NULL) + return boost::filesystem::path(); + else + return boost::filesystem::path(dir); + } +} + namespace Files { @@ -24,21 +41,17 @@ MacOsPath::MacOsPath(const std::string& application_name) boost::filesystem::path MacOsPath::getUserConfigPath() const { - boost::filesystem::path userPath("."); + boost::filesystem::path userPath (getUserHome()); + userPath /= "Library/Preferences/"; - const char* theDir = getenv("HOME"); - if (theDir == NULL) - { - struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) - { - theDir = pwd->pw_dir; - } - } - if (theDir != NULL) - { - userPath = boost::filesystem::path(theDir) / "Library/Preferences/"; - } + return userPath / mName; +} + +boost::filesystem::path MacOsPath::getUserDataPath() const +{ + // TODO: probably wrong? + boost::filesystem::path userPath (getUserHome()); + userPath /= "Library/Preferences/"; return userPath / mName; } @@ -51,23 +64,9 @@ boost::filesystem::path MacOsPath::getGlobalConfigPath() const boost::filesystem::path MacOsPath::getCachePath() const { - boost::filesystem::path userPath("."); - - const char* theDir = getenv("HOME"); - if (theDir == NULL) - { - struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) - { - theDir = pwd->pw_dir; - } - } - if (theDir != NULL) - { - userPath = boost::filesystem::path(theDir) / "Library/Caches" / mName; - } - - return userPath; + boost::filesystem::path userPath (getUserHome()); + userPath /= "Library/Caches"; + return userPath / mName; } boost::filesystem::path MacOsPath::getLocalPath() const @@ -85,17 +84,9 @@ boost::filesystem::path MacOsPath::getInstallPath() const { boost::filesystem::path installPath; - char *homePath = getenv("HOME"); - if (homePath == NULL) - { - struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) - { - homePath = pwd->pw_dir; - } - } + boost::filesystem::path homePath = getUserHome(); - if (homePath != NULL) + if (!homePath.empty()) { boost::filesystem::path wineDefaultRegistry(homePath); wineDefaultRegistry /= ".wine/system.reg"; diff --git a/components/files/macospath.hpp b/components/files/macospath.hpp index e9307c464..7a7dc5577 100644 --- a/components/files/macospath.hpp +++ b/components/files/macospath.hpp @@ -25,6 +25,8 @@ struct MacOsPath */ boost::filesystem::path getUserConfigPath() const; + boost::filesystem::path getUserDataPath() const; + /** * \brief Return path to the global (system) directory. * diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index 001611f99..5bb8a6a02 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -41,6 +41,12 @@ boost::filesystem::path WindowsPath::getUserConfigPath() const return userPath / mName; } +boost::filesystem::path WindowsPath::getUserDataPath() const +{ + // Have some chaos, windows people! + return getUserConfigPath(); +} + boost::filesystem::path WindowsPath::getGlobalConfigPath() const { boost::filesystem::path globalPath("."); diff --git a/components/files/windowspath.hpp b/components/files/windowspath.hpp index dc3b71d91..31d0e0e7c 100644 --- a/components/files/windowspath.hpp +++ b/components/files/windowspath.hpp @@ -31,6 +31,8 @@ struct WindowsPath */ boost::filesystem::path getUserConfigPath() const; + boost::filesystem::path getUserDataPath() const; + /** * \brief Returns "X:\Program Files\" * From 85ed21dbd297498d9da29fed1be624082fce76c5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 02:09:43 +0100 Subject: [PATCH 213/889] Remove unused command line option --- apps/openmw/engine.cpp | 4 ---- apps/openmw/engine.hpp | 2 -- apps/openmw/main.cpp | 4 ---- 3 files changed, 10 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 01cca1b78..3c2423345 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -65,10 +65,6 @@ void OMW::Engine::executeLocalScripts() localScripts.setIgnore (MWWorld::Ptr()); } -void OMW::Engine::setAnimationVerbose(bool animverbose) -{ -} - bool OMW::Engine::frameStarted (const Ogre::FrameEvent& evt) { bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode(); diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index e0f8f94e6..9292e81bb 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -171,8 +171,6 @@ namespace OMW /// Font encoding void setEncoding(const ToUTF8::FromType& encoding); - void setAnimationVerbose(bool animverbose); - void setFallbackValues(std::map map); /// Enable console-only script functionality diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index e3158d268..abb3723d4 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -121,9 +121,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("content", bpo::value()->default_value(StringsVector(), "") ->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon") - ("anim-verbose", bpo::value()->implicit_value(true) - ->default_value(false), "output animation indices files") - ("nosound", bpo::value()->implicit_value(true) ->default_value(false), "disable all sounds") @@ -240,7 +237,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setSoundUsage(!variables["nosound"].as()); engine.setScriptsVerbosity(variables["script-verbose"].as()); engine.setCompileAll(variables["script-all"].as()); - engine.setAnimationVerbose(variables["anim-verbose"].as()); engine.setFallbackValues(variables["fallback"].as().mMap); engine.setScriptConsoleMode (variables["script-console"].as()); engine.setStartupScript (variables["script-run"].as()); From fb845e81a40aa9aab0facfe2b5b3c01fa4a3f039 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 02:24:12 +0100 Subject: [PATCH 214/889] Rename nosound to no-sound for consistency --- apps/mwiniimporter/importer.cpp | 2 +- apps/openmw/main.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index b8b7e4c9d..cf1589114 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -16,7 +16,7 @@ MwIniImporter::MwIniImporter() const char *map[][2] = { { "fps", "General:Show FPS" }, - { "nosound", "General:Disable Audio" }, + { "no-sound", "General:Disable Audio" }, { 0, 0 } }; const char *fallback[] = { diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index abb3723d4..2bf48c1bb 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -121,7 +121,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("content", bpo::value()->default_value(StringsVector(), "") ->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon") - ("nosound", bpo::value()->implicit_value(true) + ("no-sound", bpo::value()->implicit_value(true) ->default_value(false), "disable all sounds") ("script-verbose", bpo::value()->implicit_value(true) @@ -234,7 +234,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setNewGame(variables["new-game"].as()); // other settings - engine.setSoundUsage(!variables["nosound"].as()); + engine.setSoundUsage(!variables["no-sound"].as()); engine.setScriptsVerbosity(variables["script-verbose"].as()); engine.setCompileAll(variables["script-all"].as()); engine.setFallbackValues(variables["fallback"].as().mMap); From def93f991093d2b72c66444e81fdf9bae0361ada Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 02:12:50 +0100 Subject: [PATCH 215/889] Readme updates --- readme.txt | 81 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/readme.txt b/readme.txt index afcfadea3..5b9aafcb3 100644 --- a/readme.txt +++ b/readme.txt @@ -36,49 +36,58 @@ https://wiki.openmw.org/index.php?title=Development_Environment_Setup THE DATA PATH -The data path tells OpenMW where to find your Morrowind files. From 0.12.0 on OpenMW should be able to +The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly (installing Morrowind under WINE is considered a proper install). -If that does not work for you, please check if you have any leftover openmw.cfg files from versions earlier than 0.12.0. These can interfere with the configuration process, so try to remove then. - -If you are running OpenMW without installing it, you still need to manually adjust the data path. Create a text file named openmw.cfg in the location of the binary and enter the following line: - -data=path to your data directory - -(where you replace "path to your data directory" with the actual location of your data directory) - - COMMAND LINE OPTIONS Syntax: openmw Allowed options: - --help print help message - --version print version information and quit - --data arg (=data) set data directories (later directories have higher priority) - --data-local arg set local data directory (highest priority) - --resources arg (=resources) set resources directory - --start arg (=Beshara) set initial cell - --master arg master file(s) - --plugin arg plugin file(s) - --anim-verbose [=arg(=1)] (=0) output animation indices files - --debug [=arg(=1)] (=0) debug mode - --nosound [=arg(=1)] (=0) disable all sounds - --script-verbose [=arg(=1)] (=0) verbose script output - --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue scripts) at startup - --script-console [=arg(=1)] (=0) enable console-only script functionality - --script-run arg select a file containing a list of console commands that is executed on startup - --new-game [=arg(=1)] (=0) activate char gen/new game mechanics - --fs-strict [=arg(=1)] (=0) strict file system handling (no case folding) - --encoding arg (=win1252) Character encoding used in OpenMW game messages: - - win1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages - - win1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages - - win1252 - Western European (Latin) alphabet, used by default - - --fallback arg fallback values + --help print help message + --version print version information and quit + --data arg (=data) set data directories (later directories + have higher priority) + --data-local arg set local data directory (highest + priority) + --fallback-archive arg (=fallback-archive) + set fallback BSA archives (later + archives have higher priority) + --resources arg (=resources) set resources directory + --start arg (=Beshara) set initial cell + --content arg content file(s): esm/esp, or + omwgame/omwaddon + --anim-verbose [=arg(=1)] (=0) output animation indices files + --no-sound [=arg(=1)] (=0) disable all sounds + --script-verbose [=arg(=1)] (=0) verbose script output + --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue + scripts) at startup + --script-console [=arg(=1)] (=0) enable console-only script + functionality + --script-run arg select a file containing a list of + console commands that is executed on + startup + --new-game [=arg(=1)] (=0) activate char gen/new game mechanics + --fs-strict [=arg(=1)] (=0) strict file system handling (no case + folding) + --encoding arg (=win1252) Character encoding used in OpenMW game + messages: + + win1250 - Central and Eastern European + such as Polish, Czech, Slovak, + Hungarian, Slovene, Bosnian, Croatian, + Serbian (Latin script), Romanian and + Albanian languages + + win1251 - Cyrillic alphabet such as + Russian, Bulgarian, Serbian Cyrillic + and other languages + + win1252 - Western European (Latin) + alphabet, used by default + --fallback arg fallback values + --no-grab Don't grab mouse cursor + --activate-dist arg (=-1) activation distance override CHANGELOG From a3ff9e5be870aa8f91c5357003979b223812dd88 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 15:57:54 +0100 Subject: [PATCH 216/889] Change destruction order - fixes a shutdown crash discovered with mesa --- apps/opencs/main.cpp | 4 ++-- libs/openengine/ogre/renderer.cpp | 11 ++++++++++- libs/openengine/ogre/renderer.hpp | 9 ++++++--- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index 341bdc780..57eaf2d25 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -42,10 +42,10 @@ int main(int argc, char *argv[]) // TODO: Ogre startup shouldn't be here, but it currently has to: // SceneWidget destructor will delete the created render window, which would be called _after_ Root has shut down :( - OgreInit::OgreInit ogreInit; - ogreInit.init("./opencsOgre.log"); // TODO log path? Application mApplication (argc, argv); + OgreInit::OgreInit ogreInit; + ogreInit.init("./opencsOgre.log"); // TODO log path? #ifdef Q_OS_MAC QDir dir(QCoreApplication::applicationDirPath()); diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index a0fe6ca84..07bc8f3c9 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -11,6 +11,8 @@ #include +#include + #include #include @@ -23,6 +25,12 @@ void OgreRenderer::cleanup() delete mFader; mFader = NULL; + Ogre::Root::getSingleton().destroyRenderTarget(mWindow); + mWindow = NULL; + + delete mOgreInit; + mOgreInit = NULL; + // If we don't do this, the desktop resolution is not restored on exit SDL_SetWindowFullscreen(mSDLWindow, 0); @@ -50,7 +58,8 @@ void OgreRenderer::configure(const std::string &logPath, const std::string& rttMode ) { - mRoot = mOgreInit.init(logPath + "/ogre.log"); + mOgreInit = new OgreInit::OgreInit(); + mRoot = mOgreInit->init(logPath + "/ogre.log"); RenderSystem* rs = mRoot->getRenderSystemByName(renderSystem); if (rs == 0) diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index e4af0bf77..f45f09b01 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -9,8 +9,6 @@ #include -#include - struct SDL_Window; struct SDL_Surface; @@ -26,6 +24,11 @@ namespace Ogre class ParticleAffectorFactory; } +namespace OgreInit +{ + class OgreInit; +} + namespace OEngine { namespace Render @@ -57,7 +60,7 @@ namespace OEngine Ogre::Camera *mCamera; Ogre::Viewport *mView; - OgreInit::OgreInit mOgreInit; + OgreInit::OgreInit* mOgreInit; Fader* mFader; From 5931fdcbde09d0b736fbe7c3d139c294aab80a92 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 18:16:28 +0100 Subject: [PATCH 217/889] Implement NiBillboardNode. Flags not handled yet. --- apps/openmw/mwrender/actors.cpp | 7 +++++-- apps/openmw/mwrender/actors.hpp | 2 +- apps/openmw/mwrender/animation.cpp | 12 ++++++++++++ apps/openmw/mwrender/animation.hpp | 3 +++ apps/openmw/mwrender/npcanimation.cpp | 11 +++++++++++ apps/openmw/mwrender/npcanimation.hpp | 3 +++ apps/openmw/mwrender/objects.cpp | 7 ++++++- apps/openmw/mwrender/objects.hpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 6 +++--- components/nif/niffile.cpp | 2 +- components/nif/record.hpp | 1 + components/nifogre/ogrenifloader.cpp | 22 ++++++++++++++++++++++ components/nifogre/ogrenifloader.hpp | 6 ++++++ components/nifogre/skeleton.cpp | 1 + 14 files changed, 76 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index 1bdec6e19..639045bbe 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -150,9 +150,12 @@ void Actors::removeCell(MWWorld::Ptr::CellStore* store) } } -void Actors::update (float duration) +void Actors::update (Ogre::Camera* camera) { - // Nothing to do + for(PtrAnimationMap::iterator iter = mAllActors.begin();iter != mAllActors.end(); ++iter) + { + iter->second->preRender(camera); + } } Animation* Actors::getAnimation(const MWWorld::Ptr &ptr) diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index 61a0808f5..d91321843 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -47,7 +47,7 @@ namespace MWRender void removeCell(MWWorld::CellStore* store); - void update (float duration); + void update (Ogre::Camera* camera); /// Updates containing cell for object rendering data void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 3b04457b6..d03e91ab3 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1117,6 +1117,16 @@ void Animation::updateEffects(float duration) } } +void Animation::preRender(Ogre::Camera *camera) +{ + for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ++it) + { + NifOgre::ObjectScenePtr objects = it->mObjects; + objects->rotateBillboardNodes(camera); + } + mObjectRoot->rotateBillboardNodes(camera); +} + // TODO: Should not be here Ogre::Vector3 Animation::getEnchantmentColor(MWWorld::Ptr item) { @@ -1180,6 +1190,8 @@ bool ObjectAnimation::canBatch() const { if(!mObjectRoot->mParticles.empty() || !mObjectRoot->mLights.empty() || !mObjectRoot->mControllers.empty()) return false; + if (!mObjectRoot->mBillboardNodes.empty()) + return false; return std::find_if(mObjectRoot->mEntities.begin(), mObjectRoot->mEntities.end(), FindEntityTransparency()) == mObjectRoot->mEntities.end(); } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index aa04e39e2..c33328ab2 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -216,6 +216,9 @@ public: void removeEffect (int effectId); void getLoopingEffects (std::vector& out); + /// Prepare this animation for being rendered with \a camera (rotates billboard nodes) + virtual void preRender (Ogre::Camera* camera); + virtual void setAlpha(float alpha) {} private: void updateEffects(float duration); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index eb0c5dfbc..8d2f1c7da 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -730,6 +730,17 @@ void NpcAnimation::setAlpha(float alpha) } } +void NpcAnimation::preRender(Ogre::Camera *camera) +{ + Animation::preRender(camera); + for (int i=0; irotateBillboardNodes(camera); + } +} + void NpcAnimation::applyAlpha(float alpha, Ogre::Entity *ent, NifOgre::ObjectScenePtr scene) { ent->getSubEntity(0)->setRenderQueueGroup(alpha != 1.f || ent->getSubEntity(0)->getMaterial()->isTransparent() diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 04dde87c7..b6ed3f857 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -116,6 +116,9 @@ public: /// Make the NPC only partially visible virtual void setAlpha(float alpha); + + /// Prepare this animation for being rendered with \a camera (rotates billboard nodes) + virtual void preRender (Ogre::Camera* camera); }; } diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 827b9b52a..267320713 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -245,11 +245,16 @@ void Objects::disableLights() it->second->enableLights(false); } -void Objects::update(const float dt) +void Objects::update(float dt, Ogre::Camera* camera) { PtrAnimationMap::const_iterator it = mObjects.begin(); for(;it != mObjects.end();it++) it->second->runAnimation(dt); + + it = mObjects.begin(); + for(;it != mObjects.end();it++) + it->second->preRender(camera); + } void Objects::rebuildStaticGeometry() diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 022752fae..8a5074503 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -48,7 +48,7 @@ public: void enableLights(); void disableLights(); - void update (const float dt); + void update (float dt, Ogre::Camera* camera); ///< per-frame update Ogre::AxisAlignedBox getDimensions(MWWorld::CellStore*); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 0b10791b8..8ee292de1 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -372,9 +372,9 @@ void RenderingManager::update (float duration, bool paused) if(paused) return; - mActors->update (duration); - mObjects->update (duration); - + mActors->update (mRendering.getCamera()); + mPlayerAnimation->preRender(mRendering.getCamera()); + mObjects->update (duration, mRendering.getCamera()); mSkyManager->update(duration); diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 402eadefb..0f7e658fb 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -210,7 +210,7 @@ static const RecordFactoryEntry recordFactories [] = { { "AvoidNode", &construct , RC_AvoidNode }, { "NiBSParticleNode", &construct , RC_NiBSParticleNode }, { "NiBSAnimationNode", &construct , RC_NiBSAnimationNode }, - { "NiBillboardNode", &construct , RC_NiNode }, + { "NiBillboardNode", &construct , RC_NiBillboardNode }, { "NiTriShape", &construct , RC_NiTriShape }, { "NiRotatingParticles", &construct , RC_NiRotatingParticles }, { "NiAutoNormalParticles", &construct , RC_NiAutoNormalParticles }, diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 87e342dca..079b335f0 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -36,6 +36,7 @@ enum RecordType { RC_MISSING = 0, RC_NiNode, + RC_NiBillboardNode, RC_AvoidNode, RC_NiTriShape, RC_NiRotatingParticles, diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index acf8ac13a..ddf42393f 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -110,6 +110,17 @@ ObjectScene::~ObjectScene() mSkelBase = NULL; } +void ObjectScene::rotateBillboardNodes(Ogre::Camera *camera) +{ + for (std::vector::iterator it = mBillboardNodes.begin(); it != mBillboardNodes.end(); ++it) + { + assert(mSkelBase); + Ogre::Node* node = *it; + node->_setDerivedOrientation(mSkelBase->getParentNode()->_getDerivedOrientation().Inverse() * + camera->getRealOrientation()); + } +} + // Animates a texture class FlipController { @@ -1007,6 +1018,17 @@ class NIFObjectLoader else flags |= node->flags; + if (node->recType == Nif::RC_NiBillboardNode) + { + // TODO: figure out what the flags mean. + // NifSkope has names for them, but doesn't implement them. + // Change mBillboardNodes to map + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex); + Ogre::Bone* bone = scene->mSkelBase->getSkeleton()->getBone(trgtid); + bone->setManuallyControlled(true); + scene->mBillboardNodes.push_back(bone); + } + Nif::ExtraPtr e = node->extra; while(!e.empty()) { diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index 5858aef39..badb6ccd3 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -61,6 +61,9 @@ struct ObjectScene { std::vector mParticles; std::vector mLights; + // Nodes that should always face the camera when rendering + std::vector mBillboardNodes; + Ogre::SceneManager* mSceneMgr; // The maximum length on any of the controllers. For animations with controllers, but no text keys, consider this the animation length. @@ -76,6 +79,9 @@ struct ObjectScene { { } ~ObjectScene(); + + // Rotate nodes in mBillboardNodes so they face the given camera + void rotateBillboardNodes(Ogre::Camera* camera); }; typedef Ogre::SharedPtr ObjectScenePtr; diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp index 04bffdeab..9ec6f15b0 100644 --- a/components/nifogre/skeleton.cpp +++ b/components/nifogre/skeleton.cpp @@ -30,6 +30,7 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */ node->recType == Nif::RC_NiTriShape || /* Handled in the mesh loader */ node->recType == Nif::RC_NiBSAnimationNode || /* Handled in the object loader */ + node->recType == Nif::RC_NiBillboardNode || /* Handled in the object loader */ node->recType == Nif::RC_NiBSParticleNode || node->recType == Nif::RC_NiCamera || node->recType == Nif::RC_NiAutoNormalParticles || From eab2c89346ef2acc9b8ed78c3288e1fa2b94d834 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 18:46:39 +0100 Subject: [PATCH 218/889] Issue #983: Fix controllers to affect objects attached to the base node --- components/nifogre/ogrenifloader.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index ddf42393f..78f173bba 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -178,6 +178,7 @@ public: if ((texture->getName() == "diffuseMap" && mTexSlot == Nif::NiTexturingProperty::BaseTexture) || (texture->getName() == "normalMap" && mTexSlot == Nif::NiTexturingProperty::BumpTexture) || (texture->getName() == "detailMap" && mTexSlot == Nif::NiTexturingProperty::DetailTexture) + || (texture->getName() == "darkMap" && mTexSlot == Nif::NiTexturingProperty::DarkTexture) || (texture->getName() == "emissiveMap" && mTexSlot == Nif::NiTexturingProperty::GlowTexture)) texture->setTextureName(mTextures[curTexture]); } @@ -306,9 +307,6 @@ public: return mData.back().isSet; } - // FIXME: We are not getting all objects here. Skinned meshes get - // attached to the object's root node, and won't be connected via a - // TagPoint. static void setVisible(Ogre::Node *node, int vis) { Ogre::Node::ChildNodeIterator iter = node->getChildIterator(); @@ -317,6 +315,12 @@ public: node = iter.getNext(); setVisible(node, vis); + // Skinned meshes and particle systems are attached to the scene node, not the bone. + // We use the Node's user data to connect it with the mesh / particle system. + Ogre::Any customData = node->getUserObjectBindings().getUserAny(); + if (!customData.isEmpty()) + Ogre::any_cast(customData)->setVisible(vis); + Ogre::TagPoint *tag = dynamic_cast(node); if(tag != NULL) { @@ -659,6 +663,7 @@ class NIFObjectLoader { int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex); Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); + trgtbone->getUserObjectBindings().setUserAny(Ogre::Any(static_cast(entity))); scene->mSkelBase->attachObjectToBone(trgtbone->getName(), entity); } } @@ -892,6 +897,7 @@ class NIFObjectLoader int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex); Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); createParticleEmitterAffectors(partsys, partctrl, trgtbone, scene->mSkelBase->getName()); + trgtbone->getUserObjectBindings().setUserAny(Ogre::Any(static_cast(partsys))); } Ogre::ControllerValueRealPtr srcval((partflags&Nif::NiNode::ParticleFlag_AutoPlay) ? From 9877db413ccfaf7d79e8741c3dfe035ae5e7f7c1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 19:49:11 +0100 Subject: [PATCH 219/889] Connect particle systems to the particle node, not the emitter node --- components/nifogre/ogrenifloader.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 78f173bba..7ac101d2b 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -885,6 +885,10 @@ class NIFObjectLoader sceneNode->attachObject(partsys); + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partnode->recIndex); + Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); + trgtbone->getUserObjectBindings().setUserAny(Ogre::Any(static_cast(partsys))); + Nif::ControllerPtr ctrl = partnode->controller; while(!ctrl.empty()) { @@ -897,7 +901,6 @@ class NIFObjectLoader int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex); Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); createParticleEmitterAffectors(partsys, partctrl, trgtbone, scene->mSkelBase->getName()); - trgtbone->getUserObjectBindings().setUserAny(Ogre::Any(static_cast(partsys))); } Ogre::ControllerValueRealPtr srcval((partflags&Nif::NiNode::ParticleFlag_AutoPlay) ? From 71d9755ef167a25ea3ce8098325b44e0811b6bf8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 21:26:59 +0100 Subject: [PATCH 220/889] Bug #991: Don't autoequip items with harmful permanent enchantments --- apps/openmw/mwworld/inventorystore.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 57e35adce..9fd329b2c 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -180,6 +180,29 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) std::pair, bool> itemsSlots = MWWorld::Class::get (*iter).getEquipmentSlots (*iter); + // Skip items that have *only* harmful permanent effects + if (!test.getClass().getEnchantment(test).empty()) + { + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Enchantment* enchantment = store.get().find(test.getClass().getEnchantment(test)); + bool harmfulEffect = false; + bool usefulEffect = false; + if (enchantment->mData.mType == ESM::Enchantment::ConstantEffect) + { + for (std::vector::const_iterator it = enchantment->mEffects.mList.begin(); + it != enchantment->mEffects.mList.end(); ++it) + { + const ESM::MagicEffect* effect = store.get().find(it->mEffectID); + if (effect->mData.mFlags & ESM::MagicEffect::Harmful) + harmfulEffect = true; + else + usefulEffect = true; + } + } + if (harmfulEffect && !usefulEffect) + continue; + } + for (std::vector::const_iterator iter2 (itemsSlots.first.begin()); iter2!=itemsSlots.first.end(); ++iter2) { From 5054d8e6c163135062d49eebcd080bc0dcf5f178 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 22:06:13 +0100 Subject: [PATCH 221/889] Bug #1055: Check power use and mana before starting cast animation --- apps/openmw/mwbase/world.hpp | 7 ++++ apps/openmw/mwmechanics/character.cpp | 6 ++- apps/openmw/mwmechanics/spellcasting.cpp | 31 ++------------- apps/openmw/mwworld/worldimp.cpp | 49 ++++++++++++++++++++++-- apps/openmw/mwworld/worldimp.hpp | 11 ++++++ 5 files changed, 71 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 961d3d958..336ec89dc 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -414,6 +414,13 @@ namespace MWBase virtual bool toggleGodMode() = 0; + /** + * @brief startSpellCast attempt to start casting a spell. Might fail immediately if conditions are not met. + * @param actor + * @return true if the spell can be casted (i.e. the animation should start) + */ + virtual bool startSpellCast (const MWWorld::Ptr& actor) = 0; + virtual void castSpell (const MWWorld::Ptr& actor) = 0; virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects, diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index da3ed2523..89afdbe47 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -505,10 +505,14 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun mAttackType.clear(); if(mWeaponType == WeapType_Spell) { + // Unset casting flag, otherwise pressing the mouse button down would + // continue casting every frame if there is no animation + mPtr.getClass().getCreatureStats(mPtr).setAttackingOrSpell(false); + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const std::string spellid = stats.getSpells().getSelectedSpell(); - if(!spellid.empty()) + if(!spellid.empty() && MWBase::Environment::get().getWorld()->startSpellCast(mPtr)) { static const std::string schools[] = { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 74816d12e..025aa6b3a 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -300,10 +300,11 @@ namespace MWMechanics if (item.getCellRef().mEnchantmentCharge == -1) item.getCellRef().mEnchantmentCharge = enchantment->mData.mCharge; - if (mCaster.getRefData().getHandle() == "player" && item.getCellRef().mEnchantmentCharge < castCost) + if (item.getCellRef().mEnchantmentCharge < castCost) { // TODO: Should there be a sound here? - MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}"); + if (mCaster.getRefData().getHandle() == "player") + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}"); return false; } @@ -370,33 +371,7 @@ namespace MWMechanics fatigue.setCurrent(std::max(0.f, fatigue.getCurrent() - fatigueLoss)); stats.setFatigue(fatigue); - // Check mana bool fail = false; - DynamicStat magicka = stats.getMagicka(); - if (magicka.getCurrent() < spell->mData.mCost) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientSP}"); - fail = true; - } - - // Reduce mana - if (!fail) - { - magicka.setCurrent(magicka.getCurrent() - spell->mData.mCost); - stats.setMagicka(magicka); - } - - // If this is a power, check if it was already used in last 24h - if (!fail && spell->mData.mType & ESM::Spell::ST_Power) - { - if (stats.canUsePower(spell->mId)) - stats.usePower(spell->mId); - else - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sPowerAlreadyUsed}"); - fail = true; - } - } // Check success int successChance = getSpellSuccessChance(spell, mCaster); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 448211bc2..021b6184f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2049,15 +2049,56 @@ namespace MWWorld } } + bool World::startSpellCast(const Ptr &actor) + { + MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); + + std::string message; + bool fail = false; + bool isPlayer = (actor == getPlayer().getPlayer()); + + std::string selectedSpell = stats.getSpells().getSelectedSpell(); + + if (!selectedSpell.empty()) + { + const ESM::Spell* spell = getStore().get().search(selectedSpell); + + // Check mana + MWMechanics::DynamicStat magicka = stats.getMagicka(); + if (magicka.getCurrent() < spell->mData.mCost) + { + message = "#{sMagicInsufficientSP}"; + fail = true; + } + + // If this is a power, check if it was already used in the last 24h + if (!fail && spell->mData.mType & ESM::Spell::ST_Power) + { + if (stats.canUsePower(spell->mId)) + stats.usePower(spell->mId); + else + { + message = "#{sPowerAlreadyUsed}"; + fail = true; + } + } + + // Reduce mana + magicka.setCurrent(magicka.getCurrent() - spell->mData.mCost); + stats.setMagicka(magicka); + } + + if (isPlayer && fail) + MWBase::Environment::get().getWindowManager()->messageBox(message); + + return !fail; + } + void World::castSpell(const Ptr &actor) { MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); InventoryStore& inv = actor.getClass().getInventoryStore(actor); - // Unset casting flag, otherwise pressing the mouse button down would continue casting every frame if using an enchantment - // (which casts instantly without an animation) - stats.setAttackingOrSpell(false); - MWWorld::Ptr target = getFacedObject(); std::string selectedSpell = stats.getSpells().getSelectedSpell(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 5a51cb773..998d39317 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -499,6 +499,17 @@ namespace MWWorld virtual bool toggleGodMode(); + /** + * @brief startSpellCast attempt to start casting a spell. Might fail immediately if conditions are not met. + * @param actor + * @return true if the spell can be casted (i.e. the animation should start) + */ + virtual bool startSpellCast (const MWWorld::Ptr& actor); + + /** + * @brief Cast the actual spell, should be called mid-animation + * @param actor + */ virtual void castSpell (const MWWorld::Ptr& actor); virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects, From a3017e16d46d650cc01154b5f513f68de9430661 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 22:32:39 +0100 Subject: [PATCH 222/889] Don't allow changing the spell that is being cast mid-animation --- apps/openmw/mwbase/windowmanager.hpp | 1 + apps/openmw/mwgui/quickkeysmenu.cpp | 2 - apps/openmw/mwgui/spellwindow.cpp | 75 +++----------------------- apps/openmw/mwgui/windowmanagerimp.cpp | 3 ++ apps/openmw/mwgui/windowmanagerimp.hpp | 3 ++ apps/openmw/mwmechanics/character.cpp | 9 +++- 6 files changed, 23 insertions(+), 70 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index c47ad066b..1300efc75 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -204,6 +204,7 @@ namespace MWBase virtual void activateQuickKey (int index) = 0; + virtual std::string getSelectedSpell() = 0; virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0; virtual void setSelectedEnchantItem(const MWWorld::Ptr& item) = 0; virtual void setSelectedWeapon(const MWWorld::Ptr& item) = 0; diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 395676649..deabb299e 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -275,7 +275,6 @@ namespace MWGui if (type == Type_Magic) { std::string spellId = button->getChildAt(0)->getUserString("Spell"); - spells.setSelectedSpell(spellId); store.setSelectedEnchantItem(store.end()); MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); } @@ -342,7 +341,6 @@ namespace MWGui } store.setSelectedEnchantItem(it); - spells.setSelectedSpell(""); MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item); } } diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 42a0b9865..e2817c38b 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -86,61 +86,8 @@ namespace MWGui MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); - // the following code switches between selected enchanted item and selected spell (only one of these - // can be active at a time) - std::string selectedSpell = spells.getSelectedSpell(); - MWWorld::Ptr selectedItem; - if (store.getSelectedEnchantItem() != store.end()) - { - selectedSpell = ""; - selectedItem = *store.getSelectedEnchantItem(); - - bool allowSelectedItem = true; - - // make sure that the item is still in the player inventory, otherwise it can't be selected - bool found = false; - for (MWWorld::ContainerStoreIterator it(store.begin()); it != store.end(); ++it) - { - if (*it == selectedItem) - found = true; - } - if (!found) - allowSelectedItem = false; - - // if the selected item can be equipped, make sure that it actually is equipped - std::pair, bool> slots_; - slots_ = MWWorld::Class::get(selectedItem).getEquipmentSlots(selectedItem); - if (!slots_.first.empty()) - { - bool equipped = false; - for (int i=0; i < MWWorld::InventoryStore::Slots; ++i) - { - if (store.getSlot(i) != store.end() && *store.getSlot(i) == selectedItem) - { - equipped = true; - break; - } - } - - if (!equipped) - allowSelectedItem = false; - } - - if (!allowSelectedItem) - { - store.setSelectedEnchantItem(store.end()); - spells.setSelectedSpell(""); - MWBase::Environment::get().getWindowManager()->unsetSelectedSpell(); - selectedItem = MWWorld::Ptr(); - } - } - - - for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it) - { spellList.push_back (it->first); - } const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); @@ -210,7 +157,7 @@ namespace MWGui t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel); t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onSpellSelected); - if (*it == selectedSpell) + if (*it == MWBase::Environment::get().getWindowManager()->getSelectedSpell()) t->setStateSelected(true); mHeight += spellHeight; @@ -229,7 +176,7 @@ namespace MWGui t->setUserString("Spell", *it); t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel); t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onSpellSelected); - t->setStateSelected(*it == selectedSpell); + t->setStateSelected(*it == MWBase::Environment::get().getWindowManager()->getSelectedSpell()); // cost / success chance MyGUI::Button* costChance = mSpellView->createWidget("SpellText", @@ -239,7 +186,7 @@ namespace MWGui costChance->setCaption(cost + "/" + chance); costChance->setTextAlign(MyGUI::Align::Right); costChance->setNeedMouseFocus(false); - costChance->setStateSelected(*it == selectedSpell); + costChance->setStateSelected(*it == MWBase::Environment::get().getWindowManager()->getSelectedSpell()); mHeight += spellHeight; @@ -276,7 +223,9 @@ namespace MWGui t->setUserString("Equipped", equipped ? "true" : "false"); t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onEnchantedItemSelected); t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel); - t->setStateSelected(item == selectedItem); + if (store.getSelectedEnchantItem() != store.end()) + t->setStateSelected(item == *store.getSelectedEnchantItem()); + // cost / charge MyGUI::Button* costCharge = mSpellView->createWidget(equipped ? "SpellText" : "SpellTextUnequipped", @@ -302,7 +251,8 @@ namespace MWGui costCharge->setCaption(cost + "/" + charge); costCharge->setTextAlign(MyGUI::Align::Right); costCharge->setNeedMouseFocus(false); - costCharge->setStateSelected(item == selectedItem); + if (store.getSelectedEnchantItem() != store.end()) + costCharge->setStateSelected(item == *store.getSelectedEnchantItem()); mHeight += spellHeight; } @@ -349,9 +299,7 @@ namespace MWGui void SpellWindow::onEnchantedItemSelected(MyGUI::Widget* _sender) { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); - MWMechanics::Spells& spells = stats.getSpells(); MWWorld::Ptr item = *_sender->getUserData(); // retrieve ContainerStoreIterator to the item @@ -379,7 +327,6 @@ namespace MWGui } store.setSelectedEnchantItem(it); - spells.setSelectedSpell(""); MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item); updateSpells(); @@ -389,9 +336,7 @@ namespace MWGui { std::string spellId = _sender->getUserString("Spell"); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); - MWMechanics::Spells& spells = stats.getSpells(); if (MyGUI::InputManager::getInstance().isShiftPressed()) { @@ -419,7 +364,6 @@ namespace MWGui } else { - spells.setSelectedSpell(spellId); store.setSelectedEnchantItem(store.end()); MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); } @@ -450,10 +394,7 @@ namespace MWGui MWMechanics::Spells& spells = stats.getSpells(); if (spells.getSelectedSpell() == mSpellToDelete) - { - spells.setSelectedSpell(""); MWBase::Environment::get().getWindowManager()->unsetSelectedSpell(); - } spells.remove(mSpellToDelete); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index afa020082..8818721f4 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1010,6 +1010,7 @@ namespace MWGui void WindowManager::setSelectedSpell(const std::string& spellId, int successChancePercent) { + mSelectedSpell = spellId; mHud->setSelectedSpell(spellId, successChancePercent); const ESM::Spell* spell = @@ -1020,6 +1021,7 @@ namespace MWGui void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item) { + mSelectedSpell = ""; const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get() .find(MWWorld::Class::get(item).getEnchantment(item)); @@ -1039,6 +1041,7 @@ namespace MWGui void WindowManager::unsetSelectedSpell() { + mSelectedSpell = ""; mHud->unsetSelectedSpell(); mSpellWindow->setTitle("#{sNone}"); } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 743160aa8..b332ffc36 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -200,6 +200,7 @@ namespace MWGui virtual void activateQuickKey (int index); + virtual std::string getSelectedSpell() { return mSelectedSpell; } virtual void setSelectedSpell(const std::string& spellId, int successChancePercent); virtual void setSelectedEnchantItem(const MWWorld::Ptr& item); virtual void setSelectedWeapon(const MWWorld::Ptr& item); @@ -288,6 +289,8 @@ namespace MWGui void trackWindow(OEngine::GUI::Layout* layout, const std::string& name); void onWindowChangeCoord(MyGUI::Window* _sender); + std::string mSelectedSpell; + OEngine::GUI::MyGUIManager *mGuiManager; OEngine::Render::OgreRenderer *mRendering; HUD *mHud; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 89afdbe47..3038faf31 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -511,7 +511,14 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - const std::string spellid = stats.getSpells().getSelectedSpell(); + // For the player, set the spell we want to cast + // This has to be done at the start of the casting animation, + // *not* when selecting a spell in the GUI (otherwise you could change the spell mid-animation) + if (mPtr.getRefData().getHandle() == "player") + stats.getSpells().setSelectedSpell(MWBase::Environment::get().getWindowManager()->getSelectedSpell()); + + std::string spellid = stats.getSpells().getSelectedSpell(); + if(!spellid.empty() && MWBase::Environment::get().getWorld()->startSpellCast(mPtr)) { static const std::string schools[] = { From d09a86e20861a7a675ed521573360c25685ee021 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 00:36:06 +0100 Subject: [PATCH 223/889] Issue #1018: Don't allow view mode switching while performing an action --- apps/openmw/mwinput/inputmanagerimp.cpp | 7 ++--- apps/openmw/mwrender/animation.cpp | 10 ++++++ apps/openmw/mwrender/animation.hpp | 2 ++ apps/openmw/mwrender/camera.cpp | 42 +++++++++++++++++++++++-- apps/openmw/mwrender/camera.hpp | 4 +++ 5 files changed, 59 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index ab2569635..4713d92e1 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -378,10 +378,9 @@ namespace MWInput MWBase::Environment::get().getWorld()->togglePreviewMode(true); } } else { - if (mPreviewPOVDelay > 0.5) { - //disable preview mode - MWBase::Environment::get().getWorld()->togglePreviewMode(false); - } else if (mPreviewPOVDelay > 0.f) { + //disable preview mode + MWBase::Environment::get().getWorld()->togglePreviewMode(false); + if (mPreviewPOVDelay > 0.f && mPreviewPOVDelay <= 0.5) { MWBase::Environment::get().getWorld()->togglePOV(); } mPreviewPOVDelay = 0.f; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index d03e91ab3..d425efdbc 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -996,6 +996,16 @@ void Animation::detachObjectFromBone(Ogre::MovableObject *obj) mSkelBase->detachObjectFromBone(obj); } +bool Animation::isPlaying(Group group) const +{ + for (AnimStateMap::const_iterator stateiter = mStates.begin(); stateiter != mStates.end(); ++stateiter) + { + if(stateiter->second.mGroups == group) + return true; + } + return false; +} + void Animation::addEffect(const std::string &model, int effectId, bool loop, const std::string &bonename, std::string texture) { // Early out if we already have this effect diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index c33328ab2..67d8baa3f 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -258,6 +258,8 @@ public: /** Returns true if the named animation group is playing. */ bool isPlaying(const std::string &groupname) const; + bool isPlaying(Group group) const; + /** Gets info about the given animation group. * \param groupname Animation group to check. * \param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc. diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 9af3987a8..8f54be3f8 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -29,7 +29,9 @@ namespace MWRender mNearest(30.f), mFurthest(800.f), mIsNearest(false), - mIsFurthest(false) + mIsFurthest(false), + mVanityToggleQueued(false), + mViewModeToggleQueued(false) { mVanity.enabled = false; mVanity.allowed = true; @@ -103,6 +105,23 @@ namespace MWRender void Camera::update(float duration, bool paused) { + if (!mAnimation->isPlaying(MWRender::Animation::Group_UpperBody)) + { + // Now process the view changes we queued earlier + if (mVanityToggleQueued) + { + toggleVanityMode(!mVanity.enabled); + mVanityToggleQueued = false; + } + if (mViewModeToggleQueued) + { + + togglePreviewMode(false); + toggleViewMode(); + mViewModeToggleQueued = false; + } + } + updateListener(); if (paused) return; @@ -121,6 +140,14 @@ namespace MWRender void Camera::toggleViewMode() { + // Changing the view will stop all playing animations, so if we are playing + // anything important, queue the view change for later + if (mAnimation->isPlaying(MWRender::Animation::Group_UpperBody)) + { + mViewModeToggleQueued = true; + return; + } + mFirstPersonView = !mFirstPersonView; processViewChange(); @@ -140,6 +167,14 @@ namespace MWRender bool Camera::toggleVanityMode(bool enable) { + // Changing the view will stop all playing animations, so if we are playing + // anything important, queue the view change for later + if (mAnimation->isPlaying(MWRender::Animation::Group_UpperBody)) + { + mVanityToggleQueued = true; + return false; + } + if(!mVanity.allowed && enable) return false; @@ -168,6 +203,9 @@ namespace MWRender void Camera::togglePreviewMode(bool enable) { + if (mAnimation->isPlaying(MWRender::Animation::Group_UpperBody)) + return; + if(mPreviewMode == enable) return; @@ -184,7 +222,6 @@ namespace MWRender } mCamera->setPosition(0.f, 0.f, offset); - rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); } void Camera::setSneakOffset() @@ -319,6 +356,7 @@ namespace MWRender mAnimation->setViewMode(NpcAnimation::VM_Normal); mCameraNode->attachObject(mCamera); } + rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); } void Camera::getPosition(Ogre::Vector3 &focal, Ogre::Vector3 &camera) diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index baf2f3685..87e486629 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -47,6 +47,9 @@ namespace MWRender bool mDistanceAdjusted; + bool mVanityToggleQueued; + bool mViewModeToggleQueued; + /// Updates sound manager listener data void updateListener(); @@ -77,6 +80,7 @@ namespace MWRender bool toggleVanityMode(bool enable); void allowVanityMode(bool allow); + /// @note this may be ignored if an important animation is currently playing void togglePreviewMode(bool enable); /// \brief Lowers the camera for sneak. From 5a287a7e010ff3834c97614bdd7b73ca9af1420c Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 00:41:19 +0100 Subject: [PATCH 224/889] Remove no longer accurate flagAsModified calls. Container items are now modified via ContainerStore, not RefData. --- apps/openmw/mwworld/ptr.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/apps/openmw/mwworld/ptr.cpp b/apps/openmw/mwworld/ptr.cpp index 127ab1364..384bd71b1 100644 --- a/apps/openmw/mwworld/ptr.cpp +++ b/apps/openmw/mwworld/ptr.cpp @@ -25,9 +25,6 @@ ESM::CellRef& MWWorld::Ptr::getCellRef() const { assert(mRef); - if (mContainerStore) - mContainerStore->flagAsModified(); - return mRef->mRef; } @@ -35,9 +32,6 @@ MWWorld::RefData& MWWorld::Ptr::getRefData() const { assert(mRef); - if (mContainerStore) - mContainerStore->flagAsModified(); - return mRef->mData; } From 30b1da996b7f5e58f5d5ad78836d31497781899e Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 00:51:29 +0100 Subject: [PATCH 225/889] Issue #1029 - Quick keys menu: Select compatible replacement when tool used up --- apps/openmw/mwgui/quickkeysmenu.cpp | 51 ++++++++++++++++------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index deabb299e..b8f52cd1f 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -269,8 +269,35 @@ namespace MWGui MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); - MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); - MWMechanics::Spells& spells = stats.getSpells(); + + if (type == Type_Item || type == Type_MagicItem) + { + MWWorld::Ptr item = *button->getChildAt (0)->getUserData(); + // make sure the item is available + if (item.getRefData ().getCount() < 1) + { + // Try searching for a compatible replacement + std::string id = item.getCellRef().mRefID; + + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, id)) + { + item = *it; + button->getChildAt(0)->setUserData(item); + break; + } + } + + if (item.getRefData().getCount() < 1) + { + // No replacement was found + MWBase::Environment::get().getWindowManager ()->messageBox ( + "#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item)); + return; + } + } + } if (type == Type_Magic) { @@ -282,15 +309,6 @@ namespace MWGui { MWWorld::Ptr item = *button->getChildAt (0)->getUserData(); - // make sure the item is available - if (item.getRefData ().getCount() < 1) - { - // TODO: Try to find a replacement with the same ID? - MWBase::Environment::get().getWindowManager ()->messageBox ( - "#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item)); - return; - } - boost::shared_ptr action = MWWorld::Class::get(item).use(item); action->execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); @@ -309,14 +327,6 @@ namespace MWGui { MWWorld::Ptr item = *button->getChildAt (0)->getUserData(); - // make sure the item is available - if (item.getRefData ().getCount() == 0) - { - MWBase::Environment::get().getWindowManager ()->messageBox ( - "#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item)); - return; - } - // retrieve ContainerStoreIterator to the item MWWorld::ContainerStoreIterator it = store.begin(); for (; it != store.end(); ++it) @@ -335,9 +345,6 @@ namespace MWGui MWWorld::ActionEquip action(item); action.execute (MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()); - - // since we changed equipping status, update the inventory window - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); } store.setSelectedEnchantItem(it); From 608bd0f525e68f4a89c80b51062bb131673b6d0b Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 01:34:56 +0100 Subject: [PATCH 226/889] Don't copy the base node pointer when adding a world object to a container. Fixes bug #1028 --- apps/openmw/mwworld/containerstore.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 3797e6922..2984bb319 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -130,6 +130,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr { MWWorld::ContainerStoreIterator it = addImp(itemPtr); MWWorld::Ptr item = *it; + item.getRefData().setBaseNode(NULL); std::string script = MWWorld::Class::get(item).getScript(item); if(script != "") From 561c6611565f7b41e6a3a2d34d4ec52963e63459 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 01:57:08 +0100 Subject: [PATCH 227/889] Reset starting angle / position when adding world item to a container --- apps/openmw/mwworld/containerstore.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 2984bb319..686e790a3 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -130,7 +130,15 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr { MWWorld::ContainerStoreIterator it = addImp(itemPtr); MWWorld::Ptr item = *it; + + // we may have copied an item from the world, so reset a few things first item.getRefData().setBaseNode(NULL); + item.getCellRef().mPos.rot[0] = 0; + item.getCellRef().mPos.rot[1] = 0; + item.getCellRef().mPos.rot[2] = 0; + item.getCellRef().mPos.pos[0] = 0; + item.getCellRef().mPos.pos[1] = 0; + item.getCellRef().mPos.pos[2] = 0; std::string script = MWWorld::Class::get(item).getScript(item); if(script != "") From 1c60a781a5e06561a83634b7efaf3dbca723c9f7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 14:42:22 +0100 Subject: [PATCH 228/889] Add header to CMakeLists --- apps/openmw/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 5a062575c..e87da97c3 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -20,7 +20,7 @@ add_openmw_dir (mwrender renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation actors objects renderinginterface localmap occlusionquery water shadows characterpreview externalrendering globalmap videoplayer ripplesimulation refraction - terrainstorage + terrainstorage renderconst ) add_openmw_dir (mwinput From d262d9e6b0fb761751b689923deda9d8746568f9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 14:51:59 +0100 Subject: [PATCH 229/889] Bug #1054: Set render queue group for effects --- apps/openmw/mwrender/animation.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index d425efdbc..8b636f946 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1027,6 +1027,10 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con params.mObjects = NifOgre::Loader::createObjects(mInsert, model); else params.mObjects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); + + setRenderProperties(params.mObjects, RV_Misc, + RQG_Main, RQG_Alpha, 0.f, false, NULL); + params.mLoop = loop; params.mEffectId = effectId; params.mBoneName = bonename; From 6400f23ab0205096144dd86c4af379be5d162b72 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 14:54:36 +0100 Subject: [PATCH 230/889] Use the material controller manager for effects with overridden texture --- apps/openmw/mwrender/animation.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 8b636f946..de86bcfa7 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1046,17 +1046,12 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con for(size_t i = 0;i < params.mObjects->mParticles.size(); ++i) { Ogre::ParticleSystem* partSys = params.mObjects->mParticles[i]; - sh::Factory::getInstance()._ensureMaterial(partSys->getMaterialName(), "Default"); - Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().getByName(partSys->getMaterialName()); - static int count = 0; - Ogre::String materialName = "openmw/" + Ogre::StringConverter::toString(count++); - // TODO: destroy when effect is removed - Ogre::MaterialPtr newMat = mat->clone(materialName); - partSys->setMaterialName(materialName); - for (int t=0; tgetNumTechniques(); ++t) + Ogre::MaterialPtr mat = params.mObjects->mMaterialControllerMgr.getWritableMaterial(partSys); + + for (int t=0; tgetNumTechniques(); ++t) { - Ogre::Technique* tech = newMat->getTechnique(t); + Ogre::Technique* tech = mat->getTechnique(t); for (int p=0; pgetNumPasses(); ++p) { Ogre::Pass* pass = tech->getPass(p); From a9526622b18df4e24d0f7a1ccfadd50ebf954803 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 17:45:04 +0100 Subject: [PATCH 231/889] Particle improvements: particle systems now move with the particle bone, not the scene node. This difference is not noticable if the particle bone is static, but it makes the code *much* nicer and mirrors more closely what NifSkope does. --- components/nifogre/ogrenifloader.cpp | 20 +--- components/nifogre/particles.cpp | 132 +++++---------------------- 2 files changed, 25 insertions(+), 127 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 7ac101d2b..9fa913686 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -789,8 +789,6 @@ class NIFObjectLoader emitter->setParameter("vertical_angle", Ogre::StringConverter::toString(Ogre::Radian(partctrl->verticalAngle).valueDegrees())); emitter->setParameter("horizontal_direction", Ogre::StringConverter::toString(Ogre::Radian(partctrl->horizontalDir).valueDegrees())); emitter->setParameter("horizontal_angle", Ogre::StringConverter::toString(Ogre::Radian(partctrl->horizontalAngle).valueDegrees())); - emitter->setParameter("skelbase", skelBaseName); - emitter->setParameter("bone", bone->getName()); Nif::ExtraPtr e = partctrl->extra; while(!e.empty()) @@ -883,11 +881,9 @@ class NIFObjectLoader partsys->setParticleQuota(particledata->numParticles); partsys->setKeepParticlesInLocalSpace(partflags & (Nif::NiNode::ParticleFlag_LocalSpace)); - sceneNode->attachObject(partsys); - int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partnode->recIndex); Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); - trgtbone->getUserObjectBindings().setUserAny(Ogre::Any(static_cast(partsys))); + scene->mSkelBase->attachObjectToBone(trgtbone->getName(), partsys); Nif::ControllerPtr ctrl = partnode->controller; while(!ctrl.empty()) @@ -900,6 +896,9 @@ class NIFObjectLoader { int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex); Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); + // Set the emitter bone as user data on the particle system + // so the emitters/affectors can access it easily. + partsys->getUserObjectBindings().setUserAny(Ogre::Any(trgtbone)); createParticleEmitterAffectors(partsys, partctrl, trgtbone, scene->mSkelBase->getName()); } @@ -1274,17 +1273,6 @@ ObjectScenePtr Loader::createObjects(Ogre::Entity *parent, const std::string &bo } } - for(size_t i = 0;i < scene->mParticles.size();i++) - { - Ogre::ParticleSystem *partsys = scene->mParticles[i]; - if(partsys->isAttached()) - partsys->detachFromParent(); - - Ogre::TagPoint *tag = scene->mSkelBase->attachObjectToBone( - scene->mSkelBase->getSkeleton()->getRootBone()->getName(), partsys); - tag->setScale(scale); - } - return scene; } diff --git a/components/nifogre/particles.cpp b/components/nifogre/particles.cpp index d306f4944..a1433a669 100644 --- a/components/nifogre/particles.cpp +++ b/components/nifogre/particles.cpp @@ -16,46 +16,11 @@ class NifEmitter : public Ogre::ParticleEmitter { public: - std::string mSkelBaseName; - Ogre::Bone* mBone; + Ogre::Bone* mEmitterBone; + Ogre::Bone* mParticleBone; Ogre::ParticleSystem* getPartSys() { return mParent; } - class CmdSkelBase : public Ogre::ParamCommand - { - public: - Ogre::String doGet(const void *target) const - { - assert(false && "Unimplemented"); - return ""; - } - void doSet(void *target, const Ogre::String &val) - { - NifEmitter* emitter = static_cast(target); - emitter->mSkelBaseName = val; - } - }; - - class CmdBone : public Ogre::ParamCommand - { - public: - Ogre::String doGet(const void *target) const - { - assert(false && "Unimplemented"); - return ""; - } - void doSet(void *target, const Ogre::String &val) - { - NifEmitter* emitter = static_cast(target); - assert(!emitter->mSkelBaseName.empty() && "Base entity needs to be set first"); - Ogre::ParticleSystem* partsys = emitter->getPartSys(); - Ogre::Entity* ent = partsys->getParentSceneNode()->getCreator()->getEntity(emitter->mSkelBaseName); - Ogre::Bone* bone = ent->getSkeleton()->getBone(val); - assert(bone); - emitter->mBone = bone; - } - }; - /** Command object for the emitter width (see Ogre::ParamCommand).*/ class CmdWidth : public Ogre::ParamCommand { @@ -165,8 +130,10 @@ public: NifEmitter(Ogre::ParticleSystem *psys) : Ogre::ParticleEmitter(psys) - , mBone(NULL) { + mEmitterBone = Ogre::any_cast(psys->getUserObjectBindings().getUserAny()); + Ogre::TagPoint* tag = static_cast(mParent->getParentNode()); + mParticleBone = static_cast(tag->getParent()); initDefaults("Nif"); } @@ -180,7 +147,6 @@ public: /** See Ogre::ParticleEmitter. */ void _initParticle(Ogre::Particle *particle) { - assert (mBone && "No node set"); Ogre::Vector3 xOff, yOff, zOff; // Call superclass @@ -203,7 +169,10 @@ public: Ogre::Real& totalTimeToLive = particle->totalTimeToLive; Ogre::Real& timeToLive = particle->timeToLive; #endif - position = mBone->_getDerivedPosition() + xOff + yOff + zOff; + + position = xOff + yOff + zOff + + mParticleBone->_getDerivedOrientation().Inverse() * (mEmitterBone->_getDerivedPosition() + - mParticleBone->_getDerivedPosition()); // Generate complex data by reference genEmissionColour(colour); @@ -211,7 +180,9 @@ public: // NOTE: We do not use mDirection/mAngle for the initial direction. Ogre::Radian hdir = mHorizontalDir + mHorizontalAngle*Ogre::Math::SymmetricRandom(); Ogre::Radian vdir = mVerticalDir + mVerticalAngle*Ogre::Math::SymmetricRandom(); - direction = (mBone->_getDerivedOrientation() * Ogre::Quaternion(hdir, Ogre::Vector3::UNIT_Z) * + direction = (mParticleBone->_getDerivedOrientation().Inverse() + * mEmitterBone->_getDerivedOrientation() * + Ogre::Quaternion(hdir, Ogre::Vector3::UNIT_Z) * Ogre::Quaternion(vdir, Ogre::Vector3::UNIT_X)) * Ogre::Vector3::UNIT_Z; @@ -374,16 +345,6 @@ protected: Ogre::PT_REAL), &msHorizontalAngleCmd); - dict->addParameter(Ogre::ParameterDef("bone", - "The bone where the particles should be spawned", - Ogre::PT_STRING), - &msBoneCmd); - - dict->addParameter(Ogre::ParameterDef("skelbase", - "The name of the entity containing the bone (see 'bone' parameter)", - Ogre::PT_STRING), - &msSkelBaseCmd); - return true; } return false; @@ -397,8 +358,6 @@ protected: static CmdVerticalAngle msVerticalAngleCmd; static CmdHorizontalDir msHorizontalDirCmd; static CmdHorizontalAngle msHorizontalAngleCmd; - static CmdBone msBoneCmd; - static CmdSkelBase msSkelBaseCmd; }; NifEmitter::CmdWidth NifEmitter::msWidthCmd; NifEmitter::CmdHeight NifEmitter::msHeightCmd; @@ -407,8 +366,6 @@ NifEmitter::CmdVerticalDir NifEmitter::msVerticalDirCmd; NifEmitter::CmdVerticalAngle NifEmitter::msVerticalAngleCmd; NifEmitter::CmdHorizontalDir NifEmitter::msHorizontalDirCmd; NifEmitter::CmdHorizontalAngle NifEmitter::msHorizontalAngleCmd; -NifEmitter::CmdBone NifEmitter::msBoneCmd; -NifEmitter::CmdSkelBase NifEmitter::msSkelBaseCmd; Ogre::ParticleEmitter* NifEmitterFactory::createEmitter(Ogre::ParticleSystem *psys) { @@ -575,47 +532,11 @@ class GravityAffector : public Ogre::ParticleAffector }; public: - std::string mSkelBaseName; - Ogre::Bone* mBone; + Ogre::Bone* mEmitterBone; + Ogre::Bone* mParticleBone; Ogre::ParticleSystem* getPartSys() { return mParent; } - class CmdSkelBase : public Ogre::ParamCommand - { - public: - Ogre::String doGet(const void *target) const - { - assert(false && "Unimplemented"); - return ""; - } - void doSet(void *target, const Ogre::String &val) - { - GravityAffector* affector = static_cast(target); - affector->mSkelBaseName = val; - } - }; - - class CmdBone : public Ogre::ParamCommand - { - public: - Ogre::String doGet(const void *target) const - { - assert(false && "Unimplemented"); - return ""; - } - void doSet(void *target, const Ogre::String &val) - { - GravityAffector* affector = static_cast(target); - assert(!affector->mSkelBaseName.empty() && "Base entity needs to be set first"); - Ogre::ParticleSystem* partsys = affector->getPartSys(); - Ogre::Entity* ent = partsys->getParentSceneNode()->getCreator()->getEntity(affector->mSkelBaseName); - Ogre::Bone* bone = ent->getSkeleton()->getBone(val); - assert(bone); - affector->mBone = bone; - } - }; - - /** Command object for force (see Ogre::ParamCommand).*/ class CmdForce : public Ogre::ParamCommand { @@ -709,8 +630,11 @@ public: , mForceType(Type_Wind) , mPosition(0.0f) , mDirection(0.0f) - , mBone(NULL) { + mEmitterBone = Ogre::any_cast(psys->getUserObjectBindings().getUserAny()); + Ogre::TagPoint* tag = static_cast(mParent->getParentNode()); + mParticleBone = static_cast(tag->getParent()); + mType = "Gravity"; // Init parameters @@ -731,16 +655,6 @@ public: dict->addParameter(Ogre::ParameterDef(force_type_title, force_type_descr, Ogre::PT_STRING), &msForceTypeCmd); dict->addParameter(Ogre::ParameterDef(direction_title, direction_descr, Ogre::PT_VECTOR3), &msDirectionCmd); dict->addParameter(Ogre::ParameterDef(position_title, position_descr, Ogre::PT_VECTOR3), &msPositionCmd); - - dict->addParameter(Ogre::ParameterDef("bone", - "The bone where the particles should be spawned", - Ogre::PT_STRING), - &msBoneCmd); - - dict->addParameter(Ogre::ParameterDef("skelbase", - "The name of the entity containing the bone (see 'bone' parameter)", - Ogre::PT_STRING), - &msSkelBaseCmd); } } @@ -782,13 +696,11 @@ public: static CmdForceType msForceTypeCmd; static CmdDirection msDirectionCmd; static CmdPosition msPositionCmd; - static CmdBone msBoneCmd; - static CmdSkelBase msSkelBaseCmd; protected: void applyWindForce(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed) { - const Ogre::Vector3 vec = mBone->_getDerivedOrientation() * mDirection * mForce * timeElapsed; + const Ogre::Vector3 vec = mDirection * mForce * timeElapsed; Ogre::ParticleIterator pi = psys->_getIterator(); while (!pi.end()) { @@ -813,8 +725,8 @@ protected: #else Ogre::Vector3 position = p->position; #endif - const Ogre::Vector3 vec = ( - (mBone->_getDerivedOrientation() * mPosition + mBone->_getDerivedPosition()) - position).normalisedCopy() * force; + + Ogre::Vector3 vec = (mPosition - position).normalisedCopy() * force; #if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0) p->mDirection += vec; #else @@ -835,8 +747,6 @@ GravityAffector::CmdForce GravityAffector::msForceCmd; GravityAffector::CmdForceType GravityAffector::msForceTypeCmd; GravityAffector::CmdDirection GravityAffector::msDirectionCmd; GravityAffector::CmdPosition GravityAffector::msPositionCmd; -GravityAffector::CmdBone GravityAffector::msBoneCmd; -GravityAffector::CmdSkelBase GravityAffector::msSkelBaseCmd; Ogre::ParticleAffector *GravityAffectorFactory::createAffector(Ogre::ParticleSystem *psys) { From 27092a44945b51d1beffc8612cb23a51f1aca267 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 18:28:54 +0100 Subject: [PATCH 232/889] flagAsModified should be private --- apps/openmw/mwworld/containerstore.hpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index df7168dfa..b34c71006 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -94,6 +94,8 @@ namespace MWWorld ContainerStoreIterator addNewStack (const Ptr& ptr); ///< Add the item to this container (do not try to stack it onto existing items) + virtual void flagAsModified(); + public: virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2); @@ -105,10 +107,6 @@ namespace MWWorld void clear(); ///< Empty container. - virtual void flagAsModified(); - ///< \attention This function is internal to the world model and should not be called from - /// outside. - float getWeight() const; ///< Return total weight of the items contained in *this. From 686d9efac3030a6220fce0e450c591a60f9fde24 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 18:29:15 +0100 Subject: [PATCH 233/889] Bug #1060: Fix incorrect spell type checks --- apps/openmw/mwmechanics/spells.cpp | 4 ++-- apps/openmw/mwworld/worldimp.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index 6e7ac6f31..0088bcb60 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -125,7 +125,7 @@ namespace MWMechanics const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get().find (iter->first); - if (spell->mData.mType & ESM::Spell::ST_Disease) + if (spell->mData.mType == ESM::Spell::ST_Disease) mSpells.erase(iter++); else iter++; @@ -139,7 +139,7 @@ namespace MWMechanics const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get().find (iter->first); - if (spell->mData.mType & ESM::Spell::ST_Blight) + if (spell->mData.mType == ESM::Spell::ST_Blight) mSpells.erase(iter++); else iter++; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 021b6184f..bc60ea8be 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2072,7 +2072,7 @@ namespace MWWorld } // If this is a power, check if it was already used in the last 24h - if (!fail && spell->mData.mType & ESM::Spell::ST_Power) + if (!fail && spell->mData.mType == ESM::Spell::ST_Power) { if (stats.canUsePower(spell->mId)) stats.usePower(spell->mId); From 02277db685a31617e17cc067234419b2c78ebeaa Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 19:52:10 +0100 Subject: [PATCH 234/889] Bug #1052: Don't use set/getOnlyText which discards escape characters --- apps/openmw/mwgui/class.cpp | 2 +- apps/openmw/mwgui/class.hpp | 4 ++-- apps/openmw/mwgui/textinput.hpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index c33e54d6b..6c46f2176 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -466,7 +466,7 @@ namespace MWGui std::string CreateClassDialog::getName() const { - return mEditName->getOnlyText(); + return mEditName->getCaption(); } std::string CreateClassDialog::getDescription() const diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index 15fc89658..e74370a4c 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -228,8 +228,8 @@ namespace MWGui DescriptionDialog(); ~DescriptionDialog(); - std::string getTextInput() const { return mTextEdit ? mTextEdit->getOnlyText() : ""; } - void setTextInput(const std::string &text) { if (mTextEdit) mTextEdit->setOnlyText(text); } + std::string getTextInput() const { return mTextEdit->getCaption(); } + void setTextInput(const std::string &text) { mTextEdit->setCaption(text); } protected: void onOkClicked(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwgui/textinput.hpp b/apps/openmw/mwgui/textinput.hpp index 1f53263ec..1ed80fc1e 100644 --- a/apps/openmw/mwgui/textinput.hpp +++ b/apps/openmw/mwgui/textinput.hpp @@ -15,8 +15,8 @@ namespace MWGui public: TextInputDialog(); - std::string getTextInput() const { return mTextEdit ? mTextEdit->getOnlyText() : ""; } - void setTextInput(const std::string &text) { if (mTextEdit) mTextEdit->setOnlyText(text); } + std::string getTextInput() const { return mTextEdit->getCaption(); } + void setTextInput(const std::string &text) { mTextEdit->setCaption(text); } void setNextButtonShow(bool shown); void setTextLabel(const std::string &label); From 7265b427fe27cca0b3e567e4f892ba1605cae96a Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 21:21:18 +0100 Subject: [PATCH 235/889] Bug #1013: Rewrote fall height detection --- apps/openmw/mwmechanics/character.cpp | 18 ++++++------------ apps/openmw/mwmechanics/character.hpp | 3 --- apps/openmw/mwmechanics/creaturestats.cpp | 15 ++++++++++++++- apps/openmw/mwmechanics/creaturestats.hpp | 8 ++++++++ apps/openmw/mwworld/physicssystem.cpp | 10 ++++++++++ 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 3038faf31..fdf09d2f8 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -350,7 +350,6 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim , mSkipAnim(false) , mSecondsOfRunning(0) , mSecondsOfSwimming(0) - , mFallHeight(0) { if(!mAnimation) return; @@ -800,10 +799,7 @@ void CharacterController::update(float duration) } if(sneak || inwater || flying) - { vec.z = 0.0f; - mFallHeight = mPtr.getRefData().getPosition().pos[2]; - } if(!onground && !flying && !inwater) { @@ -812,11 +808,7 @@ void CharacterController::update(float duration) if (world->isSlowFalling(mPtr)) { // SlowFalling spell effect is active, do not keep previous fall height - mFallHeight = mPtr.getRefData().getPosition().pos[2]; - } - else - { - mFallHeight = std::max(mFallHeight, mPtr.getRefData().getPosition().pos[2]); + cls.getCreatureStats(mPtr).land(); } const MWWorld::Store &gmst = world->getStore().get(); @@ -872,7 +864,8 @@ void CharacterController::update(float duration) mJumpState = JumpState_Landing; vec.z = 0.0f; - float healthLost = cls.getFallDamage(mPtr, mFallHeight - mPtr.getRefData().getPosition().pos[2]); + float height = cls.getCreatureStats(mPtr).land(); + float healthLost = cls.getFallDamage(mPtr, height); if (healthLost > 0.0f) { const float fatigueTerm = cls.getCreatureStats(mPtr).getFatigueTerm(); @@ -893,8 +886,6 @@ void CharacterController::update(float duration) //TODO: actor falls over } } - - mFallHeight = mPtr.getRefData().getPosition().pos[2]; } else { @@ -933,6 +924,9 @@ void CharacterController::update(float duration) } } + if (onground || inwater || flying) + cls.getCreatureStats(mPtr).land(); + if(movestate != CharState_None) clearAnimQueue(); diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 9e07fca7d..817fa2fd5 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -156,9 +156,6 @@ class CharacterController float mSecondsOfSwimming; float mSecondsOfRunning; - // used for acrobatics progress and fall damages - float mFallHeight; - std::string mAttackType; // slash, chop or thrust void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 345b5a156..bfbd32ff0 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -14,7 +14,8 @@ namespace MWMechanics mTalkedTo (false), mAlarmed (false), mAttacked (false), mHostile (false), mAttackingOrSpell(false), mAttackType(AT_Chop), - mIsWerewolf(false) + mIsWerewolf(false), + mFallHeight(0) { for (int i=0; i<4; ++i) mAiSettings[i] = 0; @@ -356,4 +357,16 @@ namespace MWMechanics { mUsedPowers[power] = MWBase::Environment::get().getWorld()->getTimeStamp(); } + + void CreatureStats::addToFallHeight(float height) + { + mFallHeight += height; + } + + float CreatureStats::land() + { + float height = mFallHeight; + mFallHeight = 0; + return height; + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index f28f50fc6..5fc3a7ec6 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -36,6 +36,8 @@ namespace MWMechanics bool mHostile; bool mAttackingOrSpell;//for the player, this is true if the left mouse button is pressed, false if not. + float mFallHeight; + int mAttackType; std::string mLastHitObject; // The last object to hit this actor @@ -49,6 +51,12 @@ namespace MWMechanics public: CreatureStats(); + void addToFallHeight(float height); + + /// Reset the fall height + /// @return total fall height + float land(); + bool canUsePower (const std::string& power) const; void usePower (const std::string& power); diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 433fe0892..451e093ae 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -18,6 +18,8 @@ #include "../mwbase/world.hpp" // FIXME #include "../mwbase/environment.hpp" +#include "../mwmechanics/creaturestats.hpp" + #include #include "../mwworld/esmstore.hpp" @@ -573,9 +575,17 @@ namespace MWWorld if(cell->hasWater()) waterlevel = cell->mWater; + float oldHeight = iter->first.getRefData().getPosition().pos[2]; + Ogre::Vector3 newpos = MovementSolver::move(iter->first, iter->second, mTimeAccum, world->isFlying(iter->first), waterlevel, mEngine); + + float heightDiff = newpos.z - oldHeight; + + if (heightDiff < 0) + iter->first.getClass().getCreatureStats(iter->first).addToFallHeight(-heightDiff); + mMovementResults.push_back(std::make_pair(iter->first, newpos)); } From 85ec80100ce607e0b510b2a452716fa4906298c8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 22:00:16 +0100 Subject: [PATCH 236/889] Bug #1005: Hide torches/shields during spellcasting and hand-to-hand combat --- apps/openmw/mwmechanics/character.cpp | 7 ++++--- apps/openmw/mwrender/animation.hpp | 2 +- apps/openmw/mwrender/npcanimation.cpp | 30 ++++++++++----------------- apps/openmw/mwrender/npcanimation.hpp | 4 ++-- 4 files changed, 18 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index fdf09d2f8..87a7875f5 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -425,10 +425,10 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun { forcestateupdate = true; - // Shields shouldn't be visible during spellcasting + // Shields/torches shouldn't be visible during spellcasting or hand-to-hand // There seems to be no text keys for this purpose, except maybe for "[un]equip start/stop", // but they are also present in weapon drawing animation. - mAnimation->showShield(weaptype != WeapType_Spell); + mAnimation->showCarriedLeft(weaptype != WeapType_Spell && weaptype != WeapType_HandToHand); std::string weapgroup; if(weaptype == WeapType_None) @@ -719,7 +719,8 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) + if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name() + && mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand) { if(!mAnimation->isPlaying("torch")) mAnimation->play("torch", Priority_Torch, diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 67d8baa3f..72d1c100e 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -279,7 +279,7 @@ public: virtual Ogre::Vector3 runAnimation(float duration); virtual void showWeapons(bool showWeapon); - virtual void showShield(bool show) {} + virtual void showCarriedLeft(bool show) {} void enableLights(bool enable); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 8d2f1c7da..ddbdde83a 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -117,7 +117,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v mListenerDisabled(disableListener), mViewMode(viewMode), mShowWeapons(false), - mShowShield(true), + mShowCarriedLeft(true), mFirstPersonOffset(0.f, 0.f, 0.f), mAlpha(1.f) { @@ -319,7 +319,7 @@ void NpcAnimation::updateParts() } showWeapons(mShowWeapons); - showShield(mShowShield); + showCarriedLeft(mShowCarriedLeft); // Remember body parts so we only have to search through the store once for each race/gender/viewmode combination static std::map< std::pair,std::vector > sRaceMapping; @@ -654,32 +654,24 @@ void NpcAnimation::showWeapons(bool showWeapon) mAlpha = 1.f; } -void NpcAnimation::showShield(bool show) +void NpcAnimation::showCarriedLeft(bool show) { - mShowShield = show; + mShowCarriedLeft = show; MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); - MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if (shield != inv.end() && shield->getTypeName() == typeid(ESM::Light).name()) + if(show && iter != inv.end()) { - // ... Except for lights, which are still shown during spellcasting since they - // have their own (one-handed) casting animations - show = true; - } - if(show && shield != inv.end()) - { - Ogre::Vector3 glowColor = getEnchantmentColor(*shield); - std::string mesh = MWWorld::Class::get(*shield).getModel(*shield); + Ogre::Vector3 glowColor = getEnchantmentColor(*iter); + std::string mesh = MWWorld::Class::get(*iter).getModel(*iter); addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, - mesh, !shield->getClass().getEnchantment(*shield).empty(), &glowColor); + mesh, !iter->getClass().getEnchantment(*iter).empty(), &glowColor); - if (shield->getTypeName() == typeid(ESM::Light).name()) - addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], shield->get()->mBase); + if (iter->getTypeName() == typeid(ESM::Light).name()) + addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], iter->get()->mBase); } else - { removeIndividualPart(ESM::PRT_Shield); - } } void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound) diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index b6ed3f857..28bb81ddd 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -53,7 +53,7 @@ private: std::string mHairModel; ViewMode mViewMode; bool mShowWeapons; - bool mShowShield; + bool mShowCarriedLeft; int mVisibilityFlags; @@ -100,7 +100,7 @@ public: virtual Ogre::Vector3 runAnimation(float timepassed); virtual void showWeapons(bool showWeapon); - virtual void showShield(bool showShield); + virtual void showCarriedLeft(bool showa); void setViewMode(ViewMode viewMode); From 03235bf0a274b7dfd1e95a1a58f2662ffbbececc Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 27 Dec 2013 22:13:55 +0100 Subject: [PATCH 237/889] NPC. Still WIP. --- .../opencs/model/tools/referenceablecheck.cpp | 260 +++++++++++++++++- .../opencs/model/tools/referenceablecheck.hpp | 9 +- apps/opencs/model/world/refiddata.cpp | 229 ++++++++------- apps/opencs/model/world/refiddata.hpp | 4 + components/esm/loadnpc.hpp | 2 +- 5 files changed, 383 insertions(+), 121 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index d37808810..6d06d6151 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "../world/record.hpp" @@ -22,7 +23,10 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefId mIngredientsSize(mReferencables.getIngredients().getSize()), mCreaturesLevListsSize(mReferencables.getCreatureLevelledLists().getSize()), mItemLevelledListsSize(mReferencables.getItemLevelledList().getSize()), - mLightsSize(mReferencables.getLights().getSize()) + mLightsSize(mReferencables.getLights().getSize()), + mLockpicksSize(mReferencables.getLocpicks().getSize()), + mMiscellaneousSize(mReferencables.getMiscellaneous().getSize()), + mNPCsSize(mReferencables.getNPCs().getSize()) { } @@ -111,11 +115,35 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str if (stage < mItemLevelledListsSize) { - mItemLevelledListCheck(stage, mReferencables.getItemLevelledList(), messages); + itemLevelledListCheck(stage, mReferencables.getItemLevelledList(), messages); return; } stage -= mItemLevelledListsSize; + + if (stage < mLightsSize) + { + lightCheck(stage, mReferencables.getLights(), messages); + return; + } + + stage -= mLightsSize; + + if (stage < mLockpicksSize) + { + lockpickCheck(stage, mReferencables.getLocpicks(), messages); + return; + } + + stage -= mLockpicksSize; + + if (stage < mMiscellaneousSize) + { + miscCheck(stage, mReferencables.getMiscellaneous(), messages); + return; + } + + stage -= mMiscellaneousSize; } int CSMTools::ReferenceableCheckStage::setup() @@ -602,7 +630,7 @@ void CSMTools::ReferenceableCheckStage::creaturesLevListCheck(int stage, const C const ESM::CreatureLevList& CreatureLevList = (static_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_CreatureLevelledList, CreatureLevList.mId); //CreatureLevList but Type_CreatureLevelledList :/ - for (int i = 0; i < CreatureLevList.mList.size(); ++i) + for (unsigned i = 0; i < CreatureLevList.mList.size(); ++i) { if (CreatureLevList.mList[i].mId.empty()) { @@ -614,14 +642,9 @@ void CSMTools::ReferenceableCheckStage::creaturesLevListCheck(int stage, const C messages.push_back(id.toString() + "|" + CreatureLevList.mId + " contains item with non-positive level"); } } - - if (CreatureLevList.mChanceNone < 0 or CreatureLevList.mChanceNone > 100) - { - messages.push_back(id.toString() + "|" + CreatureLevList.mId + " chance to be empty is not beetween 0 and 100"); - } } -void CSMTools::ReferenceableCheckStage::mItemLevelledListCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::ItemLevList >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::itemLevelledListCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::ItemLevList >& records, std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -633,7 +656,7 @@ void CSMTools::ReferenceableCheckStage::mItemLevelledListCheck(int stage, const const ESM::ItemLevList& ItemLevList = (static_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_ItemLevelledList, ItemLevList.mId); - for (int i = 0; i < ItemLevList.mList.size(); ++i) + for (unsigned i = 0; i < ItemLevList.mList.size(); ++i) { if (ItemLevList.mList[i].mId.empty()) { @@ -645,9 +668,222 @@ void CSMTools::ReferenceableCheckStage::mItemLevelledListCheck(int stage, const messages.push_back(id.toString() + "|" + ItemLevList.mId + " contains item with non-positive level"); } } +} - if (ItemLevList.mChanceNone < 0 or ItemLevList.mChanceNone > 100) +void CSMTools::ReferenceableCheckStage::lightCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Light >& records, std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + + if (baserecord.isDeleted()) { - messages.push_back(id.toString() + "|" + ItemLevList.mId + " chance to be empty is not beetween 0 and 100"); + return; + } + + const ESM::Light& Light = (static_cast& >(baserecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Light, Light.mId); + + if (Light.mData.mRadius < 0) + { + messages.push_back(id.toString() + "|" + Light.mId + " has negative light radius"); + } + + if (Light.mData.mFlags & ESM::Light::Carry) + { + if (Light.mIcon.empty()) //Needs to be checked with carrable flag + { + messages.push_back(id.toString() + "|" + Light.mId + " has no icon"); + } + + if (Light.mData.mWeight < 0) //probabbly needs to be checked only for carrable lights TODO + { + messages.push_back(id.toString() + "|" + Light.mId + " has negative weight"); + } + + if (Light.mData.mValue < 0) //probabbly needs to be checked only for carrable lights TODO + { + messages.push_back(id.toString() + "|" + Light.mId + " has negative value"); + } + + if (Light.mModel.empty()) + { + messages.push_back(id.toString() + "|" + Light.mId + " has no model"); + } + + if (Light.mData.mTime < 0) + { + messages.push_back(id.toString() + "|" + Light.mId + " has negative duration"); + } } } + +void CSMTools::ReferenceableCheckStage::lockpickCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Lockpick >& records, std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + + if (baserecord.isDeleted()) + { + return; + } + + const ESM::Lockpick& Lockpick = (static_cast& >(baserecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Lockpick, Lockpick.mId); + + //Checking for name + if (Lockpick.mName.empty()) + { + messages.push_back(id.toString() + "|" + Lockpick.mId + " has an empty name"); + } + + //Checking for weight + if (Lockpick.mData.mWeight < 0) + { + messages.push_back(id.toString() + "|" + Lockpick.mId + " has negative weight"); + } + + //Checking for value + if (Lockpick.mData.mValue < 0) + { + messages.push_back(id.toString() + "|" + Lockpick.mId + " has negative value"); + } + +//checking for model + if (Lockpick.mModel.empty()) + { + messages.push_back(id.toString() + "|" + Lockpick.mId + " has no model"); + } + + //checking for icon + if (Lockpick.mIcon.empty()) + { + messages.push_back(id.toString() + "|" + Lockpick.mId + " has no icon"); + } + + if (Lockpick.mData.mQuality <= 0) + { + messages.push_back(id.toString() + "|" + Lockpick.mId + " has non-positive quality"); + } + + if (Lockpick.mData.mUses <= 0) + { + messages.push_back(id.toString() + "|" + Lockpick.mId + " has no uses left"); + } +} + +void CSMTools::ReferenceableCheckStage::miscCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Miscellaneous >& records, std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + + if (baserecord.isDeleted()) + { + return; + } + + const ESM::Miscellaneous& Miscellaneous = (static_cast& >(baserecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Miscellaneous, Miscellaneous.mId); + + //Checking for name + if (Miscellaneous.mName.empty()) + { + messages.push_back(id.toString() + "|" + Miscellaneous.mId + " has an empty name"); + } + + //Checking for weight + if (Miscellaneous.mData.mWeight < 0) + { + messages.push_back(id.toString() + "|" + Miscellaneous.mId + " has negative weight"); + } + + //Checking for value + if (Miscellaneous.mData.mValue < 0) + { + messages.push_back(id.toString() + "|" + Miscellaneous.mId + " has negative value"); + } + +//checking for model + if (Miscellaneous.mModel.empty()) + { + messages.push_back(id.toString() + "|" + Miscellaneous.mId + " has no model"); + } + + //checking for icon + if (Miscellaneous.mIcon.empty()) + { + messages.push_back(id.toString() + "|" + Miscellaneous.mId + " has no icon"); + } +} + +void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::NPC >& records, std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + + if (baserecord.isDeleted()) + { + return; + } + + const ESM::NPC& NPC = (static_cast& >(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); + //Don't know what unknown is for + int Gold(NPC.mNpdt52.mGold); + + if (NPC.mNpdtType == 12) + { + if (NPC.mFlags ^ ESM::NPC::Flags::Autocalc) + { + messages.push_back(id.toString() + "|" + NPC.mId + " mNpdtType and flags mismatch!"); //should not happend? + return; + } + + level = NPC.mNpdt12.mLevel; + Disposition = NPC.mNpdt12.mDisposition; + Reputation = NPC.mNpdt12.mReputation; + Rank = NPC.mNpdt12.mRank; + Gold = NPC.mNpdt12.mGold; + } + else + { + if (NPC.mNpdt52.mHealth < 0) + { + messages.push_back(id.toString() + "|" + NPC.mId + " health is negative value"); + } + + if (NPC.mNpdt52.mMana < 0) + { + messages.push_back(id.toString() + "|" + NPC.mId + " mana is negative value"); + } + + if (NPC.mNpdt52.mFatigue < 0) + { + messages.push_back(id.toString() + "|" + NPC.mId + " fatigue is negative value"); + } + } + + if (level < 1) + { + messages.push_back(id.toString() + "|" + NPC.mId + " level is non positive"); + } + + if (Gold < 0) + { + messages.push_back(id.toString() + "|" + NPC.mId + " gold is negative value"); + } + + if (NPC.mName.empty()) + { + messages.push_back(id.toString() + "|" + NPC.mId + " has any empty name"); + } + + if (NPC.mClass.empty()) + { + messages.push_back(id.toString() + "|" + NPC.mId + " has any empty class"); + } + + //TODO: reputation, Disposition, rank, everything else +} diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index 6bffc2b25..d4f7697ae 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -28,7 +28,11 @@ namespace CSMTools void doorCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void ingredientCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void creaturesLevListCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); - void mItemLevelledListCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void itemLevelledListCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void lightCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void lockpickCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void miscCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void npcCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); const CSMWorld::RefIdData mReferencables; @@ -46,6 +50,9 @@ namespace CSMTools const int mCreaturesLevListsSize; const int mItemLevelledListsSize; const int mLightsSize; + const int mLockpicksSize; + const int mMiscellaneousSize; + const int mNPCsSize; }; } #endif // REFERENCEABLECHECKSTAGE_H diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 54e2cd12f..9c0a857a5 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -9,187 +9,187 @@ CSMWorld::RefIdDataContainerBase::~RefIdDataContainerBase() {} CSMWorld::RefIdData::RefIdData() { - mRecordContainers.insert (std::make_pair (UniversalId::Type_Activator, &mActivators)); - mRecordContainers.insert (std::make_pair (UniversalId::Type_Potion, &mPotions)); - mRecordContainers.insert (std::make_pair (UniversalId::Type_Apparatus, &mApparati)); - mRecordContainers.insert (std::make_pair (UniversalId::Type_Armor, &mArmors)); - mRecordContainers.insert (std::make_pair (UniversalId::Type_Book, &mBooks)); - mRecordContainers.insert (std::make_pair (UniversalId::Type_Clothing, &mClothing)); - mRecordContainers.insert (std::make_pair (UniversalId::Type_Container, &mContainers)); - mRecordContainers.insert (std::make_pair (UniversalId::Type_Creature, &mCreatures)); - mRecordContainers.insert (std::make_pair (UniversalId::Type_Door, &mDoors)); - mRecordContainers.insert (std::make_pair (UniversalId::Type_Ingredient, &mIngredients)); - mRecordContainers.insert (std::make_pair (UniversalId::Type_CreatureLevelledList, - &mCreatureLevelledLists)); - mRecordContainers.insert (std::make_pair (UniversalId::Type_ItemLevelledList, &mItemLevelledLists)); - mRecordContainers.insert (std::make_pair (UniversalId::Type_Light, &mLights)); - mRecordContainers.insert (std::make_pair (UniversalId::Type_Lockpick, &mLockpicks)); - mRecordContainers.insert (std::make_pair (UniversalId::Type_Miscellaneous, &mMiscellaneous)); - mRecordContainers.insert (std::make_pair (UniversalId::Type_Npc, &mNpcs)); - mRecordContainers.insert (std::make_pair (UniversalId::Type_Probe, &mProbes)); - mRecordContainers.insert (std::make_pair (UniversalId::Type_Repair, &mRepairs)); - mRecordContainers.insert (std::make_pair (UniversalId::Type_Static, &mStatics)); - mRecordContainers.insert (std::make_pair (UniversalId::Type_Weapon, &mWeapons)); + mRecordContainers.insert(std::make_pair(UniversalId::Type_Activator, &mActivators)); + mRecordContainers.insert(std::make_pair(UniversalId::Type_Potion, &mPotions)); + mRecordContainers.insert(std::make_pair(UniversalId::Type_Apparatus, &mApparati)); + mRecordContainers.insert(std::make_pair(UniversalId::Type_Armor, &mArmors)); + mRecordContainers.insert(std::make_pair(UniversalId::Type_Book, &mBooks)); + mRecordContainers.insert(std::make_pair(UniversalId::Type_Clothing, &mClothing)); + mRecordContainers.insert(std::make_pair(UniversalId::Type_Container, &mContainers)); + mRecordContainers.insert(std::make_pair(UniversalId::Type_Creature, &mCreatures)); + mRecordContainers.insert(std::make_pair(UniversalId::Type_Door, &mDoors)); + mRecordContainers.insert(std::make_pair(UniversalId::Type_Ingredient, &mIngredients)); + mRecordContainers.insert(std::make_pair(UniversalId::Type_CreatureLevelledList, + &mCreatureLevelledLists)); + mRecordContainers.insert(std::make_pair(UniversalId::Type_ItemLevelledList, &mItemLevelledLists)); + mRecordContainers.insert(std::make_pair(UniversalId::Type_Light, &mLights)); + mRecordContainers.insert(std::make_pair(UniversalId::Type_Lockpick, &mLockpicks)); + mRecordContainers.insert(std::make_pair(UniversalId::Type_Miscellaneous, &mMiscellaneous)); + mRecordContainers.insert(std::make_pair(UniversalId::Type_Npc, &mNpcs)); + mRecordContainers.insert(std::make_pair(UniversalId::Type_Probe, &mProbes)); + mRecordContainers.insert(std::make_pair(UniversalId::Type_Repair, &mRepairs)); + mRecordContainers.insert(std::make_pair(UniversalId::Type_Static, &mStatics)); + mRecordContainers.insert(std::make_pair(UniversalId::Type_Weapon, &mWeapons)); } -CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::globalToLocalIndex (int index) const +CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::globalToLocalIndex(int index) const { - for (std::map::const_iterator iter ( - mRecordContainers.begin()); iter!=mRecordContainers.end(); ++iter) + for (std::map::const_iterator iter( + mRecordContainers.begin()); iter != mRecordContainers.end(); ++iter) { - if (indexsecond->getSize()) - return LocalIndex (index, iter->first); + if (index < iter->second->getSize()) + return LocalIndex(index, iter->first); index -= iter->second->getSize(); } - throw std::runtime_error ("RefIdData index out of range"); + throw std::runtime_error("RefIdData index out of range"); } -int CSMWorld::RefIdData::localToGlobalIndex (const LocalIndex& index) - const +int CSMWorld::RefIdData::localToGlobalIndex(const LocalIndex& index) +const { - std::map::const_iterator end = - mRecordContainers.find (index.second); + std::map::const_iterator end = + mRecordContainers.find(index.second); - if (end==mRecordContainers.end()) - throw std::logic_error ("invalid local index type"); + if (end == mRecordContainers.end()) + throw std::logic_error("invalid local index type"); int globalIndex = index.first; - for (std::map::const_iterator iter ( - mRecordContainers.begin()); iter!=end; ++iter) + for (std::map::const_iterator iter( + mRecordContainers.begin()); iter != end; ++iter) globalIndex += iter->second->getSize(); return globalIndex; } -CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::searchId ( +CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::searchId( const std::string& id) const { - std::string id2 = Misc::StringUtils::lowerCase (id); + std::string id2 = Misc::StringUtils::lowerCase(id); - std::map >::const_iterator iter = mIndex.find (id2); + std::map >::const_iterator iter = mIndex.find(id2); - if (iter==mIndex.end()) - return std::make_pair (-1, CSMWorld::UniversalId::Type_None); + if (iter == mIndex.end()) + return std::make_pair(-1, CSMWorld::UniversalId::Type_None); return iter->second; } -void CSMWorld::RefIdData::erase (int index, int count) +void CSMWorld::RefIdData::erase(int index, int count) { - LocalIndex localIndex = globalToLocalIndex (index); + LocalIndex localIndex = globalToLocalIndex(index); - std::map::const_iterator iter = - mRecordContainers.find (localIndex.second); + std::map::const_iterator iter = + mRecordContainers.find(localIndex.second); - while (count>0 && iter!=mRecordContainers.end()) + while (count > 0 && iter != mRecordContainers.end()) { int size = iter->second->getSize(); - if (localIndex.first+count>size) + if (localIndex.first + count > size) { - erase (localIndex, size-localIndex.first); - count -= size-localIndex.first; + erase(localIndex, size - localIndex.first); + count -= size - localIndex.first; ++iter; - if (iter==mRecordContainers.end()) - throw std::runtime_error ("invalid count value for erase operation"); + if (iter == mRecordContainers.end()) + throw std::runtime_error("invalid count value for erase operation"); localIndex.first = 0; localIndex.second = iter->first; } else { - erase (localIndex, count); + erase(localIndex, count); count = 0; } } } -const CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord (const LocalIndex& index) const +const CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord(const LocalIndex& index) const { - std::map::const_iterator iter = - mRecordContainers.find (index.second); + std::map::const_iterator iter = + mRecordContainers.find(index.second); - if (iter==mRecordContainers.end()) - throw std::logic_error ("invalid local index type"); + if (iter == mRecordContainers.end()) + throw std::logic_error("invalid local index type"); - return iter->second->getRecord (index.first); + return iter->second->getRecord(index.first); } -CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord (const LocalIndex& index) +CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord(const LocalIndex& index) { - std::map::iterator iter = - mRecordContainers.find (index.second); + std::map::iterator iter = + mRecordContainers.find(index.second); - if (iter==mRecordContainers.end()) - throw std::logic_error ("invalid local index type"); + if (iter == mRecordContainers.end()) + throw std::logic_error("invalid local index type"); - return iter->second->getRecord (index.first); + return iter->second->getRecord(index.first); } -void CSMWorld::RefIdData::appendRecord (UniversalId::Type type, const std::string& id) +void CSMWorld::RefIdData::appendRecord(UniversalId::Type type, const std::string& id) { - std::map::iterator iter = - mRecordContainers.find (type); + std::map::iterator iter = + mRecordContainers.find(type); - if (iter==mRecordContainers.end()) - throw std::logic_error ("invalid local index type"); + if (iter == mRecordContainers.end()) + throw std::logic_error("invalid local index type"); - iter->second->appendRecord (id); + iter->second->appendRecord(id); - mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), - LocalIndex (iter->second->getSize()-1, type))); + mIndex.insert(std::make_pair(Misc::StringUtils::lowerCase(id), + LocalIndex(iter->second->getSize() - 1, type))); } -int CSMWorld::RefIdData::getAppendIndex (UniversalId::Type type) const +int CSMWorld::RefIdData::getAppendIndex(UniversalId::Type type) const { int index = 0; - for (std::map::const_iterator iter ( - mRecordContainers.begin()); iter!=mRecordContainers.end(); ++iter) + for (std::map::const_iterator iter( + mRecordContainers.begin()); iter != mRecordContainers.end(); ++iter) { index += iter->second->getSize(); - if (type==iter->first) + if (type == iter->first) break; } return index; } -void CSMWorld::RefIdData::load (const LocalIndex& index, ESM::ESMReader& reader, bool base) +void CSMWorld::RefIdData::load(const LocalIndex& index, ESM::ESMReader& reader, bool base) { - std::map::iterator iter = - mRecordContainers.find (index.second); + std::map::iterator iter = + mRecordContainers.find(index.second); - if (iter==mRecordContainers.end()) - throw std::logic_error ("invalid local index type"); + if (iter == mRecordContainers.end()) + throw std::logic_error("invalid local index type"); - iter->second->load (index.first, reader, base); + iter->second->load(index.first, reader, base); } -void CSMWorld::RefIdData::erase (const LocalIndex& index, int count) +void CSMWorld::RefIdData::erase(const LocalIndex& index, int count) { - std::map::iterator iter = - mRecordContainers.find (index.second); + std::map::iterator iter = + mRecordContainers.find(index.second); - if (iter==mRecordContainers.end()) - throw std::logic_error ("invalid local index type"); + if (iter == mRecordContainers.end()) + throw std::logic_error("invalid local index type"); - for (int i=index.first; i::iterator result = - mIndex.find (Misc::StringUtils::lowerCase (iter->second->getId (i))); + mIndex.find(Misc::StringUtils::lowerCase(iter->second->getId(i))); - if (result!=mIndex.end()) - mIndex.erase (result); + if (result != mIndex.end()) + mIndex.erase(result); } - iter->second->erase (index.first, count); + iter->second->erase(index.first, count); } int CSMWorld::RefIdData::getSize() const @@ -197,39 +197,39 @@ int CSMWorld::RefIdData::getSize() const return mIndex.size(); } -std::vector CSMWorld::RefIdData::getIds (bool listDeleted) const +std::vector CSMWorld::RefIdData::getIds(bool listDeleted) const { std::vector ids; - for (std::map::const_iterator iter (mIndex.begin()); iter!=mIndex.end(); - ++iter) + for (std::map::const_iterator iter(mIndex.begin()); iter != mIndex.end(); + ++iter) { - if (listDeleted || !getRecord (iter->second).isDeleted()) + if (listDeleted || !getRecord(iter->second).isDeleted()) { - std::map::const_iterator container = - mRecordContainers.find (iter->second.second); + std::map::const_iterator container = + mRecordContainers.find(iter->second.second); - if (container==mRecordContainers.end()) - throw std::logic_error ("Invalid referenceable ID type"); + if (container == mRecordContainers.end()) + throw std::logic_error("Invalid referenceable ID type"); - ids.push_back (container->second->getId (iter->second.first)); + ids.push_back(container->second->getId(iter->second.first)); } } return ids; } -void CSMWorld::RefIdData::save (int index, ESM::ESMWriter& writer) const +void CSMWorld::RefIdData::save(int index, ESM::ESMWriter& writer) const { - LocalIndex localIndex = globalToLocalIndex (index); + LocalIndex localIndex = globalToLocalIndex(index); - std::map::const_iterator iter = - mRecordContainers.find (localIndex.second); + std::map::const_iterator iter = + mRecordContainers.find(localIndex.second); - if (iter==mRecordContainers.end()) - throw std::logic_error ("invalid local index type"); + if (iter == mRecordContainers.end()) + throw std::logic_error("invalid local index type"); - iter->second->save (localIndex.first, writer); + iter->second->save(localIndex.first, writer); } const CSMWorld::RefIdDataContainer< ESM::Book >& CSMWorld::RefIdData::getBooks() const @@ -296,3 +296,18 @@ const CSMWorld::RefIdDataContainer< ESM::Light >& CSMWorld::RefIdData::getLights { return mLights; } + +const CSMWorld::RefIdDataContainer< ESM::Lockpick >& CSMWorld::RefIdData::getLocpicks() const +{ + return mLockpicks; +} + +const CSMWorld::RefIdDataContainer< ESM::Miscellaneous >& CSMWorld::RefIdData::getMiscellaneous() const +{ + return mMiscellaneous; +} + +const CSMWorld::RefIdDataContainer< ESM::NPC >& CSMWorld::RefIdData::getNPCs() const +{ + return mNpcs; +} diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 6b6e01e01..8aa3c2522 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -234,7 +234,11 @@ namespace CSMWorld const RefIdDataContainer& getCreatureLevelledLists() const; const RefIdDataContainer& getItemLevelledList() const; const RefIdDataContainer& getLights() const; + const RefIdDataContainer& getLocpicks() const; + const RefIdDataContainer& getMiscellaneous() const; + const RefIdDataContainer& getNPCs() const; }; } #endif + diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index 1eac8d64f..08f678b45 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -105,7 +105,7 @@ struct NPC char mNpdtType; NPDTstruct52 mNpdt52; - NPDTstruct12 mNpdt12; // Use this if npdt52.gold == -10 + NPDTstruct12 mNpdt12; //for autocalculated characters int mFlags; From 596c9b80a99c0d8e95bde93129145be466e3785b Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 22:38:30 +0100 Subject: [PATCH 238/889] Check if threads are joinable before joining (issue with boost 1.52) --- apps/openmw/mwrender/videoplayer.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index ee2b80f73..88bc6d8ac 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -989,9 +989,12 @@ void VideoState::deinit() this->audioq.cond.notify_one(); this->videoq.cond.notify_one(); - this->parse_thread.join(); - this->video_thread.join(); - this->refresh_thread.join(); + if (this->parse_thread.joinable()) + this->parse_thread.join(); + if (this->video_thread.joinable()) + this->video_thread.join(); + if (this->refresh_thread.joinable()) + this->refresh_thread.join(); if(this->audio_st) avcodec_close((*this->audio_st)->codec); From da3dda896ac2ad02f2ef8ad40f42c8fc4e0e3cd0 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 27 Dec 2013 22:57:36 +0100 Subject: [PATCH 239/889] Code compiles now --- apps/opencs/model/tools/referenceablecheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 6d06d6151..40f7c26d2 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -835,7 +835,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI if (NPC.mNpdtType == 12) { - if (NPC.mFlags ^ ESM::NPC::Flags::Autocalc) + if (NPC.mFlags ^ 0x0008) { messages.push_back(id.toString() + "|" + NPC.mId + " mNpdtType and flags mismatch!"); //should not happend? return; From 6d47d710a013fee4a38764358f6d489c43430f1c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 28 Dec 2013 00:51:09 +0100 Subject: [PATCH 240/889] Reimplement NiGeomMorpherController using Ogre's pose animation system --- components/nifogre/mesh.cpp | 52 ++++++++++++-------- components/nifogre/ogrenifloader.cpp | 72 ++++++---------------------- 2 files changed, 47 insertions(+), 77 deletions(-) diff --git a/components/nifogre/mesh.cpp b/components/nifogre/mesh.cpp index ef4fbbe8d..80e377a49 100644 --- a/components/nifogre/mesh.cpp +++ b/components/nifogre/mesh.cpp @@ -116,21 +116,6 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape Ogre::HardwareBuffer::Usage vertUsage = Ogre::HardwareBuffer::HBU_STATIC; bool vertShadowBuffer = false; - bool geomMorpherController = false; - if(!shape->controller.empty()) - { - Nif::ControllerPtr ctrl = shape->controller; - do { - if(ctrl->recType == Nif::RC_NiGeomMorpherController) - { - vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY; - vertShadowBuffer = true; - geomMorpherController = true; - break; - } - } while(!(ctrl=ctrl->next).empty()); - } - if(skin != NULL) { vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY; @@ -350,10 +335,39 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape mesh->buildTangentVectors(Ogre::VES_TANGENT, src,dest); } - // Create a dummy vertex animation track if there's a geom morpher controller - // This is required to make Ogre create the buffers we will use for software vertex animation - if (srcVerts.size() && geomMorpherController) - mesh->createAnimation("dummy", 0)->createVertexTrack(1, sub->vertexData, Ogre::VAT_MORPH); + + if(!shape->controller.empty()) + { + Nif::ControllerPtr ctrl = shape->controller; + do { + // Load GeomMorpherController into an Ogre::Pose and Animation + if(ctrl->recType == Nif::RC_NiGeomMorpherController) + { + const Nif::NiGeomMorpherController *geom = + static_cast(ctrl.getPtr()); + + const std::vector& morphs = geom->data.getPtr()->mMorphs; + // Note we are not interested in morph 0, which just contains the original vertices + for (unsigned int i = 1; i < morphs.size(); ++i) + { + Ogre::Pose* pose = mesh->createPose(i); + const Nif::NiMorphData::MorphData& data = morphs[i]; + for (unsigned int v = 0; v < data.mVertices.size(); ++v) + pose->addVertex(v, data.mVertices[v]); + + Ogre::String animationID = Ogre::StringConverter::toString(ctrl->recIndex) + + "_" + Ogre::StringConverter::toString(i); + Ogre::VertexAnimationTrack* track = + mesh->createAnimation(animationID, 0) + ->createVertexTrack(1, Ogre::VAT_POSE); + Ogre::VertexPoseKeyFrame* keyframe = track->createVertexPoseKeyFrame(0); + keyframe->addPoseReference(i-1, 1); + } + + break; + } + } while(!(ctrl=ctrl->next).empty()); + } } diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 9fa913686..1e3598b33 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -534,18 +534,18 @@ public: class Value : public Ogre::ControllerValue, public ValueInterpolator { private: - Ogre::SubEntity *mSubEntity; + Ogre::Entity *mEntity; std::vector mMorphs; - std::vector mValues; + size_t mControllerIndex; std::vector mVertices; public: - Value(Ogre::SubEntity *subent, const Nif::NiMorphData *data) - : mSubEntity(subent) + Value(Ogre::Entity *ent, const Nif::NiMorphData *data, size_t controllerIndex) + : mEntity(ent) , mMorphs(data->mMorphs) + , mControllerIndex(controllerIndex) { - mValues.resize(mMorphs.size()-1, 0.f); } virtual Ogre::Real getValue() const @@ -558,21 +558,7 @@ public: { if (mMorphs.size() <= 1) return; - -#if OGRE_DOUBLE_PRECISION -#error "This code needs to be rewritten for double precision mode" -#endif - - Ogre::VertexData* data = mSubEntity->_getSoftwareVertexAnimVertexData(); - - const Ogre::VertexElement* posElem = - data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION); - - Ogre::HardwareVertexBufferSharedPtr vbuf = - data->vertexBufferBinding->getBuffer(posElem->getSource()); - - bool needToUpdate = false; - int i=0; + int i = 1; for (std::vector::iterator it = mMorphs.begin()+1; it != mMorphs.end(); ++it,++i) { float val = 0; @@ -580,37 +566,13 @@ public: val = interpKey(it->mData.mKeys, time); val = std::max(0.f, std::min(1.f, val)); - if (val != mValues[i]) - needToUpdate = true; - mValues[i] = val; + Ogre::String animationID = Ogre::StringConverter::toString(mControllerIndex) + + "_" + Ogre::StringConverter::toString(i); + + Ogre::AnimationState* state = mEntity->getAnimationState(animationID); + state->setEnabled(val > 0); + state->setWeight(val); } - if (!needToUpdate) - return; - - // The first morph key always contains the original positions - mVertices = mMorphs[0].mVertices; - - i = 0; - for (std::vector::iterator it = mMorphs.begin()+1; it != mMorphs.end(); ++it,++i) - { - float val = mValues[i]; - - if (it->mVertices.size() != mMorphs[0].mVertices.size()) - continue; - - if (val != 0) - { - for (unsigned int v=0; vmVertices[v] * val; - } - } - - if (mVertices.size() * sizeof(float)*3 != vbuf->getSizeInBytes()) - return; - - vbuf->writeData(0, vbuf->getSizeInBytes(), &mVertices[0]); - - mSubEntity->_markBuffersUsedForAnimation(); } }; @@ -629,13 +591,6 @@ class NIFObjectLoader std::cerr << "NIFObjectLoader: Warn: " << msg << std::endl; } - static void fail(const std::string &msg) - { - std::cerr << "NIFObjectLoader: Fail: "<< msg << std::endl; - abort(); - } - - static void createEntity(const std::string &name, const std::string &group, Ogre::SceneManager *sceneMgr, ObjectScenePtr scene, const Nif::Node *node, int flags, int animflags) @@ -693,7 +648,8 @@ class NIFObjectLoader Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : Ogre::ControllerValueRealPtr()); - Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(entity->getSubEntity(0), geom->data.getPtr())); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value( + entity, geom->data.getPtr(), geom->recIndex)); GeomMorpherController::Function* function = OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); From 5406a70fdd7337d1aad3b4d86d9fae2eac2301f4 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 28 Dec 2013 11:34:51 +0100 Subject: [PATCH 241/889] still working on npc check --- .../opencs/model/tools/referenceablecheck.cpp | 62 +++++++++++++++++-- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 40f7c26d2..8ab163fc5 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -833,9 +833,9 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI //Don't know what unknown is for int Gold(NPC.mNpdt52.mGold); - if (NPC.mNpdtType == 12) + if (NPC.mNpdtType == 12) //12 = autocalculated { - if (NPC.mFlags ^ 0x0008) + if (NPC.mFlags ^ 0x0008) //0x0008 = autocalculated flag { messages.push_back(id.toString() + "|" + NPC.mId + " mNpdtType and flags mismatch!"); //should not happend? return; @@ -851,18 +851,64 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI { if (NPC.mNpdt52.mHealth < 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " health is negative value"); + messages.push_back(id.toString() + "|" + NPC.mId + " health has negative value"); } if (NPC.mNpdt52.mMana < 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " mana is negative value"); + messages.push_back(id.toString() + "|" + NPC.mId + " mana has negative value"); } if (NPC.mNpdt52.mFatigue < 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " fatigue is negative value"); + messages.push_back(id.toString() + "|" + NPC.mId + " fatigue has negative value"); } + + if (NPC.mNpdt52.mAgility < 0) + { + messages.push_back(id.toString() + "|" + NPC.mId + " agility has negative value"); + } + + if (NPC.mNpdt52.mEndurance < 0) + { + messages.push_back(id.toString() + "|" + NPC.mId + " endurance has negative value"); + } + + if (NPC.mNpdt52.mIntelligence < 0) + { + messages.push_back(id.toString() + "|" + NPC.mId + " intelligence has negative value"); + } + + if (NPC.mNpdt52.mLuck < 0) + { + messages.push_back(id.toString() + "|" + NPC.mId + " luck has negative value"); + } + + if (NPC.mNpdt52.mPersonality < 0) + { + messages.push_back(id.toString() + "|" + NPC.mId + " personality has negative value"); + } + + if (NPC.mNpdt52.mStrength < 0) + { + messages.push_back(id.toString() + "|" + NPC.mId + " strength has negative value"); + } + + if (NPC.mNpdt52.mSpeed < 0) + { + messages.push_back(id.toString() + "|" + NPC.mId + " speed has negative value"); + } + + if (NPC.mNpdt52.mAgility < 0) + { + messages.push_back(id.toString() + "|" + NPC.mId + " agility has negative value"); + } + + if (NPC.mNpdt52.mWillpower < 0) + { + messages.push_back(id.toString() + "|" + NPC.mId + " willpower has negative value"); + } + } if (level < 1) @@ -884,6 +930,10 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI { messages.push_back(id.toString() + "|" + NPC.mId + " has any empty class"); } - + + if (NPC.mRace.empty()) + { + messages.push_back(id.toString() + "|" + NPC.mId + " has any empty race"); + } //TODO: reputation, Disposition, rank, everything else } From 9b73d231392b1e8a867e9f5627d9b6054f82b253 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sat, 28 Dec 2013 13:47:01 +0100 Subject: [PATCH 242/889] Fix warning about uninitialized variable inside stream.peak(): MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit openmw/mwgui/bookpage.cpp:394:13: warning: ‘*((void*)& stream +24)’ may be used uninitialized in this function [-Wmaybe-uninitialized] Signed-off-by: Lukasz Gromanowski --- components/misc/utf8stream.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/misc/utf8stream.hpp b/components/misc/utf8stream.hpp index 19a0688b2..5e9dde251 100644 --- a/components/misc/utf8stream.hpp +++ b/components/misc/utf8stream.hpp @@ -14,12 +14,12 @@ public: static UnicodeChar sBadChar () { return UnicodeChar (0xFFFFFFFF); } Utf8Stream (Point begin, Point end) : - cur (begin), nxt (begin), end (end) + cur (begin), nxt (begin), end (end), val(Utf8Stream::sBadChar()) { } Utf8Stream (std::pair range) : - cur (range.first), nxt (range.first), end (range.second) + cur (range.first), nxt (range.first), end (range.second), val(Utf8Stream::sBadChar()) { } From be6e47bcb6f40427a42bad1ec5f967be7cddbcfa Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 28 Dec 2013 15:51:38 +0100 Subject: [PATCH 243/889] Corrections --- .../opencs/model/tools/referenceablecheck.cpp | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 8ab163fc5..44dfbbef7 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -864,49 +864,44 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI messages.push_back(id.toString() + "|" + NPC.mId + " fatigue has negative value"); } - if (NPC.mNpdt52.mAgility < 0) + if (NPC.mNpdt52.mAgility == 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " agility has negative value"); + messages.push_back(id.toString() + "|" + NPC.mId + " agility has zero value"); } - if (NPC.mNpdt52.mEndurance < 0) + if (NPC.mNpdt52.mEndurance == 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " endurance has negative value"); + messages.push_back(id.toString() + "|" + NPC.mId + " endurance has zero value"); } - if (NPC.mNpdt52.mIntelligence < 0) + if (NPC.mNpdt52.mIntelligence == 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " intelligence has negative value"); + messages.push_back(id.toString() + "|" + NPC.mId + " intelligence has zero value"); } - if (NPC.mNpdt52.mLuck < 0) + if (NPC.mNpdt52.mLuck == 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " luck has negative value"); + messages.push_back(id.toString() + "|" + NPC.mId + " luck has zero value"); } - if (NPC.mNpdt52.mPersonality < 0) + if (NPC.mNpdt52.mPersonality == 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " personality has negative value"); + messages.push_back(id.toString() + "|" + NPC.mId + " personality has zero value"); } - if (NPC.mNpdt52.mStrength < 0) + if (NPC.mNpdt52.mStrength == 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " strength has negative value"); + messages.push_back(id.toString() + "|" + NPC.mId + " strength has zero value"); } - if (NPC.mNpdt52.mSpeed < 0) + if (NPC.mNpdt52.mSpeed == 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " speed has negative value"); + messages.push_back(id.toString() + "|" + NPC.mId + " speed has zero value"); } - if (NPC.mNpdt52.mAgility < 0) + if (NPC.mNpdt52.mWillpower == 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " agility has negative value"); - } - - if (NPC.mNpdt52.mWillpower < 0) - { - messages.push_back(id.toString() + "|" + NPC.mId + " willpower has negative value"); + messages.push_back(id.toString() + "|" + NPC.mId + " willpower has zero value"); } } From 100edda8c086f72599884bafd591d1bb260de76f Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sat, 28 Dec 2013 16:15:34 +0100 Subject: [PATCH 244/889] Fixes #417: Apply weather instantly when teleporting Change speed of weather transition from blight to other (twice fast as normal) and from other to blight (four times faster than normal). Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwworld/weather.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 8b05d2256..5641d3493 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -195,7 +195,22 @@ void WeatherManager::setWeather(const String& weather, bool instant) } mNextWeather = weather; - mRemainingTransitionTime = mWeatherSettings[mCurrentWeather].mTransitionDelta * 24.f * 3600; + + /** + * Issue #417: + * + * Change speed of weather transition from blight to other (twice fast as normal) + * and from other to blight (four times faster than normal) + */ + mRemainingTransitionTime = (mWeatherSettings[mCurrentWeather].mTransitionDelta * 24.f * 3600.f); + if (mCurrentWeather == "blight" && mNextWeather != "") + { + mRemainingTransitionTime *= 0.25f; + } + else if (mNextWeather == "blight") + { + mRemainingTransitionTime *= 0.5f; + } } mFirstUpdate = false; } From b9d1047ad640bb4a7b3c45d09f9a39fa36b3210f Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 28 Dec 2013 16:18:16 +0100 Subject: [PATCH 245/889] Small fix for lights. --- apps/opencs/model/tools/referenceablecheck.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 44dfbbef7..752a57f3e 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -694,12 +694,12 @@ void CSMTools::ReferenceableCheckStage::lightCheck(int stage, const CSMWorld::Re messages.push_back(id.toString() + "|" + Light.mId + " has no icon"); } - if (Light.mData.mWeight < 0) //probabbly needs to be checked only for carrable lights TODO + if (Light.mData.mWeight < 0) { messages.push_back(id.toString() + "|" + Light.mId + " has negative weight"); } - if (Light.mData.mValue < 0) //probabbly needs to be checked only for carrable lights TODO + if (Light.mData.mValue < 0) { messages.push_back(id.toString() + "|" + Light.mId + " has negative value"); } @@ -709,9 +709,9 @@ void CSMTools::ReferenceableCheckStage::lightCheck(int stage, const CSMWorld::Re messages.push_back(id.toString() + "|" + Light.mId + " has no model"); } - if (Light.mData.mTime < 0) + if (Light.mData.mTime == 0) { - messages.push_back(id.toString() + "|" + Light.mId + " has negative duration"); + messages.push_back(id.toString() + "|" + Light.mId + " has zero duration"); } } } @@ -930,5 +930,6 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI { messages.push_back(id.toString() + "|" + NPC.mId + " has any empty race"); } + //TODO: reputation, Disposition, rank, everything else } From 27b45dd3cde2be061459652b187c4f08e57a3524 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 28 Dec 2013 16:34:32 +0100 Subject: [PATCH 246/889] Remove unused input --- files/materials/terrain.shader | 1 - 1 file changed, 1 deletion(-) diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 3b80c3aec..86eef36ff 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -78,7 +78,6 @@ #endif shVertexInput(float2, uv0) - shVertexInput(float2, uv1) // lodDelta, lodThreshold #if LIGHTING shNormalInput(float4) From 2a8ab932efcd6c05887146561a44f6338799e21c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 28 Dec 2013 17:19:35 +0100 Subject: [PATCH 247/889] Bug #951: Only recalculate derived stats when attributes change --- apps/openmw/mwclass/creature.cpp | 16 +++---- apps/openmw/mwclass/npc.cpp | 23 +++++----- apps/openmw/mwgui/levelupdialog.cpp | 3 +- apps/openmw/mwmechanics/actors.cpp | 3 +- apps/openmw/mwmechanics/creaturestats.cpp | 42 +++++++++++++++---- apps/openmw/mwmechanics/creaturestats.hpp | 9 +++- .../mwmechanics/mechanicsmanagerimp.cpp | 21 +++++----- apps/openmw/mwscript/statsextensions.cpp | 23 +++++----- 8 files changed, 85 insertions(+), 55 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 2e7f00dd3..b225441aa 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -68,14 +68,14 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); // creature stats - data->mCreatureStats.getAttribute(0).set (ref->mBase->mData.mStrength); - data->mCreatureStats.getAttribute(1).set (ref->mBase->mData.mIntelligence); - data->mCreatureStats.getAttribute(2).set (ref->mBase->mData.mWillpower); - data->mCreatureStats.getAttribute(3).set (ref->mBase->mData.mAgility); - data->mCreatureStats.getAttribute(4).set (ref->mBase->mData.mSpeed); - data->mCreatureStats.getAttribute(5).set (ref->mBase->mData.mEndurance); - data->mCreatureStats.getAttribute(6).set (ref->mBase->mData.mPersonality); - data->mCreatureStats.getAttribute(7).set (ref->mBase->mData.mLuck); + data->mCreatureStats.setAttribute(ESM::Attribute::Strength, ref->mBase->mData.mStrength); + data->mCreatureStats.setAttribute(ESM::Attribute::Intelligence, ref->mBase->mData.mIntelligence); + data->mCreatureStats.setAttribute(ESM::Attribute::Willpower, ref->mBase->mData.mWillpower); + data->mCreatureStats.setAttribute(ESM::Attribute::Agility, ref->mBase->mData.mAgility); + data->mCreatureStats.setAttribute(ESM::Attribute::Speed, ref->mBase->mData.mSpeed); + data->mCreatureStats.setAttribute(ESM::Attribute::Endurance, ref->mBase->mData.mEndurance); + data->mCreatureStats.setAttribute(ESM::Attribute::Personality, ref->mBase->mData.mPersonality); + data->mCreatureStats.setAttribute(ESM::Attribute::Luck, ref->mBase->mData.mLuck); data->mCreatureStats.setHealth (ref->mBase->mData.mHealth); data->mCreatureStats.setMagicka (ref->mBase->mData.mMana); data->mCreatureStats.setFatigue (ref->mBase->mData.mFatigue); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index e8b9bfaf2..4187aa8c1 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -66,7 +66,7 @@ namespace for (int i=0; imData.mAttributeValues[i]; - creatureStats.getAttribute(i).setBase (male ? attribute.mMale : attribute.mFemale); + creatureStats.setAttribute(i, male ? attribute.mMale : attribute.mFemale); } // class bonus @@ -78,7 +78,7 @@ namespace int attribute = class_->mData.mAttribute[i]; if (attribute>=0 && attribute<8) { - creatureStats.getAttribute(attribute).setBase ( + creatureStats.setAttribute(attribute, creatureStats.getAttribute(attribute).getBase() + 10); } } @@ -109,7 +109,7 @@ namespace } modifierSum += add; } - creatureStats.getAttribute(attribute).setBase ( std::min(creatureStats.getAttribute(attribute).getBase() + creatureStats.setAttribute(attribute, std::min(creatureStats.getAttribute(attribute).getBase() + static_cast((level-1) * modifierSum+0.5), 100) ); } @@ -274,14 +274,15 @@ namespace MWClass for (unsigned int i=0; i< ESM::Skill::Length; ++i) data->mNpcStats.getSkill (i).setBase (ref->mBase->mNpdt52.mSkills[i]); - data->mNpcStats.getAttribute(0).set (ref->mBase->mNpdt52.mStrength); - data->mNpcStats.getAttribute(1).set (ref->mBase->mNpdt52.mIntelligence); - data->mNpcStats.getAttribute(2).set (ref->mBase->mNpdt52.mWillpower); - data->mNpcStats.getAttribute(3).set (ref->mBase->mNpdt52.mAgility); - data->mNpcStats.getAttribute(4).set (ref->mBase->mNpdt52.mSpeed); - data->mNpcStats.getAttribute(5).set (ref->mBase->mNpdt52.mEndurance); - data->mNpcStats.getAttribute(6).set (ref->mBase->mNpdt52.mPersonality); - data->mNpcStats.getAttribute(7).set (ref->mBase->mNpdt52.mLuck); + 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.setHealth (ref->mBase->mNpdt52.mHealth); data->mNpcStats.setMagicka (ref->mBase->mNpdt52.mMana); data->mNpcStats.setFatigue (ref->mBase->mNpdt52.mFatigue); diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 7c0191d49..a772b3a15 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -166,11 +166,12 @@ namespace MWGui // increase attributes for (int i=0; i<3; ++i) { - MWMechanics::Stat& attribute = creatureStats.getAttribute(mSpentAttributes[i]); + MWMechanics::Stat attribute = creatureStats.getAttribute(mSpentAttributes[i]); attribute.setBase (attribute.getBase () + pcStats.getLevelupAttributeMultiplier (mSpentAttributes[i])); if (attribute.getBase() >= 100) attribute.setBase(100); + creatureStats.setAttribute(mSpentAttributes[i], attribute); } creatureStats.levelUp(); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 22a641b34..1990292a7 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -54,7 +54,8 @@ namespace MWMechanics { // magic effects adjustMagicEffects (ptr); - calculateDynamicStats (ptr); + if (ptr.getClass().getCreatureStats(ptr).needToRecalcDynamicStats()) + calculateDynamicStats (ptr); calculateCreatureStatModifiers (ptr, duration); if(!MWBase::Environment::get().getWindowManager()->isGuiMode()) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index bfbd32ff0..95bd405b1 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -15,7 +15,7 @@ namespace MWMechanics mAttacked (false), mHostile (false), mAttackingOrSpell(false), mAttackType(AT_Chop), mIsWerewolf(false), - mFallHeight(0) + mFallHeight(0), mRecalcDynamicStats(false) { for (int i=0; i<4; ++i) mAiSettings[i] = 0; @@ -128,14 +128,6 @@ namespace MWMechanics return mAiSettings[index]; } - Stat &CreatureStats::getAttribute(int index) - { - if (index < 0 || index > 7) { - throw std::runtime_error("attribute index is out of range"); - } - return (!mIsWerewolf ? mAttributes[index] : mWerewolfAttributes[index]); - } - const DynamicStat &CreatureStats::getDynamic(int index) const { if (index < 0 || index > 2) { @@ -164,11 +156,29 @@ namespace MWMechanics return mMagicEffects; } + void CreatureStats::setAttribute(int index, int base) + { + MWMechanics::Stat current = getAttribute(index); + current.setBase(base); + setAttribute(index, current); + } + void CreatureStats::setAttribute(int index, const Stat &value) { if (index < 0 || index > 7) { throw std::runtime_error("attribute index is out of range"); } + + const Stat& currentValue = !mIsWerewolf ? mAttributes[index] : mWerewolfAttributes[index]; + + if (value.getModified() != currentValue.getModified()) + { + if (index != ESM::Attribute::Luck + && index != ESM::Attribute::Personality + && index != ESM::Attribute::Speed) + mRecalcDynamicStats = true; + } + if(!mIsWerewolf) mAttributes[index] = value; else @@ -218,6 +228,10 @@ namespace MWMechanics void CreatureStats::setMagicEffects(const MagicEffects &effects) { + if (effects.get(MWMechanics::EffectKey(ESM::MagicEffect::FortifyMaximumMagicka)).mMagnitude + != mMagicEffects.get(MWMechanics::EffectKey(ESM::MagicEffect::FortifyMaximumMagicka)).mMagnitude) + mRecalcDynamicStats = true; + mMagicEffects = effects; } @@ -369,4 +383,14 @@ namespace MWMechanics mFallHeight = 0; return height; } + + bool CreatureStats::needToRecalcDynamicStats() + { + if (mRecalcDynamicStats) + { + mRecalcDynamicStats = false; + return true; + } + return false; + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 5fc3a7ec6..4d9be0132 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -42,6 +42,9 @@ namespace MWMechanics std::string mLastHitObject; // The last object to hit this actor + // Do we need to recalculate stats derived from attributes or other factors? + bool mRecalcDynamicStats; + std::map mUsedPowers; protected: @@ -51,6 +54,8 @@ namespace MWMechanics public: CreatureStats(); + bool needToRecalcDynamicStats(); + void addToFallHeight(float height); /// Reset the fall height @@ -83,8 +88,6 @@ namespace MWMechanics int getAiSetting (int index) const; ///< 0: hello, 1 fight, 2 flee, 3 alarm - Stat & getAttribute(int index); - Spells & getSpells(); ActiveSpells & getActiveSpells(); @@ -92,6 +95,8 @@ namespace MWMechanics MagicEffects & getMagicEffects(); void setAttribute(int index, const Stat &value); + // Shortcut to set only the base + void setAttribute(int index, int base); void setHealth(const DynamicStat &value); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 1316baaeb..c084c86e5 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -31,15 +31,14 @@ namespace MWMechanics for (int i=0; i<27; ++i) npcStats.getSkill (i).setBase (player->mNpdt52.mSkills[i]); - creatureStats.getAttribute(0).setBase (player->mNpdt52.mStrength); - creatureStats.getAttribute(1).setBase (player->mNpdt52.mIntelligence); - creatureStats.getAttribute(2).setBase (player->mNpdt52.mWillpower); - creatureStats.getAttribute(3).setBase (player->mNpdt52.mAgility); - creatureStats.getAttribute(4).setBase (player->mNpdt52.mSpeed); - creatureStats.getAttribute(5).setBase (player->mNpdt52.mEndurance); - creatureStats.getAttribute(6).setBase (player->mNpdt52.mPersonality); - creatureStats.getAttribute(7).setBase (player->mNpdt52.mLuck); - + 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); const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); @@ -55,7 +54,7 @@ namespace MWMechanics { const ESM::Race::MaleFemale& attribute = race->mData.mAttributeValues[i]; - creatureStats.getAttribute(i).setBase (male ? attribute.mMale : attribute.mFemale); + creatureStats.setAttribute(i, male ? attribute.mMale : attribute.mFemale); } for (int i=0; i<27; ++i) @@ -106,7 +105,7 @@ namespace MWMechanics int attribute = class_->mData.mAttribute[i]; if (attribute>=0 && attribute<8) { - creatureStats.getAttribute(attribute).setBase ( + creatureStats.setAttribute(attribute, creatureStats.getAttribute(attribute).getBase() + 10); } } diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 6cd483ae1..dc6b1d4a7 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -124,10 +124,9 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWWorld::Class::get(ptr) - .getCreatureStats(ptr) - .getAttribute(mIndex) - .setModified (value, 0); + MWMechanics::Stat attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex); + attribute.setModified (value, 0); + ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); } }; @@ -147,16 +146,16 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - value += - MWWorld::Class::get(ptr) - .getCreatureStats(ptr) - .getAttribute(mIndex) - .getModified(); - - MWWorld::Class::get(ptr) + MWMechanics::Stat attribute = MWWorld::Class::get(ptr) .getCreatureStats(ptr) - .getAttribute(mIndex) + .getAttribute(mIndex); + + value += + attribute.getModified(); + + attribute .setModified (value, 0, 100); + ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); } }; From 147ee0ace306f2dbfb452f95ab9727aa634cac98 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 28 Dec 2013 17:35:04 +0100 Subject: [PATCH 248/889] small oops + added new variables to the constructor --- apps/opencs/model/tools/referenceablecheck.cpp | 6 ++++-- apps/opencs/model/tools/referenceablecheck.hpp | 6 ++++-- apps/opencs/model/tools/tools.cpp | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 752a57f3e..abf13ced9 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -9,8 +9,10 @@ #include "../world/universalid.hpp" -CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable) : +CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes) : mReferencables(referenceable), + mClasses(classes), + mRaces(races), mBooksSize(mReferencables.getBooks().getSize()), mActivatorsSize(mReferencables.getActivators().getSize()), mPotionsSize(mReferencables.getPotions().getSize()), @@ -913,7 +915,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI if (Gold < 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " gold is negative value"); + messages.push_back(id.toString() + "|" + NPC.mId + " gold has negative value"); } if (NPC.mName.empty()) diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index d4f7697ae..9e53f2b19 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -11,7 +11,7 @@ namespace CSMTools class ReferenceableCheckStage : public CSMDoc::Stage { public: - ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable); + ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes); virtual void perform(int stage, std::vector< std::string >& messages); virtual int setup(); @@ -34,7 +34,9 @@ namespace CSMTools void miscCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void npcCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); - const CSMWorld::RefIdData mReferencables; + const CSMWorld::RefIdData& mReferencables; + const CSMWorld::IdCollection mRaces; + const CSMWorld::IdCollection mClasses; //SIZES OF CONCRETE TYPES const int mBooksSize; diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index d8cbb2fc1..945fa191f 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -76,7 +76,7 @@ CSMDoc::Operation *CSMTools::Tools::getVerifier() mVerifier->appendStage (new SpellCheckStage (mData.getSpells())); - mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet())); + mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet()), mData.getRaces(), mData.getClasses()); } return mVerifier; From bf0383fe056472442228e4cc4518dc2f47ee2d69 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 28 Dec 2013 18:07:01 +0100 Subject: [PATCH 249/889] Last fixes. --- .../opencs/model/tools/referenceablecheck.cpp | 54 +++++++++++++++---- .../opencs/model/tools/referenceablecheck.hpp | 4 +- apps/opencs/model/tools/tools.cpp | 2 +- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index abf13ced9..a6fbe7f80 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -9,7 +9,7 @@ #include "../world/universalid.hpp" -CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes) : +CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes) : mReferencables(referenceable), mClasses(classes), mRaces(races), @@ -146,6 +146,12 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str } stage -= mMiscellaneousSize; + + if (stage < mNPCsSize) + { + npcCheck(stage, mReferencables.getNPCs(), messages); + return; + } } int CSMTools::ReferenceableCheckStage::setup() @@ -837,7 +843,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI if (NPC.mNpdtType == 12) //12 = autocalculated { - if (NPC.mFlags ^ 0x0008) //0x0008 = autocalculated flag + if (! NPC.mFlags & 0x0008) //0x0008 = autocalculated flag { messages.push_back(id.toString() + "|" + NPC.mId + " mNpdtType and flags mismatch!"); //should not happend? return; @@ -851,11 +857,6 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI } else { - if (NPC.mNpdt52.mHealth < 0) - { - messages.push_back(id.toString() + "|" + NPC.mId + " health has negative value"); - } - if (NPC.mNpdt52.mMana < 0) { messages.push_back(id.toString() + "|" + NPC.mId + " mana has negative value"); @@ -927,11 +928,46 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI { messages.push_back(id.toString() + "|" + NPC.mId + " has any empty class"); } - + else //checking if there is such class + { + bool nosuchclass(true); + + for (int i = 0; i < mClasses.getSize(); ++i) + { + if (mClasses.getRecord(i).get().mName == NPC.mClass) + { + nosuchclass = false; + break; + } + } + + if (nosuchclass) + { + messages.push_back(id.toString() + "|" + NPC.mId + " has invalid class"); + } + } + if (NPC.mRace.empty()) { messages.push_back(id.toString() + "|" + NPC.mId + " has any empty race"); } - + else //checking if there is a such race + { + bool nosuchrace(true); + + for (int i = 0; i < mRaces.getSize(); ++i) + { + if (mRaces.getRecord(i).get().mName == NPC.mRace) + { + nosuchrace = false; + break; + } + } + if (nosuchrace) + { + messages.push_back(id.toString() + "|" + NPC.mId + " has invalid race"); + } + } + //TODO: reputation, Disposition, rank, everything else } diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index 9e53f2b19..c87d80b0f 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -35,8 +35,8 @@ namespace CSMTools void npcCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); const CSMWorld::RefIdData& mReferencables; - const CSMWorld::IdCollection mRaces; - const CSMWorld::IdCollection mClasses; + const CSMWorld::IdCollection& mRaces; + const CSMWorld::IdCollection& mClasses; //SIZES OF CONCRETE TYPES const int mBooksSize; diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 945fa191f..15a7bd181 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -76,7 +76,7 @@ CSMDoc::Operation *CSMTools::Tools::getVerifier() mVerifier->appendStage (new SpellCheckStage (mData.getSpells())); - mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet()), mData.getRaces(), mData.getClasses()); + mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses())); } return mVerifier; From b50151cb38f3a4fa23ba887139ac878aaee932c3 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Sat, 28 Dec 2013 18:16:01 +0100 Subject: [PATCH 250/889] Quick build fix for windows --- components/files/windowspath.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index 5bb8a6a02..ea1ca56d3 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -69,12 +69,12 @@ boost::filesystem::path WindowsPath::getLocalPath() const boost::filesystem::path WindowsPath::getGlobalDataPath() const { - return getGlobalPath(); + return getGlobalConfigPath(); } boost::filesystem::path WindowsPath::getCachePath() const { - return getUserPath() / "cache"; + return getUserConfigPath() / "cache"; } boost::filesystem::path WindowsPath::getInstallPath() const From 17ff8165d24ff88d2808b76748c96267bcc104e8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 29 Dec 2013 00:36:36 +0100 Subject: [PATCH 251/889] Closes #1065: Don't apply fall damage when landing in water --- apps/openmw/mwmechanics/character.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 87a7875f5..2b93225e4 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -802,6 +802,9 @@ void CharacterController::update(float duration) if(sneak || inwater || flying) vec.z = 0.0f; + if (inwater || flying) + cls.getCreatureStats(mPtr).land(); + if(!onground && !flying && !inwater) { // In the air (either getting up —ascending part of jump— or falling). @@ -925,7 +928,7 @@ void CharacterController::update(float duration) } } - if (onground || inwater || flying) + if (onground) cls.getCreatureStats(mPtr).land(); if(movestate != CharState_None) From 04f9f7af56a2863d05c9ac33d3649b5b7298e4d8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 29 Dec 2013 00:58:48 +0100 Subject: [PATCH 252/889] Closes #990: Add option to unlock mouse cursor when in any menu --- apps/openmw/mwgui/settingswindow.cpp | 6 +++++- apps/openmw/mwgui/settingswindow.hpp | 1 + apps/openmw/mwinput/inputmanagerimp.cpp | 6 +++++- apps/openmw/mwinput/inputmanagerimp.hpp | 2 ++ files/mygui/openmw_settings_window.layout | 6 ++++++ files/settings-default.cfg | 2 ++ 6 files changed, 21 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 1969c5651..c99e2d0de 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -92,6 +92,7 @@ namespace MWGui { getWidget(mOkButton, "OkButton"); getWidget(mBestAttackButton, "BestAttackButton"); + getWidget(mGrabCursorButton, "GrabCursorButton"); getWidget(mSubtitlesButton, "SubtitlesButton"); getWidget(mCrosshairButton, "CrosshairButton"); getWidget(mResolutionList, "ResolutionList"); @@ -133,6 +134,7 @@ namespace MWGui mSubtitlesButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mCrosshairButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mBestAttackButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); + mGrabCursorButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mInvertYButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); mShadersButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShadersToggled); @@ -201,6 +203,7 @@ namespace MWGui mSubtitlesButton->setCaptionWithReplacing(Settings::Manager::getBool("subtitles", "GUI") ? "#{sOn}" : "#{sOff}"); mCrosshairButton->setCaptionWithReplacing(Settings::Manager::getBool("crosshair", "HUD") ? "#{sOn}" : "#{sOff}"); mBestAttackButton->setCaptionWithReplacing(Settings::Manager::getBool("best attack", "Game") ? "#{sOn}" : "#{sOff}"); + mGrabCursorButton->setCaptionWithReplacing(Settings::Manager::getBool("grab cursor", "Input") ? "#{sOn}" : "#{sOff}"); float fovVal = (Settings::Manager::getFloat("field of view", "General")-sFovMin)/(sFovMax-sFovMin); mFOVSlider->setScrollPosition(fovVal * (mFOVSlider->getScrollRange()-1)); @@ -393,7 +396,8 @@ namespace MWGui Settings::Manager::setBool("subtitles", "GUI", newState); else if (_sender == mBestAttackButton) Settings::Manager::setBool("best attack", "Game", newState); - + else if (_sender == mGrabCursorButton) + Settings::Manager::setBool("grab cursor", "Input", newState); apply(); } } diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index c81a86ab0..6b9ce414b 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -33,6 +33,7 @@ namespace MWGui MyGUI::Button* mSubtitlesButton; MyGUI::Button* mCrosshairButton; MyGUI::Button* mBestAttackButton; + MyGUI::Button* mGrabCursorButton; // graphics MyGUI::ListBox* mResolutionList; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 4713d92e1..ffb2af81e 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -103,6 +103,7 @@ namespace MWInput , mCameraSensitivity (Settings::Manager::getFloat("camera sensitivity", "Input")) , mUISensitivity (Settings::Manager::getFloat("ui sensitivity", "Input")) , mCameraYMultiplier (Settings::Manager::getFloat("camera y multiplier", "Input")) + , mGrabCursor (Settings::Manager::getBool("grab cursor", "Input")) , mPreviewPOVDelay(0.f) , mTimeIdle(0.f) , mOverencumberedMessageDelay(0.f) @@ -287,7 +288,7 @@ namespace MWInput mInputManager->setMouseRelative(is_relative); //we let the mouse escape in the main menu - mInputManager->setGrabPointer(grab); + mInputManager->setGrabPointer(grab && (mGrabCursor || is_relative)); //we switched to non-relative mode, move our cursor to where the in-game //cursor is @@ -430,6 +431,9 @@ namespace MWInput if (it->first == "Input" && it->second == "ui sensitivity") mUISensitivity = Settings::Manager::getFloat("ui sensitivity", "Input"); + if (it->first == "Input" && it->second == "grab cursor") + mGrabCursor = Settings::Manager::getBool("grab cursor", "Input"); + } } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 8efa6cfc5..d4693ff28 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -138,6 +138,8 @@ namespace MWInput bool mDragDrop; + bool mGrabCursor; + bool mInvertY; float mCameraSensitivity; diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index e6002b51d..61103963d 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -65,6 +65,12 @@
+ + + + + + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 94bdd8c9d..2ca59159b 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -156,6 +156,8 @@ voice volume = 1.0 [Input] +grab cursor = true + invert y axis = false camera sensitivity = 1.0 From c0dba2834b6fa566e990dcd0e575e1b43fd93827 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 29 Dec 2013 01:20:57 +0100 Subject: [PATCH 253/889] Closes #855: Don't try to look up bone if there's no skeleton --- components/nifogre/ogrenifloader.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 1e3598b33..345e7d6fd 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -1000,8 +1000,11 @@ class NIFObjectLoader { const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); - int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex); - extractTextKeys(tk, scene->mTextKeys[trgtid]); + if (scene->mSkelBase) + { + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex); + extractTextKeys(tk, scene->mTextKeys[trgtid]); + } } else if(e->recType == Nif::RC_NiStringExtraData) { From faf8011c48d0e5bf553ef3de08c2a474e95941e3 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 29 Dec 2013 12:47:44 +0100 Subject: [PATCH 254/889] Fixes #417: Apply weather instantly when teleporting Removed changing speed of weather transition introduced in previous commit. Instead try to detect player "teleporting" (ie. coc), and then switch instantly to the next weather type. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwworld/weather.cpp | 59 +++++++++++++++++++++++--------- apps/openmw/mwworld/weather.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 20 ++++++++++- apps/openmw/mwworld/worldimp.hpp | 2 +- 4 files changed, 64 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 5641d3493..4a8def184 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -195,22 +195,7 @@ void WeatherManager::setWeather(const String& weather, bool instant) } mNextWeather = weather; - - /** - * Issue #417: - * - * Change speed of weather transition from blight to other (twice fast as normal) - * and from other to blight (four times faster than normal) - */ - mRemainingTransitionTime = (mWeatherSettings[mCurrentWeather].mTransitionDelta * 24.f * 3600.f); - if (mCurrentWeather == "blight" && mNextWeather != "") - { - mRemainingTransitionTime *= 0.25f; - } - else if (mNextWeather == "blight") - { - mRemainingTransitionTime *= 0.5f; - } + mRemainingTransitionTime = mWeatherSettings[mCurrentWeather].mTransitionDelta * 24.f * 3600.f; } mFirstUpdate = false; } @@ -722,3 +707,45 @@ float WeatherManager::getWindSpeed() const { return mWindSpeed; } + +void WeatherManager::switchToNextWeather(bool instantly) +{ + const bool exterior = (MWBase::Environment::get().getWorld()->isCellExterior() || MWBase::Environment::get().getWorld()->isCellQuasiExterior()); + if (!exterior) + { + mRendering->sunDisable(false); + mRendering->skyDisable(); + mRendering->getSkyManager()->setLightningStrength(0.f); + stopSounds(true); + return; + } + + // Exterior + std::string regionstr = Misc::StringUtils::lowerCase(MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion); + + if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) + { + mCurrentRegion = regionstr; + mWeatherUpdateTime = mHoursBetweenWeatherChanges * 3600; + + std::string weatherType = "clear"; + + if (mRegionOverrides.find(regionstr) != mRegionOverrides.end()) + { + weatherType = mRegionOverrides[regionstr]; + } + else + { + // get weather probabilities for the current region + const ESM::Region *region = + MWBase::Environment::get().getWorld()->getStore().get().search (regionstr); + + if (region != 0) + { + weatherType = nextWeather(region); + } + } + + setWeather(weatherType, instantly); + } +} diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 80cbe0418..2111cac39 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -128,6 +128,7 @@ namespace MWWorld * @param ID of the weather setting to shift to */ void changeWeather(const std::string& region, const unsigned int id); + void switchToNextWeather(bool instantly = true); /** * Per-frame update diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 448211bc2..9ce4f47d2 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1287,7 +1287,7 @@ namespace MWWorld mRendering->playVideo(mFallback.getFallbackString("Movies_New_Game"), true); } - mWeatherManager->update (duration); + updateWeather(duration); mWorldScene->update (duration, paused); @@ -2244,4 +2244,22 @@ namespace MWWorld actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Invisibility); actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility); } + + void World::updateWeather(float duration) + { + static const float TELEPORTATION_STEP_THRESHOLD = 256.f; + static ESM::Position lastPlayerPos; + ESM::Position currentPos = mPlayer->getPlayer().getRefData().getPosition(); + + if (fabs(fabs(lastPlayerPos.pos[0]) - fabs(currentPos.pos[0])) >= TELEPORTATION_STEP_THRESHOLD + || fabs(fabs(lastPlayerPos.pos[1]) - fabs(currentPos.pos[1])) >= TELEPORTATION_STEP_THRESHOLD) + { + lastPlayerPos = currentPos; + mWeatherManager->switchToNextWeather(true); + } + else + { + mWeatherManager->update (duration); + } + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 5a51cb773..346c64d82 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -106,7 +106,7 @@ namespace MWWorld }; std::map mProjectiles; - + void updateWeather(float duration); int getDaysPerMonth (int month) const; void rotateObjectImp (const Ptr& ptr, Ogre::Vector3 rot, bool adjust); From c65f018760539e9a990782ad10fbf6c9f81908f3 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 29 Dec 2013 15:09:49 +0100 Subject: [PATCH 255/889] Fixes #417: Apply weather instantly when teleporting Corrected constant name. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwworld/worldimp.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9ce4f47d2..18dc77d3a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2247,12 +2247,12 @@ namespace MWWorld void World::updateWeather(float duration) { - static const float TELEPORTATION_STEP_THRESHOLD = 256.f; + static const float teleportationStepTreshold = 256.f; static ESM::Position lastPlayerPos; ESM::Position currentPos = mPlayer->getPlayer().getRefData().getPosition(); - if (fabs(fabs(lastPlayerPos.pos[0]) - fabs(currentPos.pos[0])) >= TELEPORTATION_STEP_THRESHOLD - || fabs(fabs(lastPlayerPos.pos[1]) - fabs(currentPos.pos[1])) >= TELEPORTATION_STEP_THRESHOLD) + if (fabs(fabs(lastPlayerPos.pos[0]) - fabs(currentPos.pos[0])) >= teleportationStepTreshold + || fabs(fabs(lastPlayerPos.pos[1]) - fabs(currentPos.pos[1])) >= teleportationStepTreshold) { lastPlayerPos = currentPos; mWeatherManager->switchToNextWeather(true); From 9df6d23afcc929bd93368a9e553d45dea611050d Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 29 Dec 2013 20:15:00 +0100 Subject: [PATCH 256/889] removing member size values, since this does not work properly. --- .../opencs/model/tools/referenceablecheck.cpp | 113 ++++++++++-------- .../opencs/model/tools/referenceablecheck.hpp | 18 --- 2 files changed, 64 insertions(+), 67 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index a6fbe7f80..79f6009da 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -12,145 +12,159 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes) : mReferencables(referenceable), mClasses(classes), - mRaces(races), - mBooksSize(mReferencables.getBooks().getSize()), - mActivatorsSize(mReferencables.getActivators().getSize()), - mPotionsSize(mReferencables.getPotions().getSize()), - mApparatiSize(mReferencables.getApparati().getSize()), - mArmorsSzie(mReferencables.getArmors().getSize()), - mClothingSize(mReferencables.getClothing().getSize()), - mContainersSize(mReferencables.getContainers().getSize()), - mCreaturesSize(mReferencables.getCreatures().getSize()), - mDoorsSize(mReferencables.getDoors().getSize()), - mIngredientsSize(mReferencables.getIngredients().getSize()), - mCreaturesLevListsSize(mReferencables.getCreatureLevelledLists().getSize()), - mItemLevelledListsSize(mReferencables.getItemLevelledList().getSize()), - mLightsSize(mReferencables.getLights().getSize()), - mLockpicksSize(mReferencables.getLocpicks().getSize()), - mMiscellaneousSize(mReferencables.getMiscellaneous().getSize()), - mNPCsSize(mReferencables.getNPCs().getSize()) + mRaces(races) { } void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::string >& messages) { //Checks for books, than, when stage is above mBooksSize goes to other checks, with (stage - PrevSum) as stage. - if (stage < mBooksSize) + const int BookSize(mReferencables.getBooks().getSize()); + + if (stage < BookSize) { bookCheck(stage, mReferencables.getBooks(), messages); return; } - stage -= mBooksSize; + stage -= BookSize; - if (stage < mActivatorsSize) + const int ActivatorSize(mReferencables.getActivators().getSize()); + + if (stage < ActivatorSize) { activatorCheck(stage, mReferencables.getActivators(), messages); return; } - stage -= mActivatorsSize; + stage -= ActivatorSize; - if (stage < mPotionsSize) + const int PotionSize(mReferencables.getActivators().getSize()); + + if (stage < PotionSize) { potionCheck(stage, mReferencables.getPotions(), messages); return; } - stage -= mPotionsSize; + stage -= PotionSize; - if (stage < mApparatiSize) + const int ApparatusSize(mReferencables.getApparati().getSize()); + + if (stage < ApparatusSize) { apparatusCheck(stage, mReferencables.getApparati(), messages); return; } - stage -= mApparatiSize; + stage -= ApparatusSize; - if (stage < mArmorsSzie) + const int ArmorSize(mReferencables.getArmors().getSize()); + + if (stage < ArmorSize) { armorCheck(stage, mReferencables.getArmors(), messages); return; } - stage -= mArmorsSzie; + stage -= ArmorSize; - if (stage < mClothingSize) + const int ClothingSize(mReferencables.getClothing().getSize()); + + if (stage < ClothingSize) { clothingCheck(stage, mReferencables.getClothing(), messages); return; } - stage -= mClothingSize; + stage -= ClothingSize; - if (stage < mContainersSize) + const int ContainerSize(mReferencables.getContainers().getSize()); + + if (stage < ContainerSize) { containerCheck(stage, mReferencables.getContainers(), messages); return; } - stage -= mContainersSize; + stage -= ContainerSize; - if (stage < mDoorsSize) + const int DoorSize(mReferencables.getDoors().getSize()); + + if (stage < DoorSize) { doorCheck(stage, mReferencables.getDoors(), messages); return; } - stage -= mDoorsSize; + stage -= DoorSize; - if (stage < mIngredientsSize) + const int IngredientSize(mReferencables.getIngredients().getSize()); + + if (stage < IngredientSize) { ingredientCheck(stage, mReferencables.getIngredients(), messages); return; } - stage -= mIngredientsSize; + stage -= IngredientSize; - if (stage < mCreaturesLevListsSize) + const int CreatureLevListSize(mReferencables.getCreatureLevelledLists().getSize()); + + if (stage < CreatureLevListSize) { creaturesLevListCheck(stage, mReferencables.getCreatureLevelledLists(), messages); return; } - stage -= mCreaturesLevListsSize; + stage -= CreatureLevListSize; - if (stage < mItemLevelledListsSize) + const int ItemLevelledListSize(mReferencables.getItemLevelledList().getSize()); + + if (stage < ItemLevelledListSize) { itemLevelledListCheck(stage, mReferencables.getItemLevelledList(), messages); return; } - stage -= mItemLevelledListsSize; + stage -= ItemLevelledListSize; - if (stage < mLightsSize) + const int LightSize(mReferencables.getLights().getSize()); + + if (stage < LightSize) { lightCheck(stage, mReferencables.getLights(), messages); return; } - stage -= mLightsSize; + stage -= LightSize; - if (stage < mLockpicksSize) + const int LockpickSize(mReferencables.getLocpicks().getSize()); + + if (stage < LockpickSize) { lockpickCheck(stage, mReferencables.getLocpicks(), messages); return; } - stage -= mLockpicksSize; + stage -= LockpickSize; - if (stage < mMiscellaneousSize) + const int MiscSize(mReferencables.getMiscellaneous().getSize()); + + if (stage < MiscSize) { miscCheck(stage, mReferencables.getMiscellaneous(), messages); return; } - stage -= mMiscellaneousSize; - - if (stage < mNPCsSize) + stage -= MiscSize; + + const int NPCSize(mReferencables.getNPCs().getSize()); + + if (stage < NPCSize) { - npcCheck(stage, mReferencables.getNPCs(), messages); - return; + npcCheck(stage, mReferencables.getNPCs(), messages); + return; } } @@ -963,6 +977,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI break; } } + if (nosuchrace) { messages.push_back(id.toString() + "|" + NPC.mId + " has invalid race"); diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index c87d80b0f..1d85dc310 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -37,24 +37,6 @@ namespace CSMTools const CSMWorld::RefIdData& mReferencables; const CSMWorld::IdCollection& mRaces; const CSMWorld::IdCollection& mClasses; - - //SIZES OF CONCRETE TYPES - const int mBooksSize; - const int mActivatorsSize; - const int mPotionsSize; - const int mApparatiSize; - const int mArmorsSzie; - const int mClothingSize; - const int mContainersSize; - const int mCreaturesSize; - const int mDoorsSize; - const int mIngredientsSize; - const int mCreaturesLevListsSize; - const int mItemLevelledListsSize; - const int mLightsSize; - const int mLockpicksSize; - const int mMiscellaneousSize; - const int mNPCsSize; }; } #endif // REFERENCEABLECHECKSTAGE_H From e321d571e1ff72456aab8a78907489022576a248 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 29 Dec 2013 21:02:53 +0100 Subject: [PATCH 257/889] Added faction check. --- .../opencs/model/tools/referenceablecheck.cpp | 35 +++++++++++++++++-- .../opencs/model/tools/referenceablecheck.hpp | 3 +- apps/opencs/model/tools/tools.cpp | 2 +- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 79f6009da..5406ca08e 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -9,10 +9,11 @@ #include "../world/universalid.hpp" -CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes) : +CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& faction) : mReferencables(referenceable), mClasses(classes), - mRaces(races) + mRaces(races), + mFactions(faction) { } @@ -857,7 +858,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI if (NPC.mNpdtType == 12) //12 = autocalculated { - if (! NPC.mFlags & 0x0008) //0x0008 = autocalculated flag + if (NPC.mFlags & 0x0008 == 0) //0x0008 = autocalculated flag { messages.push_back(id.toString() + "|" + NPC.mId + " mNpdtType and flags mismatch!"); //should not happend? return; @@ -984,5 +985,33 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI } } + if (Disposition < 0) + { + messages.push_back(id.toString() + "|" + NPC.mId + " has negative disposition"); + } + + if (Reputation < 0) + { + messages.push_back(id.toString() + "|" + NPC.mId + " has negative reputation"); + } + + if(!NPC.mFaction.empty()) + { + bool nosuchfaction(true); + + for (int i = 0; i < mRaces.getSize(); ++i) + { + if (mFactions.getRecord(i).get().mName == NPC.mFaction) + { + nosuchfaction = false; + break; + } + } + + if (nosuchfaction) + { + messages.push_back(id.toString() + "|" + NPC.mId + " has invalid faction"); + } + } //TODO: reputation, Disposition, rank, everything else } diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index 1d85dc310..591e3d335 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -11,7 +11,7 @@ namespace CSMTools class ReferenceableCheckStage : public CSMDoc::Stage { public: - ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes); + ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& factions); virtual void perform(int stage, std::vector< std::string >& messages); virtual int setup(); @@ -37,6 +37,7 @@ namespace CSMTools const CSMWorld::RefIdData& mReferencables; const CSMWorld::IdCollection& mRaces; const CSMWorld::IdCollection& mClasses; + const CSMWorld::IdCollection& mFactions; }; } #endif // REFERENCEABLECHECKSTAGE_H diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 15a7bd181..b5320ff9f 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -76,7 +76,7 @@ CSMDoc::Operation *CSMTools::Tools::getVerifier() mVerifier->appendStage (new SpellCheckStage (mData.getSpells())); - mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses())); + mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions())); } return mVerifier; From 13637e7166825a1c9dce6f06614b7b5e214b0ea0 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 29 Dec 2013 21:45:09 +0100 Subject: [PATCH 258/889] Dynamic_casting, checking rank. Commented out faction check, since it will not work. --- .../opencs/model/tools/referenceablecheck.cpp | 72 ++++++++++--------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 5406ca08e..010fb868c 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -183,7 +183,7 @@ void CSMTools::ReferenceableCheckStage::bookCheck(int stage, const CSMWorld::Ref return; } - const ESM::Book& Book = (static_cast& >(baserecord)).get(); + const ESM::Book& Book = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Book, Book.mId); //Checking for name @@ -232,7 +232,7 @@ void CSMTools::ReferenceableCheckStage::activatorCheck(int stage, const CSMWorld return; } - const ESM::Activator& Activator = (static_cast& >(baserecord)).get(); + const ESM::Activator& Activator = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Activator, Activator.mId); //Checking for model, IIRC all activators should have a model @@ -251,7 +251,7 @@ void CSMTools::ReferenceableCheckStage::potionCheck(int stage, const CSMWorld::R return; } - const ESM::Potion& Potion = (static_cast& >(baserecord)).get(); + const ESM::Potion& Potion = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, Potion.mId); //Checking for name @@ -297,7 +297,7 @@ void CSMTools::ReferenceableCheckStage::apparatusCheck(int stage, const CSMWorld return; } - const ESM::Apparatus& Apparatus = (static_cast& >(baserecord)).get(); + const ESM::Apparatus& Apparatus = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Apparatus, Apparatus.mId); //Checking for name @@ -346,7 +346,7 @@ void CSMTools::ReferenceableCheckStage::armorCheck(int stage, const CSMWorld::Re return; } - const ESM::Armor& Armor = (static_cast& >(baserecord)).get(); + const ESM::Armor& Armor = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Armor, Armor.mId); //Checking for name @@ -407,7 +407,7 @@ void CSMTools::ReferenceableCheckStage::clothingCheck(int stage, const CSMWorld: return; } - const ESM::Clothing& Clothing = (static_cast& >(baserecord)).get(); + const ESM::Clothing& Clothing = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Clothing, Clothing.mId); //Checking for name @@ -456,7 +456,7 @@ void CSMTools::ReferenceableCheckStage::containerCheck(int stage, const CSMWorld return; } - const ESM::Container& Container = (static_cast& >(baserecord)).get(); + const ESM::Container& Container = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Container, Container.mId); //Checking for model, IIRC all containers should have a model @@ -487,7 +487,7 @@ void CSMTools::ReferenceableCheckStage::creatureCheck(int stage, const CSMWorld: return; } - const ESM::Creature& Creature = (static_cast&>(baserecord)).get(); + const ESM::Creature& Creature = (dynamic_cast&>(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Creature, Creature.mId); if (Creature.mModel.empty()) @@ -581,7 +581,7 @@ void CSMTools::ReferenceableCheckStage::doorCheck(int stage, const CSMWorld::Ref return; } - const ESM::Door& Door = (static_cast&>(baserecord)).get(); + const ESM::Door& Door = (dynamic_cast&>(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Door, Door.mId); //usual, name and model @@ -607,7 +607,7 @@ void CSMTools::ReferenceableCheckStage::ingredientCheck(int stage, const CSMWorl return; } - const ESM::Ingredient& Ingredient = (static_cast& >(baserecord)).get(); + const ESM::Ingredient& Ingredient = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Ingredient, Ingredient.mId); //Checking for name @@ -650,7 +650,7 @@ void CSMTools::ReferenceableCheckStage::creaturesLevListCheck(int stage, const C return; } - const ESM::CreatureLevList& CreatureLevList = (static_cast& >(baserecord)).get(); + const ESM::CreatureLevList& CreatureLevList = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_CreatureLevelledList, CreatureLevList.mId); //CreatureLevList but Type_CreatureLevelledList :/ for (unsigned i = 0; i < CreatureLevList.mList.size(); ++i) @@ -676,7 +676,7 @@ void CSMTools::ReferenceableCheckStage::itemLevelledListCheck(int stage, const C return; } - const ESM::ItemLevList& ItemLevList = (static_cast& >(baserecord)).get(); + const ESM::ItemLevList& ItemLevList = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_ItemLevelledList, ItemLevList.mId); for (unsigned i = 0; i < ItemLevList.mList.size(); ++i) @@ -702,7 +702,7 @@ void CSMTools::ReferenceableCheckStage::lightCheck(int stage, const CSMWorld::Re return; } - const ESM::Light& Light = (static_cast& >(baserecord)).get(); + const ESM::Light& Light = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Light, Light.mId); if (Light.mData.mRadius < 0) @@ -748,7 +748,7 @@ void CSMTools::ReferenceableCheckStage::lockpickCheck(int stage, const CSMWorld: return; } - const ESM::Lockpick& Lockpick = (static_cast& >(baserecord)).get(); + const ESM::Lockpick& Lockpick = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Lockpick, Lockpick.mId); //Checking for name @@ -801,7 +801,7 @@ void CSMTools::ReferenceableCheckStage::miscCheck(int stage, const CSMWorld::Ref return; } - const ESM::Miscellaneous& Miscellaneous = (static_cast& >(baserecord)).get(); + const ESM::Miscellaneous& Miscellaneous = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Miscellaneous, Miscellaneous.mId); //Checking for name @@ -844,7 +844,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI return; } - const ESM::NPC& NPC = (static_cast& >(baserecord)).get(); + const ESM::NPC& NPC = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Npc, NPC.mId); @@ -858,7 +858,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI if (NPC.mNpdtType == 12) //12 = autocalculated { - if (NPC.mFlags & 0x0008 == 0) //0x0008 = autocalculated flag + if ((NPC.mFlags & 0x0008) == 0) //0x0008 = autocalculated flag { messages.push_back(id.toString() + "|" + NPC.mId + " mNpdtType and flags mismatch!"); //should not happend? return; @@ -949,7 +949,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI for (int i = 0; i < mClasses.getSize(); ++i) { - if (mClasses.getRecord(i).get().mName == NPC.mClass) + if (dynamic_cast(mClasses.getRecord(i).get()).mId == NPC.mClass) { nosuchclass = false; break; @@ -972,7 +972,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI for (int i = 0; i < mRaces.getSize(); ++i) { - if (mRaces.getRecord(i).get().mName == NPC.mRace) + if (dynamic_cast(mRaces.getRecord(i).get()).mName == NPC.mRace) { nosuchrace = false; break; @@ -995,23 +995,31 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI messages.push_back(id.toString() + "|" + NPC.mId + " has negative reputation"); } - if(!NPC.mFaction.empty()) + if (!NPC.mFaction.empty()) { - bool nosuchfaction(true); - - for (int i = 0; i < mRaces.getSize(); ++i) + if (Rank < 0) { - if (mFactions.getRecord(i).get().mName == NPC.mFaction) - { - nosuchfaction = false; - break; - } + messages.push_back(id.toString() + "|" + NPC.mId + " has negative rank"); } - if (nosuchfaction) - { - messages.push_back(id.toString() + "|" + NPC.mId + " has invalid faction"); - } + //This code does not work at the moment. nosuchfaction is true for every npc record + /* + bool nosuchfaction(true); + for (int i = 0; i < mRaces.getSize(); ++i) + { + if (dynamic_cast(mFactions.getRecord(i).get()).mName == NPC.mFaction) + { + nosuchfaction = false; + break; + } + } + + if (nosuchfaction) + { + messages.push_back(id.toString() + "|" + NPC.mId + " has invalid faction"); + } + */ } + //TODO: reputation, Disposition, rank, everything else } From 842e26b8e5bb17a76298308ac3c4565964b3c891 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 29 Dec 2013 21:53:33 +0100 Subject: [PATCH 259/889] added comment --- apps/opencs/model/tools/referenceablecheck.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 010fb868c..d2053c1a6 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -972,7 +972,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI for (int i = 0; i < mRaces.getSize(); ++i) { - if (dynamic_cast(mRaces.getRecord(i).get()).mName == NPC.mRace) + if (dynamic_cast(mRaces.getRecord(i).get()).mName == NPC.mRace) //mId in class, mName for race. Stupid. { nosuchrace = false; break; @@ -990,7 +990,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI messages.push_back(id.toString() + "|" + NPC.mId + " has negative disposition"); } - if (Reputation < 0) + if (Reputation < 0) //It seems that no character in Morrowind.esm have negative reputation. I'm assuming that negative reputation is invalid { messages.push_back(id.toString() + "|" + NPC.mId + " has negative reputation"); } From 60fb75b03ab54494b52d0b07a17836d97e0d7c8f Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 29 Dec 2013 21:58:55 +0100 Subject: [PATCH 260/889] Fixed valgrind warning about uninitialized variable: ==16814== Conditional jump or move depends on uninitialised value(s) ==16814== at 0xA945B8: Terrain::QuadTreeNode::update(Ogre::Vector3 const&, Loading::Listener*) (quadtreenode.cpp:269) ==16814== by 0xA94A77: Terrain::QuadTreeNode::update(Ogre::Vector3 const&, Loading::Listener*) (quadtreenode.cpp:354) ==16814== by 0xA77541: Terrain::World::update(Ogre::Vector3 const&) (world.cpp:159) ==16814== by 0x6EBA17: MWRender::RenderingManager::requestMap(MWWorld::CellStore*) (renderingmanager.cpp:649) ==16814== by 0x8A25C4: MWWorld::Scene::loadCell(MWWorld::CellStore*, Loading::Listener*) (scene.cpp:157) ==16814== by 0x8A2CEA: MWWorld::Scene::changeCell(int, int, ESM::Position const&, bool) (scene.cpp:296) ==16814== by 0x8A2DE0: MWWorld::Scene::changeToExteriorCell(ESM::Position const&) (scene.cpp:440) ==16814== by 0x85AC17: MWWorld::World::changeToExteriorCell(ESM::Position const&) (worldimp.cpp:761) ==16814== by 0x927E38: OMW::Engine::prepareEngine(Settings::Manager&) (engine.cpp:436) ==16814== by 0x92843D: OMW::Engine::go() (engine.cpp:483) ==16814== by 0x6C6B3F: main (main.cpp:279) ==16814== Uninitialised value was created by a heap allocation ==16814== at 0x4C27CC2: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==16814== by 0xA93E60: Terrain::QuadTreeNode::createChild(Terrain::ChildDirection, float, Ogre::Vector2 const&) (quadtreenode.cpp:178) ==16814== by 0xA7733E: Terrain::World::buildQuadTree(Terrain::QuadTreeNode*) (world.cpp:139) ==16814== by 0xA76B18: Terrain::World::World(Loading::Listener*, Ogre::SceneManager*, Terrain::Storage*, int, bool, bool) (world.cpp:94) ==16814== by 0x6EC6EB: MWRender::RenderingManager::enableTerrain(bool) (renderingmanager.cpp:1013) ==16814== by 0x8A2A00: MWWorld::Scene::changeCell(int, int, ESM::Position const&, bool) (scene.cpp:206) ==16814== by 0x8A2DE0: MWWorld::Scene::changeToExteriorCell(ESM::Position const&) (scene.cpp:440) ==16814== by 0x85AC17: MWWorld::World::changeToExteriorCell(ESM::Position const&) (worldimp.cpp:761) ==16814== by 0x927E38: OMW::Engine::prepareEngine(Settings::Manager&) (engine.cpp:436) ==16814== by 0x92843D: OMW::Engine::go() (engine.cpp:483) ==16814== by 0x6C6B3F: main (main.cpp:279) Signed-off-by: Lukasz Gromanowski --- components/terrain/quadtreenode.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index e16ae55dd..02225cb02 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -140,17 +140,19 @@ namespace } QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const Ogre::Vector2 ¢er, QuadTreeNode* parent) - : mSize(size) - , mCenter(center) - , mParent(parent) - , mDirection(dir) + : mMaterialGenerator(NULL) + , mIsActive(false) , mIsDummy(false) - , mSceneNode(NULL) - , mTerrain(terrain) - , mChunk(NULL) - , mMaterialGenerator(NULL) + , mSize(size) + , mLodLevel(Log2(mSize)) , mBounds(Ogre::AxisAlignedBox::BOX_NULL) , mWorldBounds(Ogre::AxisAlignedBox::BOX_NULL) + , mDirection(dir) + , mCenter(center) + , mSceneNode(NULL) + , mParent(parent) + , mTerrain(terrain) + , mChunk(NULL) { mBounds.setNull(); for (int i=0; i<4; ++i) @@ -168,8 +170,6 @@ QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const pos = mCenter - pos; mSceneNode->setPosition(Ogre::Vector3(pos.x*8192, pos.y*8192, 0)); - mLodLevel = Log2(mSize); - mMaterialGenerator = new MaterialGenerator(mTerrain->getShadersEnabled()); } From 2f082ef7960b6d5f9cc7aeb322d93e1a59581c70 Mon Sep 17 00:00:00 2001 From: Scott Howard Date: Sun, 29 Dec 2013 16:25:49 -0500 Subject: [PATCH 261/889] remove unneeded libraries --- CMakeLists.txt | 2 +- apps/launcher/CMakeLists.txt | 5 ----- apps/opencs/CMakeLists.txt | 2 +- apps/openmw/CMakeLists.txt | 10 +++------- cmake/FindBullet.cmake | 5 +---- 5 files changed, 6 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f6014dff6..ee4a0246b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -212,7 +212,7 @@ if (HAVE_UNORDERED_MAP) endif () -set(BOOST_COMPONENTS system filesystem program_options thread date_time wave) +set(BOOST_COMPONENTS system filesystem program_options) IF(BOOST_STATIC) set(Boost_USE_STATIC_LIBS ON) diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index 18c555a24..e4638c31b 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -137,8 +137,3 @@ if (BUILD_WITH_CODE_COVERAGE) target_link_libraries(omwlauncher gcov) endif() -# Workaround for binutil => 2.23 problem when linking, should be fixed eventually upstream -if (UNIX AND NOT APPLE) -target_link_libraries(omwlauncher dl Xt) -endif() - diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 1197e2014..e2dffdbde 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -143,7 +143,7 @@ if(WIN32) set(QT_USE_QTMAIN TRUE) endif(WIN32) -find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork QtXml QtXmlPatterns REQUIRED) +find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork REQUIRED) include(${QT_USE_FILE}) qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI}) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index e87da97c3..57773507f 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -82,6 +82,8 @@ add_openmw_dir (mwbase ) # Main executable +set(BOOST_COMPONENTS system filesystem program_options thread wave) +find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) IF(OGRE_STATIC) ADD_DEFINITIONS(-DENABLE_PLUGIN_OctreeSceneManager -DENABLE_PLUGIN_ParticleFX -DENABLE_PLUGIN_GL) @@ -111,6 +113,7 @@ add_definitions(${SOUND_DEFINE}) target_link_libraries(openmw ${OGRE_LIBRARIES} ${OGRE_STATIC_PLUGINS} + ${SHINY_LIBRARIES} ${Boost_LIBRARIES} ${OPENAL_LIBRARY} ${SOUND_INPUT_LIBRARY} @@ -118,7 +121,6 @@ target_link_libraries(openmw ${MYGUI_LIBRARIES} ${SDL2_LIBRARY} ${MYGUI_PLATFORM_LIBRARIES} - ${SHINY_LIBRARIES} "oics" "sdl4ogre" components @@ -137,12 +139,6 @@ if (UNIX AND NOT APPLE) target_link_libraries(openmw ${CMAKE_THREAD_LIBS_INIT}) endif() -# Workaround for binutil => 2.23 problem when linking, should be fixed eventually upstream -if (UNIX AND NOT APPLE) -target_link_libraries(openmw dl Xt) -endif() - - if(APPLE) find_library(COCOA_FRAMEWORK Cocoa) find_library(IOKIT_FRAMEWORK IOKit) diff --git a/cmake/FindBullet.cmake b/cmake/FindBullet.cmake index 97feddffe..b75f3105a 100644 --- a/cmake/FindBullet.cmake +++ b/cmake/FindBullet.cmake @@ -60,8 +60,6 @@ _FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY BulletCollision) _FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY_DEBUG BulletCollision_Debug BulletCollision_d) _FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY BulletMath LinearMath) _FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY_DEBUG BulletMath_Debug BulletMath_d LinearMath_debug LinearMath_d) -_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY BulletSoftBody) -_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY_DEBUG BulletSoftBody_Debug BulletSoftBody_d) # handle the QUIETLY and REQUIRED arguments and set BULLET_FOUND to TRUE if @@ -69,12 +67,11 @@ _FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY_DEBUG BulletSoftBody_Debug BulletS include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Bullet DEFAULT_MSG BULLET_DYNAMICS_LIBRARY BULLET_COLLISION_LIBRARY BULLET_MATH_LIBRARY - BULLET_SOFTBODY_LIBRARY BULLET_INCLUDE_DIR) + BULLET_INCLUDE_DIR) set(BULLET_INCLUDE_DIRS ${BULLET_INCLUDE_DIR}) if(BULLET_FOUND) _BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_DYNAMICS_LIBRARY) _BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_COLLISION_LIBRARY) _BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_MATH_LIBRARY) - _BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_SOFTBODY_LIBRARY) endif() From c89608f390c1d82a749aef448240481bc80afaff Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 30 Dec 2013 11:39:03 +0100 Subject: [PATCH 262/889] Check for head and hair. Correct faction check. --- .../opencs/model/tools/referenceablecheck.cpp | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index d2053c1a6..ad52c5962 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -995,30 +995,38 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI messages.push_back(id.toString() + "|" + NPC.mId + " has negative reputation"); } - if (!NPC.mFaction.empty()) + if (NPC.mFaction.empty() == false) { if (Rank < 0) { messages.push_back(id.toString() + "|" + NPC.mId + " has negative rank"); } - //This code does not work at the moment. nosuchfaction is true for every npc record - /* - bool nosuchfaction(true); - for (int i = 0; i < mRaces.getSize(); ++i) - { - if (dynamic_cast(mFactions.getRecord(i).get()).mName == NPC.mFaction) - { - nosuchfaction = false; - break; - } - } + bool nosuchfaction(true); - if (nosuchfaction) - { - messages.push_back(id.toString() + "|" + NPC.mId + " has invalid faction"); - } - */ + for (int i = 0; i < mFactions.getSize(); ++i) + { + if (dynamic_cast(mFactions.getRecord(i).get()).mId == NPC.mFaction) + { + nosuchfaction = false; + break; + } + } + + if (nosuchfaction) + { + messages.push_back(id.toString() + "|" + NPC.mId + " has invalid faction"); + } + } + + if (NPC.mHead.empty()) + { + messages.push_back(id.toString() + "|" + NPC.mId + " has no head"); + } + + if (NPC.mHair.empty()) + { + messages.push_back(id.toString() + "|" + NPC.mId + " has no har"); } //TODO: reputation, Disposition, rank, everything else From 3758fe38340ed147e0d73a2b8f8df0d886e8624e Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 30 Dec 2013 13:23:16 +0100 Subject: [PATCH 263/889] reformatted --- .../opencs/model/tools/referenceablecheck.hpp | 22 +-- apps/opencs/model/tools/tools.cpp | 94 +++++------ apps/opencs/model/world/refidcollection.hpp | 48 +++--- apps/opencs/model/world/refiddata.hpp | 147 +++++++++--------- 4 files changed, 157 insertions(+), 154 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index 591e3d335..fcf774f14 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -16,7 +16,7 @@ namespace CSMTools virtual int setup(); private: - //CONCRETE CHECKS + //CONCRETE CHECKS void bookCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Book >& records, std::vector< std::string >& messages); void activatorCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Activator >& records, std::vector< std::string >& messages); void potionCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); @@ -26,18 +26,18 @@ namespace CSMTools void containerCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void creatureCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void doorCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); - void ingredientCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); - void creaturesLevListCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); - void itemLevelledListCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); - void lightCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); - void lockpickCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); - void miscCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); - void npcCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void ingredientCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void creaturesLevListCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void itemLevelledListCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void lightCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void lockpickCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void miscCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void npcCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); const CSMWorld::RefIdData& mReferencables; - const CSMWorld::IdCollection& mRaces; - const CSMWorld::IdCollection& mClasses; - const CSMWorld::IdCollection& mFactions; + const CSMWorld::IdCollection& mRaces; + const CSMWorld::IdCollection& mClasses; + const CSMWorld::IdCollection& mFactions; }; } #endif // REFERENCEABLECHECKSTAGE_H diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index b5320ff9f..c25811ea4 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -21,7 +21,7 @@ #include "spellcheck.hpp" #include "referenceablecheck.hpp" -CSMDoc::Operation *CSMTools::Tools::get (int type) +CSMDoc::Operation* CSMTools::Tools::get(int type) { switch (type) { @@ -31,60 +31,60 @@ CSMDoc::Operation *CSMTools::Tools::get (int type) return 0; } -const CSMDoc::Operation *CSMTools::Tools::get (int type) const +const CSMDoc::Operation* CSMTools::Tools::get(int type) const { - return const_cast (this)->get (type); + return const_cast(this)->get(type); } -CSMDoc::Operation *CSMTools::Tools::getVerifier() +CSMDoc::Operation* CSMTools::Tools::getVerifier() { if (!mVerifier) { - mVerifier = new CSMDoc::Operation (CSMDoc::State_Verifying, false); + mVerifier = new CSMDoc::Operation(CSMDoc::State_Verifying, false); - connect (mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int))); - connect (mVerifier, SIGNAL (done (int)), this, SIGNAL (done (int))); - connect (mVerifier, SIGNAL (reportMessage (const QString&, int)), - this, SLOT (verifierMessage (const QString&, int))); + connect(mVerifier, SIGNAL(progress(int, int, int)), this, SIGNAL(progress(int, int, int))); + connect(mVerifier, SIGNAL(done(int)), this, SIGNAL(done(int))); + connect(mVerifier, SIGNAL(reportMessage(const QString&, int)), + this, SLOT(verifierMessage(const QString&, int))); std::vector 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"); - mandatoryIds.push_back ("PCVampire"); - mandatoryIds.push_back ("PCWerewolf"); - mandatoryIds.push_back ("PCYear"); + mandatoryIds.push_back("Day"); + mandatoryIds.push_back("DaysPassed"); + mandatoryIds.push_back("GameHour"); + mandatoryIds.push_back("Month"); + mandatoryIds.push_back("PCRace"); + mandatoryIds.push_back("PCVampire"); + mandatoryIds.push_back("PCWerewolf"); + mandatoryIds.push_back("PCYear"); - mVerifier->appendStage (new MandatoryIdStage (mData.getGlobals(), - CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), mandatoryIds)); + mVerifier->appendStage(new MandatoryIdStage(mData.getGlobals(), + CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Globals), mandatoryIds)); - mVerifier->appendStage (new SkillCheckStage (mData.getSkills())); + mVerifier->appendStage(new SkillCheckStage(mData.getSkills())); - mVerifier->appendStage (new ClassCheckStage (mData.getClasses())); + mVerifier->appendStage(new ClassCheckStage(mData.getClasses())); - mVerifier->appendStage (new FactionCheckStage (mData.getFactions())); + mVerifier->appendStage(new FactionCheckStage(mData.getFactions())); - mVerifier->appendStage (new RaceCheckStage (mData.getRaces())); + mVerifier->appendStage(new RaceCheckStage(mData.getRaces())); - mVerifier->appendStage (new SoundCheckStage (mData.getSounds())); + mVerifier->appendStage(new SoundCheckStage(mData.getSounds())); - mVerifier->appendStage (new RegionCheckStage (mData.getRegions())); + mVerifier->appendStage(new RegionCheckStage(mData.getRegions())); - mVerifier->appendStage (new BirthsignCheckStage (mData.getBirthsigns())); + mVerifier->appendStage(new BirthsignCheckStage(mData.getBirthsigns())); - mVerifier->appendStage (new SpellCheckStage (mData.getSpells())); - - mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions())); + mVerifier->appendStage(new SpellCheckStage(mData.getSpells())); + + mVerifier->appendStage(new ReferenceableCheckStage(mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions())); } return mVerifier; } -CSMTools::Tools::Tools (CSMWorld::Data& data) : mData (data), mVerifier (0), mNextReportNumber (0) +CSMTools::Tools::Tools(CSMWorld::Data& data) : mData(data), mVerifier(0), mNextReportNumber(0) { - for (std::map::iterator iter (mReports.begin()); iter!=mReports.end(); ++iter) + for (std::map::iterator iter(mReports.begin()); iter != mReports.end(); ++iter) delete iter->second; } @@ -95,17 +95,17 @@ CSMTools::Tools::~Tools() CSMWorld::UniversalId CSMTools::Tools::runVerifier() { - mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel)); - mActiveReports[CSMDoc::State_Verifying] = mNextReportNumber-1; + mReports.insert(std::make_pair(mNextReportNumber++, new ReportModel)); + mActiveReports[CSMDoc::State_Verifying] = mNextReportNumber - 1; getVerifier()->start(); - return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_VerificationResults, mNextReportNumber-1); + return CSMWorld::UniversalId(CSMWorld::UniversalId::Type_VerificationResults, mNextReportNumber - 1); } -void CSMTools::Tools::abortOperation (int type) +void CSMTools::Tools::abortOperation(int type) { - if (CSMDoc::Operation *operation = get (type)) + if (CSMDoc::Operation* operation = get(type)) operation->abort(); } @@ -113,32 +113,32 @@ int CSMTools::Tools::getRunningOperations() const { static const int sOperations[] = { - CSMDoc::State_Verifying, + CSMDoc::State_Verifying, -1 }; int result = 0; - for (int i=0; sOperations[i]!=-1; ++i) - if (const CSMDoc::Operation *operation = get (sOperations[i])) + for (int i = 0; sOperations[i] != -1; ++i) + if (const CSMDoc::Operation* operation = get(sOperations[i])) if (operation->isRunning()) result |= sOperations[i]; return result; } -CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId& id) +CSMTools::ReportModel* CSMTools::Tools::getReport(const CSMWorld::UniversalId& id) { - if (id.getType()!=CSMWorld::UniversalId::Type_VerificationResults) - throw std::logic_error ("invalid request for report model: " + id.toString()); + if (id.getType() != CSMWorld::UniversalId::Type_VerificationResults) + throw std::logic_error("invalid request for report model: " + id.toString()); - return mReports.at (id.getIndex()); + return mReports.at(id.getIndex()); } -void CSMTools::Tools::verifierMessage (const QString& message, int type) +void CSMTools::Tools::verifierMessage(const QString& message, int type) { - std::map::iterator iter = mActiveReports.find (type); + std::map::iterator iter = mActiveReports.find(type); - if (iter!=mActiveReports.end()) - mReports[iter->second]->add (message.toStdString()); + if (iter != mActiveReports.end()) + mReports[iter->second]->add(message.toStdString()); } diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index 70cae53c8..1a21de8f4 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -25,9 +25,9 @@ namespace CSMWorld public: - RefIdColumn (int columnId, Display displayType, - int flag = Flag_Table | Flag_Dialogue, bool editable = true, - bool userEditable = true); + RefIdColumn(int columnId, Display displayType, + int flag = Flag_Table | Flag_Dialogue, bool editable = true, + bool userEditable = true); virtual bool isEditable() const; @@ -40,11 +40,11 @@ namespace CSMWorld RefIdData mData; std::deque mColumns; - std::map mAdapters; + std::map mAdapters; private: - const RefIdAdapter& findAdaptor (UniversalId::Type) const; + const RefIdAdapter& findAdaptor(UniversalId::Type) const; ///< Throws an exception if no adaptor for \a Type can be found. public: @@ -55,60 +55,60 @@ namespace CSMWorld virtual int getSize() const; - virtual std::string getId (int index) const; + virtual std::string getId(int index) const; - virtual int getIndex (const std::string& id) const; + virtual int getIndex(const std::string& id) const; virtual int getColumns() const; - virtual const ColumnBase& getColumn (int column) const; + virtual const ColumnBase& getColumn(int column) const; - virtual QVariant getData (int index, int column) const; + virtual QVariant getData(int index, int column) const; - virtual void setData (int index, int column, const QVariant& data); + virtual void setData(int index, int column, const QVariant& data); - virtual void removeRows (int index, int count); + virtual void removeRows(int index, int count); - virtual void appendBlankRecord (const std::string& id, UniversalId::Type type); + virtual void appendBlankRecord(const std::string& id, UniversalId::Type type); ///< \param type Will be ignored, unless the collection supports multiple record types - virtual int searchId (const std::string& id) const; + virtual int searchId(const std::string& id) const; ////< Search record with \a id. /// \return index of record (if found) or -1 (not found) - virtual void replace (int index, const RecordBase& record); + virtual void replace(int index, const RecordBase& record); ///< If the record type does not match, an exception is thrown. /// /// \attention \a record must not change the ID. - virtual void appendRecord (const RecordBase& record, UniversalId::Type type); + virtual void appendRecord(const RecordBase& record, UniversalId::Type type); ///< If the record type does not match, an exception is thrown. /// ///< \param type Will be ignored, unless the collection supports multiple record types - virtual const RecordBase& getRecord (const std::string& id) const; + virtual const RecordBase& getRecord(const std::string& id) const; - virtual const RecordBase& getRecord (int index) const; + virtual const RecordBase& getRecord(int index) const; - void load (ESM::ESMReader& reader, bool base, UniversalId::Type type); + void load(ESM::ESMReader& reader, bool base, UniversalId::Type type); - virtual int getAppendIndex (const std::string& id, UniversalId::Type type) const; + virtual int getAppendIndex(const std::string& id, UniversalId::Type type) const; ///< \param type Will be ignored, unless the collection supports multiple record types - virtual std::vector getIds (bool listDeleted) const; + virtual std::vector getIds(bool listDeleted) const; ///< Return a sorted collection of all IDs /// /// \param listDeleted include deleted record in the list - virtual bool reorderRows (int baseIndex, const std::vector& newOrder); + virtual bool reorderRows(int baseIndex, const std::vector& newOrder); ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). /// /// \return Success? - void save (int index, ESM::ESMWriter& writer) const; - - const RefIdData& getDataSet() const; //I can't figure out a better name for this one :( + void save(int index, ESM::ESMWriter& writer) const; + + const RefIdData& getDataSet() const; //I can't figure out a better name for this one :( }; } diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 8aa3c2522..719b4b922 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -41,19 +41,19 @@ namespace CSMWorld virtual int getSize() const = 0; - virtual const RecordBase& getRecord (int index) const = 0; + virtual const RecordBase& getRecord(int index) const = 0; - virtual RecordBase& getRecord (int index)= 0; + virtual RecordBase& getRecord(int index) = 0; - virtual void appendRecord (const std::string& id) = 0; + virtual void appendRecord(const std::string& id) = 0; - virtual void load (int index, ESM::ESMReader& reader, bool base) = 0; + virtual void load(int index, ESM::ESMReader& reader, bool base) = 0; - virtual void erase (int index, int count) = 0; + virtual void erase(int index, int count) = 0; - virtual std::string getId (int index) const = 0; + virtual std::string getId(int index) const = 0; - virtual void save (int index, ESM::ESMWriter& writer) const = 0; + virtual void save(int index, ESM::ESMWriter& writer) const = 0; }; template @@ -63,90 +63,91 @@ namespace CSMWorld virtual int getSize() const; - virtual const RecordBase& getRecord (int index) const; + virtual const RecordBase& getRecord(int index) const; - virtual RecordBase& getRecord (int index); + virtual RecordBase& getRecord(int index); - virtual void appendRecord (const std::string& id); + virtual void appendRecord(const std::string& id); - virtual void load (int index, ESM::ESMReader& reader, bool base); + virtual void load(int index, ESM::ESMReader& reader, bool base); - virtual void erase (int index, int count); + virtual void erase(int index, int count); - virtual std::string getId (int index) const; + virtual std::string getId(int index) const; - virtual void save (int index, ESM::ESMWriter& writer) const; + virtual void save(int index, ESM::ESMWriter& writer) const; }; template int RefIdDataContainer::getSize() const { - return static_cast (mContainer.size()); + return static_cast(mContainer.size()); } template - const RecordBase& RefIdDataContainer::getRecord (int index) const + const RecordBase& RefIdDataContainer::getRecord(int index) const { - return mContainer.at (index); + return mContainer.at(index); } template - RecordBase& RefIdDataContainer::getRecord (int index) + RecordBase& RefIdDataContainer::getRecord(int index) { - return mContainer.at (index); + return mContainer.at(index); } template - void RefIdDataContainer::appendRecord (const std::string& id) + void RefIdDataContainer::appendRecord(const std::string& id) { Record record; record.mModified.mId = id; record.mModified.blank(); record.mState = RecordBase::State_ModifiedOnly; - mContainer.push_back (record); + mContainer.push_back(record); } template - void RefIdDataContainer::load (int index, ESM::ESMReader& reader, bool base) + void RefIdDataContainer::load(int index, ESM::ESMReader& reader, bool base) { - (base ? mContainer.at (index).mBase : mContainer.at (index).mModified).load (reader); + (base ? mContainer.at(index).mBase : mContainer.at(index).mModified).load(reader); } template - void RefIdDataContainer::erase (int index, int count) + void RefIdDataContainer::erase(int index, int count) { - if (index<0 || index+count>=getSize()) - throw std::runtime_error ("invalid RefIdDataContainer index"); + if (index < 0 || index + count >= getSize()) + throw std::runtime_error("invalid RefIdDataContainer index"); - mContainer.erase (mContainer.begin()+index, mContainer.begin()+index+count); + mContainer.erase(mContainer.begin() + index, mContainer.begin() + index + count); } template - std::string RefIdDataContainer::getId (int index) const + std::string RefIdDataContainer::getId(int index) const { - return mContainer.at (index).get().mId; + return mContainer.at(index).get().mId; } template - void RefIdDataContainer::save (int index, ESM::ESMWriter& writer) const + void RefIdDataContainer::save(int index, ESM::ESMWriter& writer) const { - CSMWorld::RecordBase::State state = mContainer.at (index).mState; + CSMWorld::RecordBase::State state = mContainer.at(index).mState; - if (state==CSMWorld::RecordBase::State_Modified || - state==CSMWorld::RecordBase::State_ModifiedOnly) + if (state == CSMWorld::RecordBase::State_Modified || + state == CSMWorld::RecordBase::State_ModifiedOnly) { std::string type; - for (int i=0; i<4; ++i) - /// \todo make endianess agnostic (change ESMWriter interface?) - type += reinterpret_cast (&mContainer.at (index).mModified.sRecordId)[i]; - writer.startRecord (type); - writer.writeHNCString ("NAME", getId (index)); - mContainer.at (index).mModified.save (writer); - writer.endRecord (type); + for (int i = 0; i < 4; ++i) + /// \todo make endianess agnostic (change ESMWriter interface?) + type += reinterpret_cast(&mContainer.at(index).mModified.sRecordId)[i]; + + writer.startRecord(type); + writer.writeHNCString("NAME", getId(index)); + mContainer.at(index).mModified.save(writer); + writer.endRecord(type); } - else if (state==CSMWorld::RecordBase::State_Deleted) + else if (state == CSMWorld::RecordBase::State_Deleted) { /// \todo write record with delete flag } @@ -184,61 +185,63 @@ namespace CSMWorld std::map mIndex; - std::map mRecordContainers; + std::map mRecordContainers; - void erase (const LocalIndex& index, int count); + void erase(const LocalIndex& index, int count); ///< Must not spill over into another type. public: RefIdData(); - LocalIndex globalToLocalIndex (int index) const; + LocalIndex globalToLocalIndex(int index) const; - int localToGlobalIndex (const LocalIndex& index) const; + int localToGlobalIndex(const LocalIndex& index) const; - LocalIndex searchId (const std::string& id) const; + LocalIndex searchId(const std::string& id) const; - void erase (int index, int count); + void erase(int index, int count); - const RecordBase& getRecord (const LocalIndex& index) const; + const RecordBase& getRecord(const LocalIndex& index) const; - RecordBase& getRecord (const LocalIndex& index); + RecordBase& getRecord(const LocalIndex& index); - void appendRecord (UniversalId::Type type, const std::string& id); + void appendRecord(UniversalId::Type type, const std::string& id); - int getAppendIndex (UniversalId::Type type) const; + int getAppendIndex(UniversalId::Type type) const; - void load (const LocalIndex& index, ESM::ESMReader& reader, bool base); + void load(const LocalIndex& index, ESM::ESMReader& reader, bool base); int getSize() const; - std::vector getIds (bool listDeleted = true) const; + std::vector getIds(bool listDeleted = true) const; ///< Return a sorted collection of all IDs /// /// \param listDeleted include deleted record in the list - void save (int index, ESM::ESMWriter& writer) const; - - //RECORD CONTAINERS ACCESS METHODS - const RefIdDataContainer& getBooks() const; - const RefIdDataContainer& getActivators() const; - const RefIdDataContainer& getPotions() const; - const RefIdDataContainer& getApparati() const; - const RefIdDataContainer& getArmors() const; - const RefIdDataContainer& getClothing() const; - const RefIdDataContainer& getContainers() const; - const RefIdDataContainer& getCreatures() const; - const RefIdDataContainer& getDoors() const; - const RefIdDataContainer& getIngredients() const; - const RefIdDataContainer& getCreatureLevelledLists() const; - const RefIdDataContainer& getItemLevelledList() const; - const RefIdDataContainer& getLights() const; - const RefIdDataContainer& getLocpicks() const; - const RefIdDataContainer& getMiscellaneous() const; - const RefIdDataContainer& getNPCs() const; + void save(int index, ESM::ESMWriter& writer) const; + + //RECORD CONTAINERS ACCESS METHODS + const RefIdDataContainer& getBooks() const; + const RefIdDataContainer& getActivators() const; + const RefIdDataContainer& getPotions() const; + const RefIdDataContainer& getApparati() const; + const RefIdDataContainer& getArmors() const; + const RefIdDataContainer& getClothing() const; + const RefIdDataContainer& getContainers() const; + const RefIdDataContainer& getCreatures() const; + const RefIdDataContainer& getDoors() const; + const RefIdDataContainer& getIngredients() const; + const RefIdDataContainer& getCreatureLevelledLists() const; + const RefIdDataContainer& getItemLevelledList() const; + const RefIdDataContainer& getLights() const; + const RefIdDataContainer& getLocpicks() const; + const RefIdDataContainer& getMiscellaneous() const; + const RefIdDataContainer& getNPCs() const; }; } #endif + + From f3a7321a43719baf49b257ddb80d0e06c7dbac52 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 30 Dec 2013 16:44:07 +0100 Subject: [PATCH 264/889] Closes #856: More aggressive supression of skeleton base: only create for keyframe controllers, not any controllers --- components/nifogre/skeleton.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp index 9ec6f15b0..e01ae22ef 100644 --- a/components/nifogre/skeleton.cpp +++ b/components/nifogre/skeleton.cpp @@ -86,14 +86,23 @@ bool NIFSkeletonLoader::needSkeleton(const Nif::Node *node) { /* We need to be a little aggressive here, since some NIFs have a crap-ton * of nodes and Ogre only supports 256 bones. We will skip a skeleton if: - * There are no bones used for skinning, there are no controllers, there + * There are no bones used for skinning, there are no keyframe controllers, there * are no nodes named "AttachLight", and the tree consists of NiNode, * NiTriShape, and RootCollisionNode types only. */ if(node->boneTrafo) return true; - if(!node->controller.empty() || node->name == "AttachLight") + if(!node->controller.empty()) + { + Nif::ControllerPtr ctrl = node->controller; + do { + if(ctrl->recType == Nif::RC_NiKeyframeController) + return true; + } while(!(ctrl=ctrl->next).empty()); + } + + if (node->name == "AttachLight") return true; if(node->recType == Nif::RC_NiNode || node->recType == Nif::RC_RootCollisionNode) From 79a440e94aa32965b057ab24b4d980bccd0937e4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 30 Dec 2013 17:53:02 +0100 Subject: [PATCH 265/889] Many additions to 900bc06d2c236b: - Fix indentation - Consider any kind of light, not just torch_infinite_time - Hostile NPCs should also wear lights, if they have nothing else that could use the slot (or a twohanded weapon) - Remove redundant code and don't add additional lights to the inventory - World::isDark returns false for interiors which are unaffected by weather --- apps/openmw/mwbase/world.hpp | 3 +- apps/openmw/mwmechanics/actors.cpp | 65 +++++++++++++++++--------- apps/openmw/mwmechanics/actors.hpp | 1 - apps/openmw/mwmechanics/character.cpp | 8 ++-- apps/openmw/mwworld/inventorystore.cpp | 2 +- apps/openmw/mwworld/weather.cpp | 6 ++- apps/openmw/mwworld/weather.hpp | 3 +- apps/openmw/mwworld/worldimp.cpp | 4 +- apps/openmw/mwworld/worldimp.hpp | 3 +- 9 files changed, 59 insertions(+), 36 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 48803a2d8..609f873ee 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -428,7 +428,8 @@ namespace MWBase virtual void breakInvisibility (const MWWorld::Ptr& actor) = 0; - virtual bool isNight() const = 0; + // Are we in an exterior or pseudo-exterior cell and it's night? + virtual bool isDark() const = 0; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 1aa9ce9f5..62242ee8c 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -448,36 +448,57 @@ namespace MWMechanics /** * Automatically equip NPCs torches at night and unequip them at day */ - if (!isPlayer && !MWWorld::Class::get (ptr).getCreatureStats (ptr).isHostile()) + if (!isPlayer) { - if (mTorchPtr.isEmpty()) - { - mTorchPtr = inventoryStore.search("torch_infinite_time"); - } + MWWorld::ContainerStoreIterator torch = inventoryStore.end(); + for (MWWorld::ContainerStoreIterator it = inventoryStore.begin(); it != inventoryStore.end(); ++it) + { + if (it->getTypeName() == typeid(ESM::Light).name()) + { + torch = it; + break; + } + } - if (MWBase::Environment::get().getWorld()->isNight()) - { - if (heldIter != inventoryStore.end() && heldIter->getTypeName() != typeid(ESM::Light).name()) + if (MWBase::Environment::get().getWorld()->isDark()) { - inventoryStore.unequipItem(*heldIter, ptr); + if (torch != inventoryStore.end()) + { + if (!MWWorld::Class::get (ptr).getCreatureStats (ptr).isHostile()) + { + // 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 (heldIter == inventoryStore.end() + && torch->getClass().canBeEquipped(*torch, ptr).first == 1) + { + inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, torch, ptr); + } + } } - else if (heldIter == inventoryStore.end() && !mTorchPtr.isEmpty()) + else { - heldIter = inventoryStore.add(mTorchPtr, ptr); - inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, heldIter, ptr); + if (heldIter != inventoryStore.end() && heldIter->getTypeName() == typeid(ESM::Light).name()) + { + // At day, unequip lights and auto equip shields or other suitable items + // (Note: autoEquip will ignore lights) + inventoryStore.autoEquip(ptr); + } } - } - else - { - if (heldIter != inventoryStore.end() && heldIter->getTypeName() == typeid(ESM::Light).name()) - { - inventoryStore.unequipItem(*heldIter, ptr); - inventoryStore.add(*heldIter, ptr); - inventoryStore.autoEquip(ptr); - } - } } + heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + //If holding a light... if(heldIter.getType() == MWWorld::ContainerStore::Type_Light) { diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 411ac54ca..b03b68e89 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -29,7 +29,6 @@ namespace MWMechanics PtrControllerMap mActors; std::map mDeathCount; - MWWorld::Ptr mTorchPtr; void updateNpc(const MWWorld::Ptr &ptr, float duration, bool paused); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 17eff3d02..351e33f13 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -36,8 +36,6 @@ #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwworld/actionequip.hpp" -#include "../mwworld/actiontake.hpp" namespace { @@ -724,12 +722,12 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun && mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand) { - mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, - false, 1.0f, "start", "stop", 0.0f, (~(size_t)0)); + mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, + false, 1.0f, "start", "stop", 0.0f, (~(size_t)0)); } else if (mAnimation->isPlaying("torch")) { - mAnimation->disable("torch"); + mAnimation->disable("torch"); } return forcestateupdate; diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 849e9ff26..2c7c05317 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -179,7 +179,7 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) // Don't autoEquip lights if (test.getTypeName() == typeid(ESM::Light).name()) { - continue; + continue; } int testSkill = MWWorld::Class::get (test).getEquipmentSkill (test); diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index c355d86a8..5967a02fd 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -708,7 +708,9 @@ float WeatherManager::getWindSpeed() const return mWindSpeed; } -bool WeatherManager::isNight() const +bool WeatherManager::isDark() const { - return (mHour < mSunriseTime || mHour > mNightStart - 1); + bool exterior = (MWBase::Environment::get().getWorld()->isCellExterior() + || MWBase::Environment::get().getWorld()->isCellQuasiExterior()); + return exterior && (mHour < mSunriseTime || mHour > mNightStart - 1); } diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 4c412c449..5e8d059ee 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -152,7 +152,8 @@ namespace MWWorld void modRegion(const std::string ®ionid, const std::vector &chances); - bool isNight() const; + /// @see World::isDark + bool isDark() const; private: float mHour; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index cf32217b6..82029d11d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2286,8 +2286,8 @@ namespace MWWorld actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility); } - bool World::isNight() const + bool World::isDark() const { - return mWeatherManager->isNight(); + return mWeatherManager->isDark(); } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 2c2f096ee..a7948dcf2 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -516,7 +516,8 @@ namespace MWWorld const MWWorld::Ptr& actor, const std::string& sourceName); virtual void breakInvisibility (const MWWorld::Ptr& actor); - virtual bool isNight() const; + // Are we in an exterior or pseudo-exterior cell and it's night? + virtual bool isDark() const; }; } From 4a1987ddecb6385db2b26402e066d9d79f851f7b Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 30 Dec 2013 18:41:16 +0100 Subject: [PATCH 266/889] correcting --- .../opencs/model/tools/referenceablecheck.cpp | 28 +----- apps/opencs/model/tools/tools.cpp | 93 ++++++++++--------- apps/opencs/model/world/refidcollection.hpp | 47 +++++----- 3 files changed, 74 insertions(+), 94 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index ad52c5962..3ca203bdf 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -945,18 +945,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI } else //checking if there is such class { - bool nosuchclass(true); - - for (int i = 0; i < mClasses.getSize(); ++i) - { - if (dynamic_cast(mClasses.getRecord(i).get()).mId == NPC.mClass) - { - nosuchclass = false; - break; - } - } - - if (nosuchclass) + if (mClasses.searchId(NPC.mClass)) { messages.push_back(id.toString() + "|" + NPC.mId + " has invalid class"); } @@ -1002,18 +991,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI messages.push_back(id.toString() + "|" + NPC.mId + " has negative rank"); } - bool nosuchfaction(true); - - for (int i = 0; i < mFactions.getSize(); ++i) - { - if (dynamic_cast(mFactions.getRecord(i).get()).mId == NPC.mFaction) - { - nosuchfaction = false; - break; - } - } - - if (nosuchfaction) + if (mFactions.searchId(NPC.mFaction) == -1) { messages.push_back(id.toString() + "|" + NPC.mId + " has invalid faction"); } @@ -1026,7 +1004,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI if (NPC.mHair.empty()) { - messages.push_back(id.toString() + "|" + NPC.mId + " has no har"); + messages.push_back(id.toString() + "|" + NPC.mId + " has no hair"); } //TODO: reputation, Disposition, rank, everything else diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index c25811ea4..64e39ad2f 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -21,7 +21,7 @@ #include "spellcheck.hpp" #include "referenceablecheck.hpp" -CSMDoc::Operation* CSMTools::Tools::get(int type) +CSMDoc::Operation *CSMTools::Tools::get (int type) { switch (type) { @@ -31,60 +31,60 @@ CSMDoc::Operation* CSMTools::Tools::get(int type) return 0; } -const CSMDoc::Operation* CSMTools::Tools::get(int type) const +const CSMDoc::Operation *CSMTools::Tools::get (int type) const { - return const_cast(this)->get(type); + return const_cast (this)->get (type); } -CSMDoc::Operation* CSMTools::Tools::getVerifier() +CSMDoc::Operation *CSMTools::Tools::getVerifier() { if (!mVerifier) { - mVerifier = new CSMDoc::Operation(CSMDoc::State_Verifying, false); + mVerifier = new CSMDoc::Operation (CSMDoc::State_Verifying, false); - connect(mVerifier, SIGNAL(progress(int, int, int)), this, SIGNAL(progress(int, int, int))); - connect(mVerifier, SIGNAL(done(int)), this, SIGNAL(done(int))); - connect(mVerifier, SIGNAL(reportMessage(const QString&, int)), - this, SLOT(verifierMessage(const QString&, int))); + connect (mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int))); + connect (mVerifier, SIGNAL (done (int)), this, SIGNAL (done (int))); + connect (mVerifier, SIGNAL (reportMessage (const QString&, int)), + this, SLOT (verifierMessage (const QString&, int))); std::vector 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"); - mandatoryIds.push_back("PCVampire"); - mandatoryIds.push_back("PCWerewolf"); - mandatoryIds.push_back("PCYear"); + mandatoryIds.push_back ("Day"); + mandatoryIds.push_back ("DaysPassed"); + mandatoryIds.push_back ("GameHour"); + mandatoryIds.push_back ("Month"); + mandatoryIds.push_back ("PCRace"); + mandatoryIds.push_back ("PCVampire"); + mandatoryIds.push_back ("PCWerewolf"); + mandatoryIds.push_back ("PCYear"); - mVerifier->appendStage(new MandatoryIdStage(mData.getGlobals(), - CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Globals), mandatoryIds)); + mVerifier->appendStage (new MandatoryIdStage (mData.getGlobals(), + CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), mandatoryIds)); - mVerifier->appendStage(new SkillCheckStage(mData.getSkills())); + mVerifier->appendStage (new SkillCheckStage (mData.getSkills())); - mVerifier->appendStage(new ClassCheckStage(mData.getClasses())); + mVerifier->appendStage (new ClassCheckStage (mData.getClasses())); - mVerifier->appendStage(new FactionCheckStage(mData.getFactions())); + mVerifier->appendStage (new FactionCheckStage (mData.getFactions())); - mVerifier->appendStage(new RaceCheckStage(mData.getRaces())); + mVerifier->appendStage (new RaceCheckStage (mData.getRaces())); - mVerifier->appendStage(new SoundCheckStage(mData.getSounds())); + mVerifier->appendStage (new SoundCheckStage (mData.getSounds())); - mVerifier->appendStage(new RegionCheckStage(mData.getRegions())); + mVerifier->appendStage (new RegionCheckStage (mData.getRegions())); - mVerifier->appendStage(new BirthsignCheckStage(mData.getBirthsigns())); + mVerifier->appendStage (new BirthsignCheckStage (mData.getBirthsigns())); - mVerifier->appendStage(new SpellCheckStage(mData.getSpells())); + mVerifier->appendStage (new SpellCheckStage (mData.getSpells())); - mVerifier->appendStage(new ReferenceableCheckStage(mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions())); + mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions())); } return mVerifier; } -CSMTools::Tools::Tools(CSMWorld::Data& data) : mData(data), mVerifier(0), mNextReportNumber(0) +CSMTools::Tools::Tools (CSMWorld::Data& data) : mData (data), mVerifier (0), mNextReportNumber (0) { - for (std::map::iterator iter(mReports.begin()); iter != mReports.end(); ++iter) + for (std::map::iterator iter (mReports.begin()); iter!=mReports.end(); ++iter) delete iter->second; } @@ -95,17 +95,17 @@ CSMTools::Tools::~Tools() CSMWorld::UniversalId CSMTools::Tools::runVerifier() { - mReports.insert(std::make_pair(mNextReportNumber++, new ReportModel)); - mActiveReports[CSMDoc::State_Verifying] = mNextReportNumber - 1; + mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel)); + mActiveReports[CSMDoc::State_Verifying] = mNextReportNumber-1; getVerifier()->start(); - return CSMWorld::UniversalId(CSMWorld::UniversalId::Type_VerificationResults, mNextReportNumber - 1); + return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_VerificationResults, mNextReportNumber-1); } -void CSMTools::Tools::abortOperation(int type) +void CSMTools::Tools::abortOperation (int type) { - if (CSMDoc::Operation* operation = get(type)) + if (CSMDoc::Operation *operation = get (type)) operation->abort(); } @@ -113,32 +113,33 @@ int CSMTools::Tools::getRunningOperations() const { static const int sOperations[] = { - CSMDoc::State_Verifying, + CSMDoc::State_Verifying, -1 }; int result = 0; - for (int i = 0; sOperations[i] != -1; ++i) - if (const CSMDoc::Operation* operation = get(sOperations[i])) + for (int i=0; sOperations[i]!=-1; ++i) + if (const CSMDoc::Operation *operation = get (sOperations[i])) if (operation->isRunning()) result |= sOperations[i]; return result; } -CSMTools::ReportModel* CSMTools::Tools::getReport(const CSMWorld::UniversalId& id) +CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId& id) { - if (id.getType() != CSMWorld::UniversalId::Type_VerificationResults) - throw std::logic_error("invalid request for report model: " + id.toString()); + if (id.getType()!=CSMWorld::UniversalId::Type_VerificationResults) + throw std::logic_error ("invalid request for report model: " + id.toString()); - return mReports.at(id.getIndex()); + return mReports.at (id.getIndex()); } -void CSMTools::Tools::verifierMessage(const QString& message, int type) +void CSMTools::Tools::verifierMessage (const QString& message, int type) { - std::map::iterator iter = mActiveReports.find(type); + std::map::iterator iter = mActiveReports.find (type); - if (iter != mActiveReports.end()) - mReports[iter->second]->add(message.toStdString()); + if (iter!=mActiveReports.end()) + mReports[iter->second]->add (message.toStdString()); } + diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index 1a21de8f4..328680d85 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -25,9 +25,9 @@ namespace CSMWorld public: - RefIdColumn(int columnId, Display displayType, - int flag = Flag_Table | Flag_Dialogue, bool editable = true, - bool userEditable = true); + RefIdColumn (int columnId, Display displayType, + int flag = Flag_Table | Flag_Dialogue, bool editable = true, + bool userEditable = true); virtual bool isEditable() const; @@ -40,11 +40,11 @@ namespace CSMWorld RefIdData mData; std::deque mColumns; - std::map mAdapters; + std::map mAdapters; private: - const RefIdAdapter& findAdaptor(UniversalId::Type) const; + const RefIdAdapter& findAdaptor (UniversalId::Type) const; ///< Throws an exception if no adaptor for \a Type can be found. public: @@ -55,61 +55,62 @@ namespace CSMWorld virtual int getSize() const; - virtual std::string getId(int index) const; + virtual std::string getId (int index) const; - virtual int getIndex(const std::string& id) const; + virtual int getIndex (const std::string& id) const; virtual int getColumns() const; - virtual const ColumnBase& getColumn(int column) const; + virtual const ColumnBase& getColumn (int column) const; - virtual QVariant getData(int index, int column) const; + virtual QVariant getData (int index, int column) const; - virtual void setData(int index, int column, const QVariant& data); + virtual void setData (int index, int column, const QVariant& data); - virtual void removeRows(int index, int count); + virtual void removeRows (int index, int count); - virtual void appendBlankRecord(const std::string& id, UniversalId::Type type); + virtual void appendBlankRecord (const std::string& id, UniversalId::Type type); ///< \param type Will be ignored, unless the collection supports multiple record types - virtual int searchId(const std::string& id) const; + virtual int searchId (const std::string& id) const; ////< Search record with \a id. /// \return index of record (if found) or -1 (not found) - virtual void replace(int index, const RecordBase& record); + virtual void replace (int index, const RecordBase& record); ///< If the record type does not match, an exception is thrown. /// /// \attention \a record must not change the ID. - virtual void appendRecord(const RecordBase& record, UniversalId::Type type); + virtual void appendRecord (const RecordBase& record, UniversalId::Type type); ///< If the record type does not match, an exception is thrown. /// ///< \param type Will be ignored, unless the collection supports multiple record types - virtual const RecordBase& getRecord(const std::string& id) const; + virtual const RecordBase& getRecord (const std::string& id) const; - virtual const RecordBase& getRecord(int index) const; + virtual const RecordBase& getRecord (int index) const; - void load(ESM::ESMReader& reader, bool base, UniversalId::Type type); + void load (ESM::ESMReader& reader, bool base, UniversalId::Type type); - virtual int getAppendIndex(const std::string& id, UniversalId::Type type) const; + virtual int getAppendIndex (const std::string& id, UniversalId::Type type) const; ///< \param type Will be ignored, unless the collection supports multiple record types - virtual std::vector getIds(bool listDeleted) const; + virtual std::vector getIds (bool listDeleted) const; ///< Return a sorted collection of all IDs /// /// \param listDeleted include deleted record in the list - virtual bool reorderRows(int baseIndex, const std::vector& newOrder); + virtual bool reorderRows (int baseIndex, const std::vector& newOrder); ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). /// /// \return Success? - void save(int index, ESM::ESMWriter& writer) const; + void save (int index, ESM::ESMWriter& writer) const; - const RefIdData& getDataSet() const; //I can't figure out a better name for this one :( + const RefIdData& getDataSet() const; //I can't figure out a better name for this one :( }; } #endif + From a7de04d0a4acd27555ebeebc84a3ffd4182896fc Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 30 Dec 2013 18:46:18 +0100 Subject: [PATCH 267/889] reverting refiddata.cpp --- apps/opencs/model/world/refiddata.cpp | 214 +++++++++++++------------- 1 file changed, 107 insertions(+), 107 deletions(-) diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 9c0a857a5..5af215989 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -9,187 +9,187 @@ CSMWorld::RefIdDataContainerBase::~RefIdDataContainerBase() {} CSMWorld::RefIdData::RefIdData() { - mRecordContainers.insert(std::make_pair(UniversalId::Type_Activator, &mActivators)); - mRecordContainers.insert(std::make_pair(UniversalId::Type_Potion, &mPotions)); - mRecordContainers.insert(std::make_pair(UniversalId::Type_Apparatus, &mApparati)); - mRecordContainers.insert(std::make_pair(UniversalId::Type_Armor, &mArmors)); - mRecordContainers.insert(std::make_pair(UniversalId::Type_Book, &mBooks)); - mRecordContainers.insert(std::make_pair(UniversalId::Type_Clothing, &mClothing)); - mRecordContainers.insert(std::make_pair(UniversalId::Type_Container, &mContainers)); - mRecordContainers.insert(std::make_pair(UniversalId::Type_Creature, &mCreatures)); - mRecordContainers.insert(std::make_pair(UniversalId::Type_Door, &mDoors)); - mRecordContainers.insert(std::make_pair(UniversalId::Type_Ingredient, &mIngredients)); - mRecordContainers.insert(std::make_pair(UniversalId::Type_CreatureLevelledList, - &mCreatureLevelledLists)); - mRecordContainers.insert(std::make_pair(UniversalId::Type_ItemLevelledList, &mItemLevelledLists)); - mRecordContainers.insert(std::make_pair(UniversalId::Type_Light, &mLights)); - mRecordContainers.insert(std::make_pair(UniversalId::Type_Lockpick, &mLockpicks)); - mRecordContainers.insert(std::make_pair(UniversalId::Type_Miscellaneous, &mMiscellaneous)); - mRecordContainers.insert(std::make_pair(UniversalId::Type_Npc, &mNpcs)); - mRecordContainers.insert(std::make_pair(UniversalId::Type_Probe, &mProbes)); - mRecordContainers.insert(std::make_pair(UniversalId::Type_Repair, &mRepairs)); - mRecordContainers.insert(std::make_pair(UniversalId::Type_Static, &mStatics)); - mRecordContainers.insert(std::make_pair(UniversalId::Type_Weapon, &mWeapons)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Activator, &mActivators)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Potion, &mPotions)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Apparatus, &mApparati)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Armor, &mArmors)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Book, &mBooks)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Clothing, &mClothing)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Container, &mContainers)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Creature, &mCreatures)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Door, &mDoors)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Ingredient, &mIngredients)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_CreatureLevelledList, + &mCreatureLevelledLists)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_ItemLevelledList, &mItemLevelledLists)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Light, &mLights)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Lockpick, &mLockpicks)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Miscellaneous, &mMiscellaneous)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Npc, &mNpcs)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Probe, &mProbes)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Repair, &mRepairs)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Static, &mStatics)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Weapon, &mWeapons)); } -CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::globalToLocalIndex(int index) const +CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::globalToLocalIndex (int index) const { - for (std::map::const_iterator iter( - mRecordContainers.begin()); iter != mRecordContainers.end(); ++iter) + for (std::map::const_iterator iter ( + mRecordContainers.begin()); iter!=mRecordContainers.end(); ++iter) { - if (index < iter->second->getSize()) - return LocalIndex(index, iter->first); + if (indexsecond->getSize()) + return LocalIndex (index, iter->first); index -= iter->second->getSize(); } - throw std::runtime_error("RefIdData index out of range"); + throw std::runtime_error ("RefIdData index out of range"); } -int CSMWorld::RefIdData::localToGlobalIndex(const LocalIndex& index) -const +int CSMWorld::RefIdData::localToGlobalIndex (const LocalIndex& index) + const { - std::map::const_iterator end = - mRecordContainers.find(index.second); + std::map::const_iterator end = + mRecordContainers.find (index.second); - if (end == mRecordContainers.end()) - throw std::logic_error("invalid local index type"); + if (end==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); int globalIndex = index.first; - for (std::map::const_iterator iter( - mRecordContainers.begin()); iter != end; ++iter) + for (std::map::const_iterator iter ( + mRecordContainers.begin()); iter!=end; ++iter) globalIndex += iter->second->getSize(); return globalIndex; } -CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::searchId( +CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::searchId ( const std::string& id) const { - std::string id2 = Misc::StringUtils::lowerCase(id); + std::string id2 = Misc::StringUtils::lowerCase (id); - std::map >::const_iterator iter = mIndex.find(id2); + std::map >::const_iterator iter = mIndex.find (id2); - if (iter == mIndex.end()) - return std::make_pair(-1, CSMWorld::UniversalId::Type_None); + if (iter==mIndex.end()) + return std::make_pair (-1, CSMWorld::UniversalId::Type_None); return iter->second; } -void CSMWorld::RefIdData::erase(int index, int count) +void CSMWorld::RefIdData::erase (int index, int count) { - LocalIndex localIndex = globalToLocalIndex(index); + LocalIndex localIndex = globalToLocalIndex (index); - std::map::const_iterator iter = - mRecordContainers.find(localIndex.second); + std::map::const_iterator iter = + mRecordContainers.find (localIndex.second); - while (count > 0 && iter != mRecordContainers.end()) + while (count>0 && iter!=mRecordContainers.end()) { int size = iter->second->getSize(); - if (localIndex.first + count > size) + if (localIndex.first+count>size) { - erase(localIndex, size - localIndex.first); - count -= size - localIndex.first; + erase (localIndex, size-localIndex.first); + count -= size-localIndex.first; ++iter; - if (iter == mRecordContainers.end()) - throw std::runtime_error("invalid count value for erase operation"); + if (iter==mRecordContainers.end()) + throw std::runtime_error ("invalid count value for erase operation"); localIndex.first = 0; localIndex.second = iter->first; } else { - erase(localIndex, count); + erase (localIndex, count); count = 0; } } } -const CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord(const LocalIndex& index) const +const CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord (const LocalIndex& index) const { - std::map::const_iterator iter = - mRecordContainers.find(index.second); + std::map::const_iterator iter = + mRecordContainers.find (index.second); - if (iter == mRecordContainers.end()) - throw std::logic_error("invalid local index type"); + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); - return iter->second->getRecord(index.first); + return iter->second->getRecord (index.first); } -CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord(const LocalIndex& index) +CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord (const LocalIndex& index) { - std::map::iterator iter = - mRecordContainers.find(index.second); + std::map::iterator iter = + mRecordContainers.find (index.second); - if (iter == mRecordContainers.end()) - throw std::logic_error("invalid local index type"); + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); - return iter->second->getRecord(index.first); + return iter->second->getRecord (index.first); } -void CSMWorld::RefIdData::appendRecord(UniversalId::Type type, const std::string& id) +void CSMWorld::RefIdData::appendRecord (UniversalId::Type type, const std::string& id) { - std::map::iterator iter = - mRecordContainers.find(type); + std::map::iterator iter = + mRecordContainers.find (type); - if (iter == mRecordContainers.end()) - throw std::logic_error("invalid local index type"); + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); - iter->second->appendRecord(id); + iter->second->appendRecord (id); - mIndex.insert(std::make_pair(Misc::StringUtils::lowerCase(id), - LocalIndex(iter->second->getSize() - 1, type))); + mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), + LocalIndex (iter->second->getSize()-1, type))); } -int CSMWorld::RefIdData::getAppendIndex(UniversalId::Type type) const +int CSMWorld::RefIdData::getAppendIndex (UniversalId::Type type) const { int index = 0; - for (std::map::const_iterator iter( - mRecordContainers.begin()); iter != mRecordContainers.end(); ++iter) + for (std::map::const_iterator iter ( + mRecordContainers.begin()); iter!=mRecordContainers.end(); ++iter) { index += iter->second->getSize(); - if (type == iter->first) + if (type==iter->first) break; } return index; } -void CSMWorld::RefIdData::load(const LocalIndex& index, ESM::ESMReader& reader, bool base) +void CSMWorld::RefIdData::load (const LocalIndex& index, ESM::ESMReader& reader, bool base) { - std::map::iterator iter = - mRecordContainers.find(index.second); + std::map::iterator iter = + mRecordContainers.find (index.second); - if (iter == mRecordContainers.end()) - throw std::logic_error("invalid local index type"); + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); - iter->second->load(index.first, reader, base); + iter->second->load (index.first, reader, base); } -void CSMWorld::RefIdData::erase(const LocalIndex& index, int count) +void CSMWorld::RefIdData::erase (const LocalIndex& index, int count) { - std::map::iterator iter = - mRecordContainers.find(index.second); + std::map::iterator iter = + mRecordContainers.find (index.second); - if (iter == mRecordContainers.end()) - throw std::logic_error("invalid local index type"); + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); - for (int i = index.first; i < index.first + count; ++i) + for (int i=index.first; i::iterator result = - mIndex.find(Misc::StringUtils::lowerCase(iter->second->getId(i))); + mIndex.find (Misc::StringUtils::lowerCase (iter->second->getId (i))); - if (result != mIndex.end()) - mIndex.erase(result); + if (result!=mIndex.end()) + mIndex.erase (result); } - iter->second->erase(index.first, count); + iter->second->erase (index.first, count); } int CSMWorld::RefIdData::getSize() const @@ -197,39 +197,39 @@ int CSMWorld::RefIdData::getSize() const return mIndex.size(); } -std::vector CSMWorld::RefIdData::getIds(bool listDeleted) const +std::vector CSMWorld::RefIdData::getIds (bool listDeleted) const { std::vector ids; - for (std::map::const_iterator iter(mIndex.begin()); iter != mIndex.end(); - ++iter) + for (std::map::const_iterator iter (mIndex.begin()); iter!=mIndex.end(); + ++iter) { - if (listDeleted || !getRecord(iter->second).isDeleted()) + if (listDeleted || !getRecord (iter->second).isDeleted()) { - std::map::const_iterator container = - mRecordContainers.find(iter->second.second); + std::map::const_iterator container = + mRecordContainers.find (iter->second.second); - if (container == mRecordContainers.end()) - throw std::logic_error("Invalid referenceable ID type"); + if (container==mRecordContainers.end()) + throw std::logic_error ("Invalid referenceable ID type"); - ids.push_back(container->second->getId(iter->second.first)); + ids.push_back (container->second->getId (iter->second.first)); } } return ids; } -void CSMWorld::RefIdData::save(int index, ESM::ESMWriter& writer) const +void CSMWorld::RefIdData::save (int index, ESM::ESMWriter& writer) const { - LocalIndex localIndex = globalToLocalIndex(index); + LocalIndex localIndex = globalToLocalIndex (index); - std::map::const_iterator iter = - mRecordContainers.find(localIndex.second); + std::map::const_iterator iter = + mRecordContainers.find (localIndex.second); - if (iter == mRecordContainers.end()) - throw std::logic_error("invalid local index type"); + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); - iter->second->save(localIndex.first, writer); + iter->second->save (localIndex.first, writer); } const CSMWorld::RefIdDataContainer< ESM::Book >& CSMWorld::RefIdData::getBooks() const From 79bc149c73f529a80f02b829a57da2581382d839 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 30 Dec 2013 18:47:19 +0100 Subject: [PATCH 268/889] reverting refidata.hpp --- apps/opencs/model/world/refiddata.hpp | 142 +++++++++++++------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 719b4b922..51f548499 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -41,19 +41,19 @@ namespace CSMWorld virtual int getSize() const = 0; - virtual const RecordBase& getRecord(int index) const = 0; + virtual const RecordBase& getRecord (int index) const = 0; - virtual RecordBase& getRecord(int index) = 0; + virtual RecordBase& getRecord (int index)= 0; - virtual void appendRecord(const std::string& id) = 0; + virtual void appendRecord (const std::string& id) = 0; - virtual void load(int index, ESM::ESMReader& reader, bool base) = 0; + virtual void load (int index, ESM::ESMReader& reader, bool base) = 0; - virtual void erase(int index, int count) = 0; + virtual void erase (int index, int count) = 0; - virtual std::string getId(int index) const = 0; + virtual std::string getId (int index) const = 0; - virtual void save(int index, ESM::ESMWriter& writer) const = 0; + virtual void save (int index, ESM::ESMWriter& writer) const = 0; }; template @@ -63,91 +63,90 @@ namespace CSMWorld virtual int getSize() const; - virtual const RecordBase& getRecord(int index) const; + virtual const RecordBase& getRecord (int index) const; - virtual RecordBase& getRecord(int index); + virtual RecordBase& getRecord (int index); - virtual void appendRecord(const std::string& id); + virtual void appendRecord (const std::string& id); - virtual void load(int index, ESM::ESMReader& reader, bool base); + virtual void load (int index, ESM::ESMReader& reader, bool base); - virtual void erase(int index, int count); + virtual void erase (int index, int count); - virtual std::string getId(int index) const; + virtual std::string getId (int index) const; - virtual void save(int index, ESM::ESMWriter& writer) const; + virtual void save (int index, ESM::ESMWriter& writer) const; }; template int RefIdDataContainer::getSize() const { - return static_cast(mContainer.size()); + return static_cast (mContainer.size()); } template - const RecordBase& RefIdDataContainer::getRecord(int index) const + const RecordBase& RefIdDataContainer::getRecord (int index) const { - return mContainer.at(index); + return mContainer.at (index); } template - RecordBase& RefIdDataContainer::getRecord(int index) + RecordBase& RefIdDataContainer::getRecord (int index) { - return mContainer.at(index); + return mContainer.at (index); } template - void RefIdDataContainer::appendRecord(const std::string& id) + void RefIdDataContainer::appendRecord (const std::string& id) { Record record; record.mModified.mId = id; record.mModified.blank(); record.mState = RecordBase::State_ModifiedOnly; - mContainer.push_back(record); + mContainer.push_back (record); } template - void RefIdDataContainer::load(int index, ESM::ESMReader& reader, bool base) + void RefIdDataContainer::load (int index, ESM::ESMReader& reader, bool base) { - (base ? mContainer.at(index).mBase : mContainer.at(index).mModified).load(reader); + (base ? mContainer.at (index).mBase : mContainer.at (index).mModified).load (reader); } template - void RefIdDataContainer::erase(int index, int count) + void RefIdDataContainer::erase (int index, int count) { - if (index < 0 || index + count >= getSize()) - throw std::runtime_error("invalid RefIdDataContainer index"); + if (index<0 || index+count>=getSize()) + throw std::runtime_error ("invalid RefIdDataContainer index"); - mContainer.erase(mContainer.begin() + index, mContainer.begin() + index + count); + mContainer.erase (mContainer.begin()+index, mContainer.begin()+index+count); } template - std::string RefIdDataContainer::getId(int index) const + std::string RefIdDataContainer::getId (int index) const { - return mContainer.at(index).get().mId; + return mContainer.at (index).get().mId; } template - void RefIdDataContainer::save(int index, ESM::ESMWriter& writer) const + void RefIdDataContainer::save (int index, ESM::ESMWriter& writer) const { - CSMWorld::RecordBase::State state = mContainer.at(index).mState; + CSMWorld::RecordBase::State state = mContainer.at (index).mState; - if (state == CSMWorld::RecordBase::State_Modified || - state == CSMWorld::RecordBase::State_ModifiedOnly) + if (state==CSMWorld::RecordBase::State_Modified || + state==CSMWorld::RecordBase::State_ModifiedOnly) { std::string type; - - for (int i = 0; i < 4; ++i) + for (int i=0; i<4; ++i) /// \todo make endianess agnostic (change ESMWriter interface?) - type += reinterpret_cast(&mContainer.at(index).mModified.sRecordId)[i]; + type += reinterpret_cast (&mContainer.at (index).mModified.sRecordId)[i]; - writer.startRecord(type); - writer.writeHNCString("NAME", getId(index)); - mContainer.at(index).mModified.save(writer); - writer.endRecord(type); + writer.startRecord (type); + writer.writeHNCString ("NAME", getId (index)); + mContainer.at (index).mModified.save (writer); + writer.endRecord (type); } - else if (state == CSMWorld::RecordBase::State_Deleted) + else if (state==CSMWorld::RecordBase::State_Deleted) { /// \todo write record with delete flag } @@ -185,59 +184,59 @@ namespace CSMWorld std::map mIndex; - std::map mRecordContainers; + std::map mRecordContainers; - void erase(const LocalIndex& index, int count); + void erase (const LocalIndex& index, int count); ///< Must not spill over into another type. public: RefIdData(); - LocalIndex globalToLocalIndex(int index) const; + LocalIndex globalToLocalIndex (int index) const; - int localToGlobalIndex(const LocalIndex& index) const; + int localToGlobalIndex (const LocalIndex& index) const; - LocalIndex searchId(const std::string& id) const; + LocalIndex searchId (const std::string& id) const; - void erase(int index, int count); + void erase (int index, int count); - const RecordBase& getRecord(const LocalIndex& index) const; + const RecordBase& getRecord (const LocalIndex& index) const; - RecordBase& getRecord(const LocalIndex& index); + RecordBase& getRecord (const LocalIndex& index); - void appendRecord(UniversalId::Type type, const std::string& id); + void appendRecord (UniversalId::Type type, const std::string& id); - int getAppendIndex(UniversalId::Type type) const; + int getAppendIndex (UniversalId::Type type) const; - void load(const LocalIndex& index, ESM::ESMReader& reader, bool base); + void load (const LocalIndex& index, ESM::ESMReader& reader, bool base); int getSize() const; - std::vector getIds(bool listDeleted = true) const; + std::vector getIds (bool listDeleted = true) const; ///< Return a sorted collection of all IDs /// /// \param listDeleted include deleted record in the list - void save(int index, ESM::ESMWriter& writer) const; + void save (int index, ESM::ESMWriter& writer) const; - //RECORD CONTAINERS ACCESS METHODS - const RefIdDataContainer& getBooks() const; - const RefIdDataContainer& getActivators() const; - const RefIdDataContainer& getPotions() const; - const RefIdDataContainer& getApparati() const; - const RefIdDataContainer& getArmors() const; - const RefIdDataContainer& getClothing() const; - const RefIdDataContainer& getContainers() const; - const RefIdDataContainer& getCreatures() const; - const RefIdDataContainer& getDoors() const; - const RefIdDataContainer& getIngredients() const; - const RefIdDataContainer& getCreatureLevelledLists() const; - const RefIdDataContainer& getItemLevelledList() const; - const RefIdDataContainer& getLights() const; - const RefIdDataContainer& getLocpicks() const; - const RefIdDataContainer& getMiscellaneous() const; - const RefIdDataContainer& getNPCs() const; + //RECORD CONTAINERS ACCESS METHODS + const RefIdDataContainer& getBooks() const; + const RefIdDataContainer& getActivators() const; + const RefIdDataContainer& getPotions() const; + const RefIdDataContainer& getApparati() const; + const RefIdDataContainer& getArmors() const; + const RefIdDataContainer& getClothing() const; + const RefIdDataContainer& getContainers() const; + const RefIdDataContainer& getCreatures() const; + const RefIdDataContainer& getDoors() const; + const RefIdDataContainer& getIngredients() const; + const RefIdDataContainer& getCreatureLevelledLists() const; + const RefIdDataContainer& getItemLevelledList() const; + const RefIdDataContainer& getLights() const; + const RefIdDataContainer& getLocpicks() const; + const RefIdDataContainer& getMiscellaneous() const; + const RefIdDataContainer& getNPCs() const; }; } @@ -245,3 +244,4 @@ namespace CSMWorld + From b8ac37347ca2c01a086ba31fd8de7906d5de6beb Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 30 Dec 2013 19:04:35 +0100 Subject: [PATCH 269/889] Code cleanup for Class::canBeEquipped --- apps/openmw/mwclass/armor.cpp | 49 +++++++++++++------------------- apps/openmw/mwclass/clothing.cpp | 37 +++++++++--------------- apps/openmw/mwclass/weapon.cpp | 31 +++++++++----------- 3 files changed, 45 insertions(+), 72 deletions(-) diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 5b2b7caa3..c7c80ec2e 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -294,41 +294,30 @@ namespace MWClass // slots that this item can be equipped in std::pair, bool> slots_ = MWWorld::Class::get(ptr).getEquipmentSlots(ptr); + if (slots_.first.empty()) + return std::make_pair(0, ""); + std::string npcRace = npc.get()->mBase->mRace; + // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) + const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); + if(race->mData.mFlags & ESM::Race::Beast) + { + std::vector parts = ptr.get()->mBase->mParts.mParts; + + for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) + { + if((*itr).mPart == ESM::PRT_Head) + return std::make_pair(0, "#{sNotifyMessage13}"); + if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) + return std::make_pair(0, "#{sNotifyMessage14}"); + } + } + for (std::vector::const_iterator slot=slots_.first.begin(); slot!=slots_.first.end(); ++slot) { - - // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) - const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); - if(race->mData.mFlags & ESM::Race::Beast) - { - std::vector parts = ptr.get()->mBase->mParts.mParts; - - if(*slot == MWWorld::InventoryStore::Slot_Helmet) - { - for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) - { - if((*itr).mPart == ESM::PRT_Head) - { - return std::make_pair(0, "#{sNotifyMessage13}"); - } - } - } - - if (*slot == MWWorld::InventoryStore::Slot_Boots) - { - for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) - { - if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) - { - return std::make_pair(0, "#{sNotifyMessage14}"); - } - } - } - } - + // If equipping a shield, check if there's a twohanded weapon conflicting with it if(*slot == MWWorld::InventoryStore::Slot_CarriedLeft) { MWWorld::ContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index c162bbe9d..ffa96260d 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -235,37 +235,26 @@ namespace MWClass // slots that this item can be equipped in std::pair, bool> slots_ = MWWorld::Class::get(ptr).getEquipmentSlots(ptr); + if (slots_.first.empty()) + return std::make_pair(0, ""); + std::string npcRace = npc.get()->mBase->mRace; - for (std::vector::const_iterator slot=slots_.first.begin(); - slot!=slots_.first.end(); ++slot) + // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) + const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); + if(race->mData.mFlags & ESM::Race::Beast) { + std::vector parts = ptr.get()->mBase->mParts.mParts; - // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) - const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); - if(race->mData.mFlags & ESM::Race::Beast) + for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) { - std::vector parts = ptr.get()->mBase->mParts.mParts; - - if(*slot == MWWorld::InventoryStore::Slot_Helmet) - { - for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) - { - if((*itr).mPart == ESM::PRT_Head) - return std::make_pair(0, "#{sNotifyMessage13}"); - } - } - - if (*slot == MWWorld::InventoryStore::Slot_Boots) - { - for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) - { - if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) - return std::make_pair(0, "#{sNotifyMessage15}"); - } - } + if((*itr).mPart == ESM::PRT_Head) + return std::make_pair(0, "#{sNotifyMessage13}"); + if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) + return std::make_pair(0, "#{sNotifyMessage15}"); } } + return std::make_pair (1, ""); } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index a09e83380..7b0a5eb47 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -390,26 +390,21 @@ namespace MWClass { std::pair, bool> slots_ = MWWorld::Class::get(ptr).getEquipmentSlots(ptr); - // equip the item in the first free slot - for (std::vector::const_iterator slot=slots_.first.begin(); - slot!=slots_.first.end(); ++slot) + if (slots_.first.empty()) + return std::make_pair (0, ""); + + if(ptr.get()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand || + ptr.get()->mBase->mData.mType == ESM::Weapon::BluntTwoClose || + ptr.get()->mBase->mData.mType == ESM::Weapon::BluntTwoWide || + ptr.get()->mBase->mData.mType == ESM::Weapon::SpearTwoWide || + ptr.get()->mBase->mData.mType == ESM::Weapon::AxeTwoHand || + ptr.get()->mBase->mData.mType == ESM::Weapon::MarksmanBow || + ptr.get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) { - if(*slot == MWWorld::InventoryStore::Slot_CarriedRight) - { - if(ptr.get()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand || - ptr.get()->mBase->mData.mType == ESM::Weapon::BluntTwoClose || - ptr.get()->mBase->mData.mType == ESM::Weapon::BluntTwoWide || - ptr.get()->mBase->mData.mType == ESM::Weapon::SpearTwoWide || - ptr.get()->mBase->mData.mType == ESM::Weapon::AxeTwoHand || - ptr.get()->mBase->mData.mType == ESM::Weapon::MarksmanBow || - ptr.get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) - { - return std::make_pair (2, ""); - } - } - return std::make_pair(1, ""); + return std::make_pair (2, ""); } - return std::make_pair (0, ""); + + return std::make_pair(1, ""); } boost::shared_ptr Weapon::use (const MWWorld::Ptr& ptr) const From cb723710fefd35f8b4b83550ecc06878c37812e5 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 30 Dec 2013 19:24:53 +0100 Subject: [PATCH 270/889] Corrected stupid typo. --- apps/opencs/model/tools/referenceablecheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 3ca203bdf..2b796ccc0 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -945,7 +945,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI } else //checking if there is such class { - if (mClasses.searchId(NPC.mClass)) + if (mClasses.searchId(NPC.mClass) == -1) { messages.push_back(id.toString() + "|" + NPC.mId + " has invalid class"); } From 5c5f87445b437d480e5877518403706d64acae78 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Mon, 30 Dec 2013 21:47:06 +0100 Subject: [PATCH 271/889] Fixes for "Conditional jump or move depends on uninitialised value(s)" and memleaks reported by valgrind. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwmechanics/actors.cpp | 10 ++++++++++ apps/openmw/mwmechanics/actors.hpp | 1 + apps/openmw/mwmechanics/objects.cpp | 10 ++++++++++ apps/openmw/mwmechanics/objects.hpp | 1 + apps/openmw/mwrender/camera.cpp | 10 ++++++---- apps/openmw/mwrender/water.cpp | 1 + apps/openmw/mwsound/ffmpeg_decoder.cpp | 25 +++++++++++++++++++++++-- libs/openengine/ogre/renderer.hpp | 3 ++- 8 files changed, 54 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 1aa9ce9f5..8f03b8349 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -516,6 +516,16 @@ namespace MWMechanics Actors::Actors() {} + Actors::~Actors() + { + PtrControllerMap::iterator it(mActors.begin()); + for (; it != mActors.end(); ++it) + { + delete it->second; + it->second = NULL; + } + } + void Actors::addActor (const MWWorld::Ptr& ptr) { // erase previous death events since we are currently only tracking them while in an active cell diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 411ac54ca..80040680b 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -49,6 +49,7 @@ namespace MWMechanics public: Actors(); + ~Actors(); /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently /// paused we may want to do it manually (after equipping permanent enchantment) diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp index 694987855..41d6b4ffa 100644 --- a/apps/openmw/mwmechanics/objects.cpp +++ b/apps/openmw/mwmechanics/objects.cpp @@ -14,6 +14,16 @@ Objects::Objects() { } +Objects::~Objects() +{ + PtrControllerMap::iterator it(mObjects.begin()); + for (; it != mObjects.end();++it) + { + delete it->second; + it->second = NULL; + } +} + void Objects::addObject(const MWWorld::Ptr& ptr) { removeObject(ptr); diff --git a/apps/openmw/mwmechanics/objects.hpp b/apps/openmw/mwmechanics/objects.hpp index 5cdcdaa0a..32432c130 100644 --- a/apps/openmw/mwmechanics/objects.hpp +++ b/apps/openmw/mwmechanics/objects.hpp @@ -21,6 +21,7 @@ namespace MWMechanics public: Objects(); + ~Objects(); void addObject (const MWWorld::Ptr& ptr); ///< Register an animated object diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 8f54be3f8..bf71505bc 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -19,25 +19,27 @@ namespace MWRender Camera::Camera (Ogre::Camera *camera) : mCamera(camera), mCameraNode(NULL), + mAnimation(NULL), mFirstPersonView(true), mPreviewMode(false), mFreeLook(true), - mHeight(128.f), - mCameraDistance(300.f), - mDistanceAdjusted(false), - mAnimation(NULL), mNearest(30.f), mFurthest(800.f), mIsNearest(false), mIsFurthest(false), + mHeight(128.f), + mCameraDistance(300.f), + mDistanceAdjusted(false), mVanityToggleQueued(false), mViewModeToggleQueued(false) { mVanity.enabled = false; mVanity.allowed = true; + mPreviewCam.pitch = 0.f; mPreviewCam.yaw = 0.f; mPreviewCam.offset = 400.f; + mMainCam.pitch = 0.f; mMainCam.yaw = 0.f; mMainCam.offset = 400.f; } diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 082551f37..0a4db30e9 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -292,6 +292,7 @@ Water::~Water() delete mReflection; delete mRefraction; + delete mSimulation; } void Water::changeCell(const ESM::Cell* cell) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index d5c382a41..b5a2ff1d1 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -158,6 +158,11 @@ void FFmpeg_Decoder::open(const std::string &fname) mFormatCtx->pb = avio_alloc_context(NULL, 0, 0, this, readPacket, writePacket, seek); if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), NULL, NULL) != 0) { + if (mFormatCtx->pb != NULL) + { + avio_close(mFormatCtx->pb); + mFormatCtx->pb = NULL; + } avformat_free_context(mFormatCtx); mFormatCtx = NULL; fail("Failed to allocate input stream"); @@ -195,6 +200,9 @@ void FFmpeg_Decoder::open(const std::string &fname) } catch(std::exception &e) { + avio_close(mFormatCtx->pb); + mFormatCtx->pb = NULL; + avformat_close_input(&mFormatCtx); throw; } @@ -211,9 +219,22 @@ void FFmpeg_Decoder::close() if(mFormatCtx) { - AVIOContext* context = mFormatCtx->pb; + if (mFormatCtx->pb != NULL) + { + avio_flush(mFormatCtx->pb); + + // + // avio-close() gives segfault, but with av_free valgrind shows memleak + // near mFormatCtx->pb + // + // to be checked! + // + // avio_close(mFormatCtx->pb); + // + av_free(mFormatCtx->pb); + mFormatCtx->pb = NULL; + } avformat_close_input(&mFormatCtx); - av_free(context); } mDataStream.setNull(); diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index f45f09b01..767e7cf99 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -74,8 +74,9 @@ namespace OEngine , mScene(NULL) , mCamera(NULL) , mView(NULL) - , mWindowListener(NULL) + , mOgreInit(NULL) , mFader(NULL) + , mWindowListener(NULL) { } From 6107d5bad26188ba772a998639b17aeadd6dfd28 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Mon, 30 Dec 2013 22:16:06 +0100 Subject: [PATCH 272/889] Updated ffmpeg decoder fix Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwsound/ffmpeg_decoder.cpp | 30 +++++++++++++++++--------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index b5a2ff1d1..13d11f691 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -160,7 +160,12 @@ void FFmpeg_Decoder::open(const std::string &fname) { if (mFormatCtx->pb != NULL) { - avio_close(mFormatCtx->pb); + if (mFormatCtx->pb->buffer != NULL) + { + av_free(mFormatCtx->pb->buffer); + mFormatCtx->pb->buffer = NULL; + } + av_free(mFormatCtx->pb); mFormatCtx->pb = NULL; } avformat_free_context(mFormatCtx); @@ -200,7 +205,12 @@ void FFmpeg_Decoder::open(const std::string &fname) } catch(std::exception &e) { - avio_close(mFormatCtx->pb); + if (mFormatCtx->pb->buffer != NULL) + { + av_free(mFormatCtx->pb->buffer); + mFormatCtx->pb->buffer = NULL; + } + av_free(mFormatCtx->pb); mFormatCtx->pb = NULL; avformat_close_input(&mFormatCtx); @@ -221,16 +231,16 @@ void FFmpeg_Decoder::close() { if (mFormatCtx->pb != NULL) { - avio_flush(mFormatCtx->pb); - + // valgrind shows memleak near mFormatCtx->pb // - // avio-close() gives segfault, but with av_free valgrind shows memleak - // near mFormatCtx->pb - // - // to be checked! - // - // avio_close(mFormatCtx->pb); + // As scrawl pointed, memleak could be related to this ffmpeg ticket: + // https://trac.ffmpeg.org/ticket/1357 // + if (mFormatCtx->pb->buffer != NULL) + { + av_free(mFormatCtx->pb->buffer); + mFormatCtx->pb->buffer = NULL; + } av_free(mFormatCtx->pb); mFormatCtx->pb = NULL; } From cb04f43384603728e89b220df6d403571d5a46c6 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Mon, 30 Dec 2013 23:08:53 +0100 Subject: [PATCH 273/889] Fixes for "Conditional jump or move depends on uninitialised value(s)" and memleaks reported by valgrind. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwgui/hud.cpp | 2 ++ apps/openmw/mwgui/messagebox.cpp | 9 ++++++++ apps/openmw/mwgui/messagebox.hpp | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 1 + apps/openmw/mwrender/videoplayer.cpp | 29 ++++++++++++++++++++++++-- 5 files changed, 40 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index a78b1a6d1..8ef5e59d0 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -55,6 +55,8 @@ namespace MWGui , mWorldMouseOver(false) , mEnemyHealthTimer(0) , mIsDrowning(false) + , mWeaponSpellTimer(0.f) + , mDrowningFlashTheta(0.f) { setCoord(0,0, width, height); diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 671845552..378e76e46 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -16,6 +16,15 @@ namespace MWGui mLastButtonPressed = -1; } + MessageBoxManager::~MessageBoxManager () + { + std::vector::iterator it(mMessageBoxes.begin()); + for (; it != mMessageBoxes.end(); ++it) + { + delete *it; + } + } + void MessageBoxManager::onFrame (float frameDuration) { std::vector::iterator it; diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index aac4704fa..0288f366c 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -23,6 +23,7 @@ namespace MWGui { public: MessageBoxManager (); + ~MessageBoxManager (); void onFrame (float frameDuration); void createMessageBox (const std::string& message, bool stat = false); void removeStaticMessageBox (); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 8818721f4..627c542f2 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -322,6 +322,7 @@ namespace MWGui delete mSoulgemDialog; delete mCursorManager; delete mRecharge; + delete mCompanionWindow; cleanupGarbage(); diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 88bc6d8ac..74b52c06b 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -950,8 +950,22 @@ void VideoState::init(const std::string& resourceName) // Open video file /// \todo leak here, ffmpeg or valgrind bug ? + /// + /// https://trac.ffmpeg.org/ticket/1357 + /// if(!this->format_ctx || avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL)) { + if (this->format_ctx != NULL) + { + if (this->format_ctx->pb != NULL) + { + av_free(this->format_ctx->pb->buffer); + this->format_ctx->pb->buffer = NULL; + + av_free(this->format_ctx->pb); + this->format_ctx->pb = NULL; + } + } // "Note that a user-supplied AVFormatContext will be freed on failure." this->format_ctx = NULL; av_free(ioCtx); @@ -1009,9 +1023,20 @@ void VideoState::deinit() if(this->format_ctx) { - AVIOContext *ioContext = this->format_ctx->pb; + // valgrind shows memleak near format_ctx->pb + // + // As scrawl pointed, memleak could be related to this ffmpeg ticket: + // https://trac.ffmpeg.org/ticket/1357 + // + if (this->format_ctx->pb != NULL) + { + av_free(this->format_ctx->pb->buffer); + this->format_ctx->pb->buffer = NULL; + + av_free(this->format_ctx->pb); + this->format_ctx->pb = NULL;; + } avformat_close_input(&this->format_ctx); - av_free(ioContext); } } From 50b6e828cc8473031c6bc9e8b078587b27d00d05 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Mon, 30 Dec 2013 23:51:44 +0100 Subject: [PATCH 274/889] Added asserts in Interpreter::installSegmentX methods. Signed-off-by: Lukasz Gromanowski --- components/compiler/opcodes.hpp | 8 ++++---- components/interpreter/interpreter.cpp | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index a885a373d..7ca2a1a64 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -25,9 +25,9 @@ namespace Compiler const int opcodeAiFollowExplicit = 0x20023; const int opcodeAiFollowCell = 0x20024; const int opcodeAiFollowCellExplicit = 0x20025; - const int opcodeSetHello = 0x200015e; + const int opcodeSetHello = 0x200015e; // Same id as opcodeSetFight const int opcodeSetHelloExplicit = 0x200015d; - const int opcodeSetFight = 0x200015e; + const int opcodeSetFight = 0x200015e; // Same id as opcodeSetHello const int opcodeSetFightExplicit = 0x200015f; const int opcodeSetFlee = 0x2000160; const int opcodeSetFleeExplicit = 0x2000161; @@ -69,7 +69,7 @@ namespace Compiler { const int opcodeCellChanged = 0x2000000; const int opcodeCOC = 0x2000026; - const int opcodeCOE = 0x200008e; + const int opcodeCOE = 0x200008e; // Same ID as opcodeGetSkill const int opcodeGetInterior = 0x2000131; const int opcodeGetPCCell = 0x2000136; const int opcodeGetWaterLevel = 0x2000141; @@ -290,7 +290,7 @@ namespace Compiler const int opcodeGetDynamicGetRatio = 0x200006f; const int opcodeGetDynamicGetRatioExplicit = 0x2000072; - const int opcodeGetSkill = 0x200008e; + const int opcodeGetSkill = 0x200008e; // Same ID as opcodeCOE const int opcodeGetSkillExplicit = 0x20000a9; const int opcodeSetSkill = 0x20000c4; const int opcodeSetSkillExplicit = 0x20000df; diff --git a/components/interpreter/interpreter.cpp b/components/interpreter/interpreter.cpp index 10937e6bc..ea1e9fc91 100644 --- a/components/interpreter/interpreter.cpp +++ b/components/interpreter/interpreter.cpp @@ -166,31 +166,37 @@ namespace Interpreter void Interpreter::installSegment0 (int code, Opcode1 *opcode) { + assert(mSegment0.find(code) == mSegment0.end()); mSegment0.insert (std::make_pair (code, opcode)); } void Interpreter::installSegment1 (int code, Opcode2 *opcode) { + assert(mSegment1.find(code) == mSegment1.end()); mSegment1.insert (std::make_pair (code, opcode)); } void Interpreter::installSegment2 (int code, Opcode1 *opcode) { + assert(mSegment2.find(code) == mSegment2.end()); mSegment2.insert (std::make_pair (code, opcode)); } void Interpreter::installSegment3 (int code, Opcode1 *opcode) { + assert(mSegment3.find(code) == mSegment3.end()); mSegment3.insert (std::make_pair (code, opcode)); } void Interpreter::installSegment4 (int code, Opcode2 *opcode) { + assert(mSegment4.find(code) == mSegment4.end()); mSegment4.insert (std::make_pair (code, opcode)); } void Interpreter::installSegment5 (int code, Opcode0 *opcode) { + assert(mSegment5.find(code) == mSegment5.end()); mSegment5.insert (std::make_pair (code, opcode)); } From 94cdc1efd2a119bc6c344b043ef42db17f07f844 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 31 Dec 2013 00:54:40 +0100 Subject: [PATCH 275/889] Enable mipmap generator for 1.9+ --- apps/openmw/mwrender/renderingmanager.cpp | 9 ++++++--- files/materials/water.mat | 2 +- files/settings-default.cfg | 3 +-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8ee292de1..b739a9f95 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -109,10 +109,13 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b mFactory->loadAllFiles(); - // Set default mipmap level (NB some APIs ignore this) - // Mipmap generation is currently disabled because it causes issues on Intel/AMD - //TextureManager::getSingleton().setDefaultNumMipmaps(Settings::Manager::getInt("num mipmaps", "General")); + // Compressed textures with 0 mip maps are bugged in 1.8, so disable mipmap generator in that case + // ( https://ogre3d.atlassian.net/browse/OGRE-259 ) +#if OGRE_VERSION >= (1 << 16 | 9 << 8 | 0) + TextureManager::getSingleton().setDefaultNumMipmaps(Settings::Manager::getInt("num mipmaps", "General")); +#else TextureManager::getSingleton().setDefaultNumMipmaps(0); +#endif // Set default texture filtering options TextureFilterOptions tfo; diff --git a/files/materials/water.mat b/files/materials/water.mat index 1e5f8c8e0..ade55f326 100644 --- a/files/materials/water.mat +++ b/files/materials/water.mat @@ -37,7 +37,7 @@ material Water texture_unit normalMap { - texture water_nm.png 5 + texture water_nm.png } texture_unit rippleNormalMap diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 2ca59159b..22391fe93 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -53,8 +53,7 @@ texture filtering = anisotropic anisotropy = 4 # Number of texture mipmaps to generate -# This setting is currently ignored due to mipmap generation problems on Intel/AMD -#num mipmaps = 5 +num mipmaps = 8 shader mode = From 254eba350eb2ecc873b6866b43cd1a482fe8afb8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 31 Dec 2013 00:56:04 +0100 Subject: [PATCH 276/889] Not handling interpolation type should be an error, since it will fail reading the next record if it hasn't read the previous one completely. --- components/nif/niffile.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index 6e629772e..91ae93b40 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -200,7 +200,7 @@ struct KeyListT { } } else - nif->file->warn("Unhandled interpolation type: "+Ogre::StringConverter::toString(mInterpolationType)); + nif->file->fail("Unhandled interpolation type: "+Ogre::StringConverter::toString(mInterpolationType)); } }; typedef KeyListT FloatKeyList; From d6345bce91e3d65cf69c5f7368e58be348a22763 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Tue, 31 Dec 2013 13:24:20 +0200 Subject: [PATCH 277/889] added npc hit reactions --- apps/openmw/mwclass/npc.cpp | 1 + apps/openmw/mwmechanics/character.cpp | 134 ++++++++++++++------------ apps/openmw/mwmechanics/character.hpp | 11 ++- 3 files changed, 86 insertions(+), 60 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4187aa8c1..87d98a578 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -604,6 +604,7 @@ namespace MWClass // something, alert the character controller, scripts, etc. MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); + getCreatureStats(ptr).setAttacked(true); if(object.isEmpty()) { diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 17eff3d02..f4d6feab6 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -65,15 +65,24 @@ struct StateInfo { const char groupname[32]; }; -static const StateInfo sDeathList[] = { - { CharState_Death1, "death1" }, - { CharState_Death2, "death2" }, - { CharState_Death3, "death3" }, - { CharState_Death4, "death4" }, - { CharState_Death5, "death5" }, - { CharState_SwimDeath, "swimdeath" }, +static const std::string sDeathList[] = { + "death1" , + "death2" , + "death3" , + "death4" , + "death5" , + "swimdeath", }; -static const StateInfo *sDeathListEnd = &sDeathList[sizeof(sDeathList)/sizeof(sDeathList[0])]; +static const int sDeathListSize = sizeof(sDeathList)/sizeof(sDeathList[0]); + +static const std::string sHitList[] = { + "hit1" , + "hit2" , + "hit3" , + "hit4" , + "hit5" , +}; +static const int sHitListSize = sizeof(sHitList)/sizeof(sHitList[0]); static const StateInfo sMovementList[] = { { CharState_WalkForward, "walkforward" }, @@ -150,6 +159,38 @@ public: void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force) { + if(MWWorld::Class::get(mPtr).getCreatureStats(mPtr).getAttacked()) + { + mHitState = CharState_Hit; + MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setAttacked(false); + + if(!mAnimation->isPlaying(mCurrentHit)) + { + mAnimation->disable(mCurrentHit); + if(mJumpState != JumpState_None && !MWBase::Environment::get().getWorld()->isFlying(mPtr) + && !MWBase::Environment::get().getWorld()->isSwimming(mPtr) ) + mCurrentHit = "knockdown"; + else + { + int iHit = rand() % sHitListSize; + mCurrentHit = sHitList[iHit]; + } + mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, false, 1, "start", "stop", 0.0f, 0); + if(mUpperBodyState == UpperCharState_WeapEquiped) + mUpdateAfterHit = false; + else + mUpdateAfterHit = true; + } + return; + } + else if(!mAnimation->isPlaying(mCurrentHit)) + { + mAnimation->disable(mCurrentHit); + mCurrentHit.erase(); + mHitState = CharState_None; + mUpdateAfterHit = true; + } + const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); if(force || idle != mIdleState) @@ -338,6 +379,23 @@ MWWorld::ContainerStoreIterator CharacterController::getActiveWeapon(NpcStats &s return inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); } +void CharacterController::playRandomDeath(float startpoint) +{ + if(MWBase::Environment::get().getWorld()->isSwimming(mPtr)) + { + mDeathState = CharState_SwimDeath; + mCurrentDeath = sDeathList[sDeathListSize-1]; //last in the list is 'swimdeath' + } + else + { + int num = rand() % (sDeathListSize-1); + mDeathState = static_cast(CharState_Death1 + num); + mCurrentDeath = sDeathList[num]; + } + + mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, + false, 1.0f, "start", "stop", 0.0f, 0); +} CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim) : mPtr(ptr) @@ -346,6 +404,8 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim , mMovementState(CharState_None) , mMovementSpeed(0.0f) , mDeathState(CharState_None) + , mHitState(CharState_None) + , mUpdateAfterHit(true) , mUpperBodyState(UpperCharState_Nothing) , mJumpState(JumpState_None) , mWeaponType(WeapType_None) @@ -392,13 +452,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim refreshCurrentAnims(mIdleState, mMovementState, true); if(mDeathState != CharState_None) { - const StateInfo *state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState)); - if(state == sDeathListEnd) - throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState)); - - mCurrentDeath = state->groupname; - mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, - false, 1.0f, "start", "stop", 1.0f, 0); + playRandomDeath(1.0f); } } @@ -766,6 +820,8 @@ void CharacterController::update(float duration) bool sneak = cls.getStance(mPtr, MWWorld::Class::Sneak); bool flying = world->isFlying(mPtr); Ogre::Vector3 vec = cls.getMovementVector(mPtr); + if(mHitState != CharState_None && mJumpState == JumpState_None) + vec = Ogre::Vector3(0.0f); Ogre::Vector3 rot = cls.getRotationVector(mPtr); mMovementSpeed = cls.getSpeed(mPtr); @@ -778,6 +834,7 @@ void CharacterController::update(float duration) isrunning = isrunning && std::abs(vec[0])+std::abs(vec[1]) > 0.0f; + // advance athletics if(std::abs(vec[0])+std::abs(vec[1]) > 0.0f && mPtr.getRefData().getHandle() == "player") { @@ -951,7 +1008,7 @@ void CharacterController::update(float duration) } } - if(cls.isNpc()) + if(cls.isNpc() && mUpdateAfterHit) forcestateupdate = updateNpcState(onground, inwater, isrunning, sneak) || forcestateupdate; refreshCurrentAnims(idlestate, movestate, forcestateupdate); @@ -1055,14 +1112,7 @@ void CharacterController::forceStateUpdate() refreshCurrentAnims(mIdleState, mMovementState, true); if(mDeathState != CharState_None) { - const StateInfo *state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState)); - if(state == sDeathListEnd) - throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState)); - - mCurrentDeath = state->groupname; - if(!mAnimation->getInfo(mCurrentDeath)) - mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, - false, 1.0f, "start", "stop", 0.0f, 0); + playRandomDeath(0.0f); } } @@ -1071,44 +1121,10 @@ void CharacterController::kill() if(mDeathState != CharState_None) return; - if(mPtr.getTypeName() == typeid(ESM::NPC).name()) - { - const StateInfo *state = NULL; - if(MWBase::Environment::get().getWorld()->isSwimming(mPtr)) - { - mDeathState = CharState_SwimDeath; - state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState)); - if(state == sDeathListEnd) - throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState)); - } - - static const CharacterState deathstates[5] = { - CharState_Death1, CharState_Death2, CharState_Death3, CharState_Death4, CharState_Death5 - }; - std::vector states(&deathstates[0], &deathstates[5]); - - while(states.size() > 1 && (!state || !mAnimation->hasAnimation(state->groupname))) - { - int pos = (int)(rand()/((double)RAND_MAX+1.0)*states.size()); - mDeathState = states[pos]; - states.erase(states.begin()+pos); - - state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState)); - if(state == sDeathListEnd) - throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState)); - } - mCurrentDeath = state->groupname; - } - else - { - mDeathState = CharState_Death1; - mCurrentDeath = "death1"; - } + playRandomDeath(0.0f); if(mAnimation) { - mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, - false, 1.0f, "start", "stop", 0.0f, 0); mAnimation->disable(mCurrentIdle); } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 817fa2fd5..e0b065cec 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -28,6 +28,7 @@ enum Priority { Priority_Default, Priority_Jump, Priority_Movement, + Priority_Hit, Priority_Weapon, Priority_Torch, @@ -87,7 +88,9 @@ enum CharacterState { CharState_Death3, CharState_Death4, CharState_Death5, - CharState_SwimDeath + CharState_SwimDeath, + + CharState_Hit }; enum WeaponType { @@ -142,6 +145,10 @@ class CharacterController CharacterState mDeathState; std::string mCurrentDeath; + CharacterState mHitState; + std::string mCurrentHit; + bool mUpdateAfterHit;//don't allow attack if it hadn't been started before actor got hit + UpperBodyCharacterState mUpperBodyState; JumpingState mJumpState; @@ -172,6 +179,8 @@ class CharacterController void updateVisibility(); + void playRandomDeath(float startpoint); + public: CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim); virtual ~CharacterController(); From 07a9b7623a7b1e543c372a9908bef8b773b3e24c Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 31 Dec 2013 01:12:50 +0100 Subject: [PATCH 278/889] Enable skeleton-based bounding boxes added in Ogre 1.10 --- components/nifogre/ogrenifloader.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 345e7d6fd..453eff236 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -607,6 +607,14 @@ class NIFObjectLoader NIFMeshLoader::createMesh(name, fullname, group, shape->recIndex); Ogre::Entity *entity = sceneMgr->createEntity(fullname); + +#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0) + // Enable skeleton-based bounding boxes. With the static bounding box, + // the animation may cause parts to go outside the box and cause culling problems. + if (entity->hasSkeleton()) + entity->setUpdateBoundingBoxFromSkeleton(true); +#endif + entity->setVisible(!(flags&Nif::NiNode::Flag_Hidden)); scene->mEntities.push_back(entity); From 8381cad5a4d5155f58d3e8a4386b78d44a29c425 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Tue, 31 Dec 2013 14:13:42 +0100 Subject: [PATCH 279/889] Don't try to set a cursor before one exists --- apps/openmw/mwgui/windowmanagerimp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 8818721f4..6c6f51967 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -174,11 +174,11 @@ namespace MWGui MyGUI::InputManager::getInstance().eventChangeKeyFocus += MyGUI::newDelegate(this, &WindowManager::onKeyFocusChanged); - mCursorManager->setEnabled(true); - onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer()); SDL_ShowCursor(false); + mCursorManager->setEnabled(true); + // hide mygui's pointer MyGUI::PointerManager::getInstance().setVisible(false); } From 764ec9bc5f2f3fe3cb331226ec19a1fac7da5388 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 31 Dec 2013 15:50:27 +0100 Subject: [PATCH 280/889] Closes #716: Use the particle controller's size instead of NiAutoNormalParticlesData particle radius. Same as NifSkope now. --- components/nifogre/ogrenifloader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 453eff236..5f9674c91 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -839,8 +839,6 @@ class NIFObjectLoader vertprop, zprop, specprop, wireprop, needTangents)); - partsys->setDefaultDimensions(particledata->particleRadius*2.0f, - particledata->particleRadius*2.0f); partsys->setCullIndividually(false); partsys->setParticleQuota(particledata->numParticles); partsys->setKeepParticlesInLocalSpace(partflags & (Nif::NiNode::ParticleFlag_LocalSpace)); @@ -856,6 +854,8 @@ class NIFObjectLoader { const Nif::NiParticleSystemController *partctrl = static_cast(ctrl.getPtr()); + partsys->setDefaultDimensions(partctrl->size, partctrl->size); + if(!partctrl->emitter.empty()) { int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex); From a8fb1ae51c89c21aadaf2e4a3c263a2c6efa8f49 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Tue, 31 Dec 2013 17:55:04 +0200 Subject: [PATCH 281/889] improved, added knockdown after falling --- apps/openmw/mwmechanics/character.cpp | 27 ++++++++++++++------------- apps/openmw/mwmechanics/character.hpp | 1 - 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index f4d6feab6..a7ae29824 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -166,7 +166,6 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat if(!mAnimation->isPlaying(mCurrentHit)) { - mAnimation->disable(mCurrentHit); if(mJumpState != JumpState_None && !MWBase::Environment::get().getWorld()->isFlying(mPtr) && !MWBase::Environment::get().getWorld()->isSwimming(mPtr) ) mCurrentHit = "knockdown"; @@ -175,20 +174,13 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat int iHit = rand() % sHitListSize; mCurrentHit = sHitList[iHit]; } - mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, false, 1, "start", "stop", 0.0f, 0); - if(mUpperBodyState == UpperCharState_WeapEquiped) - mUpdateAfterHit = false; - else - mUpdateAfterHit = true; + mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } - return; } else if(!mAnimation->isPlaying(mCurrentHit)) { - mAnimation->disable(mCurrentHit); mCurrentHit.erase(); mHitState = CharState_None; - mUpdateAfterHit = true; } const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); @@ -405,7 +397,6 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim , mMovementSpeed(0.0f) , mDeathState(CharState_None) , mHitState(CharState_None) - , mUpdateAfterHit(true) , mUpperBodyState(UpperCharState_Nothing) , mJumpState(JumpState_None) , mWeaponType(WeapType_None) @@ -554,7 +545,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun bool animPlaying; if(stats.getAttackingOrSpell()) { - if(mUpperBodyState == UpperCharState_WeapEquiped) + if(mUpperBodyState == UpperCharState_WeapEquiped && mHitState == CharState_None) { MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); mAttackType.clear(); @@ -718,9 +709,18 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun if(mUpperBodyState == UpperCharState_EquipingWeap || mUpperBodyState == UpperCharState_FollowStartToFollowStop || mUpperBodyState == UpperCharState_CastingSpell) + { mUpperBodyState = UpperCharState_WeapEquiped; + //don't allow to continue playing hit animation after actor had attacked during it + if(mHitState != CharState_None) + { + mAnimation->disable(mCurrentHit); + mCurrentHit.clear(); + mHitState = CharState_None; + } + } else if(mUpperBodyState == UpperCharState_UnEquipingWeap) - mUpperBodyState = UpperCharState_Nothing; + mUpperBodyState = UpperCharState_Nothing; } else if(complete >= 1.0f) { @@ -938,6 +938,7 @@ void CharacterController::update(float duration) int realHealthLost = healthLost * (1.0f - 0.25 * fatigueTerm); health.setCurrent(health.getCurrent() - realHealthLost); cls.getCreatureStats(mPtr).setHealth(health); + cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), true); // report acrobatics progression if (mPtr.getRefData().getHandle() == "player") @@ -1008,7 +1009,7 @@ void CharacterController::update(float duration) } } - if(cls.isNpc() && mUpdateAfterHit) + if(cls.isNpc()) forcestateupdate = updateNpcState(onground, inwater, isrunning, sneak) || forcestateupdate; refreshCurrentAnims(idlestate, movestate, forcestateupdate); diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index e0b065cec..4974b7c08 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -147,7 +147,6 @@ class CharacterController CharacterState mHitState; std::string mCurrentHit; - bool mUpdateAfterHit;//don't allow attack if it hadn't been started before actor got hit UpperBodyCharacterState mUpperBodyState; From 86b2211932412dfb64c9ac0f3f90b063ca357333 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 31 Dec 2013 17:33:15 +0100 Subject: [PATCH 282/889] Don't warn about NiFlipController (already implemented) --- components/nifogre/material.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index c7c9abfc1..8ae86b64a 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -152,7 +152,8 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, Nif::ControllerPtr ctrls = texprop->controller; while(!ctrls.empty()) { - warn("Unhandled texture controller "+ctrls->recName+" in "+name); + if (ctrls->recType != Nif::RC_NiFlipController) // Handled in ogrenifloader + warn("Unhandled texture controller "+ctrls->recName+" in "+name); ctrls = ctrls->next; } } From 1ce4663065e34dbf612261a9e47226f872328b98 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Tue, 31 Dec 2013 18:13:38 +0100 Subject: [PATCH 283/889] Updated compiler opcodes for COE, and setHello. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwscript/docs/vmformat.txt | 4 ++-- components/compiler/opcodes.hpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 9e024f872..cf533451c 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -127,7 +127,6 @@ op 0x200007e-0x2000084: Enable Controls op 0x2000085-0x200008b: Disable Controls op 0x200008c: Unlock op 0x200008d: Unlock, explicit reference -op 0x200008e: COE op 0x200008e-0x20000a8: GetSkill op 0x20000a9-0x20000c3: GetSkill, explicit reference op 0x20000c4-0x20000de: SetSkill @@ -358,5 +357,6 @@ op 0x2000222: GetLineOfSight op 0x2000223: GetLineOfSightExplicit op 0x2000224: ToggleAI op 0x2000225: ToggleAIExplicit +op 0x2000226: COE -opcodes 0x2000226-0x3ffffff unused +opcodes 0x2000227-0x3ffffff unused diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 7ca2a1a64..b7d44a851 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -25,9 +25,9 @@ namespace Compiler const int opcodeAiFollowExplicit = 0x20023; const int opcodeAiFollowCell = 0x20024; const int opcodeAiFollowCellExplicit = 0x20025; - const int opcodeSetHello = 0x200015e; // Same id as opcodeSetFight + const int opcodeSetHello = 0x200015c; const int opcodeSetHelloExplicit = 0x200015d; - const int opcodeSetFight = 0x200015e; // Same id as opcodeSetHello + const int opcodeSetFight = 0x200015e; const int opcodeSetFightExplicit = 0x200015f; const int opcodeSetFlee = 0x2000160; const int opcodeSetFleeExplicit = 0x2000161; @@ -69,7 +69,7 @@ namespace Compiler { const int opcodeCellChanged = 0x2000000; const int opcodeCOC = 0x2000026; - const int opcodeCOE = 0x200008e; // Same ID as opcodeGetSkill + const int opcodeCOE = 0x2000226; const int opcodeGetInterior = 0x2000131; const int opcodeGetPCCell = 0x2000136; const int opcodeGetWaterLevel = 0x2000141; @@ -290,7 +290,7 @@ namespace Compiler const int opcodeGetDynamicGetRatio = 0x200006f; const int opcodeGetDynamicGetRatioExplicit = 0x2000072; - const int opcodeGetSkill = 0x200008e; // Same ID as opcodeCOE + const int opcodeGetSkill = 0x200008e; const int opcodeGetSkillExplicit = 0x20000a9; const int opcodeSetSkill = 0x20000c4; const int opcodeSetSkillExplicit = 0x20000df; From be1938ee905efc028056b292915bb3bf6d1297f7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 31 Dec 2013 18:32:46 +0100 Subject: [PATCH 284/889] Closes #805: Don't add entities that are supposed to be invisible to static geometry --- apps/openmw/mwrender/animation.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index de86bcfa7..faf9d979a 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1211,7 +1211,8 @@ void ObjectAnimation::fillBatch(Ogre::StaticGeometry *sg) for(;iter != mObjectRoot->mEntities.rend();++iter) { Ogre::Node *node = (*iter)->getParentNode(); - sg->addEntity(*iter, node->_getDerivedPosition(), node->_getDerivedOrientation(), node->_getDerivedScale()); + if ((*iter)->isVisible()) + sg->addEntity(*iter, node->_getDerivedPosition(), node->_getDerivedOrientation(), node->_getDerivedScale()); } } From 3604b9d171738a51f2a89ac3f502c50c36727fd6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 31 Dec 2013 18:35:46 +0100 Subject: [PATCH 285/889] Closes #566: In interior cells, update global map position marker using the first exterior teleport door --- apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwgui/mapwindow.cpp | 37 +++++++++++++++++--------- apps/openmw/mwgui/mapwindow.hpp | 2 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 5 ++++ apps/openmw/mwworld/worldimp.cpp | 23 +++++++++++++++- apps/openmw/mwworld/worldimp.hpp | 2 ++ 6 files changed, 58 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 609f873ee..44b45badf 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -430,6 +430,8 @@ namespace MWBase // Are we in an exterior or pseudo-exterior cell and it's night? virtual bool isDark() const = 0; + + virtual bool findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, Ogre::Vector3& result) = 0; }; } diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 93e1d11a3..00380aefd 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -396,20 +396,18 @@ namespace MWGui void MapWindow::globalMapUpdatePlayer () { - Ogre::Vector3 pos = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedPosition (); - Ogre::Quaternion orient = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedOrientation (); - Ogre::Vector2 dir (orient.yAxis ().x, orient.yAxis().y); - - float worldX, worldY; - mGlobalMapRender->worldPosToImageSpace (pos.x, pos.y, worldX, worldY); - worldX *= mGlobalMapRender->getWidth(); - worldY *= mGlobalMapRender->getHeight(); - - - // for interiors, we have no choice other than using the last position & direction. - /// \todo save this last position in the savegame? + // For interiors, position is set by WindowManager via setGlobalMapPlayerPosition if (MWBase::Environment::get().getWorld ()->isCellExterior ()) { + Ogre::Vector3 pos = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedPosition (); + Ogre::Quaternion orient = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedOrientation (); + Ogre::Vector2 dir (orient.yAxis ().x, orient.yAxis().y); + + float worldX, worldY; + mGlobalMapRender->worldPosToImageSpace (pos.x, pos.y, worldX, worldY); + worldX *= mGlobalMapRender->getWidth(); + worldY *= mGlobalMapRender->getHeight(); + mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(worldX - 16, worldY - 16)); MyGUI::ISubWidget* main = mPlayerArrowGlobal->getSubWidgetMain(); @@ -444,4 +442,19 @@ namespace MWGui "#{sWorld}"); } + void MapWindow::setGlobalMapPlayerPosition(float worldX, float worldY) + { + float x, y; + mGlobalMapRender->worldPosToImageSpace (worldX, worldY, x, y); + x *= mGlobalMapRender->getWidth(); + y *= mGlobalMapRender->getHeight(); + + mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(x - 16, y - 16)); + + // set the view offset so that player is in the center + MyGUI::IntSize viewsize = mGlobalMap->getSize(); + MyGUI::IntPoint viewoffs(0.5*viewsize.width - x, 0.5*viewsize.height - y); + mGlobalMap->setViewOffset(viewoffs); + } + } diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 5518ab4a8..2c90c422e 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -81,6 +81,8 @@ namespace MWGui void addVisitedLocation(const std::string& name, int x, int y); // adds the marker to the global map void cellExplored(int x, int y); + void setGlobalMapPlayerPosition (float worldX, float worldY); + virtual void open(); private: diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 8818721f4..7753c2574 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -774,6 +774,11 @@ namespace MWGui mHud->setCellName( cell->mCell->mName ); mMap->setCellPrefix( cell->mCell->mName ); mHud->setCellPrefix( cell->mCell->mName ); + + Ogre::Vector3 worldPos; + if (MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(cell, worldPos)) + mMap->setGlobalMapPlayerPosition(worldPos.x, worldPos.y); + } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 82029d11d..8f979da3b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2288,6 +2288,27 @@ namespace MWWorld bool World::isDark() const { - return mWeatherManager->isDark(); + return mWeatherManager->isDark(); + } + + bool World::findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, Ogre::Vector3& result) + { + MWWorld::CellRefList& doors = cell->mDoors; + CellRefList::List& refList = doors.mList; + + // Check if any door in the cell leads to an exterior directly + for (CellRefList::List::iterator it = refList.begin(); it != refList.end(); ++it) + { + MWWorld::LiveCellRef& ref = *it; + if (ref.mRef.mTeleport && ref.mRef.mDestCell.empty()) + { + ESM::Position pos = ref.mRef.mDoorDest; + result = Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2]); + return true; + } + } + + // No luck :( + return false; } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index a7948dcf2..fad683d18 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -518,6 +518,8 @@ namespace MWWorld virtual void breakInvisibility (const MWWorld::Ptr& actor); // Are we in an exterior or pseudo-exterior cell and it's night? virtual bool isDark() const; + + virtual bool findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, Ogre::Vector3& result); }; } From c7c3ba0ac69ba09d2696b3d89341f55c20c757a7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 31 Dec 2013 18:37:55 +0100 Subject: [PATCH 286/889] Tweak .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f22f1bd49..c061ca637 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ CMakeFiles */CMakeFiles CMakeCache.txt cmake_install.cmake -CMakeLists.txt.user Makefile makefile build @@ -22,6 +21,8 @@ Doxygen .project .settings .directory +## qt-creator +CMakeLists.txt.user* ## resources data From b02b966c446d688931f164f58293b8e76f1219f8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 31 Dec 2013 19:11:16 +0100 Subject: [PATCH 287/889] Closes #994: Don't cap skills to 100 when set via console (except for modX variants) --- apps/openmw/mwscript/statsextensions.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index dc6b1d4a7..45e5b0f18 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -355,8 +355,6 @@ namespace MWScript if (newLevel<0) newLevel = 0; - else if (newLevel>100) - newLevel = 100; progress = (progress / stats.getSkillGain (mIndex, class_, -1, level)) * stats.getSkillGain (mIndex, class_, -1, newLevel); From e9844e1b3724c8cecdf89631023cc3d7e716a352 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Tue, 31 Dec 2013 20:40:23 +0100 Subject: [PATCH 288/889] Fixes #417: Apply weather instantly when teleporting Changed teleporting detection from "position tracking" to manually setting "teleportation" flag ( player->setTeleported(true) ). Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwmechanics/spellcasting.cpp | 7 ++ apps/openmw/mwscript/cellextensions.cpp | 14 ++- .../mwscript/transformationextensions.cpp | 12 +++ apps/openmw/mwworld/actionteleport.cpp | 8 +- apps/openmw/mwworld/player.cpp | 13 ++- apps/openmw/mwworld/player.hpp | 5 +- apps/openmw/mwworld/weather.cpp | 101 ++++++------------ apps/openmw/mwworld/worldimp.cpp | 23 ++-- 8 files changed, 92 insertions(+), 91 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 74816d12e..411fa6bdf 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -7,6 +7,7 @@ #include "../mwworld/containerstore.hpp" +#include "../mwworld/player.hpp" #include "../mwrender/animation.hpp" @@ -240,11 +241,15 @@ namespace MWMechanics else if (effectId == ESM::MagicEffect::DivineIntervention) { + MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); + // We need to be able to get the world location of an interior cell before implementing this // or alternatively, the last known exterior location of the player, which is how vanilla does it. } else if (effectId == ESM::MagicEffect::AlmsiviIntervention) { + MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); + // Same as above } @@ -254,6 +259,8 @@ namespace MWMechanics } else if (effectId == ESM::MagicEffect::Recall) { + MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); + // TODO } } diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index 316f912da..f26602f7a 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -43,10 +43,13 @@ namespace MWScript ESM::Position pos; MWBase::World *world = MWBase::Environment::get().getWorld(); - if (world->findExteriorPosition(cell, pos)) { + world->getPlayer().setTeleported(true); + if (world->findExteriorPosition(cell, pos)) + { world->changeToExteriorCell(pos); } - else { + else + { // Change to interior even if findInteriorPosition() // yields false. In this case position will be zero-point. world->findInteriorPosition(cell, pos); @@ -68,13 +71,14 @@ namespace MWScript runtime.pop(); ESM::Position pos; - - MWBase::Environment::get().getWorld()->indexToPosition (x, y, pos.pos[0], pos.pos[1], true); + MWBase::World *world = MWBase::Environment::get().getWorld(); + world->getPlayer().setTeleported(true); + world->indexToPosition (x, y, pos.pos[0], pos.pos[1], true); pos.pos[2] = 0; pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; - MWBase::Environment::get().getWorld()->changeToExteriorCell (pos); + world->changeToExteriorCell (pos); } }; diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index ae9ac041e..47a632ae9 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -207,6 +207,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); + if (ptr.getRefData().getHandle() == "player") + { + MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); + } std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); @@ -271,6 +275,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); + if (ptr.getRefData().getHandle() == "player") + { + MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); + } Interpreter::Type_Float x = runtime[0].mFloat; runtime.pop(); @@ -328,6 +336,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); + if (ptr.getRefData().getHandle() == "player") + { + MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); + } Interpreter::Type_Float x = runtime[0].mFloat; runtime.pop(); diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index ae5ffc3b9..773fde81e 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -3,6 +3,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "player.hpp" namespace MWWorld { @@ -14,9 +15,12 @@ namespace MWWorld void ActionTeleport::executeImp (const Ptr& actor) { + MWBase::World* world = MWBase::Environment::get().getWorld(); + world->getPlayer().setTeleported(true); + if (mCellName.empty()) - MWBase::Environment::get().getWorld()->changeToExteriorCell (mPosition); + world->changeToExteriorCell (mPosition); else - MWBase::Environment::get().getWorld()->changeToInteriorCell (mCellName, mPosition); + world->changeToInteriorCell (mCellName, mPosition); } } diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index e26c2e2a5..291490ae0 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -19,7 +19,8 @@ namespace MWWorld Player::Player (const ESM::NPC *player, const MWBase::World& world) : mCellStore(0), mAutoMove(false), - mForwardBackward (0) + mForwardBackward (0), + mTeleported(false) { mPlayer.mBase = player; mPlayer.mRef.mRefID = "player"; @@ -145,4 +146,14 @@ namespace MWWorld MWWorld::Ptr ptr = getPlayer(); return MWWorld::Class::get(ptr).getNpcStats(ptr).getDrawState(); } + + bool Player::wasTeleported() const + { + return mTeleported; + } + + void Player::setTeleported(bool teleported) + { + mTeleported = teleported; + } } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index d78b1901c..55d8ff2c8 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -30,7 +30,7 @@ namespace MWWorld bool mAutoMove; int mForwardBackward; - + bool mTeleported; public: Player(const ESM::NPC *player, const MWBase::World& world); @@ -64,6 +64,9 @@ namespace MWWorld void yaw(float yaw); void pitch(float pitch); void roll(float roll); + + bool wasTeleported() const; + void setTeleported(bool teleported); }; } #endif diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 4a8def184..124fc14ab 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -324,42 +324,7 @@ void WeatherManager::update(float duration) mWeatherUpdateTime -= timePassed; - const bool exterior = (MWBase::Environment::get().getWorld()->isCellExterior() || MWBase::Environment::get().getWorld()->isCellQuasiExterior()); - if (!exterior) - { - mRendering->sunDisable(false); - mRendering->skyDisable(); - mRendering->getSkyManager()->setLightningStrength(0.f); - stopSounds(true); - return; - } - - // Exterior - std::string regionstr = Misc::StringUtils::lowerCase(MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion); - - if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) - { - mCurrentRegion = regionstr; - mWeatherUpdateTime = mHoursBetweenWeatherChanges * 3600; - - std::string weatherType = "clear"; - - if (mRegionOverrides.find(regionstr) != mRegionOverrides.end()) - weatherType = mRegionOverrides[regionstr]; - else - { - // get weather probabilities for the current region - const ESM::Region *region = - MWBase::Environment::get().getWorld()->getStore().get().search (regionstr); - - if (region != 0) - { - weatherType = nextWeather(region); - } - } - - setWeather(weatherType, false); - } + switchToNextWeather(false); if (mNextWeather != "") { @@ -710,42 +675,42 @@ float WeatherManager::getWindSpeed() const void WeatherManager::switchToNextWeather(bool instantly) { - const bool exterior = (MWBase::Environment::get().getWorld()->isCellExterior() || MWBase::Environment::get().getWorld()->isCellQuasiExterior()); - if (!exterior) - { - mRendering->sunDisable(false); - mRendering->skyDisable(); - mRendering->getSkyManager()->setLightningStrength(0.f); - stopSounds(true); - return; - } + MWBase::World* world = MWBase::Environment::get().getWorld(); + const bool exterior = (world->isCellExterior() || world->isCellQuasiExterior()); + if (!exterior) + { + mRendering->sunDisable(false); + mRendering->skyDisable(); + mRendering->getSkyManager()->setLightningStrength(0.f); + stopSounds(true); + return; + } - // Exterior - std::string regionstr = Misc::StringUtils::lowerCase(MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion); + // Exterior + std::string regionstr = Misc::StringUtils::lowerCase(world->getPlayer().getPlayer().getCell()->mCell->mRegion); - if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) - { - mCurrentRegion = regionstr; - mWeatherUpdateTime = mHoursBetweenWeatherChanges * 3600; + if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) + { + mCurrentRegion = regionstr; + mWeatherUpdateTime = mHoursBetweenWeatherChanges * 3600; - std::string weatherType = "clear"; + std::string weatherType = "clear"; - if (mRegionOverrides.find(regionstr) != mRegionOverrides.end()) - { - weatherType = mRegionOverrides[regionstr]; - } - else - { - // get weather probabilities for the current region - const ESM::Region *region = - MWBase::Environment::get().getWorld()->getStore().get().search (regionstr); + if (mRegionOverrides.find(regionstr) != mRegionOverrides.end()) + { + weatherType = mRegionOverrides[regionstr]; + } + else + { + // get weather probabilities for the current region + const ESM::Region *region = world->getStore().get().search (regionstr); - if (region != 0) - { - weatherType = nextWeather(region); - } - } + if (region != 0) + { + weatherType = nextWeather(region); + } + } - setWeather(weatherType, instantly); - } + setWeather(weatherType, instantly); + } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 18dc77d3a..86dc38a8e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2247,19 +2247,14 @@ namespace MWWorld void World::updateWeather(float duration) { - static const float teleportationStepTreshold = 256.f; - static ESM::Position lastPlayerPos; - ESM::Position currentPos = mPlayer->getPlayer().getRefData().getPosition(); - - if (fabs(fabs(lastPlayerPos.pos[0]) - fabs(currentPos.pos[0])) >= teleportationStepTreshold - || fabs(fabs(lastPlayerPos.pos[1]) - fabs(currentPos.pos[1])) >= teleportationStepTreshold) - { - lastPlayerPos = currentPos; - mWeatherManager->switchToNextWeather(true); - } - else - { - mWeatherManager->update (duration); - } + if (mPlayer->wasTeleported()) + { + mPlayer->setTeleported(false); + mWeatherManager->switchToNextWeather(true); + } + else + { + mWeatherManager->update (duration); + } } } From 101813fd0dc353f4732f5cd2508fdd144349e150 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Tue, 31 Dec 2013 21:18:10 +0100 Subject: [PATCH 289/889] Fixes #417: Apply weather instantly when teleporting Correction to previous commit - WeatherManager->update() will always be called. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwworld/worldimp.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 86dc38a8e..b4b1bd1f6 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2252,9 +2252,7 @@ namespace MWWorld mPlayer->setTeleported(false); mWeatherManager->switchToNextWeather(true); } - else - { - mWeatherManager->update (duration); - } + + mWeatherManager->update(duration); } } From c86760e3cd73336041755d70c5d8426f6afe6751 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 00:12:31 +0100 Subject: [PATCH 290/889] Remember the last known exterior position of the player in case we fail to map the interior to a world position. --- apps/openmw/mwgui/windowmanagerimp.cpp | 9 ++++++--- apps/openmw/mwworld/player.cpp | 1 + apps/openmw/mwworld/player.hpp | 11 +++++++++++ apps/openmw/mwworld/worldimp.cpp | 6 ++++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 7753c2574..4a47d0f9f 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -16,6 +16,7 @@ #include "../mwbase/inputmanager.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" #include "console.hpp" #include "journalwindow.hpp" @@ -776,9 +777,11 @@ namespace MWGui mHud->setCellPrefix( cell->mCell->mName ); Ogre::Vector3 worldPos; - if (MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(cell, worldPos)) - mMap->setGlobalMapPlayerPosition(worldPos.x, worldPos.y); - + if (!MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(cell, worldPos)) + worldPos = MWBase::Environment::get().getWorld()->getPlayer().getLastKnownExteriorPosition(); + else + MWBase::Environment::get().getWorld()->getPlayer().setLastKnownExteriorPosition(worldPos); + mMap->setGlobalMapPlayerPosition(worldPos.x, worldPos.y); } } diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index e26c2e2a5..04dc8d0a1 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -18,6 +18,7 @@ namespace MWWorld { Player::Player (const ESM::NPC *player, const MWBase::World& world) : mCellStore(0), + mLastKnownExteriorPosition(0,0,0), mAutoMove(false), mForwardBackward (0) { diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index d78b1901c..23317f675 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -6,6 +6,8 @@ #include "../mwmechanics/drawstate.hpp" +#include + namespace ESM { struct NPC; @@ -28,6 +30,8 @@ namespace MWWorld MWWorld::CellStore *mCellStore; std::string mSign; + Ogre::Vector3 mLastKnownExteriorPosition; + bool mAutoMove; int mForwardBackward; @@ -35,6 +39,13 @@ namespace MWWorld Player(const ESM::NPC *player, const MWBase::World& world); + /// Interiors can not always be mapped to a world position. However + /// world position is still required for divine / almsivi magic effects + /// and the player arrow on the global map. + /// TODO: This should be stored in the savegame, too. + void setLastKnownExteriorPosition (const Ogre::Vector3& position) { mLastKnownExteriorPosition = position; } + Ogre::Vector3 getLastKnownExteriorPosition() const { return mLastKnownExteriorPosition; } + void set (const ESM::NPC *player); void setCell (MWWorld::CellStore *cellStore); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8f979da3b..670c0387f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1297,6 +1297,12 @@ namespace MWWorld performUpdateSceneQueries (); updateWindowManager (); + + if (mPlayer->getPlayer().getCell()->isExterior()) + { + ESM::Position pos = mPlayer->getPlayer().getRefData().getPosition(); + mPlayer->setLastKnownExteriorPosition(Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2])); + } } void World::updateWindowManager () From ea3b88951a79e5bb8593ca98ec53a8a6c1fb0930 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 02:22:11 +0100 Subject: [PATCH 291/889] Implement divine/almsivi intervention magic effects --- apps/openmw/mwbase/world.hpp | 6 +++++ apps/openmw/mwmechanics/spellcasting.cpp | 19 +++++++++++--- apps/openmw/mwworld/cells.cpp | 16 +++++++++--- apps/openmw/mwworld/cells.hpp | 7 +++++ apps/openmw/mwworld/worldimp.cpp | 33 ++++++++++++++++++++++-- apps/openmw/mwworld/worldimp.hpp | 6 +++++ 6 files changed, 78 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 44b45badf..d9640f4d2 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -432,6 +432,12 @@ namespace MWBase virtual bool isDark() const = 0; virtual bool findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, Ogre::Vector3& result) = 0; + + /// Teleports \a ptr to the reference of \a id (e.g. DivineMarker, PrisonMarker, TempleMarker) + /// closest to \a worldPos. + /// @note id must be lower case + virtual void teleportToClosestMarker (const MWWorld::Ptr& ptr, + const std::string& id, Ogre::Vector3 worldPos) = 0; }; } diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 025aa6b3a..ee907284d 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -7,6 +7,7 @@ #include "../mwworld/containerstore.hpp" +#include "../mwworld/player.hpp" #include "../mwrender/animation.hpp" @@ -238,14 +239,24 @@ namespace MWMechanics else if (effectId == ESM::MagicEffect::RemoveCurse) target.getClass().getCreatureStats(target).getSpells().purgeCurses(); - else if (effectId == ESM::MagicEffect::DivineIntervention) + if (target.getRefData().getHandle() != "player") + return; + if (!MWBase::Environment::get().getWorld()->isTeleportingEnabled()) + return; + + Ogre::Vector3 worldPos; + if (!MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(target.getCell(), worldPos)) + worldPos = MWBase::Environment::get().getWorld()->getPlayer().getLastKnownExteriorPosition(); + + if (effectId == ESM::MagicEffect::DivineIntervention) { - // We need to be able to get the world location of an interior cell before implementing this - // or alternatively, the last known exterior location of the player, which is how vanilla does it. + MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker", + worldPos); } else if (effectId == ESM::MagicEffect::AlmsiviIntervention) { - // Same as above + MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "templemarker", + worldPos); } else if (effectId == ESM::MagicEffect::Mark) diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 37c4b6a3f..865c0d01f 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -129,9 +129,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& ce if (cell.mState==Ptr::CellStore::State_Preloaded) { - std::string lowerCase = Misc::StringUtils::lowerCase(name); - - if (std::binary_search (cell.mIds.begin(), cell.mIds.end(), lowerCase)) + if (std::binary_search (cell.mIds.begin(), cell.mIds.end(), name)) { cell.load (mStore, mReader); } @@ -261,3 +259,15 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) // giving up return Ptr(); } + +void MWWorld::Cells::getExteriorPtrs(const std::string &name, std::vector &out) +{ + for (std::map, Ptr::CellStore>::iterator iter = mExteriors.begin(); + iter!=mExteriors.end(); ++iter) + { + Ptr ptr = getPtrAndCache (name, iter->second); + if (!ptr.isEmpty()) + out.push_back(ptr); + } + +} diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 0c51cf452..31de2f60e 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -47,8 +47,15 @@ namespace MWWorld Ptr getPtr (const std::string& name, CellStore& cellStore, bool searchInContainers = false); ///< \param searchInContainers Only affect loaded cells. + /// @note name must be lower case + /// @note name must be lower case Ptr getPtr (const std::string& name); + + /// Get all Ptrs referencing \a name in exterior cells + /// @note Due to the current implementation of getPtr this only supports one Ptr per cell. + /// @note name must be lower case + void getExteriorPtrs (const std::string& name, std::vector& out); }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 670c0387f..fe2d2e64e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -39,6 +39,7 @@ #include "cellfunctors.hpp" #include "containerstore.hpp" #include "inventorystore.hpp" +#include "actionteleport.hpp" #include "contentloader.hpp" #include "esmloader.hpp" @@ -498,12 +499,14 @@ namespace MWWorld if (!ptr.isEmpty()) return ptr; + std::string lowerCaseName = Misc::StringUtils::lowerCase(name); + // active cells for (Scene::CellStoreCollection::const_iterator iter (mWorldScene->getActiveCells().begin()); iter!=mWorldScene->getActiveCells().end(); ++iter) { Ptr::CellStore* cellstore = *iter; - Ptr ptr = mCells.getPtr (name, *cellstore, true); + Ptr ptr = mCells.getPtr (lowerCaseName, *cellstore, true); if (!ptr.isEmpty()) return ptr; @@ -511,7 +514,7 @@ namespace MWWorld if (!activeOnly) { - Ptr ptr = mCells.getPtr (name); + Ptr ptr = mCells.getPtr (lowerCaseName); if (!ptr.isEmpty()) return ptr; @@ -2317,4 +2320,30 @@ namespace MWWorld // No luck :( return false; } + + void World::teleportToClosestMarker (const MWWorld::Ptr& ptr, + const std::string& id, Ogre::Vector3 worldPos) + { + MWWorld::Ptr closestMarker; + float closestDistance = FLT_MAX; + + std::vector markers; + mCells.getExteriorPtrs(id, markers); + + for (std::vector::iterator it = markers.begin(); it != markers.end(); ++it) + { + ESM::Position pos = it->getRefData().getPosition(); + Ogre::Vector3 markerPos = Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2]); + float distance = worldPos.squaredDistance(markerPos); + if (distance < closestDistance) + { + closestDistance = distance; + closestMarker = *it; + } + + } + + MWWorld::ActionTeleport action("", closestMarker.getRefData().getPosition()); + action.execute(ptr); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index fad683d18..2ec5e42f0 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -520,6 +520,12 @@ namespace MWWorld virtual bool isDark() const; virtual bool findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, Ogre::Vector3& result); + + /// Teleports \a ptr to the reference of \a id (e.g. DivineMarker, PrisonMarker, TempleMarker) + /// closest to \a worldPos. + /// @note id must be lower case + virtual void teleportToClosestMarker (const MWWorld::Ptr& ptr, + const std::string& id, Ogre::Vector3 worldPos); }; } From fe34c9cf1faf3d41cb725e1c832cfa19d0e1c434 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 1 Jan 2014 15:06:31 +0100 Subject: [PATCH 292/889] increased version number --- CMakeLists.txt | 4 ++-- readme.txt | 38 +++++++++++++++++++------------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ee4a0246b..c1eca064e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ include (OpenMWMacros) # Version set (OPENMW_VERSION_MAJOR 0) -set (OPENMW_VERSION_MINOR 27) +set (OPENMW_VERSION_MINOR 28) set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") @@ -323,7 +323,7 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg configure_file(${OpenMW_SOURCE_DIR}/files/opencs.cfg "${OpenMW_BINARY_DIR}/opencs.cfg") - + configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters "${OpenMW_BINARY_DIR}/resources/defaultfilters" COPYONLY) diff --git a/readme.txt b/readme.txt index 5b9aafcb3..946df64ca 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. -Version: 0.27.0 +Version: 0.28.0 License: GPL (see GPL3.txt for more information) Website: http://www.openmw.org @@ -48,42 +48,42 @@ Allowed options: --version print version information and quit --data arg (=data) set data directories (later directories have higher priority) - --data-local arg set local data directory (highest + --data-local arg set local data directory (highest priority) --fallback-archive arg (=fallback-archive) - set fallback BSA archives (later + set fallback BSA archives (later archives have higher priority) --resources arg (=resources) set resources directory --start arg (=Beshara) set initial cell - --content arg content file(s): esm/esp, or + --content arg content file(s): esm/esp, or omwgame/omwaddon --anim-verbose [=arg(=1)] (=0) output animation indices files --no-sound [=arg(=1)] (=0) disable all sounds --script-verbose [=arg(=1)] (=0) verbose script output --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue scripts) at startup - --script-console [=arg(=1)] (=0) enable console-only script + --script-console [=arg(=1)] (=0) enable console-only script functionality - --script-run arg select a file containing a list of - console commands that is executed on + --script-run arg select a file containing a list of + console commands that is executed on startup --new-game [=arg(=1)] (=0) activate char gen/new game mechanics - --fs-strict [=arg(=1)] (=0) strict file system handling (no case + --fs-strict [=arg(=1)] (=0) strict file system handling (no case folding) - --encoding arg (=win1252) Character encoding used in OpenMW game + --encoding arg (=win1252) Character encoding used in OpenMW game messages: - - win1250 - Central and Eastern European - such as Polish, Czech, Slovak, - Hungarian, Slovene, Bosnian, Croatian, - Serbian (Latin script), Romanian and + + win1250 - Central and Eastern European + such as Polish, Czech, Slovak, + Hungarian, Slovene, Bosnian, Croatian, + Serbian (Latin script), Romanian and Albanian languages - - win1251 - Cyrillic alphabet such as - Russian, Bulgarian, Serbian Cyrillic + + win1251 - Cyrillic alphabet such as + Russian, Bulgarian, Serbian Cyrillic and other languages - - win1252 - Western European (Latin) + + win1252 - Western European (Latin) alphabet, used by default --fallback arg fallback values --no-grab Don't grab mouse cursor From b19b277f182ef4dbe2d2445d4d53754acc0dffb4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 1 Jan 2014 15:16:51 +0100 Subject: [PATCH 293/889] temporarily disable OGRE-integration in OpenCS (need to sort out path problem first) --- apps/opencs/editor.cpp | 3 +++ apps/opencs/main.cpp | 3 +++ apps/opencs/view/world/scenesubview.cpp | 16 +++++++++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 44926610b..37f06c8c7 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -210,6 +210,8 @@ int CS::Editor::run() if (mLocal.empty()) return 1; +// temporarily disable OGRE-integration (need to fix path problem first) +#if 0 // TODO: setting Ogre::Root::getSingleton().setRenderSystem(Ogre::Root::getSingleton().getRenderSystemByName("OpenGL Rendering Subsystem")); @@ -223,6 +225,7 @@ int CS::Editor::run() params.insert(std::make_pair("hidden", "true")); Ogre::RenderWindow* hiddenWindow = Ogre::Root::getSingleton().createRenderWindow("InactiveHidden", 1, 1, false, ¶ms); hiddenWindow->setActive(false); +#endif mStartup.show(); diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index 57eaf2d25..931d63312 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -44,8 +44,11 @@ int main(int argc, char *argv[]) // SceneWidget destructor will delete the created render window, which would be called _after_ Root has shut down :( Application mApplication (argc, argv); +// temporarily disable OGRE-integration (need to fix path problem first) +#if 0 OgreInit::OgreInit ogreInit; ogreInit.init("./opencsOgre.log"); // TODO log path? +#endif #ifdef Q_OS_MAC QDir dir(QCoreApplication::applicationDirPath()); diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 83b30be13..33ae327a0 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -43,12 +43,26 @@ toolbar->addTool (new SceneToolMode (toolbar)); toolbar->addTool (new SceneToolMode (toolbar)); layout2->addWidget (toolbar, 0); - +// temporarily disable OGRE-integration (need to fix path problem first) +#if 0 CSVRender::SceneWidget* sceneWidget = new CSVRender::SceneWidget(this); layout2->addWidget (sceneWidget, 1); layout->insertLayout (0, layout2, 1); +#endif + /// \todo replace with rendering widget + QPalette palette2 (palette()); + palette2.setColor (QPalette::Background, Qt::white); + QLabel *placeholder = new QLabel ("Here goes the 3D scene", this); + placeholder->setAutoFillBackground (true); + placeholder->setPalette (palette2); + placeholder->setAlignment (Qt::AlignHCenter); + + layout2->addWidget (placeholder, 1); + + layout->insertLayout (0, layout2, 1); + CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); From b3cd10dbeaeebb27cc5af27dda5bfac47fb46dee Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 15:18:25 +0100 Subject: [PATCH 294/889] Remove redundant setTeleported calls --- apps/openmw/mwmechanics/spellcasting.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 633771b39..c9750cd4c 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -250,14 +250,10 @@ namespace MWMechanics if (effectId == ESM::MagicEffect::DivineIntervention) { - MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); - MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker", worldPos); } else if (effectId == ESM::MagicEffect::AlmsiviIntervention) { - MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); - MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "templemarker", worldPos); } From 69ba8a40bf0f6fe8642a8c2ac2510e88c2f5e60d Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 16:13:20 +0100 Subject: [PATCH 295/889] Fix weird formatting added during the merge --- apps/openmw/mwmechanics/spellcasting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index c9750cd4c..7db8f5367 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -250,7 +250,7 @@ namespace MWMechanics if (effectId == ESM::MagicEffect::DivineIntervention) { - MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker", worldPos); + MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker", worldPos); } else if (effectId == ESM::MagicEffect::AlmsiviIntervention) { From 722469d57b12fe48c83cdbd40c128720278163e5 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Wed, 1 Jan 2014 16:45:39 +0100 Subject: [PATCH 296/889] Correction to the e9844e1 commit. Restored interrior check in WeatherManager::update(). Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwworld/weather.cpp | 61 +++++++++++++++++---------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 124fc14ab..06c2e2a36 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -324,6 +324,17 @@ void WeatherManager::update(float duration) mWeatherUpdateTime -= timePassed; + MWBase::World* world = MWBase::Environment::get().getWorld(); + const bool exterior = (world->isCellExterior() || world->isCellQuasiExterior()); + if (!exterior) + { + mRendering->sunDisable(false); + mRendering->skyDisable(); + mRendering->getSkyManager()->setLightningStrength(0.f); + stopSounds(true); + return; + } + switchToNextWeather(false); if (mNextWeather != "") @@ -676,41 +687,33 @@ float WeatherManager::getWindSpeed() const void WeatherManager::switchToNextWeather(bool instantly) { MWBase::World* world = MWBase::Environment::get().getWorld(); - const bool exterior = (world->isCellExterior() || world->isCellQuasiExterior()); - if (!exterior) + if (world->isCellExterior() || world->isCellQuasiExterior()) { - mRendering->sunDisable(false); - mRendering->skyDisable(); - mRendering->getSkyManager()->setLightningStrength(0.f); - stopSounds(true); - return; - } + std::string regionstr = Misc::StringUtils::lowerCase(world->getPlayer().getPlayer().getCell()->mCell->mRegion); - // Exterior - std::string regionstr = Misc::StringUtils::lowerCase(world->getPlayer().getPlayer().getCell()->mCell->mRegion); - - if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) - { - mCurrentRegion = regionstr; - mWeatherUpdateTime = mHoursBetweenWeatherChanges * 3600; - - std::string weatherType = "clear"; - - if (mRegionOverrides.find(regionstr) != mRegionOverrides.end()) + if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) { - weatherType = mRegionOverrides[regionstr]; - } - else - { - // get weather probabilities for the current region - const ESM::Region *region = world->getStore().get().search (regionstr); + mCurrentRegion = regionstr; + mWeatherUpdateTime = mHoursBetweenWeatherChanges * 3600; - if (region != 0) + std::string weatherType = "clear"; + + if (mRegionOverrides.find(regionstr) != mRegionOverrides.end()) { - weatherType = nextWeather(region); + weatherType = mRegionOverrides[regionstr]; } - } + else + { + // get weather probabilities for the current region + const ESM::Region *region = world->getStore().get().search (regionstr); - setWeather(weatherType, instantly); + if (region != 0) + { + weatherType = nextWeather(region); + } + } + + setWeather(weatherType, instantly); + } } } From 2d4e06cd5021f18bc143067ff7c2680fb599875e Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Wed, 1 Jan 2014 17:05:49 +0100 Subject: [PATCH 297/889] Updated comments about freeing format_ctx->pb->buffer. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwrender/videoplayer.cpp | 15 +++++++++------ apps/openmw/mwsound/ffmpeg_decoder.cpp | 4 ++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 74b52c06b..adf20dc63 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -949,7 +949,9 @@ void VideoState::init(const std::string& resourceName) this->format_ctx->pb = ioCtx; // Open video file - /// \todo leak here, ffmpeg or valgrind bug ? + /// + /// format_ctx->pb->buffer must be freed by hand, + /// if not, valgrind will show memleak, see: /// /// https://trac.ffmpeg.org/ticket/1357 /// @@ -1023,11 +1025,12 @@ void VideoState::deinit() if(this->format_ctx) { - // valgrind shows memleak near format_ctx->pb - // - // As scrawl pointed, memleak could be related to this ffmpeg ticket: - // https://trac.ffmpeg.org/ticket/1357 - // + /// + /// format_ctx->pb->buffer must be freed by hand, + /// if not, valgrind will show memleak, see: + /// + /// https://trac.ffmpeg.org/ticket/1357 + /// if (this->format_ctx->pb != NULL) { av_free(this->format_ctx->pb->buffer); diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 13d11f691..c83697442 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -231,9 +231,9 @@ void FFmpeg_Decoder::close() { if (mFormatCtx->pb != NULL) { - // valgrind shows memleak near mFormatCtx->pb + // mFormatCtx->pb->buffer must be freed by hand, + // if not, valgrind will show memleak, see: // - // As scrawl pointed, memleak could be related to this ffmpeg ticket: // https://trac.ffmpeg.org/ticket/1357 // if (mFormatCtx->pb->buffer != NULL) From 8e5cae1081ca926c5f7157f07457c0a4046eed54 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 17:06:21 +0100 Subject: [PATCH 298/889] Implement mark/recall magic effects --- apps/openmw/mwmechanics/spellcasting.cpp | 15 ++++++++++++--- apps/openmw/mwworld/player.cpp | 16 +++++++++++++++- apps/openmw/mwworld/player.hpp | 8 ++++++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 7db8f5367..0b897c165 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -8,6 +8,7 @@ #include "../mwworld/containerstore.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/actionteleport.hpp" #include "../mwrender/animation.hpp" @@ -259,13 +260,21 @@ namespace MWMechanics else if (effectId == ESM::MagicEffect::Mark) { - // TODO + MWBase::Environment::get().getWorld()->getPlayer().markPosition( + target.getCell(), target.getRefData().getPosition()); } else if (effectId == ESM::MagicEffect::Recall) { - MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); + MWWorld::CellStore* markedCell = NULL; + ESM::Position markedPosition; - // TODO + MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition); + if (markedCell) + { + MWWorld::ActionTeleport action(markedCell->isExterior() ? "" : markedCell->mCell->mName, + markedPosition); + action.execute(target); + } } } } diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 8be6a4d98..c59445402 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -21,7 +21,8 @@ namespace MWWorld mLastKnownExteriorPosition(0,0,0), mAutoMove(false), mForwardBackward (0), - mTeleported(false) + mTeleported(false), + mMarkedCell(NULL) { mPlayer.mBase = player; mPlayer.mRef.mRefID = "player"; @@ -157,4 +158,17 @@ namespace MWWorld { mTeleported = teleported; } + + void Player::markPosition(CellStore *markedCell, ESM::Position markedPosition) + { + mMarkedCell = markedCell; + mMarkedPosition = markedPosition; + } + + void Player::getMarkedPosition(CellStore*& markedCell, ESM::Position &markedPosition) const + { + markedCell = mMarkedCell; + if (mMarkedCell) + markedPosition = mMarkedPosition; + } } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 48f5d06c1..1df848111 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -32,6 +32,10 @@ namespace MWWorld Ogre::Vector3 mLastKnownExteriorPosition; + ESM::Position mMarkedPosition; + // If no position was marked, this is NULL + CellStore* mMarkedCell; + bool mAutoMove; int mForwardBackward; bool mTeleported; @@ -39,6 +43,10 @@ namespace MWWorld Player(const ESM::NPC *player, const MWBase::World& world); + // For mark/recall magic effects + void markPosition (CellStore* markedCell, ESM::Position markedPosition); + void getMarkedPosition (CellStore*& markedCell, ESM::Position& markedPosition) const; + /// Interiors can not always be mapped to a world position. However /// world position is still required for divine / almsivi magic effects /// and the player arrow on the global map. From eab7ffd6b408995594896d7d33142240faa4dc49 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 18:05:28 +0100 Subject: [PATCH 299/889] Remove redundant finding of default exterior position height --- apps/openmw/mwworld/worldimp.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index db58a1280..5d7e9deeb 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1940,17 +1940,8 @@ namespace MWWorld int y = ext->getGridY(); indexToPosition(x, y, pos.pos[0], pos.pos[1], true); - ESM::Land* land = getStore().get().search(x, y); - if (land) { - if (!land->isDataLoaded(ESM::Land::DATA_VHGT)) { - land->loadData(ESM::Land::DATA_VHGT); - } - pos.pos[2] = land->mLandData->mHeights[ESM::Land::LAND_NUM_VERTS / 2 + 1]; - } - else { - std::cerr << "Land data for cell at (" << x << ", " << y << ") not found\n"; - pos.pos[2] = 0; - } + // Note: Z pos will be adjusted by adjustPosition later + pos.pos[2] = 0; return true; } From 43cbff6333d3a838b15bed3ceed1a07d63efe287 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Wed, 1 Jan 2014 18:32:55 +0100 Subject: [PATCH 300/889] Bumped libogre to v1.9 in travis configuration file. Signed-off-by: Lukasz Gromanowski --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 112cf94f1..04d019c0d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ before_install: - sudo apt-get install -qq libqt4-dev libxaw7-dev libxrandr-dev libfreeimage-dev libpng-dev - sudo apt-get install -qq libopenal-dev libmpg123-dev libsndfile1-dev - sudo apt-get install -qq libavcodec-dev libavformat-dev libavdevice-dev libavutil-dev libswscale-dev libpostproc-dev - - sudo apt-get install -qq libbullet-dev libogre-1.8-dev libmygui-dev libsdl2-dev libunshield-dev + - sudo apt-get install -qq libbullet-dev libogre-1.9-dev libmygui-dev libsdl2-dev libunshield-dev - sudo mkdir /usr/src/gtest/build - cd /usr/src/gtest/build - sudo cmake .. -DBUILD_SHARED_LIBS=1 From 09a0a69b04cf80645dc1bb708f19b11c2bdb715f Mon Sep 17 00:00:00 2001 From: mrcheko Date: Wed, 1 Jan 2014 21:40:31 +0200 Subject: [PATCH 301/889] more improvements --- apps/openmw/mwmechanics/character.cpp | 159 +++++++++++++++++--------- apps/openmw/mwmechanics/character.hpp | 3 + apps/openmw/mwrender/animation.cpp | 15 ++- apps/openmw/mwrender/animation.hpp | 1 + 4 files changed, 124 insertions(+), 54 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index a7ae29824..4a8cf5ea8 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -81,6 +81,7 @@ static const std::string sHitList[] = { "hit3" , "hit4" , "hit5" , + "knockdown" , }; static const int sHitListSize = sizeof(sHitList)/sizeof(sHitList[0]); @@ -161,23 +162,23 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat { if(MWWorld::Class::get(mPtr).getCreatureStats(mPtr).getAttacked()) { - mHitState = CharState_Hit; MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setAttacked(false); - if(!mAnimation->isPlaying(mCurrentHit)) + if(mHitState == CharState_None) { + mHitState = CharState_Hit; if(mJumpState != JumpState_None && !MWBase::Environment::get().getWorld()->isFlying(mPtr) && !MWBase::Environment::get().getWorld()->isSwimming(mPtr) ) - mCurrentHit = "knockdown"; + mCurrentHit = sHitList[sHitListSize-1]; //knockdown animation else { - int iHit = rand() % sHitListSize; + int iHit = rand() % (sHitListSize-1); mCurrentHit = sHitList[iHit]; } mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } } - else if(!mAnimation->isPlaying(mCurrentHit)) + else if(mHitState != CharState_None && !mAnimation->isPlaying(mCurrentHit)) { mCurrentHit.erase(); mHitState = CharState_None; @@ -458,9 +459,27 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr) } +void CharacterController::playWeaponAnim(const std::string& start, const std::string& stop, + float speed, bool autoDisable, bool disablePrevious, float startpoint, bool currentWeapon) +{ + std::string weapgroup; + if (currentWeapon) + weapgroup = mCurrentWeapon; + else + getWeaponGroup(mWeaponType, weapgroup); + + if (disablePrevious) + mAnimation->disable(weapgroup); + + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_All, autoDisable, + speed, start, stop, + startpoint, 0); +} + bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak) { - const MWWorld::Class &cls = MWWorld::Class::get(mPtr); + const MWWorld::Class &cls = MWWorld::Class::get(mPtr); NpcStats &stats = cls.getNpcStats(mPtr); WeaponType weaptype = WeapType_None; MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); @@ -490,6 +509,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun { getWeaponGroup(weaptype, weapgroup); mAnimation->showWeapons(false); + mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, true, 1.0f, "equip start", "equip stop", 0.0f, 0); @@ -642,7 +662,14 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun { if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || mWeaponType == WeapType_ThowWeapon) + { mAttackType = "shoot"; + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" start", mAttackType+" attach", + 0.0f, 0); + mUpperBodyState = UpperCharState_StartToAttach; + } else { int attackType = stats.getAttackType(); @@ -655,13 +682,13 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun mAttackType = "slash"; else mAttackType = "thrust"; - } mAnimation->play(mCurrentWeapon, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, weapSpeed, mAttackType+" start", mAttackType+" min attack", 0.0f, 0); mUpperBodyState = UpperCharState_StartToMinAttack; + } } } animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); @@ -711,79 +738,105 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun mUpperBodyState == UpperCharState_CastingSpell) { mUpperBodyState = UpperCharState_WeapEquiped; - //don't allow to continue playing hit animation after actor had attacked during it - if(mHitState != CharState_None) + + if(mHitState != CharState_None) //don't allow to continue playing hit animation after actor had attacked during it { - mAnimation->disable(mCurrentHit); + mAnimation->changeGroups(mCurrentHit, MWRender::Animation::Group_LowerBody); mCurrentHit.clear(); mHitState = CharState_None; } } else if(mUpperBodyState == UpperCharState_UnEquipingWeap) - mUpperBodyState = UpperCharState_Nothing; + mUpperBodyState = UpperCharState_Nothing; } else if(complete >= 1.0f) { - if(mUpperBodyState == UpperCharState_StartToMinAttack) + std::string start, stop; + switch(mUpperBodyState) { - mAnimation->disable(mCurrentWeapon); - mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed, mAttackType+" min attack", mAttackType+" max attack", - 0.0f, 0); - mUpperBodyState = UpperCharState_MinAttackToMaxAttack; + case UpperCharState_StartToMinAttack: + start = mAttackType+" min attack"; + stop = mAttackType+" max attack"; + mUpperBodyState = UpperCharState_MinAttackToMaxAttack; + break; + case UpperCharState_StartToAttach: //only bows, crossbows, throwing weapons here + start = mAttackType+" attach"; + stop = mAttackType+" min attack"; + mUpperBodyState = UpperCharState_StartToMinAttack; + break; + case UpperCharState_MaxAttackToMinHit: + if(mAttackType == "shoot") + { + start = mAttackType+" min hit"; + stop = mAttackType+" release"; + } + else + { + start = mAttackType+" min hit"; + stop = mAttackType+" hit"; + } + mUpperBodyState = UpperCharState_MinHitToHit; + break; + case UpperCharState_MinHitToHit: + if(mAttackType == "shoot") + { + start = mAttackType+" follow start"; + stop = mAttackType+" follow stop"; + } + else + { + float str = stats.getAttackStrength(); + start = mAttackType+((str < 0.5f) ? " small follow start" + : (str < 1.0f) ? " medium follow start" + : " large follow start"); + stop = mAttackType+((str < 0.5f) ? " small follow stop" + : (str < 1.0f) ? " medium follow stop" + : " large follow stop"); + } + mUpperBodyState = UpperCharState_FollowStartToFollowStop; + break; + default: + break; } - else if(mUpperBodyState == UpperCharState_MaxAttackToMinHit) + + if(!start.empty()) { mAnimation->disable(mCurrentWeapon); - if(mAttackType == "shoot") - mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed, mAttackType+" min hit", mAttackType+" follow start", - 0.0f, 0); - else - mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed, mAttackType+" min hit", mAttackType+" hit", - 0.0f, 0); - mUpperBodyState = UpperCharState_MinHitToHit; - } - else if(mUpperBodyState == UpperCharState_MinHitToHit) - { - mAnimation->disable(mCurrentWeapon); - if(mAttackType == "shoot") - mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, true, - weapSpeed, mAttackType+" follow start", mAttackType+" follow stop", - 0.0f, 0); - else - { - float str = stats.getAttackStrength(); - std::string start = mAttackType+((str < 0.5f) ? " small follow start" - : (str < 1.0f) ? " medium follow start" - : " large follow start"); - std::string stop = mAttackType+((str < 0.5f) ? " small follow stop" - : (str < 1.0f) ? " medium follow stop" - : " large follow stop"); + if (mUpperBodyState == UpperCharState_FollowStartToFollowStop) mAnimation->play(mCurrentWeapon, Priority_Weapon, MWRender::Animation::Group_UpperBody, true, weapSpeed, start, stop, 0.0f, 0); - } - mUpperBodyState = UpperCharState_FollowStartToFollowStop; + else + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, start, stop, 0.0f, 0); } } + //if playing combat animation and lowerbody is not busy switch to whole body animation + if(weaptype != WeaponType::WeapType_None && complete>0.0f) + { + if( mMovementState != CharState_None || + mJumpState != JumpState_None || + mHitState != CharState_None || + MWBase::Environment::get().getWorld()->isSwimming(mPtr) || + cls.getStance(mPtr, MWWorld::Class::Sneak)) + mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_UpperBody); + else + mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_All); + } + MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name() && mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand) { - mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, - false, 1.0f, "start", "stop", 0.0f, (~(size_t)0)); + mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, + false, 1.0f, "start", "stop", 0.0f, (~(size_t)0)); } else if (mAnimation->isPlaying("torch")) { - mAnimation->disable("torch"); + mAnimation->disable("torch"); } return forcestateupdate; diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 4974b7c08..3cf34b747 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -114,6 +114,7 @@ enum UpperBodyCharacterState { UpperCharState_UnEquipingWeap, UpperCharState_WeapEquiped, UpperCharState_StartToMinAttack, + UpperCharState_StartToAttach, UpperCharState_MinAttackToMaxAttack, UpperCharState_MaxAttackToMinHit, UpperCharState_MinHitToHit, @@ -175,6 +176,8 @@ class CharacterController void clearAnimQueue(); bool updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak); + void playWeaponAnim(const std::string& start, const std::string& stop, + float speed = 1.0f, bool autoDisable = true, bool disablePrevious = false, float startpoint = 0.0f, bool currentWeapon = true); void updateVisibility(); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index de86bcfa7..c7537aa90 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -672,7 +672,20 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co MWBase::Environment::get().getWorld()->castSpell(mPtr); } - +void Animation::changeGroups(const std::string &groupname, int groups) +{ + AnimStateMap::iterator stateiter = mStates.begin(); + stateiter = mStates.find(groupname); + if(stateiter != mStates.end()) + { + if(stateiter->second.mGroups != groups) + { + stateiter->second.mGroups = groups; + resetActiveGroups(); + } + return; + } +} void Animation::play(const std::string &groupname, int priority, int groups, bool autodisable, float speedmult, const std::string &start, const std::string &stop, float startpoint, size_t loops) { if(!mSkelBase || mAnimSources.empty()) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 72d1c100e..f5f79dd72 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -272,6 +272,7 @@ public: * \param groupname Animation group to disable. */ void disable(const std::string &groupname); + void changeGroups(const std::string &groupname, int group); /** Retrieves the velocity (in units per second) that the animation will move. */ float getVelocity(const std::string &groupname) const; From bfc9fcf2cdd5e405c08e3e1d475dfcf54b0bb978 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 21:20:37 +0100 Subject: [PATCH 302/889] Add starting spells when building player (F_PCStart flag) --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index c084c86e5..7740240b6 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -12,6 +12,8 @@ #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include "spellcasting.hpp" + namespace MWMechanics { void MechanicsManager::buildPlayer() @@ -123,6 +125,19 @@ namespace MWMechanics npcStats.getSkill (index).setBase ( npcStats.getSkill (index).getBase() + bonus); } + + if (i==1) + { + // Major skill - add starting spells for this skill if existing + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + MWWorld::Store::iterator it = store.get().begin(); + for (; it != store.get().end(); ++it) + { + if (it->mData.mFlags & ESM::Spell::F_PCStart + && spellSchoolToSkill(getSpellSchool(&*it, ptr)) == index) + creatureStats.getSpells().add(it->mId); + } + } } } From 16e10cc702e8f133172a1bd3570ef2135505c376 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Wed, 1 Jan 2014 22:46:29 +0200 Subject: [PATCH 303/889] error fix --- apps/openmw/mwmechanics/character.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 4a8cf5ea8..aa1a812aa 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -814,7 +814,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun } //if playing combat animation and lowerbody is not busy switch to whole body animation - if(weaptype != WeaponType::WeapType_None && complete>0.0f) + if(weaptype != WeapType_None && complete>0.0f) { if( mMovementState != CharState_None || mJumpState != JumpState_None || From 9245faf2aa882846bd62f72eb0d0e3fc3bacec66 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 22:19:02 +0100 Subject: [PATCH 304/889] Don't destroyRenderTarget with a NULL window --- libs/openengine/ogre/renderer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 07bc8f3c9..9e5ec5414 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -25,7 +25,8 @@ void OgreRenderer::cleanup() delete mFader; mFader = NULL; - Ogre::Root::getSingleton().destroyRenderTarget(mWindow); + if (mWindow) + Ogre::Root::getSingleton().destroyRenderTarget(mWindow); mWindow = NULL; delete mOgreInit; From 57296722624b58837858424caa1b74975a2d9644 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 22:37:52 +0100 Subject: [PATCH 305/889] Show marked position on map. Implement Detect X magic effects. --- apps/openmw/mwbase/world.hpp | 14 +- apps/openmw/mwclass/misc.cpp | 7 + apps/openmw/mwclass/misc.hpp | 2 + apps/openmw/mwgui/mapwindow.cpp | 262 +++++++++++++++++++-------- apps/openmw/mwgui/mapwindow.hpp | 7 + apps/openmw/mwrender/localmap.cpp | 102 +++++------ apps/openmw/mwworld/cellfunctors.hpp | 8 +- apps/openmw/mwworld/cellstore.hpp | 2 +- apps/openmw/mwworld/class.hpp | 2 + apps/openmw/mwworld/worldimp.cpp | 93 +++++++++- apps/openmw/mwworld/worldimp.hpp | 8 +- 11 files changed, 362 insertions(+), 145 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index d9640f4d2..3a8897114 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -130,7 +130,7 @@ namespace MWBase virtual Ogre::Vector2 getNorthVector (MWWorld::CellStore* cell) = 0; ///< get north vector (OGRE coordinates) for given interior cell - virtual std::vector getDoorMarkers (MWWorld::CellStore* cell) = 0; + virtual void getDoorMarkers (MWWorld::CellStore* cell, std::vector& out) = 0; ///< get a list of teleport door markers for a given cell, to be displayed on the local map virtual void getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) = 0; @@ -438,6 +438,18 @@ namespace MWBase /// @note id must be lower case virtual void teleportToClosestMarker (const MWWorld::Ptr& ptr, const std::string& id, Ogre::Vector3 worldPos) = 0; + + enum DetectionType + { + Detect_Enchantment, + Detect_Key, + Detect_Creature + }; + /// List all references (filtered by \a type) detected by \a ptr. The range + /// is determined by the current magnitude of the "Detect X" magic effect belonging to \a type. + /// @note This also works for references in containers. + virtual void listDetectedReferences (const MWWorld::Ptr& ptr, std::vector& out, + DetectionType type) = 0; }; } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 1a40c4555..5e216fed4 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -252,4 +252,11 @@ namespace MWClass return ref->mBase->mData.mWeight; } + bool Miscellaneous::isKey(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mIsKey; + } + } diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index 16a8e8c05..16e9ca10b 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -57,6 +57,8 @@ namespace MWClass virtual float getWeight (const MWWorld::Ptr& ptr) const; virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + + virtual bool isKey (const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 00380aefd..2f34df236 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -103,27 +103,80 @@ namespace MWGui void LocalMapBase::onMarkerFocused (MyGUI::Widget* w1, MyGUI::Widget* w2) { + // Workaround to not make the marker visible if it's under fog of war applyFogOfWar (); } void LocalMapBase::onMarkerUnfocused (MyGUI::Widget* w1, MyGUI::Widget* w2) { + // Workaround to not make the marker visible if it's under fog of war applyFogOfWar (); } + MyGUI::IntPoint LocalMapBase::getMarkerPosition(float worldX, float worldY, MarkerPosition& markerPos) + { + MyGUI::IntPoint widgetPos; + // normalized cell coordinates + float nX,nY; + + markerPos.interior = mInterior; + + if (!mInterior) + { + int cellX, cellY; + MWBase::Environment::get().getWorld()->positionToIndex(worldX, worldY, cellX, cellY); + const int cellSize = 8192; + nX = (worldX - cellSize * cellX) / cellSize; + // Image space is -Y up, cells are Y up + nY = 1 - (worldY - cellSize * cellY) / cellSize; + + float cellDx = cellX - mCurX; + float cellDy = cellY - mCurY; + + markerPos.cellX = cellX; + markerPos.cellY = cellY; + + widgetPos = MyGUI::IntPoint(nX * 512 + (1+cellDx) * 512, + nY * 512 - (cellDy-1) * 512); + } + else + { + int cellX, cellY; + Ogre::Vector2 worldPos (worldX, worldY); + MWBase::Environment::get().getWorld ()->getInteriorMapPosition (worldPos, nX, nY, cellX, cellY); + + markerPos.cellX = cellX; + markerPos.cellY = cellY; + + widgetPos = MyGUI::IntPoint(nX * 512 + (1+cellX-mCurX) * 512, + nY * 512 + (1+cellY-mCurY) * 512); + } + + markerPos.nX = nX; + markerPos.nY = nY; + return widgetPos; + } + void LocalMapBase::setActiveCell(const int x, const int y, bool interior) { - if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) return; // don't do anything if we're still in the same cell + if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) + return; // don't do anything if we're still in the same cell + + mCurX = x; + mCurY = y; + mInterior = interior; + mChanged = false; // clear all previous markers for (unsigned int i=0; i< mLocalMap->getChildCount(); ++i) { - if (mLocalMap->getChildAt(i)->getName ().substr (0, 6) == "Marker") + if (mLocalMap->getChildAt(i)->getName ().substr (0, 4) == "Door") { MyGUI::Gui::getInstance ().destroyWidget (mLocalMap->getChildAt(i)); } } + // Update the map textures for (int mx=0; mx<3; ++mx) { for (int my=0; my<3; ++my) @@ -138,78 +191,57 @@ namespace MWGui box->setImageTexture(image); else box->setImageTexture("black.png"); - - - // door markers - - // interior map only consists of one cell, so handle the markers only once - if (interior && (mx != 2 || my != 2)) - continue; - - MWWorld::CellStore* cell; - if (interior) - cell = MWBase::Environment::get().getWorld ()->getInterior (mPrefix); - else - cell = MWBase::Environment::get().getWorld ()->getExterior (x+mx-1, y-(my-1)); - - std::vector doors = MWBase::Environment::get().getWorld ()->getDoorMarkers (cell); - - for (std::vector::iterator it = doors.begin(); it != doors.end(); ++it) - { - MWBase::World::DoorMarker marker = *it; - - // convert world coordinates to normalized cell coordinates - MyGUI::IntCoord widgetCoord; - float nX,nY; - int cellDx, cellDy; - if (!interior) - { - const int cellSize = 8192; - - nX = (marker.x - cellSize * (x+mx-1)) / cellSize; - nY = 1 - (marker.y - cellSize * (y-(my-1))) / cellSize; - - widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + mx * 512, nY * 512 - 4 + my * 512, 8, 8); - } - else - { - Ogre::Vector2 position (marker.x, marker.y); - MWBase::Environment::get().getWorld ()->getInteriorMapPosition (position, nX, nY, cellDx, cellDy); - - widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + (1+cellDx-x) * 512, nY * 512 - 4 + (1+cellDy-y) * 512, 8, 8); - } - - static int counter = 0; - ++counter; - MyGUI::Button* markerWidget = mLocalMap->createWidget("ButtonImage", - widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(counter)); - markerWidget->setImageResource("DoorMarker"); - markerWidget->setUserString("ToolTipType", "Layout"); - markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); - markerWidget->setUserString("Caption_TextOneLine", marker.name); - markerWidget->setUserString("IsMarker", "true"); - markerWidget->eventMouseSetFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerFocused); - markerWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerUnfocused); - - MarkerPosition markerPos; - markerPos.interior = interior; - markerPos.cellX = interior ? cellDx : x + mx - 1; - markerPos.cellY = interior ? cellDy : y + ((my - 1)*-1); - markerPos.nX = nX; - markerPos.nY = nY; - - markerWidget->setUserData(markerPos); - } - - } } - mInterior = interior; - mCurX = x; - mCurY = y; - mChanged = false; - // fog of war + MWBase::World* world = MWBase::Environment::get().getWorld(); + + // Retrieve the door markers we want to show + std::vector doors; + if (interior) + { + MWWorld::CellStore* cell = world->getInterior (mPrefix); + world->getDoorMarkers(cell, doors); + } + else + { + for (int dX=-1; dX<2; ++dX) + { + for (int dY=-1; dY<2; ++dY) + { + MWWorld::CellStore* cell = world->getExterior (mCurX+dX, mCurY+dY); + world->getDoorMarkers(cell, doors); + } + } + } + + // Create a widget for each marker + int counter = 0; + for (std::vector::iterator it = doors.begin(); it != doors.end(); ++it) + { + MWBase::World::DoorMarker marker = *it; + + MarkerPosition markerPos; + MyGUI::IntPoint widgetPos = getMarkerPosition(marker.x, marker.y, markerPos); + MyGUI::IntCoord widgetCoord(widgetPos.left - 4, + widgetPos.top - 4, + 8, 8); + ++counter; + MyGUI::Button* markerWidget = mLocalMap->createWidget("ButtonImage", + widgetCoord, MyGUI::Align::Default, "Door" + boost::lexical_cast(counter)); + markerWidget->setImageResource("DoorMarker"); + markerWidget->setUserString("ToolTipType", "Layout"); + markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); + markerWidget->setUserString("Caption_TextOneLine", marker.name); + markerWidget->eventMouseSetFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerFocused); + markerWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerUnfocused); + // Used by tooltips to not show the tooltip if marker is hidden by fog of war + markerWidget->setUserString("IsMarker", "true"); + markerWidget->setUserData(markerPos); + } + + updateMarkers(); + applyFogOfWar(); // set the compass texture again, because MyGUI determines sorting of ImageBox widgets @@ -222,6 +254,8 @@ namespace MWGui void LocalMapBase::setPlayerPos(const float x, const float y) { + updateMarkers(); + if (x == mLastPositionX && y == mLastPositionY) return; @@ -255,6 +289,88 @@ namespace MWGui mLastDirectionY = y; } + void LocalMapBase::addDetectionMarkers(int type) + { + std::vector markers; + MWBase::World* world = MWBase::Environment::get().getWorld(); + world->listDetectedReferences( + world->getPlayer().getPlayer(), + markers, MWBase::World::DetectionType(type)); + if (markers.empty()) + return; + + std::string markerTexture; + MyGUI::Colour markerColour; + if (type == MWBase::World::Detect_Creature) + { + markerTexture = "textures\\menu_map_dcreature.dds"; + markerColour = MyGUI::Colour(1,0,0,1); + } + if (type == MWBase::World::Detect_Key) + { + markerTexture = "textures\\menu_map_dkey.dds"; + markerColour = MyGUI::Colour(0,1,0,1); + } + if (type == MWBase::World::Detect_Enchantment) + { + markerTexture = "textures\\menu_map_dmagic.dds"; + markerColour = MyGUI::Colour(0,0,1,1); + } + + int counter = 0; + for (std::vector::iterator it = markers.begin(); it != markers.end(); ++it) + { + const ESM::Position& worldPos = it->getRefData().getPosition(); + MarkerPosition markerPos; + MyGUI::IntPoint widgetPos = getMarkerPosition(worldPos.pos[0], worldPos.pos[1], markerPos); + MyGUI::IntCoord widgetCoord(widgetPos.left - 4, + widgetPos.top - 4, + 8, 8); + ++counter; + MyGUI::ImageBox* markerWidget = mLocalMap->createWidget("ImageBox", + widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(counter)); + markerWidget->setImageTexture(markerTexture); + markerWidget->setUserString("IsMarker", "true"); + markerWidget->setUserData(markerPos); + markerWidget->setColour(markerColour); + } + } + + void LocalMapBase::updateMarkers() + { + // clear all previous markers + for (unsigned int i=0; i< mLocalMap->getChildCount(); ++i) + { + if (mLocalMap->getChildAt(i)->getName ().substr (0, 6) == "Marker") + { + MyGUI::Gui::getInstance ().destroyWidget (mLocalMap->getChildAt(i)); + } + } + + addDetectionMarkers(MWBase::World::Detect_Creature); + addDetectionMarkers(MWBase::World::Detect_Key); + addDetectionMarkers(MWBase::World::Detect_Enchantment); + + // Add marker for the spot marked with Mark magic effect + MWWorld::CellStore* markedCell = NULL; + ESM::Position markedPosition; + MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition); + if (markedCell && markedCell->isExterior() == !mInterior + && (!mInterior || Misc::StringUtils::ciEqual(markedCell->mCell->mName, mPrefix))) + { + MarkerPosition markerPos; + MyGUI::IntPoint widgetPos = getMarkerPosition(markedPosition.pos[0], markedPosition.pos[1], markerPos); + MyGUI::IntCoord widgetCoord(widgetPos.left - 4, + widgetPos.top - 4, + 8, 8); + MyGUI::ImageBox* markerWidget = mLocalMap->createWidget("ImageBox", + widgetCoord, MyGUI::Align::Default, "MarkerMarked"); + markerWidget->setImageTexture("textures\\menu_map_smark.dds"); + markerWidget->setUserString("IsMarker", "true"); + markerWidget->setUserData(markerPos); + } + } + // ------------------------------------------------------------------------------------------ MapWindow::MapWindow(const std::string& cacheDir) @@ -319,7 +435,7 @@ namespace MWGui static int _counter=0; MyGUI::Button* markerWidget = mGlobalMapImage->createWidget("ButtonImage", - widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(_counter)); + widgetCoord, MyGUI::Align::Default, "Door" + boost::lexical_cast(_counter)); markerWidget->setImageResource("DoorMarker"); markerWidget->setUserString("ToolTipType", "Layout"); markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); @@ -385,7 +501,7 @@ namespace MWGui for (unsigned int i=0; igetChildCount (); ++i) { - if (mGlobalMapImage->getChildAt (i)->getName().substr(0,6) == "Marker") + if (mGlobalMapImage->getChildAt (i)->getName().substr(0,4) == "Door") mGlobalMapImage->getChildAt (i)->castType()->setImageResource("DoorMarker"); } diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 2c90c422e..7df2105dc 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -55,9 +55,16 @@ namespace MWGui void onMarkerFocused(MyGUI::Widget* w1, MyGUI::Widget* w2); void onMarkerUnfocused(MyGUI::Widget* w1, MyGUI::Widget* w2); + MyGUI::IntPoint getMarkerPosition (float worldX, float worldY, MarkerPosition& markerPos); + virtual void notifyPlayerUpdate() {} virtual void notifyMapChanged() {} + // Update markers (Detect X effects, Mark/Recall effects) + // Note, door markers handled in setActiveCell + void updateMarkers(); + void addDetectionMarkers(int type); + OEngine::GUI::Layout* mLayout; bool mMapDragAndDrop; diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 5f4128978..3ea3380e8 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -225,64 +225,54 @@ void LocalMap::render(const float x, const float y, tex = TextureManager::getSingleton().getByName(texture); if (tex.isNull()) { - // try loading from disk - //if (boost::filesystem::exists(texture+".jpg")) - //{ - /// \todo - //} - //else + // render + tex = TextureManager::getSingleton().createManual( + texture, + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + TEX_TYPE_2D, + xw*sMapResolution/sSize, yw*sMapResolution/sSize, + 0, + PF_R8G8B8, + TU_RENDERTARGET); + + RenderTarget* rtt = tex->getBuffer()->getRenderTarget(); + + rtt->setAutoUpdated(false); + Viewport* vp = rtt->addViewport(mCellCamera); + vp->setOverlaysEnabled(false); + vp->setShadowsEnabled(false); + vp->setBackgroundColour(ColourValue(0, 0, 0)); + vp->setVisibilityMask(RV_Map); + vp->setMaterialScheme("local_map"); + + rtt->update(); + + // create "fog of war" texture + TexturePtr tex2 = TextureManager::getSingleton().createManual( + texture + "_fog", + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + TEX_TYPE_2D, + xw*sFogOfWarResolution/sSize, yw*sFogOfWarResolution/sSize, + 0, + PF_A8R8G8B8, + TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); + + // create a buffer to use for dynamic operations + std::vector buffer; + buffer.resize(sFogOfWarResolution*sFogOfWarResolution); + + // initialize to (0, 0, 0, 1) + for (int p=0; pgetBuffer()->getRenderTarget(); - - rtt->setAutoUpdated(false); - Viewport* vp = rtt->addViewport(mCellCamera); - vp->setOverlaysEnabled(false); - vp->setShadowsEnabled(false); - vp->setBackgroundColour(ColourValue(0, 0, 0)); - vp->setVisibilityMask(RV_Map); - vp->setMaterialScheme("local_map"); - - rtt->update(); - - // create "fog of war" texture - TexturePtr tex2 = TextureManager::getSingleton().createManual( - texture + "_fog", - ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - TEX_TYPE_2D, - xw*sFogOfWarResolution/sSize, yw*sFogOfWarResolution/sSize, - 0, - PF_A8R8G8B8, - TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); - - // create a buffer to use for dynamic operations - std::vector buffer; - buffer.resize(sFogOfWarResolution*sFogOfWarResolution); - - // initialize to (0, 0, 0, 1) - for (int p=0; pgetBuffer()->lock(HardwareBuffer::HBL_DISCARD), &buffer[0], sFogOfWarResolution*sFogOfWarResolution*4); - tex2->getBuffer()->unlock(); - - mBuffers[texture] = buffer; - - // save to cache for next time - //rtt->writeContentsToFile("./" + texture + ".jpg"); + buffer[p] = (255 << 24); } + + memcpy(tex2->getBuffer()->lock(HardwareBuffer::HBL_DISCARD), &buffer[0], sFogOfWarResolution*sFogOfWarResolution*4); + tex2->getBuffer()->unlock(); + + mBuffers[texture] = buffer; } + mRenderingManager->enableLights(true); mLight->setVisible(false); @@ -339,8 +329,6 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni Vector3 playerdirection = mCameraRotNode->convertWorldToLocalOrientation(orientation).yAxis(); - Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().y); - if (!mInterior) { x = std::ceil(pos.x / sSize)-1; diff --git a/apps/openmw/mwworld/cellfunctors.hpp b/apps/openmw/mwworld/cellfunctors.hpp index 4b1f70096..5115fa02d 100644 --- a/apps/openmw/mwworld/cellfunctors.hpp +++ b/apps/openmw/mwworld/cellfunctors.hpp @@ -4,7 +4,7 @@ #include #include -#include "refdata.hpp" +#include "ptr.hpp" namespace ESM { @@ -18,13 +18,13 @@ namespace MWWorld { std::vector mHandles; - bool operator() (ESM::CellRef& ref, RefData& data) + bool operator() (MWWorld::Ptr ptr) { - Ogre::SceneNode* handle = data.getBaseNode(); + Ogre::SceneNode* handle = ptr.getRefData().getBaseNode(); if (handle) mHandles.push_back (handle); - data.setBaseNode(0); + ptr.getRefData().setBaseNode(0); return true; } }; diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 8a01caf18..8731c42dc 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -143,7 +143,7 @@ namespace MWWorld { if (!iter->mData.getCount()) continue; - if (!functor (iter->mRef, iter->mData)) + if (!functor (MWWorld::Ptr(&*iter, this))) return false; } return true; diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index cb6690a46..d737c18a2 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -290,6 +290,8 @@ namespace MWWorld virtual bool isPersistent (const MWWorld::Ptr& ptr) const; + virtual bool isKey (const MWWorld::Ptr& ptr) const { return false; } + virtual Ptr copyToCell(const Ptr &ptr, CellStore &cell) const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5d7e9deeb..ff2ae7161 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1442,10 +1442,8 @@ namespace MWWorld return d; } - std::vector World::getDoorMarkers (CellStore* cell) + void World::getDoorMarkers (CellStore* cell, std::vector& out) { - std::vector result; - MWWorld::CellRefList& doors = cell->mDoors; CellRefList::List& refList = doors.mList; for (CellRefList::List::iterator it = refList.begin(); it != refList.end(); ++it) @@ -1461,11 +1459,9 @@ namespace MWWorld newMarker.x = pos.pos[0]; newMarker.y = pos.pos[1]; - result.push_back(newMarker); + out.push_back(newMarker); } } - - return result; } void World::getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) @@ -1829,9 +1825,9 @@ namespace MWWorld { std::vector mHandles; - bool operator() (ESM::CellRef& ref, RefData& data) + bool operator() (Ptr ptr) { - Ogre::SceneNode* handle = data.getBaseNode(); + Ogre::SceneNode* handle = ptr.getRefData().getBaseNode(); if (handle) mHandles.push_back(handle->getName()); return true; @@ -2348,4 +2344,85 @@ namespace MWWorld mWeatherManager->update(duration); } + + struct AddDetectedReference + { + AddDetectedReference(std::vector& out, Ptr detector, World::DetectionType type, float squaredDist) + : mOut(out), mDetector(detector), mType(type), mSquaredDist(squaredDist) + { + } + + std::vector& mOut; + Ptr mDetector; + float mSquaredDist; + World::DetectionType mType; + bool operator() (MWWorld::Ptr ptr) + { + if (Ogre::Vector3(ptr.getRefData().getPosition().pos).squaredDistance( + Ogre::Vector3(mDetector.getRefData().getPosition().pos)) >= mSquaredDist) + return true; + + if (!ptr.getRefData().isEnabled()) + return true; + + // Consider references inside containers as well + if (ptr.getClass().isActor() || ptr.getClass().getTypeName() == typeid(ESM::Container).name()) + { + MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr); + { + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + if (needToAdd(*it)) + { + mOut.push_back(ptr); + return true; + } + } + } + } + + if (needToAdd(ptr)) + mOut.push_back(ptr); + + return true; + } + + bool needToAdd (MWWorld::Ptr ptr) + { + if (mType == World::Detect_Creature && ptr.getClass().getTypeName() != typeid(ESM::Creature).name()) + return false; + if (mType == World::Detect_Key && !ptr.getClass().isKey(ptr)) + return false; + if (mType == World::Detect_Enchantment && ptr.getClass().getEnchantment(ptr).empty()) + return false; + return true; + } + }; + + void World::listDetectedReferences(const Ptr &ptr, std::vector &out, DetectionType type) + { + const MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(ptr).getMagicEffects(); + float dist=0; + if (type == World::Detect_Creature) + dist = effects.get(MWMechanics::EffectKey(ESM::MagicEffect::DetectAnimal)).mMagnitude; + else if (type == World::Detect_Key) + dist = effects.get(MWMechanics::EffectKey(ESM::MagicEffect::DetectKey)).mMagnitude; + else if (type == World::Detect_Enchantment) + dist = effects.get(MWMechanics::EffectKey(ESM::MagicEffect::DetectEnchantment)).mMagnitude; + + if (!dist) + return; + + // TODO: "1 foot" = 20 game units? + dist *= 20; + + AddDetectedReference functor (out, ptr, type, dist*dist); + + const Scene::CellStoreCollection& active = mWorldScene->getActiveCells(); + for (Scene::CellStoreCollection::const_iterator it = active.begin(); it != active.end(); ++it) + { + MWWorld::CellStore* cellStore = *it; + cellStore->forEach(functor); + } + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index def8b2fa4..7e3b0befe 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -202,7 +202,7 @@ namespace MWWorld virtual Ogre::Vector2 getNorthVector (CellStore* cell); ///< get north vector (OGRE coordinates) for given interior cell - virtual std::vector getDoorMarkers (MWWorld::CellStore* cell); + virtual void getDoorMarkers (MWWorld::CellStore* cell, std::vector& out); ///< get a list of teleport door markers for a given cell, to be displayed on the local map virtual void getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y); @@ -526,6 +526,12 @@ namespace MWWorld /// @note id must be lower case virtual void teleportToClosestMarker (const MWWorld::Ptr& ptr, const std::string& id, Ogre::Vector3 worldPos); + + /// List all references (filtered by \a type) detected by \a ptr. The range + /// is determined by the current magnitude of the "Detect X" magic effect belonging to \a type. + /// @note This also works for references in containers. + virtual void listDetectedReferences (const MWWorld::Ptr& ptr, std::vector& out, + DetectionType type); }; } From 531bef6193c1e875cb5e5861e758c3c57a251576 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 22:46:10 +0100 Subject: [PATCH 306/889] Shorter Vector3 initialisation --- apps/openmw/mwsound/soundmanagerimp.cpp | 6 +++--- apps/openmw/mwworld/worldimp.cpp | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 372be8393..2e52739ac 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -252,7 +252,7 @@ namespace MWSound float basevol = volumeFromType(Play_TypeVoice); std::string filePath = "Sound/"+filename; const ESM::Position &pos = ptr.getRefData().getPosition(); - const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); + const Ogre::Vector3 objpos(pos.pos); MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, 1.0f, basevol, 1.0f, 20.0f, 12750.0f, Play_Normal|Play_TypeVoice, 0); @@ -354,7 +354,7 @@ namespace MWSound float min, max; std::string file = lookup(soundId, volume, min, max); const ESM::Position &pos = ptr.getRefData().getPosition(); - const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); + const Ogre::Vector3 objpos(pos.pos); sound = mOutput->playSound3D(file, objpos, volume, basevol, pitch, min, max, mode|type, offset); if((mode&Play_NoTrack)) @@ -584,7 +584,7 @@ namespace MWSound if(!ptr.isEmpty()) { const ESM::Position &pos = ptr.getRefData().getPosition(); - const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); + const Ogre::Vector3 objpos(pos.pos); snditer->first->setPosition(objpos); } //update fade out diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ff2ae7161..e6c095baa 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1063,7 +1063,7 @@ namespace MWWorld void World::adjustPosition(const Ptr &ptr) { - Ogre::Vector3 pos (ptr.getRefData().getPosition().pos[0], ptr.getRefData().getPosition().pos[1], ptr.getRefData().getPosition().pos[2]); + Ogre::Vector3 pos (ptr.getRefData().getPosition().pos); if(!ptr.getRefData().getBaseNode()) { @@ -1304,7 +1304,7 @@ namespace MWWorld if (mPlayer->getPlayer().getCell()->isExterior()) { ESM::Position pos = mPlayer->getPlayer().getRefData().getPosition(); - mPlayer->setLastKnownExteriorPosition(Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2])); + mPlayer->setLastKnownExteriorPosition(Ogre::Vector3(pos.pos)); } } @@ -1584,7 +1584,7 @@ namespace MWWorld pos.rot[1] = 0; Ogre::Vector3 orig = - Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2]); + Ogre::Vector3(pos.pos); Ogre::Vector3 dir = Ogre::Vector3(0, 0, -1); float len = (pos.pos[2] >= 0) ? pos.pos[2] : -pos.pos[2]; @@ -2299,7 +2299,7 @@ namespace MWWorld if (ref.mRef.mTeleport && ref.mRef.mDestCell.empty()) { ESM::Position pos = ref.mRef.mDoorDest; - result = Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2]); + result = Ogre::Vector3(pos.pos); return true; } } @@ -2320,7 +2320,7 @@ namespace MWWorld for (std::vector::iterator it = markers.begin(); it != markers.end(); ++it) { ESM::Position pos = it->getRefData().getPosition(); - Ogre::Vector3 markerPos = Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2]); + Ogre::Vector3 markerPos = Ogre::Vector3(pos.pos); float distance = worldPos.squaredDistance(markerPos); if (distance < closestDistance) { From f6387d5979a9d1571f61f4b217aff09c6b7ab09a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 23:30:58 +0100 Subject: [PATCH 307/889] Implement Telekinesis magic effect. Remove some duplicate code. --- apps/openmw/mwworld/worldimp.cpp | 39 +++++++++----------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e6c095baa..d2e8a21b7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -796,32 +796,9 @@ namespace MWWorld MWWorld::Ptr World::getFacedObject() { - std::pair result; - - if (!mRendering->occlusionQuerySupported()) - result = mPhysics->getFacedHandle (getMaxActivationDistance ()); - else - result = std::make_pair (mFacedDistance, mFacedHandle); - - if (result.second.empty()) - return MWWorld::Ptr (); - - MWWorld::Ptr object = searchPtrViaHandle (result.second); - if (object.isEmpty()) - return object; - float ActivationDistance; - - if (MWBase::Environment::get().getWindowManager()->isConsoleMode()) - ActivationDistance = getObjectActivationDistance ()*50; - else if (object.getTypeName ().find("NPC") != std::string::npos) - ActivationDistance = getNpcActivationDistance (); - else - ActivationDistance = getObjectActivationDistance (); - - if (result.first > ActivationDistance) - return MWWorld::Ptr (); - - return object; + if (mFacedHandle.empty()) + return MWWorld::Ptr(); + return searchPtrViaHandle(mFacedHandle); } std::pair World::getHitContact(const MWWorld::Ptr &ptr, float distance) @@ -1346,6 +1323,12 @@ namespace MWWorld void World::updateFacedHandle () { + float telekinesisRangeBonus = + mPlayer->getPlayer().getClass().getCreatureStats(mPlayer->getPlayer()).getMagicEffects() + .get(ESM::MagicEffect::Telekinesis).mMagnitude * 22; + + float activationDistance = getMaxActivationDistance() + telekinesisRangeBonus; + // send new query // figure out which object we want to test against std::vector < std::pair < float, std::string > > results; @@ -1353,13 +1336,13 @@ namespace MWWorld { float x, y; MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); - results = mPhysics->getFacedHandles(x, y, getMaxActivationDistance ()); + results = mPhysics->getFacedHandles(x, y, activationDistance); if (MWBase::Environment::get().getWindowManager()->isConsoleMode()) results = mPhysics->getFacedHandles(x, y, getMaxActivationDistance ()*50); } else { - results = mPhysics->getFacedHandles(getMaxActivationDistance ()); + results = mPhysics->getFacedHandles(activationDistance); } // ignore the player and other things we're not interested in From 24aa743573464fabaf20db7b15c3f7c6bf18bbfe Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 23:34:18 +0100 Subject: [PATCH 308/889] Add function for converting feet to game units --- apps/openmw/mwworld/worldimp.cpp | 13 ++++++++++--- apps/openmw/mwworld/worldimp.hpp | 2 ++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d2e8a21b7..c11de7753 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1325,7 +1325,8 @@ namespace MWWorld { float telekinesisRangeBonus = mPlayer->getPlayer().getClass().getCreatureStats(mPlayer->getPlayer()).getMagicEffects() - .get(ESM::MagicEffect::Telekinesis).mMagnitude * 22; + .get(ESM::MagicEffect::Telekinesis).mMagnitude; + telekinesisRangeBonus = feetToGameUnits(telekinesisRangeBonus); float activationDistance = getMaxActivationDistance() + telekinesisRangeBonus; @@ -2396,8 +2397,7 @@ namespace MWWorld if (!dist) return; - // TODO: "1 foot" = 20 game units? - dist *= 20; + dist = feetToGameUnits(dist); AddDetectedReference functor (out, ptr, type, dist*dist); @@ -2408,4 +2408,11 @@ namespace MWWorld cellStore->forEach(functor); } } + + float World::feetToGameUnits(float feet) + { + // Looks like there is no GMST for this. This factor was determined in experiments + // with the Telekinesis effect. + return feet * 22; + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7e3b0befe..1aecb6fb6 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -155,6 +155,8 @@ namespace MWWorld /// Called when \a object is moved to an inactive cell void objectLeftActiveCell (MWWorld::Ptr object, MWWorld::Ptr movedPtr); + float feetToGameUnits(float feet); + public: World (OEngine::Render::OgreRenderer& renderer, From c6421276bdc5dc2a25b9f45de5283f4f3f688ced Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 23:59:17 +0100 Subject: [PATCH 309/889] Closes #841: Correct activation distance in third person mode --- apps/openmw/mwrender/camera.cpp | 5 +++++ apps/openmw/mwrender/camera.hpp | 2 ++ apps/openmw/mwrender/renderingmanager.cpp | 5 +++++ apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 1 + 5 files changed, 14 insertions(+) diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 8f54be3f8..b2ec9884d 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -278,6 +278,11 @@ namespace MWRender } } + float Camera::getCameraDistance() const + { + return mCamera->getPosition().z; + } + void Camera::setCameraDistance(float dist, bool adjust, bool override) { if(mFirstPersonView && !mPreviewMode && !mVanity.enabled) diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index 87e486629..d31d9e56c 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -105,6 +105,8 @@ namespace MWRender /// Restore default camera distance for current mode. void setCameraDistance(); + float getCameraDistance() const; + void setAnimation(NpcAnimation *anim); /// Stores focal and camera world positions in passed arguments diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index b739a9f95..f6e69b726 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1026,4 +1026,9 @@ void RenderingManager::enableTerrain(bool enable) mTerrain->setVisible(false); } +float RenderingManager::getCameraDistance() const +{ + return mCamera->getCameraDistance(); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index abc8fd71a..b13e546e8 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -90,6 +90,7 @@ public: bool vanityRotateCamera(const float *rot); void setCameraDistance(float dist, bool adjust = false, bool override = true); + float getCameraDistance() const; void setupPlayer(const MWWorld::Ptr &ptr); void renderPlayer(const MWWorld::Ptr &ptr); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c11de7753..715270780 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1329,6 +1329,7 @@ namespace MWWorld telekinesisRangeBonus = feetToGameUnits(telekinesisRangeBonus); float activationDistance = getMaxActivationDistance() + telekinesisRangeBonus; + activationDistance += mRendering->getCameraDistance(); // send new query // figure out which object we want to test against From 899214a906a7330b92c9fb6ddf3b7bee50143820 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 00:13:23 +0100 Subject: [PATCH 310/889] Use fVanityDelay --- apps/openmw/mwinput/inputmanagerimp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index ffb2af81e..1ad409515 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -831,9 +831,11 @@ namespace MWInput void InputManager::updateIdleTime(float dt) { + static const float vanityDelay = MWBase::Environment::get().getWorld()->getStore().get() + .find("fVanityDelay")->getFloat(); if (mTimeIdle >= 0.f) mTimeIdle += dt; - if (mTimeIdle > 30.f) { + if (mTimeIdle > vanityDelay) { MWBase::Environment::get().getWorld()->toggleVanityMode(true); mTimeIdle = -1.f; } From 590c8cb4a0c6bc1dc55f88c20a29c8f9453f757b Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 01:03:44 +0100 Subject: [PATCH 311/889] Implement Disintegrate effects. When an armor/weapon breaks, unequip it and do not allow equipping it again. --- apps/openmw/mwclass/armor.cpp | 3 ++ apps/openmw/mwclass/npc.cpp | 10 +++++ apps/openmw/mwclass/weapon.cpp | 3 ++ apps/openmw/mwmechanics/actors.cpp | 65 ++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+) diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index c7c80ec2e..0fae1b05c 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -291,6 +291,9 @@ namespace MWClass { MWWorld::InventoryStore& invStore = MWWorld::Class::get(npc).getInventoryStore(npc); + if (ptr.getCellRef().mCharge == 0) + return std::make_pair(0, "#{sInventoryMessage1}"); + // slots that this item can be equipped in std::pair, bool> slots_ = MWWorld::Class::get(ptr).getEquipmentSlots(ptr); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4187aa8c1..2ba0566dd 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -493,6 +493,11 @@ namespace MWClass if (!MWBase::Environment::get().getWorld()->getGodModeState()) weapon.getCellRef().mCharge -= std::min(std::max(1, (int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weapon.getCellRef().mCharge); + + // Weapon broken? unequip it + if (weapon.getCellRef().mCharge == 0) + weapon = *inv.unequipItem(weapon, ptr); + } healthdmg = true; } @@ -644,6 +649,11 @@ namespace MWClass armorref.mCharge = armor.get()->mBase->mData.mHealth; armorref.mCharge -= std::min(std::max(1, (int)damagediff), armorref.mCharge); + + // Armor broken? unequip it + if (armorref.mCharge == 0) + inv.unequipItem(armor, ptr); + switch(get(armor).getEquipmentSkill(armor)) { case ESM::Skill::LightArmor: diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 7b0a5eb47..5e93e0d81 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -388,6 +388,9 @@ namespace MWClass std::pair Weapon::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const { + if (ptr.getCellRef().mCharge == 0) + return std::make_pair(0, "#{sInventoryMessage1}"); + std::pair, bool> slots_ = MWWorld::Class::get(ptr).getEquipmentSlots(ptr); if (slots_.first.empty()) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 62242ee8c..13b5da11a 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -46,6 +46,44 @@ void adjustBoundItem (const std::string& item, bool bound, const MWWorld::Ptr& a } } +bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate) +{ + // TODO: remove this check once creatures support inventory store + if (ptr.getTypeName() == typeid(ESM::NPC).name()) + { + MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr); + MWWorld::ContainerStoreIterator item = + inv.getSlot(slot); + if (item != inv.end()) + { + if (!item->getClass().hasItemHealth(*item)) + return false; + if (item->getCellRef().mCharge == -1) + item->getCellRef().mCharge = item->getClass().getItemMaxHealth(*item); + + if (item->getCellRef().mCharge == 0) + return false; + + item->getCellRef().mCharge -= + std::min(disintegrate, + static_cast(item->getCellRef().mCharge)); + + if (item->getCellRef().mCharge == 0) + { + // Will unequip the broken item and try to find a replacement + if (ptr.getRefData().getHandle() != "player") + inv.autoEquip(ptr); + else + inv.unequipItem(*item, ptr); + } + + return true; + } + } + return true; +} + + } namespace MWMechanics @@ -241,6 +279,33 @@ namespace MWMechanics creatureStats.setDynamic(i, stat); } + // Apply disintegration (reduces item health) + float disintegrateWeapon = effects.get(EffectKey(ESM::MagicEffect::DisintegrateWeapon)).mMagnitude; + if (disintegrateWeapon > 0) + disintegrateSlot(ptr, MWWorld::InventoryStore::Slot_CarriedRight, disintegrateWeapon*duration); + float disintegrateArmor = effects.get(EffectKey(ESM::MagicEffect::DisintegrateArmor)).mMagnitude; + if (disintegrateArmor > 0) + { + // According to UESP + int priorities[] = { + MWWorld::InventoryStore::Slot_CarriedLeft, + MWWorld::InventoryStore::Slot_Cuirass, + MWWorld::InventoryStore::Slot_LeftPauldron, + MWWorld::InventoryStore::Slot_RightPauldron, + MWWorld::InventoryStore::Slot_LeftGauntlet, + MWWorld::InventoryStore::Slot_RightGauntlet, + MWWorld::InventoryStore::Slot_Helmet, + MWWorld::InventoryStore::Slot_Greaves, + MWWorld::InventoryStore::Slot_Boots + }; + + for (unsigned int i=0; i Date: Thu, 2 Jan 2014 01:31:06 +0100 Subject: [PATCH 312/889] Some checks to prevent bound item abuse --- apps/openmw/mwgui/inventorywindow.cpp | 8 ++++++++ apps/openmw/mwgui/pickpocketitemmodel.cpp | 8 ++++++++ apps/openmw/mwgui/tradeitemmodel.cpp | 7 +++++++ apps/openmw/mwmechanics/actors.cpp | 5 +++++ components/esm/cellref.hpp | 4 ++-- 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index ffd81e98b..c14971f6e 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -163,6 +163,14 @@ namespace MWGui MWWorld::Ptr object = item.mBase; int count = item.mCount; + // Bound items may not be moved + if (item.mBase.getCellRef().mRefID.size() > 6 + && item.mBase.getCellRef().mRefID.substr(0,6) == "bound_") + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sBarterDialog12}"); + return; + } + if (item.mType == ItemStack::Type_Equipped) { MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); diff --git a/apps/openmw/mwgui/pickpocketitemmodel.cpp b/apps/openmw/mwgui/pickpocketitemmodel.cpp index 16be5f6cc..13ee4396d 100644 --- a/apps/openmw/mwgui/pickpocketitemmodel.cpp +++ b/apps/openmw/mwgui/pickpocketitemmodel.cpp @@ -40,6 +40,14 @@ namespace MWGui for (size_t i = 0; igetItemCount(); ++i) { const ItemStack& item = mSourceModel->getItem(i); + + // Bound items may not be stolen + if (item.mBase.getCellRef().mRefID.size() > 6 + && item.mBase.getCellRef().mRefID.substr(0,6) == "bound_") + { + continue; + } + if (std::find(mHiddenItems.begin(), mHiddenItems.end(), item) == mHiddenItems.end() && item.mType != ItemStack::Type_Equipped) mItems.push_back(item); diff --git a/apps/openmw/mwgui/tradeitemmodel.cpp b/apps/openmw/mwgui/tradeitemmodel.cpp index e836355d3..5c12843da 100644 --- a/apps/openmw/mwgui/tradeitemmodel.cpp +++ b/apps/openmw/mwgui/tradeitemmodel.cpp @@ -154,6 +154,13 @@ namespace MWGui if(!MWWorld::Class::get(base).canSell(base, services)) continue; + // Bound items may not be bought + if (item.mBase.getCellRef().mRefID.size() > 6 + && item.mBase.getCellRef().mRefID.substr(0,6) == "bound_") + { + continue; + } + // don't show equipped items if(mMerchant.getTypeName() == typeid(ESM::NPC).name()) { diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 13b5da11a..840167e3a 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -695,6 +695,11 @@ namespace MWMechanics iter->second->kill(); + // Reset magic effects and recalculate derived effects + // One case where we need this is to make sure bound items are removed upon death + stats.setMagicEffects(MWMechanics::MagicEffects()); + calculateCreatureStatModifiers(iter->first, 0); + ++mDeathCount[cls.getId(iter->first)]; if(cls.isEssential(iter->first)) diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 47cb0b99e..5f1066cf8 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -20,7 +20,7 @@ namespace ESM public: int mRefnum; // Reference number - std::string mRefID; // ID of object being referenced + std::string mRefID; // ID of object being referenced (must be lowercase) float mScale; // Scale applied to mesh @@ -89,4 +89,4 @@ namespace ESM }; } -#endif \ No newline at end of file +#endif From 5b0a4c94245cb632801e3798effc0d3998ff8202 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 01:42:30 +0100 Subject: [PATCH 313/889] Get rid of unused file and some other cruft. --- CMakeLists.txt | 2 - libs/openengine/bullet/CMotionState.cpp | 46 ---------------------- libs/openengine/bullet/CMotionState.h | 52 ------------------------- libs/openengine/bullet/physic.cpp | 10 +---- libs/openengine/bullet/physic.hpp | 17 -------- 5 files changed, 2 insertions(+), 125 deletions(-) delete mode 100644 libs/openengine/bullet/CMotionState.cpp delete mode 100644 libs/openengine/bullet/CMotionState.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ee4a0246b..64f8121c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,8 +99,6 @@ set(OENGINE_BULLET ${LIBDIR}/openengine/bullet/BtOgreExtras.h ${LIBDIR}/openengine/bullet/BtOgreGP.h ${LIBDIR}/openengine/bullet/BtOgrePG.h - ${LIBDIR}/openengine/bullet/CMotionState.cpp - ${LIBDIR}/openengine/bullet/CMotionState.h ${LIBDIR}/openengine/bullet/physic.cpp ${LIBDIR}/openengine/bullet/physic.hpp ${LIBDIR}/openengine/bullet/BulletShapeLoader.cpp diff --git a/libs/openengine/bullet/CMotionState.cpp b/libs/openengine/bullet/CMotionState.cpp deleted file mode 100644 index c20415884..000000000 --- a/libs/openengine/bullet/CMotionState.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "CMotionState.h" -#include "physic.hpp" - -#include -#include -#include - -namespace OEngine { -namespace Physic -{ - - CMotionState::CMotionState(PhysicEngine* eng,std::string name) - : isPC(false) - , isNPC(true) - { - pEng = eng; - tr.setIdentity(); - pName = name; - } - - void CMotionState::getWorldTransform(btTransform &worldTrans) const - { - worldTrans = tr; - } - - void CMotionState::setWorldTransform(const btTransform &worldTrans) - { - tr = worldTrans; - - PhysicEvent evt; - evt.isNPC = isNPC; - evt.isPC = isPC; - evt.newTransform = tr; - evt.RigidBodyName = pName; - - if(isPC) - { - pEng->PEventList.push_back(evt); - } - else - { - pEng->NPEventList.push_back(evt); - } - } - -}} diff --git a/libs/openengine/bullet/CMotionState.h b/libs/openengine/bullet/CMotionState.h deleted file mode 100644 index 3508ab4ef..000000000 --- a/libs/openengine/bullet/CMotionState.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef OENGINE_CMOTIONSTATE_H -#define OENGINE_CMOTIONSTATE_H - -#include -#include - -namespace OEngine { -namespace Physic -{ - class PhysicEngine; - - /** - * A CMotionState is associated with a single RigidBody. - * When the RigidBody is moved by bullet, bullet will call the function setWorldTransform. - * for more info, see the bullet Wiki at btMotionState. - */ - class CMotionState:public btMotionState - { - public: - - CMotionState(PhysicEngine* eng,std::string name); - - /** - * Return the position of the RigidBody. - */ - virtual void getWorldTransform(btTransform &worldTrans) const; - - /** - * Function called by bullet when the RigidBody is moved. - * It add an event to the EventList of the PhysicEngine class. - */ - virtual void setWorldTransform(const btTransform &worldTrans); - - protected: - PhysicEngine* pEng; - btTransform tr; - bool isNPC; - bool isPC; - - std::string pName; - }; - - struct PhysicEvent - { - bool isNPC; - bool isPC; - btTransform newTransform; - std::string RigidBodyName; - }; - -}} -#endif diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index e33edda18..4e80088bf 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -3,7 +3,6 @@ #include #include #include -#include "CMotionState.h" #include "OgreRoot.h" #include "btKinematicCharacterController.h" #include "BtOgrePG.h" @@ -318,9 +317,7 @@ namespace Physic btVector3 scl(triSize, triSize, 1); hfShape->setLocalScaling(scl); - CMotionState* newMotionState = new CMotionState(this,name); - - btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,newMotionState,hfShape); + btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,0,hfShape); RigidBody* body = new RigidBody(CI,name); body->getWorldTransform().setOrigin(btVector3( (x+0.5)*triSize*(sqrtVerts-1), (y+0.5)*triSize*(sqrtVerts-1), (maxh+minh)/2.f)); @@ -401,12 +398,9 @@ namespace Physic else shape->mRaycastingShape->setLocalScaling( btVector3(scale,scale,scale)); - //create the motionState - CMotionState* newMotionState = new CMotionState(this,name); - //create the real body btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo - (0,newMotionState, raycasting ? shape->mRaycastingShape : shape->mCollisionShape); + (0,0, raycasting ? shape->mRaycastingShape : shape->mCollisionShape); RigidBody* body = new RigidBody(CI,name); body->mPlaceable = placeable; diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index f28f95ccb..6cd7244b8 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -38,7 +38,6 @@ namespace MWWorld namespace OEngine { namespace Physic { - class CMotionState; struct PhysicEvent; class PhysicEngine; class RigidBody; @@ -157,17 +156,7 @@ namespace Physic private: void disableCollisionBody(); void enableCollisionBody(); -public: -//HACK: in Visual Studio 2010 and presumably above, this structures alignment -// must be 16, but the built in operator new & delete don't properly -// perform this alignment. -#if _MSC_VER >= 1600 - void * operator new (size_t Size) { return _aligned_malloc (Size, 16); } - void operator delete (void * Data) { _aligned_free (Data); } -#endif - - private: OEngine::Physic::RigidBody* mBody; OEngine::Physic::RigidBody* mRaycastingBody; @@ -329,12 +318,6 @@ public: const btVector3 &origin, btCollisionObject *object); - //event list of non player object - std::list NPEventList; - - //event list affecting the player - std::list PEventList; - //Bullet Stuff btOverlappingPairCache* pairCache; btBroadphaseInterface* broadphase; From 0f2b2fabdb35125e85ec648ad334b6bae40a81fa Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 02:03:11 +0100 Subject: [PATCH 314/889] Implement water walking --- apps/openmw/mwworld/physicssystem.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 451e093ae..2a7f5948e 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -577,10 +577,28 @@ namespace MWWorld float oldHeight = iter->first.getRefData().getPosition().pos[2]; + bool waterCollision = false; + if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects() + .get(ESM::MagicEffect::WaterWalking).mMagnitude + && cell->hasWater() + && !world->isUnderwater(iter->first.getCell(), + Ogre::Vector3(iter->first.getRefData().getPosition().pos))) + waterCollision = true; + + btStaticPlaneShape planeShape(btVector3(0,0,1), waterlevel); + btCollisionObject object; + object.setCollisionShape(&planeShape); + + if (waterCollision) + mEngine->dynamicsWorld->addCollisionObject(&object); + Ogre::Vector3 newpos = MovementSolver::move(iter->first, iter->second, mTimeAccum, world->isFlying(iter->first), waterlevel, mEngine); + if (waterCollision) + mEngine->dynamicsWorld->removeCollisionObject(&object); + float heightDiff = newpos.z - oldHeight; if (heightDiff < 0) From 993edf03844cbf5b614371b031daf55db57b9550 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 02:27:48 +0100 Subject: [PATCH 315/889] Bug #1063: Safety check in moveObject. Required when items in containers execute script commands like setAtStart etc. --- apps/openmw/mwworld/worldimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 715270780..d02e1022e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -911,7 +911,7 @@ namespace MWWorld ptr.getRefData().setCount(0); } } - if (haveToMove) + if (haveToMove && ptr.getRefData().getBaseNode()) { mRendering->moveObject(ptr, vec); mPhysics->moveObject (ptr); From c558e12212ace15ec20097b1e365a9bdfcc052aa Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 03:06:48 +0100 Subject: [PATCH 316/889] Don't try to move objects that are not in a cell --- .../mwscript/transformationextensions.cpp | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 47a632ae9..e44180977 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -207,6 +207,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); + + if (!ptr.isInCell()) + return; + if (ptr.getRefData().getHandle() == "player") { MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); @@ -275,6 +279,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); + + if (!ptr.isInCell()) + return; + if (ptr.getRefData().getHandle() == "player") { MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); @@ -336,6 +344,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); + + if (!ptr.isInCell()) + return; + if (ptr.getRefData().getHandle() == "player") { MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); @@ -605,6 +617,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); + + if (!ptr.isInCell()) + return; + ptr.getRefData().getLocalRotation().rot[0] = 0; ptr.getRefData().getLocalRotation().rot[1] = 0; ptr.getRefData().getLocalRotation().rot[2] = 0; @@ -624,6 +640,9 @@ namespace MWScript { const MWWorld::Ptr& ptr = R()(runtime); + if (!ptr.isInCell()) + return; + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); @@ -659,6 +678,9 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); + if (!ptr.isInCell()) + return; + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); From e4c802eac402cfc77e07bc4ea233aac097eae7ed Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 2 Jan 2014 14:17:59 +0100 Subject: [PATCH 317/889] updated changelog --- readme.txt | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/readme.txt b/readme.txt index 946df64ca..132485119 100644 --- a/readme.txt +++ b/readme.txt @@ -91,6 +91,92 @@ Allowed options: CHANGELOG +0.28.0 + +Bug #399: Inventory changes are not visible immediately +Bug #417: Apply weather instantly when teleporting +Bug #566: Global Map position marker not updated for interior cells +Bug #712: Looting corpse delay +Bug #716: Problem with the "Vurt's Ascadian Isles Mod" mod +Bug #805: Two TR meshes appear black (v0.24RC) +Bug #841: Third-person activation distance taken from camera rather than head +Bug #845: NPCs hold torches during the day +Bug #855: Vvardenfell Visages Volume I some hairs don´t appear since 0,24 +Bug #856: Maormer race by Mac Kom - The heads are way up +Bug #864: Walk locks during loading in 3rd person +Bug #871: active weapon/magic item icon is not immediately made blank if item is removed during dialog +Bug #882: Hircine's Ring doesn't always work +Bug #909: [Tamriel Rebuilt] crashes in Akamora +Bug #922: Launcher writing merged openmw.cfg files +Bug #943: Random magnitude should be calculated per effect +Bug #948: Negative fatigue level should be allowed +Bug #949: Particles in world space +Bug #950: Hard crash on x64 Linux running --new-game (on startup) +Bug #951: setMagicka and setFatigue have no effect +Bug #954: Problem with equipping inventory items when using a keyboard shortcut +Bug #955: Issues with equipping torches +Bug #966: Shield is visible when casting spell +Bug #967: Game crashes when equipping silver candlestick +Bug #970: Segmentation fault when starting at Bal Isra +Bug #977: Pressing down key in console doesn't go forward in history +Bug #979: Tooltip disappears when changing inventory +Bug #980: Barter: item category is remembered, but not shown +Bug #981: Mod: replacing model has wrong position/orientation +Bug #982: Launcher: Addon unchecking is not saved +Bug #983: Fix controllers to affect objects attached to the base node +Bug #985: Player can talk to NPCs who are in combat +Bug #989: OpenMW crashes when trying to include mod with capital .ESP +Bug #991: Merchants equip items with harmful constant effect enchantments +Bug #994: Don't cap skills/attributes when set via console +Bug #998: Setting the max health should also set the current health +Bug #1005: Torches are visible when casting spells and during hand to hand combat. +Bug #1006: Many NPCs have 0 skill +Bug #1007: Console fills up with text +Bug #1013: Player randomly loses health or dies +Bug #1014: Persuasion window is not centered in maximized window +Bug #1015: Player status window scroll state resets on status change +Bug #1016: Notification window not big enough for all skill level ups +Bug #1020: Saved window positions are not rescaled appropriately on resolution change +Bug #1022: Messages stuck permanently on screen when they pile up +Bug #1023: Journals doesn't open +Bug #1026: Game loses track of torch usage. +Bug #1028: Crash on pickup of jug in Unexplored Shipwreck, Upper level +Bug #1029: Quick keys menu: Select compatible replacement when tool used up +Bug #1041: Music playback issues on OS X >= 10.9 +Bug #1042: TES3 header data wrong encoding +Bug #1045: OS X: deployed OpenCS won't launch +Bug #1046: All damaged weaponry is worth 1 gold +Bug #1048: Links in "locked" dialogue are still clickable +Bug #1052: Using color codes when naming your character actually changes the name's color +Bug #1054: Spell effects not visible in front of water +Bug #1055: Power-Spell animation starts even though you already casted it that day +Bug #1059: Cure disease potion removes all effects from player, even your race bonus and race ability +Bug #1063: Crash upon checking out game start ship area in Seyda Neen +Bug #1064: openmw binaries link to unnecessary libraries +Bug #1065: Landing from a high place in water still causes fall damage +Feature #43: Visuals for Magic Effects +Feature #51: Ranged Magic +Feature #52: Touch Range Magic +Feature #53: Self Range Magic +Feature #54: Spell Casting +Feature #70: Vampirism +Feature #100: Combat AI +Feature #171: Implement NIF record NiFlipController +Feature #410: Window to restore enchanted item charge +Feature #647: Enchanted item glow +Feature #723: Invisibility/Chameleon magic effects +Feature #737: Resist Magicka magic effect +Feature #758: GetLOS +Feature #926: Editor: Info-Record tables +Feature #958: Material controllers +Feature #959: Terrain bump, specular, & parallax mapping +Feature #990: Request: unlock mouse when in any menu +Feature #1018: Do not allow view mode switching while performing an action +Feature #1027: Vertex morph animation (NiGeomMorpherController) +Feature #1031: Handle NiBillboardNode +Feature #1051: Implement NIF texture slot DarkTexture +Task #873: Unify OGRE initialisation + 0.27.0 Bug #597: Assertion `dialogue->mId == id' failed in esmstore.cpp From e6c0e187bcd1614294bdfac84e53f33b707aa878 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 15:42:57 +0100 Subject: [PATCH 318/889] Closes #1073: Check for all gold types in canSell. For containers, only gold_001 is relevant, but items in the world can be sold as well. --- apps/openmw/mwclass/misc.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 5e216fed4..d21189103 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -242,7 +242,11 @@ namespace MWClass item.get(); return !ref->mBase->mData.mIsKey && (npcServices & ESM::NPC::Misc) - && !Misc::StringUtils::ciEqual(item.getCellRef().mRefID, "gold_001"); + && !Misc::StringUtils::ciEqual(item.getCellRef().mRefID, "gold_001") + && !Misc::StringUtils::ciEqual(item.getCellRef().mRefID, "gold_005") + && !Misc::StringUtils::ciEqual(item.getCellRef().mRefID, "gold_010") + && !Misc::StringUtils::ciEqual(item.getCellRef().mRefID, "gold_025") + && !Misc::StringUtils::ciEqual(item.getCellRef().mRefID, "gold_100"); } float Miscellaneous::getWeight(const MWWorld::Ptr &ptr) const From 0990ca4b75fa4dba73f72cbd14742415ee259ff4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 15:58:00 +0100 Subject: [PATCH 319/889] Closes #1072: Fix extra light being added multiple times when showCarriedLeft(true) is called repeatedly --- apps/openmw/mwrender/npcanimation.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index ddbdde83a..b1455f0dc 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -664,11 +664,12 @@ void NpcAnimation::showCarriedLeft(bool show) { Ogre::Vector3 glowColor = getEnchantmentColor(*iter); std::string mesh = MWWorld::Class::get(*iter).getModel(*iter); - addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, - mesh, !iter->getClass().getEnchantment(*iter).empty(), &glowColor); - - if (iter->getTypeName() == typeid(ESM::Light).name()) - addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], iter->get()->mBase); + if (addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, + mesh, !iter->getClass().getEnchantment(*iter).empty(), &glowColor)) + { + if (iter->getTypeName() == typeid(ESM::Light).name()) + addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], iter->get()->mBase); + } } else removeIndividualPart(ESM::PRT_Shield); From 12de351febd378fdb453f99bf0f49607898cce17 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 16:38:23 +0100 Subject: [PATCH 320/889] Check if levitation is enabled before levitating --- apps/openmw/mwclass/npc.cpp | 3 ++- apps/openmw/mwworld/worldimp.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 2ba0566dd..f4e15423d 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -848,7 +848,8 @@ namespace MWClass float moveSpeed; if(normalizedEncumbrance >= 1.0f) moveSpeed = 0.0f; - else if(mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude > 0) + else if(mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude > 0 && + world->isLevitationEnabled()) { float flySpeed = 0.01f*(npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() + mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d02e1022e..148c8f301 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1606,7 +1606,8 @@ namespace MWWorld return false; const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); - if(stats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Levitate)).mMagnitude > 0) + if(stats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Levitate)).mMagnitude > 0 + && isLevitationEnabled()) return true; // TODO: Check if flying creature From 29acc3f72290a152be14ae6760b39f401dd9c81d Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 16:56:31 +0100 Subject: [PATCH 321/889] Fix particles being too small. Looks like this should actually be size*2. --- components/nifogre/ogrenifloader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 5f9674c91..63e905766 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -854,7 +854,7 @@ class NIFObjectLoader { const Nif::NiParticleSystemController *partctrl = static_cast(ctrl.getPtr()); - partsys->setDefaultDimensions(partctrl->size, partctrl->size); + partsys->setDefaultDimensions(partctrl->size*2, partctrl->size*2); if(!partctrl->emitter.empty()) { From 94d2ec8e4e87c791462c6942fce88f81dae1aa43 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 20:02:28 +0100 Subject: [PATCH 322/889] Add missing spells update --- apps/openmw/mwmechanics/activespells.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index dc79901b0..e9786fb43 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -172,6 +172,7 @@ namespace MWMechanics void ActiveSpells::purgeAll() { mSpells.clear(); + mSpellsChanged = true; } void ActiveSpells::purgeEffect(short effectId) @@ -187,6 +188,6 @@ namespace MWMechanics effectIt++; } } - + mSpellsChanged = true; } } From 596e0c8a499ea3e61f52b017ab5ffe83c4f7047a Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 20:15:07 +0100 Subject: [PATCH 323/889] Correct Dispel effect (use magnitude as chance) --- apps/openmw/mwmechanics/activespells.cpp | 11 +++++++++-- apps/openmw/mwmechanics/activespells.hpp | 4 ++-- apps/openmw/mwmechanics/spellcasting.cpp | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index e9786fb43..05877e454 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -169,9 +169,16 @@ namespace MWMechanics } } - void ActiveSpells::purgeAll() + void ActiveSpells::purgeAll(float chance) { - mSpells.clear(); + for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ) + { + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (roll < chance) + mSpells.erase(it++); + else + ++it; + } mSpellsChanged = true; } diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index b3f499c6b..92faf790f 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -82,8 +82,8 @@ namespace MWMechanics /// Remove all active effects with this id void purgeEffect (short effectId); - /// Remove all active effects - void purgeAll (); + /// Remove all active effects, if roll succeeds (for each effect) + void purgeAll (float chance); bool isSpellActive (std::string id) const; ///< case insensitive diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 0b897c165..735652163 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -236,7 +236,7 @@ namespace MWMechanics else if (effectId == ESM::MagicEffect::CureCorprusDisease) target.getClass().getCreatureStats(target).getSpells().purgeCorprusDisease(); else if (effectId == ESM::MagicEffect::Dispel) - target.getClass().getCreatureStats(target).getActiveSpells().purgeAll(); + target.getClass().getCreatureStats(target).getActiveSpells().purgeAll(magnitude); else if (effectId == ESM::MagicEffect::RemoveCurse) target.getClass().getCreatureStats(target).getSpells().purgeCurses(); From b017a3be3e4e88d8aea6150287893f3677d6fb69 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Thu, 2 Jan 2014 20:20:57 +0100 Subject: [PATCH 324/889] Bug #1074: Inventory paperdoll obscures armour rating Changed size of inventory paperdoll. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwrender/characterpreview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 5e659ca1d..a0ba01b37 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -128,7 +128,7 @@ namespace MWRender InventoryPreview::InventoryPreview(MWWorld::Ptr character) - : CharacterPreview(character, 512, 1024, "CharacterPreview", Ogre::Vector3(0, 65, -180), Ogre::Vector3(0,65,0)) + : CharacterPreview(character, 512, 1024, "CharacterPreview", Ogre::Vector3(0, 62, -200), Ogre::Vector3(0, 62, 0)) , mSelectionBuffer(NULL) { } From 0ded85893b042cf987ddefe881d56a5ef2982ae3 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 2 Jan 2014 20:47:43 +0100 Subject: [PATCH 325/889] updated changelog again --- readme.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.txt b/readme.txt index 132485119..5a74770f2 100644 --- a/readme.txt +++ b/readme.txt @@ -154,6 +154,8 @@ Bug #1059: Cure disease potion removes all effects from player, even your race b Bug #1063: Crash upon checking out game start ship area in Seyda Neen Bug #1064: openmw binaries link to unnecessary libraries Bug #1065: Landing from a high place in water still causes fall damage +Bug #1072: Drawing weapon increases torch brightness +Bug #1073: Merchants sell stacks of gold Feature #43: Visuals for Magic Effects Feature #51: Ranged Magic Feature #52: Touch Range Magic From 79e972bdb6b9d5545347a2ef8c2708adebaf34a8 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Thu, 2 Jan 2014 21:54:41 +0200 Subject: [PATCH 326/889] resolved accompanying minor problems --- apps/openmw/mwmechanics/character.cpp | 127 +++++++++++++------------- apps/openmw/mwmechanics/character.hpp | 8 +- 2 files changed, 67 insertions(+), 68 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index aa1a812aa..8e2e348b9 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -160,28 +160,34 @@ public: void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force) { - if(MWWorld::Class::get(mPtr).getCreatureStats(mPtr).getAttacked()) + if(MWWorld::Class::get(mPtr).isActor()) { - MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setAttacked(false); - - if(mHitState == CharState_None) + if(MWWorld::Class::get(mPtr).getCreatureStats(mPtr).getAttacked()) { - mHitState = CharState_Hit; - if(mJumpState != JumpState_None && !MWBase::Environment::get().getWorld()->isFlying(mPtr) - && !MWBase::Environment::get().getWorld()->isSwimming(mPtr) ) - mCurrentHit = sHitList[sHitListSize-1]; //knockdown animation - else + MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setAttacked(false); + + if(mHitState == CharState_None) { - int iHit = rand() % (sHitListSize-1); - mCurrentHit = sHitList[iHit]; + mHitState = CharState_Hit; + if(mJumpState != JumpState_None && !MWBase::Environment::get().getWorld()->isFlying(mPtr) + && !MWBase::Environment::get().getWorld()->isSwimming(mPtr) ) + { + mCurrentHit = sHitList[sHitListSize-1]; //knockdown animation + mHitState = CharState_KnockDown; + } + else + { + int iHit = rand() % (sHitListSize-1); + mCurrentHit = sHitList[iHit]; + } + mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } - mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } - } - else if(mHitState != CharState_None && !mAnimation->isPlaying(mCurrentHit)) - { - mCurrentHit.erase(); - mHitState = CharState_None; + else if(mHitState != CharState_None && !mAnimation->isPlaying(mCurrentHit)) + { + mCurrentHit.erase(); + mHitState = CharState_None; + } } const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); @@ -374,16 +380,24 @@ MWWorld::ContainerStoreIterator CharacterController::getActiveWeapon(NpcStats &s void CharacterController::playRandomDeath(float startpoint) { - if(MWBase::Environment::get().getWorld()->isSwimming(mPtr)) + if(MWWorld::Class::get(mPtr).isNpc()) { - mDeathState = CharState_SwimDeath; - mCurrentDeath = sDeathList[sDeathListSize-1]; //last in the list is 'swimdeath' + if(MWBase::Environment::get().getWorld()->isSwimming(mPtr)) + { + mDeathState = CharState_SwimDeath; + mCurrentDeath = sDeathList[sDeathListSize-1]; //last in the list is 'swimdeath' + } + else + { + int num = rand() % (sDeathListSize-1); + mDeathState = static_cast(CharState_Death1 + num); + mCurrentDeath = sDeathList[num]; + } } else { - int num = rand() % (sDeathListSize-1); - mDeathState = static_cast(CharState_Death1 + num); - mCurrentDeath = sDeathList[num]; + mDeathState = CharState_Death1; + mCurrentDeath = "death1"; } mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, @@ -442,6 +456,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim } refreshCurrentAnims(mIdleState, mMovementState, true); + if(mDeathState != CharState_None) { playRandomDeath(1.0f); @@ -458,25 +473,6 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr) mPtr = ptr; } - -void CharacterController::playWeaponAnim(const std::string& start, const std::string& stop, - float speed, bool autoDisable, bool disablePrevious, float startpoint, bool currentWeapon) -{ - std::string weapgroup; - if (currentWeapon) - weapgroup = mCurrentWeapon; - else - getWeaponGroup(mWeaponType, weapgroup); - - if (disablePrevious) - mAnimation->disable(weapgroup); - - mAnimation->play(weapgroup, Priority_Weapon, - MWRender::Animation::Group_All, autoDisable, - speed, start, stop, - startpoint, 0); -} - bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak) { const MWWorld::Class &cls = MWWorld::Class::get(mPtr); @@ -662,14 +658,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun { if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || mWeaponType == WeapType_ThowWeapon) - { mAttackType = "shoot"; - mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed, mAttackType+" start", mAttackType+" attach", - 0.0f, 0); - mUpperBodyState = UpperCharState_StartToAttach; - } else { int attackType = stats.getAttackType(); @@ -682,13 +671,13 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun mAttackType = "slash"; else mAttackType = "thrust"; + } mAnimation->play(mCurrentWeapon, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, weapSpeed, mAttackType+" start", mAttackType+" min attack", 0.0f, 0); - mUpperBodyState = UpperCharState_StartToMinAttack; - } + mUpperBodyState = UpperCharState_StartToMinAttack; } } animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); @@ -739,11 +728,13 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun { mUpperBodyState = UpperCharState_WeapEquiped; - if(mHitState != CharState_None) //don't allow to continue playing hit animation after actor had attacked during it + //don't allow to continue playing hit animation on UpperBody after actor had attacked during it + if(mHitState != CharState_None) { mAnimation->changeGroups(mCurrentHit, MWRender::Animation::Group_LowerBody); - mCurrentHit.clear(); + //commenting out following 2 lines will give a bit different combat dynamics(slower) mHitState = CharState_None; + mCurrentHit.clear(); } } else if(mUpperBodyState == UpperCharState_UnEquipingWeap) @@ -759,11 +750,6 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun stop = mAttackType+" max attack"; mUpperBodyState = UpperCharState_MinAttackToMaxAttack; break; - case UpperCharState_StartToAttach: //only bows, crossbows, throwing weapons here - start = mAttackType+" attach"; - stop = mAttackType+" min attack"; - mUpperBodyState = UpperCharState_StartToMinAttack; - break; case UpperCharState_MaxAttackToMinHit: if(mAttackType == "shoot") { @@ -813,17 +799,27 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun } } + if(!animPlaying) + { + mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f)); + } //if playing combat animation and lowerbody is not busy switch to whole body animation - if(weaptype != WeapType_None && complete>0.0f) + if(weaptype != WeapType_None && animPlaying) { if( mMovementState != CharState_None || mJumpState != JumpState_None || mHitState != CharState_None || MWBase::Environment::get().getWorld()->isSwimming(mPtr) || cls.getStance(mPtr, MWWorld::Class::Sneak)) + { + mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f)); mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_UpperBody); + } else + { + mAnimation->setAccumulation(Ogre::Vector3(0.0f, 0.0f, 0.0f)); mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_All); + } } MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); @@ -1067,8 +1063,13 @@ void CharacterController::update(float duration) refreshCurrentAnims(idlestate, movestate, forcestateupdate); - rot *= duration * Ogre::Math::RadiansToDegrees(1.0f); - world->rotateObject(mPtr, rot.x, rot.y, rot.z, true); + if(mHitState != CharState_KnockDown) + { + rot *= duration * Ogre::Math::RadiansToDegrees(1.0f); + world->rotateObject(mPtr, rot.x, rot.y, rot.z, true); + } + else + world->rotateObject(mPtr, rot.x, rot.y, 0.0f, true); world->queueMovement(mPtr, vec); movement = vec; @@ -1166,7 +1167,7 @@ void CharacterController::forceStateUpdate() refreshCurrentAnims(mIdleState, mMovementState, true); if(mDeathState != CharState_None) { - playRandomDeath(0.0f); + playRandomDeath(); } } @@ -1175,7 +1176,7 @@ void CharacterController::kill() if(mDeathState != CharState_None) return; - playRandomDeath(0.0f); + playRandomDeath(); if(mAnimation) { diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 3cf34b747..b7c29e9ef 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -90,7 +90,8 @@ enum CharacterState { CharState_Death5, CharState_SwimDeath, - CharState_Hit + CharState_Hit, + CharState_KnockDown }; enum WeaponType { @@ -114,7 +115,6 @@ enum UpperBodyCharacterState { UpperCharState_UnEquipingWeap, UpperCharState_WeapEquiped, UpperCharState_StartToMinAttack, - UpperCharState_StartToAttach, UpperCharState_MinAttackToMaxAttack, UpperCharState_MaxAttackToMinHit, UpperCharState_MinHitToHit, @@ -176,12 +176,10 @@ class CharacterController void clearAnimQueue(); bool updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak); - void playWeaponAnim(const std::string& start, const std::string& stop, - float speed = 1.0f, bool autoDisable = true, bool disablePrevious = false, float startpoint = 0.0f, bool currentWeapon = true); void updateVisibility(); - void playRandomDeath(float startpoint); + void playRandomDeath(float startpoint = 0.0f); public: CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim); From 299690631f39104988c1ed2c1cedd8342dc5bd87 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 21:21:28 +0100 Subject: [PATCH 327/889] Implement SoulTrap magic effect --- apps/openmw/mwgui/spellicons.cpp | 3 +- apps/openmw/mwgui/spellicons.hpp | 3 +- apps/openmw/mwmechanics/activespells.cpp | 6 +- apps/openmw/mwmechanics/activespells.hpp | 8 ++- apps/openmw/mwmechanics/actors.cpp | 70 ++++++++++++++++++++++++ apps/openmw/mwmechanics/magiceffects.hpp | 3 +- apps/openmw/mwmechanics/repair.cpp | 14 +---- apps/openmw/mwmechanics/spellcasting.cpp | 6 +- apps/openmw/mwmechanics/spells.cpp | 2 +- apps/openmw/mwworld/inventorystore.cpp | 2 +- 10 files changed, 94 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index e93e96c4b..891206532 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -22,7 +22,8 @@ namespace MWGui { void EffectSourceVisitor::visit (MWMechanics::EffectKey key, - const std::string& sourceName, float magnitude, float remainingTime) + const std::string& sourceName, const std::string& casterHandle, + float magnitude, float remainingTime) { MagicEffectInfo newEffectSource; newEffectSource.mKey = key; diff --git a/apps/openmw/mwgui/spellicons.hpp b/apps/openmw/mwgui/spellicons.hpp index a29e2a00a..1bb80f3d4 100644 --- a/apps/openmw/mwgui/spellicons.hpp +++ b/apps/openmw/mwgui/spellicons.hpp @@ -43,7 +43,8 @@ namespace MWGui std::map > mEffectSources; virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, float magnitude, float remainingTime = -1); + const std::string& sourceName, const std::string& casterHandle, + float magnitude, float remainingTime = -1); }; class SpellIcons diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 05877e454..1cdb74407 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -126,7 +126,8 @@ namespace MWMechanics return mSpells; } - void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector effects, const std::string &displayName) + void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector effects, + const std::string &displayName, const std::string& casterHandle) { bool exists = false; for (TContainer::const_iterator it = begin(); it != end(); ++it) @@ -139,6 +140,7 @@ namespace MWMechanics params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp(); params.mEffects = effects; params.mDisplayName = displayName; + params.mCasterHandle = casterHandle; if (!exists || stack) mSpells.insert (std::make_pair(id, params)); @@ -164,7 +166,7 @@ namespace MWMechanics float magnitude = effectIt->mMagnitude; if (magnitude) - visitor.visit(effectIt->mKey, name, magnitude, remainingTime); + visitor.visit(effectIt->mKey, name, it->second.mCasterHandle, magnitude, remainingTime); } } } diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 92faf790f..57f224f6f 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -37,8 +37,8 @@ namespace MWMechanics MWWorld::TimeStamp mTimeStamp; std::string mDisplayName; - // TODO: To handle CASTER_LINKED flag (spell is purged when caster dies), - // we should probably store a handle to the caster here. + // Handle to the caster that that inflicted this spell on us + std::string mCasterHandle; }; typedef std::multimap TContainer; @@ -76,8 +76,10 @@ namespace MWMechanics /// \param stack If false, the spell is not added if one with the same ID exists already. /// \param effects /// \param displayName Name for display in magic menu. + /// \param casterHandle /// - void addSpell (const std::string& id, bool stack, std::vector effects, const std::string& displayName); + void addSpell (const std::string& id, bool stack, std::vector effects, + const std::string& displayName, const std::string& casterHandle); /// Remove all active effects with this id void purgeEffect (short effectId); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 79dd98562..68804c1d7 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -88,6 +88,68 @@ bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate) namespace MWMechanics { + + class SoulTrap : public MWMechanics::EffectSourceVisitor + { + MWWorld::Ptr mCreature; + MWWorld::Ptr mActor; + public: + SoulTrap(MWWorld::Ptr trappedCreature) + : mCreature(trappedCreature) {} + + virtual void visit (MWMechanics::EffectKey key, + const std::string& sourceName, const std::string& casterHandle, + float magnitude, float remainingTime = -1) + { + if (key.mId != ESM::MagicEffect::Soultrap) + return; + if (magnitude <= 0) + return; + + MWBase::World* world = MWBase::Environment::get().getWorld(); + + MWWorld::Ptr caster = world->searchPtrViaHandle(casterHandle); + if (caster.isEmpty() || !caster.getClass().isActor()) + return; + + static const float fSoulgemMult = world->getStore().get().find("fSoulgemMult")->getFloat(); + + float creatureSoulValue = mCreature.get()->mBase->mData.mSoul; + + // Use the smallest soulgem that is large enough to hold the soul + MWWorld::ContainerStore& container = caster.getClass().getContainerStore(caster); + MWWorld::ContainerStoreIterator gem = container.end(); + float gemCapacity = std::numeric_limits().max(); + std::string soulgemFilter = "misc_soulgem"; // no other way to check for soulgems? :/ + for (MWWorld::ContainerStoreIterator it = container.begin(MWWorld::ContainerStore::Type_Miscellaneous); + it != container.end(); ++it) + { + const std::string& id = it->getCellRef().mRefID; + if (id.size() >= soulgemFilter.size() + && id.substr(0,soulgemFilter.size()) == soulgemFilter) + { + float thisGemCapacity = it->get()->mBase->mData.mValue * fSoulgemMult; + if (thisGemCapacity >= creatureSoulValue && thisGemCapacity < gemCapacity + && it->getCellRef().mSoul.empty()) + { + gem = it; + gemCapacity = thisGemCapacity; + } + } + } + + if (gem == container.end()) + return; + + // Set the soul on just one of the gems, not the whole stack + gem->getContainerStore()->unstack(*gem, caster); + gem->getCellRef().mSoul = mCreature.getCellRef().mRefID; + + if (caster.getRefData().getHandle() == "player") + MWBase::Environment::get().getWindowManager()->messageBox("#{sSoultrapSuccess}"); + } + }; + void Actors::updateActor (const MWWorld::Ptr& ptr, float duration) { // magic effects @@ -705,6 +767,13 @@ namespace MWMechanics iter->second->kill(); + // Apply soultrap + if (iter->first.getTypeName() == typeid(ESM::Creature).name()) + { + SoulTrap soulTrap (iter->first); + stats.getActiveSpells().visitEffectSources(soulTrap); + } + // Reset magic effects and recalculate derived effects // One case where we need this is to make sure bound items are removed upon death stats.setMagicEffects(MWMechanics::MagicEffects()); @@ -714,6 +783,7 @@ namespace MWMechanics if(cls.isEssential(iter->first)) MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); + } } diff --git a/apps/openmw/mwmechanics/magiceffects.hpp b/apps/openmw/mwmechanics/magiceffects.hpp index 2c1b363b7..45abda21d 100644 --- a/apps/openmw/mwmechanics/magiceffects.hpp +++ b/apps/openmw/mwmechanics/magiceffects.hpp @@ -56,7 +56,8 @@ namespace MWMechanics struct EffectSourceVisitor { virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, float magnitude, float remainingTime = -1) = 0; + const std::string& sourceName, const std::string& casterHandle, + float magnitude, float remainingTime = -1) = 0; }; /// \brief Effects currently affecting a NPC or creature diff --git a/apps/openmw/mwmechanics/repair.cpp b/apps/openmw/mwmechanics/repair.cpp index 38b2a48d7..5e8a46fd4 100644 --- a/apps/openmw/mwmechanics/repair.cpp +++ b/apps/openmw/mwmechanics/repair.cpp @@ -24,21 +24,13 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair) MWWorld::LiveCellRef *ref = mTool.get(); + // unstack tool if required + player.getClass().getContainerStore(player).unstack(mTool, player); + // reduce number of uses left int uses = (mTool.getCellRef().mCharge != -1) ? mTool.getCellRef().mCharge : ref->mBase->mData.mUses; mTool.getCellRef().mCharge = uses-1; - // unstack tool if required - if (mTool.getRefData().getCount() > 1 && uses == ref->mBase->mData.mUses) - { - MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); - MWWorld::ContainerStoreIterator it = store.add(mTool, player); - it->getRefData().setCount(mTool.getRefData().getCount()-1); - it->getCellRef().mCharge = -1; - - mTool.getRefData().setCount(1); - } - MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats(player); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 735652163..64a824030 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -154,7 +154,8 @@ namespace MWMechanics ActiveSpells::Effect effect_ = effect; effect_.mMagnitude *= -1; effects.push_back(effect_); - caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true, effects, mSourceName); + caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true, + effects, mSourceName, caster.getRefData().getHandle()); } } } @@ -198,7 +199,8 @@ namespace MWMechanics inflict(caster, target, reflectedEffects, range, true); if (appliedLastingEffects.size()) - target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects, mSourceName); + target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects, + mSourceName, caster.getRefData().getHandle()); } void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, short effectId, float magnitude) diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index 0088bcb60..f231a558c 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -192,7 +192,7 @@ namespace MWMechanics effectIt != list.mList.end(); ++effectIt, ++i) { float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * it->second[i]; - visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, magnitude); + visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, "", magnitude); } } } diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 2c7c05317..509c34afb 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -607,7 +607,7 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID][i]; float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom; magnitude *= params.mMultiplier; - visitor.visit(MWMechanics::EffectKey(*effectIt), (**iter).getClass().getName(**iter), magnitude); + visitor.visit(MWMechanics::EffectKey(*effectIt), (**iter).getClass().getName(**iter), "", magnitude); ++i; } From eba60858dde7ccb0a4ed3911c4ca227951d1cf2e Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 21:48:33 +0100 Subject: [PATCH 328/889] Closes #1078: Show stat bar text even when 0 --- apps/openmw/mwgui/widgets.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index c37ae15fa..0df6c5477 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -528,14 +528,9 @@ namespace MWGui if (mBarTextWidget) { - if (mValue >= 0 && mMax > 0) - { - std::stringstream out; - out << mValue << "/" << mMax; - static_cast(mBarTextWidget)->setCaption(out.str().c_str()); - } - else - static_cast(mBarTextWidget)->setCaption(""); + std::stringstream out; + out << mValue << "/" << mMax; + static_cast(mBarTextWidget)->setCaption(out.str().c_str()); } } void MWDynamicStat::setTitle(const std::string& text) From 623cdef69fbd72d650f370e7faf207c22aaba1f6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 21:49:04 +0100 Subject: [PATCH 329/889] Code cleanup --- apps/openmw/mwclass/npc.cpp | 18 +++++++++--------- apps/openmw/mwgui/waitdialog.cpp | 2 +- apps/openmw/mwmechanics/actors.cpp | 22 +++++++++++----------- apps/openmw/mwmechanics/creaturestats.cpp | 6 +++--- apps/openmw/mwrender/renderingmanager.cpp | 4 ++-- apps/openmw/mwworld/worldimp.cpp | 10 +++++----- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index f4e15423d..f2481195b 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -450,8 +450,8 @@ namespace MWClass (stats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f); hitchance *= stats.getFatigueTerm(); - hitchance += mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::FortifyAttack)).mMagnitude - - mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::Blind)).mMagnitude; + hitchance += mageffects.get(ESM::MagicEffect::FortifyAttack).mMagnitude - + mageffects.get(ESM::MagicEffect::Blind).mMagnitude; hitchance -= otherstats.getEvasion(); if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f) @@ -848,11 +848,11 @@ namespace MWClass float moveSpeed; if(normalizedEncumbrance >= 1.0f) moveSpeed = 0.0f; - else if(mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude > 0 && + else if(mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 && world->isLevitationEnabled()) { float flySpeed = 0.01f*(npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() + - mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude); + mageffects.get(ESM::MagicEffect::Levitate).mMagnitude); flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat()); flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; flySpeed = std::max(0.0f, flySpeed); @@ -863,7 +863,7 @@ namespace MWClass float swimSpeed = walkSpeed; if(Npc::getStance(ptr, Run, false)) swimSpeed = runSpeed; - swimSpeed *= 1.0f + 0.01f * mageffects.get(MWMechanics::EffectKey(1/*swift swim*/)).mMagnitude; + swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude; swimSpeed *= fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()* fSwimRunAthleticsMult->getFloat(); moveSpeed = swimSpeed; @@ -897,7 +897,7 @@ namespace MWClass float x = fJumpAcrobaticsBase->getFloat() + std::pow(a / 15.0f, fJumpAcroMultiplier->getFloat()); x += 3.0f * b * fJumpAcroMultiplier->getFloat(); - x += mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::Jump)).mMagnitude * 64; + x += mageffects.get(ESM::MagicEffect::Jump).mMagnitude * 64; x *= encumbranceTerm; if(Npc::getStance(ptr, Run, false)) @@ -920,7 +920,7 @@ namespace MWClass { const float acrobaticsSkill = MWWorld::Class::get(ptr).getNpcStats (ptr).getSkill(ESM::Skill::Acrobatics).getModified(); const CustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); - const float jumpSpellBonus = npcdata->mNpcStats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Jump)).mMagnitude; + const float jumpSpellBonus = npcdata->mNpcStats.getMagicEffects().get(ESM::MagicEffect::Jump).mMagnitude; const float fallAcroBase = gmst.find("fFallAcroBase")->getFloat(); const float fallAcroMult = gmst.find("fFallAcroMult")->getFloat(); const float fallDistanceBase = gmst.find("fFallDistanceBase")->getFloat(); @@ -1025,8 +1025,8 @@ namespace MWClass if(!stats.isWerewolf()) { weight = getContainerStore(ptr).getWeight(); - weight -= stats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Feather)).mMagnitude; - weight += stats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Burden)).mMagnitude; + weight -= stats.getMagicEffects().get(ESM::MagicEffect::Feather).mMagnitude; + weight += stats.getMagicEffects().get(ESM::MagicEffect::Burden).mMagnitude; if(weight < 0.0f) weight = 0.0f; } diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 2467f6c40..0a05cd22b 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -154,7 +154,7 @@ namespace MWGui float hourlyHealthDelta = stats.getAttribute(ESM::Attribute::Endurance).getModified() * 0.1; - bool stunted = (stats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::StuntedMagicka)).mMagnitude > 0); + bool stunted = (stats.getMagicEffects().get(ESM::MagicEffect::StuntedMagicka).mMagnitude > 0); float fRestMagicMult = store.get().find("fRestMagicMult")->getFloat(); float hourlyMagickaDelta = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified(); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 68804c1d7..21a6dfd84 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -278,7 +278,7 @@ namespace MWMechanics { // the actor is sleeping, restore health and magicka - bool stunted = stats.getMagicEffects ().get(MWMechanics::EffectKey(ESM::MagicEffect::StuntedMagicka)).mMagnitude > 0; + bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).mMagnitude > 0; DynamicStat health = stats.getHealth(); health.setCurrent (health.getCurrent() + 0.1 * endurance); @@ -329,23 +329,23 @@ namespace MWMechanics for(int i = 0;i < 3;++i) { DynamicStat stat = creatureStats.getDynamic(i); - stat.setModifier(effects.get(EffectKey(ESM::MagicEffect::FortifyHealth+i)).mMagnitude - - effects.get(EffectKey(ESM::MagicEffect::DrainHealth+i)).mMagnitude); + stat.setModifier(effects.get(ESM::MagicEffect::FortifyHealth+i).mMagnitude - + effects.get(ESM::MagicEffect::DrainHealth+i).mMagnitude); - float currentDiff = creatureStats.getMagicEffects().get(EffectKey(ESM::MagicEffect::RestoreHealth+i)).mMagnitude - - creatureStats.getMagicEffects().get(EffectKey(ESM::MagicEffect::DamageHealth+i)).mMagnitude - - creatureStats.getMagicEffects().get(EffectKey(ESM::MagicEffect::AbsorbHealth+i)).mMagnitude; + float currentDiff = creatureStats.getMagicEffects().get(ESM::MagicEffect::RestoreHealth+i).mMagnitude + - creatureStats.getMagicEffects().get(ESM::MagicEffect::DamageHealth+i).mMagnitude + - creatureStats.getMagicEffects().get(ESM::MagicEffect::AbsorbHealth+i).mMagnitude; stat.setCurrent(stat.getCurrent() + currentDiff * duration); creatureStats.setDynamic(i, stat); } // Apply disintegration (reduces item health) - float disintegrateWeapon = effects.get(EffectKey(ESM::MagicEffect::DisintegrateWeapon)).mMagnitude; + float disintegrateWeapon = effects.get(ESM::MagicEffect::DisintegrateWeapon).mMagnitude; if (disintegrateWeapon > 0) disintegrateSlot(ptr, MWWorld::InventoryStore::Slot_CarriedRight, disintegrateWeapon*duration); - float disintegrateArmor = effects.get(EffectKey(ESM::MagicEffect::DisintegrateArmor)).mMagnitude; + float disintegrateArmor = effects.get(ESM::MagicEffect::DisintegrateArmor).mMagnitude; if (disintegrateArmor > 0) { // According to UESP @@ -377,7 +377,7 @@ namespace MWMechanics DynamicStat health = creatureStats.getHealth(); for (unsigned int i=0; i::iterator it = boundItemsMap.begin(); it != boundItemsMap.end(); ++it) { bool found = creatureStats.mBoundItems.find(it->first) != creatureStats.mBoundItems.end(); - int magnitude = creatureStats.getMagicEffects().get(EffectKey(it->first)).mMagnitude; + int magnitude = creatureStats.getMagicEffects().get(it->first).mMagnitude; if (found != (magnitude > 0)) { std::string item = "bound_" + it->second; @@ -472,7 +472,7 @@ namespace MWMechanics for (std::map::iterator it = summonMap.begin(); it != summonMap.end(); ++it) { bool found = creatureStats.mSummonedCreatures.find(it->first) != creatureStats.mSummonedCreatures.end(); - int magnitude = creatureStats.getMagicEffects().get(EffectKey(it->first)).mMagnitude; + int magnitude = creatureStats.getMagicEffects().get(it->first).mMagnitude; if (found != (magnitude > 0)) { if (magnitude > 0) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 95bd405b1..fa55be9b9 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -228,8 +228,8 @@ namespace MWMechanics void CreatureStats::setMagicEffects(const MagicEffects &effects) { - if (effects.get(MWMechanics::EffectKey(ESM::MagicEffect::FortifyMaximumMagicka)).mMagnitude - != mMagicEffects.get(MWMechanics::EffectKey(ESM::MagicEffect::FortifyMaximumMagicka)).mMagnitude) + if (effects.get(ESM::MagicEffect::FortifyMaximumMagicka).mMagnitude + != mMagicEffects.get(ESM::MagicEffect::FortifyMaximumMagicka).mMagnitude) mRecalcDynamicStats = true; mMagicEffects = effects; @@ -343,7 +343,7 @@ namespace MWMechanics float evasion = (getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) + (getAttribute(ESM::Attribute::Luck).getModified() / 10.0f); evasion *= getFatigueTerm(); - evasion += mMagicEffects.get(EffectKey(ESM::MagicEffect::Sanctuary)).mMagnitude; + evasion += mMagicEffects.get(ESM::MagicEffect::Sanctuary).mMagnitude; return evasion; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index f6e69b726..a40535030 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -328,7 +328,7 @@ void RenderingManager::update (float duration, bool paused) MWWorld::Ptr player = world->getPlayer().getPlayer(); - int blind = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Blind)).mMagnitude; + int blind = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(ESM::MagicEffect::Blind).mMagnitude; mRendering.getFader()->setFactor(std::max(0.f, 1.f-(blind / 100.f))); setAmbientMode(); @@ -593,7 +593,7 @@ void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour) mAmbientColor = colour; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - int nightEye = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::NightEye)).mMagnitude; + int nightEye = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(ESM::MagicEffect::NightEye).mMagnitude; Ogre::ColourValue final = colour; final += Ogre::ColourValue(0.7,0.7,0.7,0) * std::min(1.f, (nightEye/100.f)); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 148c8f301..b410da2d8 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1606,7 +1606,7 @@ namespace MWWorld return false; const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); - if(stats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Levitate)).mMagnitude > 0 + if(stats.getMagicEffects().get(ESM::MagicEffect::Levitate).mMagnitude > 0 && isLevitationEnabled()) return true; @@ -1626,7 +1626,7 @@ namespace MWWorld return false; const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); - if(stats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::SlowFall)).mMagnitude > 0) + if(stats.getMagicEffects().get(ESM::MagicEffect::SlowFall).mMagnitude > 0) return true; return false; @@ -2390,11 +2390,11 @@ namespace MWWorld const MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(ptr).getMagicEffects(); float dist=0; if (type == World::Detect_Creature) - dist = effects.get(MWMechanics::EffectKey(ESM::MagicEffect::DetectAnimal)).mMagnitude; + dist = effects.get(ESM::MagicEffect::DetectAnimal).mMagnitude; else if (type == World::Detect_Key) - dist = effects.get(MWMechanics::EffectKey(ESM::MagicEffect::DetectKey)).mMagnitude; + dist = effects.get(ESM::MagicEffect::DetectKey).mMagnitude; else if (type == World::Detect_Enchantment) - dist = effects.get(MWMechanics::EffectKey(ESM::MagicEffect::DetectEnchantment)).mMagnitude; + dist = effects.get(ESM::MagicEffect::DetectEnchantment).mMagnitude; if (!dist) return; From 32ff3b530c8b3e7b3dd57b26841b96b9a0a24666 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 01:59:15 +0100 Subject: [PATCH 330/889] Change all instances of skill/attribute values to use an appropriate typedef. --- apps/openmw/mwbase/windowmanager.hpp | 8 ++++---- apps/openmw/mwgui/charactercreation.cpp | 12 ++++++------ apps/openmw/mwgui/charactercreation.hpp | 4 ++-- apps/openmw/mwgui/levelupdialog.cpp | 2 +- apps/openmw/mwgui/review.cpp | 8 ++++---- apps/openmw/mwgui/review.hpp | 6 +++--- apps/openmw/mwgui/statswindow.cpp | 8 ++++---- apps/openmw/mwgui/statswindow.hpp | 6 +++--- apps/openmw/mwgui/widgets.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 12 ++++++------ apps/openmw/mwgui/windowmanagerimp.hpp | 12 ++++++------ apps/openmw/mwmechanics/actors.cpp | 2 +- apps/openmw/mwmechanics/creaturestats.cpp | 10 +++++----- apps/openmw/mwmechanics/creaturestats.hpp | 8 ++++---- apps/openmw/mwmechanics/npcstats.cpp | 4 ++-- apps/openmw/mwmechanics/npcstats.hpp | 4 ++-- apps/openmw/mwmechanics/stat.hpp | 3 +++ apps/openmw/mwscript/statsextensions.cpp | 4 ++-- 18 files changed, 59 insertions(+), 56 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 1300efc75..6c85be5fd 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -137,8 +137,8 @@ namespace MWBase virtual void wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount) = 0; /// Set value for the given ID. - virtual void setValue (const std::string& id, const MWMechanics::Stat& value) = 0; - virtual void setValue (int parSkill, const MWMechanics::Stat& value) = 0; + virtual void setValue (const std::string& id, const MWMechanics::AttributeValue& value) = 0; + virtual void setValue (int parSkill, const MWMechanics::SkillValue& value) = 0; virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value) = 0; virtual void setValue (const std::string& id, const std::string& value) = 0; virtual void setValue (const std::string& id, int value) = 0; @@ -236,8 +236,8 @@ namespace MWBase virtual void onFrame (float frameDuration) = 0; /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. - virtual std::map > getPlayerSkillValues() = 0; - virtual std::map > getPlayerAttributeValues() = 0; + virtual std::map getPlayerSkillValues() = 0; + virtual std::map getPlayerAttributeValues() = 0; virtual SkillList getPlayerMinorSkills() = 0; virtual SkillList getPlayerMajorSkills() = 0; diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index b829f219d..04507cfc6 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -75,7 +75,7 @@ namespace MWGui mGenerateClassSpecializations[2] = 0; } - void CharacterCreation::setValue (const std::string& id, const MWMechanics::Stat& value) + void CharacterCreation::setValue (const std::string& id, const MWMechanics::AttributeValue& value) { if (mReviewDialog) { @@ -113,7 +113,7 @@ namespace MWGui } } - void CharacterCreation::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value) + void CharacterCreation::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) { if (mReviewDialog) mReviewDialog->setSkillValue(parSkill, value); @@ -229,8 +229,8 @@ namespace MWGui } { - std::map > attributes = MWBase::Environment::get().getWindowManager()->getPlayerAttributeValues(); - for (std::map >::iterator it = attributes.begin(); + std::map attributes = MWBase::Environment::get().getWindowManager()->getPlayerAttributeValues(); + for (std::map::iterator it = attributes.begin(); it != attributes.end(); ++it) { mReviewDialog->setAttribute(static_cast (it->first), it->second); @@ -238,8 +238,8 @@ namespace MWGui } { - std::map > skills = MWBase::Environment::get().getWindowManager()->getPlayerSkillValues(); - for (std::map >::iterator it = skills.begin(); + std::map skills = MWBase::Environment::get().getWindowManager()->getPlayerSkillValues(); + for (std::map::iterator it = skills.begin(); it != skills.end(); ++it) { mReviewDialog->setSkillValue(static_cast (it->first), it->second); diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index b80aaae41..924f40c28 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -31,9 +31,9 @@ namespace MWGui //Show a dialog void spawnDialog(const char id); - void setValue (const std::string& id, const MWMechanics::Stat& value); + void setValue (const std::string& id, const MWMechanics::AttributeValue& value); void setValue (const std::string& id, const MWMechanics::DynamicStat& value); - void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value); + void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value); void configureSkills (const SkillList& major, const SkillList& minor); void doRenderUpdate(); diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index a772b3a15..e3d8f5dd7 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -166,7 +166,7 @@ namespace MWGui // increase attributes for (int i=0; i<3; ++i) { - MWMechanics::Stat attribute = creatureStats.getAttribute(mSpentAttributes[i]); + MWMechanics::AttributeValue attribute = creatureStats.getAttribute(mSpentAttributes[i]); attribute.setBase (attribute.getBase () + pcStats.getLevelupAttributeMultiplier (mSpentAttributes[i])); if (attribute.getBase() >= 100) diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index dfc86a547..b1c85e1c6 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -74,7 +74,7 @@ namespace MWGui for (int i = 0; i < ESM::Skill::Length; ++i) { - mSkillValues.insert(std::make_pair(i, MWMechanics::Stat())); + mSkillValues.insert(std::make_pair(i, MWMechanics::SkillValue())); mSkillWidgetMap.insert(std::make_pair(i, static_cast (0))); } @@ -152,7 +152,7 @@ namespace MWGui mFatigue->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); } - void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::Stat& value) + void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::AttributeValue& value) { std::map::iterator attr = mAttributeWidgets.find(static_cast(attributeId)); if (attr == mAttributeWidgets.end()) @@ -161,7 +161,7 @@ namespace MWGui attr->second->setAttributeValue(value); } - void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::Stat& value) + void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::SkillValue& value) { mSkillValues[skillId] = value; MyGUI::TextBox* widget = mSkillWidgetMap[skillId]; @@ -279,7 +279,7 @@ namespace MWGui continue; assert(skillId >= 0 && skillId < ESM::Skill::Length); const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; - const MWMechanics::Stat &stat = mSkillValues.find(skillId)->second; + const MWMechanics::SkillValue &stat = mSkillValues.find(skillId)->second; float base = stat.getBase(); float modified = stat.getModified(); diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index 1c24fec74..5d0767d21 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -38,10 +38,10 @@ namespace MWGui void setMagicka(const MWMechanics::DynamicStat& value); void setFatigue(const MWMechanics::DynamicStat& value); - void setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::Stat& value); + void setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::AttributeValue& value); void configureSkills(const SkillList& major, const SkillList& minor); - void setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::Stat& value); + void setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::SkillValue& value); virtual void open(); @@ -85,7 +85,7 @@ namespace MWGui std::map mAttributeWidgets; SkillList mMajorSkills, mMinorSkills, mMiscSkills; - std::map > mSkillValues; + std::map mSkillValues; std::map mSkillWidgetMap; std::string mName, mRaceId, mBirthSignId; ESM::Class mKlass; diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index ab6096b7e..17583b1c0 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -61,7 +61,7 @@ namespace MWGui for (int i = 0; i < ESM::Skill::Length; ++i) { - mSkillValues.insert(std::pair >(i, MWMechanics::Stat())); + mSkillValues.insert(std::pair(i, MWMechanics::SkillValue())); mSkillWidgetMap.insert(std::pair(i, (MyGUI::TextBox*)NULL)); } @@ -102,7 +102,7 @@ namespace MWGui adjustWindowCaption(); } - void StatsWindow::setValue (const std::string& id, const MWMechanics::Stat& value) + void StatsWindow::setValue (const std::string& id, const MWMechanics::AttributeValue& value) { static const char *ids[] = { @@ -179,7 +179,7 @@ namespace MWGui } } - void StatsWindow::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value) + void StatsWindow::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) { mSkillValues[parSkill] = value; MyGUI::TextBox* widget = mSkillWidgetMap[(int)parSkill]; @@ -358,7 +358,7 @@ namespace MWGui continue; assert(skillId >= 0 && skillId < ESM::Skill::Length); const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; - const MWMechanics::Stat &stat = mSkillValues.find(skillId)->second; + const MWMechanics::SkillValue &stat = mSkillValues.find(skillId)->second; float base = stat.getBase(); float modified = stat.getModified(); int progressPercent = (modified - float(static_cast(modified))) * 100; diff --git a/apps/openmw/mwgui/statswindow.hpp b/apps/openmw/mwgui/statswindow.hpp index 49b44a2e1..28d96ca90 100644 --- a/apps/openmw/mwgui/statswindow.hpp +++ b/apps/openmw/mwgui/statswindow.hpp @@ -26,11 +26,11 @@ namespace MWGui void setPlayerName(const std::string& playerName); /// Set value for the given ID. - void setValue (const std::string& id, const MWMechanics::Stat& value); + void setValue (const std::string& id, const MWMechanics::AttributeValue& value); void setValue (const std::string& id, const MWMechanics::DynamicStat& value); void setValue (const std::string& id, const std::string& value); void setValue (const std::string& id, int value); - void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value); + void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value); void configureSkills (const SkillList& major, const SkillList& minor); void setReputation (int reputation) { if (reputation != mReputation) mChanged = true; this->mReputation = reputation; } @@ -61,7 +61,7 @@ namespace MWGui MyGUI::ScrollView* mSkillView; SkillList mMajorSkills, mMinorSkills, mMiscSkills; - std::map > mSkillValues; + std::map mSkillValues; std::map mSkillWidgetMap; std::map mFactionWidgetMap; FactionList mFactions; ///< Stores a list of factions and the current rank diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index 1630ab3c9..adc56f423 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -133,7 +133,7 @@ namespace MWGui public: MWAttribute(); - typedef MWMechanics::Stat AttributeValue; + typedef MWMechanics::AttributeValue AttributeValue; void setAttributeId(int attributeId); void setAttributeValue(const AttributeValue& value); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 90ced66d3..3accd925f 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -249,12 +249,12 @@ namespace MWGui // Setup player stats for (int i = 0; i < ESM::Attribute::Length; ++i) { - mPlayerAttributes.insert(std::make_pair(ESM::Attribute::sAttributeIds[i], MWMechanics::Stat())); + mPlayerAttributes.insert(std::make_pair(ESM::Attribute::sAttributeIds[i], MWMechanics::AttributeValue())); } for (int i = 0; i < ESM::Skill::Length; ++i) { - mPlayerSkillValues.insert(std::make_pair(ESM::Skill::sSkillIds[i], MWMechanics::Stat())); + mPlayerSkillValues.insert(std::make_pair(ESM::Skill::sSkillIds[i], MWMechanics::SkillValue())); } // Set up visibility @@ -544,7 +544,7 @@ namespace MWGui } } - void WindowManager::setValue (const std::string& id, const MWMechanics::Stat& value) + void WindowManager::setValue (const std::string& id, const MWMechanics::AttributeValue& value) { mStatsWindow->setValue (id, value); mCharGen->setValue(id, value); @@ -575,7 +575,7 @@ namespace MWGui } - void WindowManager::setValue (int parSkill, const MWMechanics::Stat& value) + void WindowManager::setValue (int parSkill, const MWMechanics::SkillValue& value) { /// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we /// allow custom skills. @@ -1178,12 +1178,12 @@ namespace MWGui return mGuiModes.back(); } - std::map > WindowManager::getPlayerSkillValues() + std::map WindowManager::getPlayerSkillValues() { return mPlayerSkillValues; } - std::map > WindowManager::getPlayerAttributeValues() + std::map WindowManager::getPlayerAttributeValues() { return mPlayerAttributes; } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index b332ffc36..a3b135ad4 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -152,8 +152,8 @@ namespace MWGui virtual void wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount); ///< Set value for the given ID. - virtual void setValue (const std::string& id, const MWMechanics::Stat& value); - virtual void setValue (int parSkill, const MWMechanics::Stat& value); + virtual void setValue (const std::string& id, const MWMechanics::AttributeValue& value); + virtual void setValue (int parSkill, const MWMechanics::SkillValue& value); virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value); virtual void setValue (const std::string& id, const std::string& value); virtual void setValue (const std::string& id, int value); @@ -229,8 +229,8 @@ namespace MWGui virtual void onFrame (float frameDuration); /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. - virtual std::map > getPlayerSkillValues(); - virtual std::map > getPlayerAttributeValues(); + virtual std::map getPlayerSkillValues(); + virtual std::map getPlayerAttributeValues(); virtual SkillList getPlayerMinorSkills(); virtual SkillList getPlayerMajorSkills(); @@ -346,9 +346,9 @@ namespace MWGui // Various stats about player as needed by window manager std::string mPlayerName; std::string mPlayerRaceId; - std::map > mPlayerAttributes; + std::map mPlayerAttributes; SkillList mPlayerMajorSkills, mPlayerMinorSkills; - std::map > mPlayerSkillValues; + std::map mPlayerSkillValues; MyGUI::Gui *mGui; // Gui std::vector mGuiModes; diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 21a6dfd84..30ea6aa2c 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -317,7 +317,7 @@ namespace MWMechanics // attributes for(int i = 0;i < ESM::Attribute::Length;++i) { - Stat stat = creatureStats.getAttribute(i); + AttributeValue stat = creatureStats.getAttribute(i); stat.setModifier(effects.get(EffectKey(ESM::MagicEffect::FortifyAttribute, i)).mMagnitude - effects.get(EffectKey(ESM::MagicEffect::DrainAttribute, i)).mMagnitude - effects.get(EffectKey(ESM::MagicEffect::AbsorbAttribute, i)).mMagnitude); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index fa55be9b9..6fc8f2597 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -74,7 +74,7 @@ namespace MWMechanics - gmst.find ("fFatigueMult")->getFloat() * (1-normalised); } - const Stat &CreatureStats::getAttribute(int index) const + const AttributeValue &CreatureStats::getAttribute(int index) const { if (index < 0 || index > 7) { throw std::runtime_error("attribute index is out of range"); @@ -158,20 +158,20 @@ namespace MWMechanics void CreatureStats::setAttribute(int index, int base) { - MWMechanics::Stat current = getAttribute(index); + AttributeValue current = getAttribute(index); current.setBase(base); setAttribute(index, current); } - void CreatureStats::setAttribute(int index, const Stat &value) + void CreatureStats::setAttribute(int index, const AttributeValue &value) { if (index < 0 || index > 7) { throw std::runtime_error("attribute index is out of range"); } - const Stat& currentValue = !mIsWerewolf ? mAttributes[index] : mWerewolfAttributes[index]; + const AttributeValue& currentValue = !mIsWerewolf ? mAttributes[index] : mWerewolfAttributes[index]; - if (value.getModified() != currentValue.getModified()) + if (value != currentValue) { if (index != ESM::Attribute::Luck && index != ESM::Attribute::Personality diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 4d9be0132..8a525523d 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -18,7 +18,7 @@ namespace MWMechanics /// class CreatureStats { - Stat mAttributes[8]; + AttributeValue mAttributes[8]; DynamicStat mDynamic[3]; // health, magicka, fatigue int mLevel; Spells mSpells; @@ -49,7 +49,7 @@ namespace MWMechanics protected: bool mIsWerewolf; - Stat mWerewolfAttributes[8]; + AttributeValue mWerewolfAttributes[8]; public: CreatureStats(); @@ -65,7 +65,7 @@ namespace MWMechanics bool canUsePower (const std::string& power) const; void usePower (const std::string& power); - const Stat & getAttribute(int index) const; + const AttributeValue & getAttribute(int index) const; const DynamicStat & getHealth() const; @@ -94,7 +94,7 @@ namespace MWMechanics MagicEffects & getMagicEffects(); - void setAttribute(int index, const Stat &value); + void setAttribute(int index, const AttributeValue &value); // Shortcut to set only the base void setAttribute(int index, int base); diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 1fdefc84f..c4b8b0b7a 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -84,7 +84,7 @@ void MWMechanics::NpcStats::setMovementFlag (Flag flag, bool state) mMovementFlags &= ~flag; } -const MWMechanics::Stat& MWMechanics::NpcStats::getSkill (int index) const +const MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill (int index) const { if (index<0 || index>=ESM::Skill::Length) throw std::runtime_error ("skill index out of range"); @@ -92,7 +92,7 @@ const MWMechanics::Stat& MWMechanics::NpcStats::getSkill (int index) cons return (!mIsWerewolf ? mSkill[index] : mWerewolfSkill[index]); } -MWMechanics::Stat& MWMechanics::NpcStats::getSkill (int index) +MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill (int index) { if (index<0 || index>=ESM::Skill::Length) throw std::runtime_error ("skill index out of range"); diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 6b7efa5b7..df0f084ce 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -94,8 +94,8 @@ namespace MWMechanics void setMovementFlag (Flag flag, bool state); - const Stat& getSkill (int index) const; - Stat& getSkill (int index); + const SkillValue& getSkill (int index) const; + SkillValue& getSkill (int index); const std::map& getFactionRanks() const; std::map& getFactionRanks(); diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index cb6c09014..c7778a3d8 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -205,6 +205,9 @@ namespace MWMechanics { return !(left==right); } + + typedef Stat AttributeValue; + typedef Stat SkillValue; } #endif diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 45e5b0f18..f32d602da 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -124,7 +124,7 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWMechanics::Stat attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex); + MWMechanics::AttributeValue attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex); attribute.setModified (value, 0); ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); } @@ -146,7 +146,7 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWMechanics::Stat attribute = MWWorld::Class::get(ptr) + MWMechanics::AttributeValue attribute = MWWorld::Class::get(ptr) .getCreatureStats(ptr) .getAttribute(mIndex); From b42240be6d80d8fe125a774110a1882c6103b8e7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 03:46:30 +0100 Subject: [PATCH 331/889] Implement Damage/restore skill/attribute effects. Use dedicated classes for skill and attribute values (instead of Stat) since there are some important differences. --- apps/openmw/mwclass/npc.cpp | 28 ++++++------- apps/openmw/mwgui/review.cpp | 6 +-- apps/openmw/mwgui/statswindow.cpp | 9 ++-- apps/openmw/mwgui/tradewindow.cpp | 4 +- apps/openmw/mwgui/widgets.cpp | 2 +- apps/openmw/mwmechanics/actors.cpp | 2 +- .../mwmechanics/mechanicsmanagerimp.cpp | 4 +- apps/openmw/mwmechanics/npcstats.cpp | 27 +++++------- apps/openmw/mwmechanics/npcstats.hpp | 4 +- apps/openmw/mwmechanics/spellcasting.cpp | 40 ++++++++++++++++-- apps/openmw/mwmechanics/spellcasting.hpp | 2 +- apps/openmw/mwmechanics/stat.hpp | 41 ++++++++++++++++++- apps/openmw/mwscript/statsextensions.cpp | 27 +++++------- apps/openmw/mwworld/worldimp.cpp | 2 +- 14 files changed, 126 insertions(+), 72 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index f2481195b..307ad3d7c 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -179,29 +179,29 @@ namespace for (int raceSkillIndex = 0; raceSkillIndex < 7; ++raceSkillIndex) { - if (race->mData.mBonus[raceSkillIndex].mSkill == skillIndex) - { - raceBonus = race->mData.mBonus[raceSkillIndex].mBonus; - break; - } + if (race->mData.mBonus[raceSkillIndex].mSkill == skillIndex) + { + raceBonus = race->mData.mBonus[raceSkillIndex].mBonus; + break; + } } for (int k = 0; k < 5; ++k) { - // is this a minor or major skill? - if ((class_->mData.mSkills[k][0] == skillIndex) || (class_->mData.mSkills[k][1] == skillIndex)) - { - majorMultiplier = 1.0f; - break; - } + // is this a minor or major skill? + if ((class_->mData.mSkills[k][0] == skillIndex) || (class_->mData.mSkills[k][1] == skillIndex)) + { + majorMultiplier = 1.0f; + break; + } } // is this skill in the same Specialization as the class? const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().get().find(skillIndex); if (skill->mData.mSpecialization == class_->mData.mSpecialization) { - specMultiplier = 0.5f; - specBonus = 5; + specMultiplier = 0.5f; + specBonus = 5; } npcStats.getSkill(skillIndex).setBase( @@ -210,7 +210,7 @@ namespace + 5 + raceBonus + specBonus - + static_cast((level-1) * (majorMultiplier + specMultiplier)), 100.0f)); + + static_cast((level-1) * (majorMultiplier + specMultiplier)), 100)); } } } diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index b1c85e1c6..e27e40ae6 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -65,7 +65,7 @@ namespace MWGui getWidget(attribute, std::string("Attribute") + boost::lexical_cast(idx)); mAttributeWidgets.insert(std::make_pair(static_cast(ESM::Attribute::sAttributeIds[idx]), attribute)); attribute->setAttributeId(ESM::Attribute::sAttributeIds[idx]); - attribute->setAttributeValue(Widgets::MWAttribute::AttributeValue(0, 0)); + attribute->setAttributeValue(Widgets::MWAttribute::AttributeValue()); } // Setup skills @@ -280,8 +280,8 @@ namespace MWGui assert(skillId >= 0 && skillId < ESM::Skill::Length); const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; const MWMechanics::SkillValue &stat = mSkillValues.find(skillId)->second; - float base = stat.getBase(); - float modified = stat.getModified(); + int base = stat.getBase(); + int modified = stat.getModified(); std::string state = "normal"; if (modified > base) diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 17583b1c0..549cf65ab 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -359,21 +359,19 @@ namespace MWGui assert(skillId >= 0 && skillId < ESM::Skill::Length); const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; const MWMechanics::SkillValue &stat = mSkillValues.find(skillId)->second; - float base = stat.getBase(); - float modified = stat.getModified(); - int progressPercent = (modified - float(static_cast(modified))) * 100; + int base = stat.getBase(); + int modified = stat.getModified(); + int progressPercent = stat.getProgress() * 100; const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); const ESM::Skill* skill = esmStore.get().find(skillId); - assert(skill); std::string icon = "icons\\k\\" + ESM::Skill::sIconNames[skillId]; const ESM::Attribute* attr = esmStore.get().find(skill->mData.mAttribute); - assert(attr); std::string state = "normal"; if (modified > base) @@ -484,7 +482,6 @@ namespace MWGui ESM::RankData rankData = faction->mData.mRankData[it->second+1]; const ESM::Attribute* attr1 = store.get().find(faction->mData.mAttribute[0]); const ESM::Attribute* attr2 = store.get().find(faction->mData.mAttribute[1]); - assert(attr1 && attr2); text += "\n#BF9959#{" + attr1->mName + "}: " + boost::lexical_cast(rankData.mAttribute1) + ", #{" + attr2->mName + "}: " + boost::lexical_cast(rankData.mAttribute2); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index d544b83cf..e6cbbf968 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -296,10 +296,10 @@ namespace MWGui const MWMechanics::NpcStats &sellerStats = MWWorld::Class::get(mPtr).getNpcStats(mPtr); const MWMechanics::NpcStats &playerStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); - float a1 = std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); + float a1 = std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100); float b1 = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float c1 = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); - float d1 = std::min(sellerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); + float d1 = std::min(sellerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100); float e1 = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float f1 = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 0df6c5477..b30cf2bae 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -178,7 +178,7 @@ namespace MWGui } if (mAttributeValueWidget) { - AttributeValue::Type modified = mValue.getModified(), base = mValue.getBase(); + int modified = mValue.getModified(), base = mValue.getBase(); static_cast(mAttributeValueWidget)->setCaption(boost::lexical_cast(modified)); if (modified > base) mAttributeValueWidget->_setWidgetState("increased"); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 30ea6aa2c..ec3b86137 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -523,7 +523,7 @@ namespace MWMechanics // skills for(int i = 0;i < ESM::Skill::Length;++i) { - Stat& skill = npcStats.getSkill(i); + SkillValue& skill = npcStats.getSkill(i); skill.setModifier(effects.get(EffectKey(ESM::MagicEffect::FortifySkill, i)).mMagnitude - effects.get(EffectKey(ESM::MagicEffect::DrainSkill, i)).mMagnitude - effects.get(EffectKey(ESM::MagicEffect::AbsorbSkill, i)).mMagnitude); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 7740240b6..2e18fae63 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -499,10 +499,10 @@ namespace MWMechanics // otherwise one would get different prices when exiting and re-entering the dialogue window... int clampedDisposition = std::max(0, std::min(getDerivedDisposition(ptr) + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange(),100)); - float a = std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); + float a = std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100); float b = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float c = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); - float d = std::min(sellerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); + float d = std::min(sellerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100); float e = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float f = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index c4b8b0b7a..0a0b51270 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -197,34 +197,25 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, if(mIsWerewolf) return; - float base = getSkill (skillIndex).getBase(); + MWMechanics::SkillValue value = getSkill (skillIndex); - int level = static_cast (base); + value.setProgress(value.getProgress() + getSkillGain (skillIndex, class_, usageType)); - base += getSkillGain (skillIndex, class_, usageType); - - if (static_cast (base)!=level) + if (value.getProgress()>=1) { // skill leveled up increaseSkill(skillIndex, class_, false); } - else - getSkill (skillIndex).setBase (base); } void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress) { - float base = getSkill (skillIndex).getBase(); + int base = getSkill (skillIndex).getBase(); - int level = static_cast (base); - - if (level >= 100) + if (base >= 100) return; - if (preserveProgress) - base += 1; - else - base = level+1; + base += 1; // if this is a major or minor skill of the class, increase level progress bool levelProgress = false; @@ -260,6 +251,8 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas } getSkill (skillIndex).setBase (base); + if (!preserveProgress) + getSkill(skillIndex).setProgress(0); } int MWMechanics::NpcStats::getLevelProgress () const @@ -387,7 +380,7 @@ void MWMechanics::NpcStats::setWerewolf (bool set) // Oh, Bethesda. It's "Intelligence". std::string name = "fWerewolf"+((i==ESM::Attribute::Intelligence) ? std::string("Intellegence") : ESM::Attribute::sAttributeNames[i]); - mWerewolfAttributes[i].setModified(int(gmst.find(name)->getFloat()), 0); + mWerewolfAttributes[i].setBase(int(gmst.find(name)->getFloat())); } for(size_t i = 0;i < ESM::Skill::Length;i++) @@ -401,7 +394,7 @@ void MWMechanics::NpcStats::setWerewolf (bool set) // "Mercantile"! >_< std::string name = "fWerewolf"+((i==ESM::Skill::Mercantile) ? std::string("Merchantile") : ESM::Skill::sSkillNames[i]); - mWerewolfSkill[i].setModified(int(gmst.find(name)->getFloat()), 0); + mWerewolfSkill[i].setBase(int(gmst.find(name)->getFloat())); } } mIsWerewolf = set; diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index df0f084ce..552422d84 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -45,8 +45,8 @@ namespace MWMechanics DrawState_ mDrawState; int mDisposition; unsigned int mMovementFlags; - Stat mSkill[27]; - Stat mWerewolfSkill[27]; + SkillValue mSkill[27]; + SkillValue mWerewolfSkill[27]; int mBounty; std::set mExpelled; std::map mFactionReputation; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 64a824030..1de4e50c4 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -135,7 +135,8 @@ namespace MWMechanics float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random; magnitude *= magnitudeMult; - if (target.getClass().isActor() && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) + bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); + if (target.getClass().isActor() && hasDuration) { ActiveSpells::Effect effect; effect.mKey = MWMechanics::EffectKey(*effectIt); @@ -160,7 +161,17 @@ namespace MWMechanics } } else - applyInstantEffect(target, effectIt->mEffectID, magnitude); + applyInstantEffect(target, EffectKey(*effectIt), magnitude); + + // HACK: Damage attribute/skill actually has a duration, even though the actual effect is instant and permanent. + // This was probably just done to have the effect visible in the magic menu for a while + // to notify the player they've been damaged? + if (effectIt->mEffectID == ESM::MagicEffect::DamageAttribute + || effectIt->mEffectID == ESM::MagicEffect::DamageSkill + || effectIt->mEffectID == ESM::MagicEffect::RestoreAttribute + || effectIt->mEffectID == ESM::MagicEffect::RestoreSkill + ) + applyInstantEffect(target, EffectKey(*effectIt), magnitude); if (target.getClass().isActor() || magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration) { @@ -203,8 +214,9 @@ namespace MWMechanics mSourceName, caster.getRefData().getHandle()); } - void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, short effectId, float magnitude) + void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, MWMechanics::EffectKey effect, float magnitude) { + short effectId = effect.mId; if (!target.getClass().isActor()) { if (effectId == ESM::MagicEffect::Lock) @@ -227,6 +239,28 @@ namespace MWMechanics } else { + if (effectId == ESM::MagicEffect::DamageAttribute || effectId == ESM::MagicEffect::RestoreAttribute) + { + int attribute = effect.mArg; + AttributeValue value = target.getClass().getCreatureStats(target).getAttribute(attribute); + if (effectId == ESM::MagicEffect::DamageAttribute) + value.damage(magnitude); + else + value.restore(magnitude); + target.getClass().getCreatureStats(target).setAttribute(attribute, value); + } + else if (effectId == ESM::MagicEffect::DamageSkill || effectId == ESM::MagicEffect::RestoreSkill) + { + if (target.getTypeName() != typeid(ESM::NPC).name()) + return; + int skill = effect.mArg; + SkillValue& value = target.getClass().getNpcStats(target).getSkill(skill); + if (effectId == ESM::MagicEffect::DamageSkill) + value.damage(magnitude); + else + value.restore(magnitude); + } + if (effectId == ESM::MagicEffect::CurePoison) target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect(ESM::MagicEffect::Poison); else if (effectId == ESM::MagicEffect::CureParalyzation) diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index e2efaa140..a55c45fd1 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -203,7 +203,7 @@ namespace MWMechanics void inflict (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false); - void applyInstantEffect (const MWWorld::Ptr& target, short effectId, float magnitude); + void applyInstantEffect (const MWWorld::Ptr& target, MWMechanics::EffectKey effect, float magnitude); }; } diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index c7778a3d8..77b3f6364 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -206,8 +206,45 @@ namespace MWMechanics return !(left==right); } - typedef Stat AttributeValue; - typedef Stat SkillValue; + class AttributeValue + { + int mBase; + int mModifier; + int mDamage; + + public: + AttributeValue() : mBase(0), mModifier(0), mDamage(0) {} + + int getModified() const { return std::max(0, mBase - mDamage + mModifier); } + int getBase() const { return mBase; } + int getModifier() const { return mModifier; } + + void setBase(int base) { mBase = std::max(0, base); } + void setModifier(int mod) { mModifier = mod; } + + void damage(int damage) { mDamage += damage; } + void restore(int amount) { mDamage -= std::min(mDamage, amount); } + int getDamage() const { return mDamage; } + }; + + class SkillValue : public AttributeValue + { + float mProgress; + public: + float getProgress() const { return mProgress; } + void setProgress(float progress) { mProgress = progress; } + }; + + inline bool operator== (const AttributeValue& left, const AttributeValue& right) + { + return left.getBase() == right.getBase() + && left.getModifier() == right.getModifier() + && left.getDamage() == right.getDamage(); + } + inline bool operator!= (const AttributeValue& left, const AttributeValue& right) + { + return !(left == right); + } } #endif diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index f32d602da..a899b05f0 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -125,7 +125,7 @@ namespace MWScript runtime.pop(); MWMechanics::AttributeValue attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex); - attribute.setModified (value, 0); + attribute.setBase (value - (attribute.getModified() - attribute.getBase())); ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); } }; @@ -150,11 +150,7 @@ namespace MWScript .getCreatureStats(ptr) .getAttribute(mIndex); - value += - attribute.getModified(); - - attribute - .setModified (value, 0, 100); + attribute.setBase (std::min(100, attribute.getBase() + value)); ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); } }; @@ -346,12 +342,10 @@ namespace MWScript const ESM::Class& class_ = *MWBase::Environment::get().getWorld()->getStore().get().find (ref->mBase->mClass); - float level = 0; - float progress = std::modf (stats.getSkill (mIndex).getBase(), &level); + float level = stats.getSkill(mIndex).getBase(); + float progress = stats.getSkill(mIndex).getProgress(); - float modifier = stats.getSkill (mIndex).getModifier(); - - int newLevel = static_cast (value-modifier); + int newLevel = value - (stats.getSkill(mIndex).getModified() - stats.getSkill(mIndex).getBase()); if (newLevel<0) newLevel = 0; @@ -362,8 +356,8 @@ namespace MWScript if (progress>=1) progress = 0.999999999; - stats.getSkill (mIndex).set (newLevel + progress); - stats.getSkill (mIndex).setModifier (modifier); + stats.getSkill (mIndex).setBase (newLevel); + stats.getSkill (mIndex).setProgress(progress); } }; @@ -383,11 +377,10 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - value += MWWorld::Class::get (ptr).getNpcStats (ptr).getSkill (mIndex). - getModified(); + MWMechanics::NpcStats& stats = ptr.getClass().getNpcStats(ptr); - MWWorld::Class::get (ptr).getNpcStats (ptr).getSkill (mIndex). - setModified (value, 0, 100); + stats.getSkill(mIndex). + setBase (std::min(100, stats.getSkill(mIndex).getBase() + value)); } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b410da2d8..083c1cf0e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2000,7 +2000,7 @@ namespace MWWorld const Store &gmst = getStore().get(); MWMechanics::NpcStats &stats = Class::get(actor).getNpcStats(actor); - stats.getSkill(ESM::Skill::Acrobatics).setModified(gmst.find("fWerewolfAcrobatics")->getFloat(), 0); + stats.getSkill(ESM::Skill::Acrobatics).setBase(gmst.find("fWerewolfAcrobatics")->getFloat()); } bool World::getGodModeState() From 93e1a2df73a45ef60963f8b59a6398ac76ef5d12 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 04:09:52 +0100 Subject: [PATCH 332/889] Implement Cast script instruction (shrines work now) --- apps/openmw/mwscript/docs/vmformat.txt | 5 +++-- apps/openmw/mwscript/miscextensions.cpp | 23 +++++++++++++++++++++++ components/compiler/extensions0.cpp | 1 + components/compiler/opcodes.hpp | 2 ++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index cf533451c..bedbb4ad2 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -358,5 +358,6 @@ op 0x2000223: GetLineOfSightExplicit op 0x2000224: ToggleAI op 0x2000225: ToggleAIExplicit op 0x2000226: COE - -opcodes 0x2000227-0x3ffffff unused +op 0x2000227: Cast +op 0x2000228: Cast, explicit +opcodes 0x2000229-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 8e2a8af8c..6dadf395d 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -23,6 +23,7 @@ #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/spellcasting.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -700,6 +701,26 @@ namespace MWScript } }; + template + class OpCast : public Interpreter::Opcode0 + { + public: + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string spell = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + std::string targetId = ::Misc::StringUtils::lowerCase(runtime.getStringLiteral (runtime[0].mInteger)); + runtime.pop(); + + MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr (targetId, false); + + MWMechanics::CastSpell cast(ptr, target); + cast.cast(spell); + } + }; void installOpcodes (Interpreter::Interpreter& interpreter) { @@ -761,6 +782,8 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeToggleGodMode, new OpToggleGodMode); interpreter.installSegment5 (Compiler::Misc::opcodeDisableLevitation, new OpEnableLevitation); interpreter.installSegment5 (Compiler::Misc::opcodeEnableLevitation, new OpEnableLevitation); + interpreter.installSegment5 (Compiler::Misc::opcodeCast, new OpCast); + interpreter.installSegment5 (Compiler::Misc::opcodeCastExplicit, new OpCast); } } } diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index e95f6f698..a512d7889 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -222,6 +222,7 @@ namespace Compiler extensions.registerInstruction ("activate", "", opcodeActivate); extensions.registerInstruction ("lock", "/l", opcodeLock, opcodeLockExplicit); extensions.registerInstruction ("unlock", "", opcodeUnlock, opcodeUnlockExplicit); + extensions.registerInstruction ("cast", "SS", opcodeCast, opcodeCastExplicit); extensions.registerInstruction ("togglecollisionboxes", "", opcodeToggleCollisionBoxes); extensions.registerInstruction ("togglecollisiongrid", "", opcodeToggleCollisionDebug); extensions.registerInstruction ("tcb", "", opcodeToggleCollisionBoxes); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index b7d44a851..24201d9a6 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -228,6 +228,8 @@ namespace Compiler const int opcodeToggleGodMode = 0x200021f; const int opcodeDisableLevitation = 0x2000220; const int opcodeEnableLevitation = 0x2000221; + const int opcodeCast = 0x2000227; + const int opcodeCastExplicit = 0x2000228; } namespace Sky From 366801f3d513a203bc30580e5a704bddbccf437f Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 04:44:50 +0100 Subject: [PATCH 333/889] Implement explodeSpell instruction (like Cast, with caster = target) --- apps/openmw/mwmechanics/spellcasting.cpp | 3 ++- apps/openmw/mwscript/docs/vmformat.txt | 4 +++- apps/openmw/mwscript/miscextensions.cpp | 18 ++++++++++++++++++ components/compiler/extensions0.cpp | 1 + components/compiler/opcodes.hpp | 2 ++ 5 files changed, 26 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 1de4e50c4..7849eb165 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -434,7 +434,8 @@ namespace MWMechanics int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] if (!fail && roll >= successChance) { - MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicSkillFail}"); + if (mCaster.getRefData().getHandle() == "player") + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicSkillFail}"); fail = true; } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index bedbb4ad2..3d7b281f8 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -360,4 +360,6 @@ op 0x2000225: ToggleAIExplicit op 0x2000226: COE op 0x2000227: Cast op 0x2000228: Cast, explicit -opcodes 0x2000229-0x3ffffff unused +op 0x2000229: ExplodeSpell +op 0x200022a: ExplodeSpell, explicit +opcodes 0x200022b-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 6dadf395d..ea4824c29 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -722,6 +722,22 @@ namespace MWScript } }; + template + class OpExplodeSpell : public Interpreter::Opcode0 + { + public: + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string spell = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWMechanics::CastSpell cast(ptr, ptr); + cast.cast(spell); + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox); @@ -784,6 +800,8 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeEnableLevitation, new OpEnableLevitation); interpreter.installSegment5 (Compiler::Misc::opcodeCast, new OpCast); interpreter.installSegment5 (Compiler::Misc::opcodeCastExplicit, new OpCast); + interpreter.installSegment5 (Compiler::Misc::opcodeExplodeSpell, new OpExplodeSpell); + interpreter.installSegment5 (Compiler::Misc::opcodeExplodeSpellExplicit, new OpExplodeSpell); } } } diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index a512d7889..47a653451 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -223,6 +223,7 @@ namespace Compiler extensions.registerInstruction ("lock", "/l", opcodeLock, opcodeLockExplicit); extensions.registerInstruction ("unlock", "", opcodeUnlock, opcodeUnlockExplicit); extensions.registerInstruction ("cast", "SS", opcodeCast, opcodeCastExplicit); + extensions.registerInstruction ("explodespell", "S", opcodeExplodeSpell, opcodeExplodeSpellExplicit); extensions.registerInstruction ("togglecollisionboxes", "", opcodeToggleCollisionBoxes); extensions.registerInstruction ("togglecollisiongrid", "", opcodeToggleCollisionDebug); extensions.registerInstruction ("tcb", "", opcodeToggleCollisionBoxes); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 24201d9a6..9bc256413 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -230,6 +230,8 @@ namespace Compiler const int opcodeEnableLevitation = 0x2000221; const int opcodeCast = 0x2000227; const int opcodeCastExplicit = 0x2000228; + const int opcodeExplodeSpell = 0x2000229; + const int opcodeExplodeSpellExplicit = 0x200022a; } namespace Sky From 7d8ca91286413cfacbfeea28142e25d8070beacb Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 04:56:54 +0100 Subject: [PATCH 334/889] Implement RemoveSpellEffects instruction --- apps/openmw/mwmechanics/activespells.cpp | 6 ++++++ apps/openmw/mwmechanics/activespells.hpp | 5 ++++- apps/openmw/mwscript/docs/vmformat.txt | 4 +++- apps/openmw/mwscript/statsextensions.cpp | 19 +++++++++++++++++++ components/compiler/extensions0.cpp | 2 ++ components/compiler/opcodes.hpp | 3 +++ 6 files changed, 37 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 1cdb74407..2a7165974 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -150,6 +150,12 @@ namespace MWMechanics mSpellsChanged = true; } + void ActiveSpells::removeEffects(const std::string &id) + { + mSpells.erase(Misc::StringUtils::lowerCase(id)); + mSpellsChanged = true; + } + void ActiveSpells::visitEffectSources(EffectSourceVisitor &visitor) const { for (TContainer::const_iterator it = begin(); it != end(); ++it) diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 57f224f6f..2ddb4ec55 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -81,7 +81,10 @@ namespace MWMechanics void addSpell (const std::string& id, bool stack, std::vector effects, const std::string& displayName, const std::string& casterHandle); - /// Remove all active effects with this id + /// Removes the active effects from this spell/potion/.. with \a id + void removeEffects (const std::string& id); + + /// Remove all active effects with this effect id void purgeEffect (short effectId); /// Remove all active effects, if roll succeeds (for each effect) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 3d7b281f8..25bf3429f 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -362,4 +362,6 @@ op 0x2000227: Cast op 0x2000228: Cast, explicit op 0x2000229: ExplodeSpell op 0x200022a: ExplodeSpell, explicit -opcodes 0x200022b-0x3ffffff unused +op 0x200022b: RemoveSpellEffects +op 0x200022c: RemoveSpellEffects, explicit +opcodes 0x200022d-0x3ffffff unused diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index a899b05f0..6aa19681d 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -459,6 +459,22 @@ namespace MWScript } }; + template + class OpRemoveSpellEffects : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string spellid = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWWorld::Class::get (ptr).getCreatureStats (ptr).getActiveSpells().removeEffects(spellid); + } + }; + template class OpGetSpell : public Interpreter::Opcode0 { @@ -1135,6 +1151,9 @@ namespace MWScript interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpell, new OpRemoveSpell); interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpellExplicit, new OpRemoveSpell); + interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpellEffects, new OpRemoveSpellEffects); + interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpellEffectsExplicit, + new OpRemoveSpellEffects); interpreter.installSegment5 (Compiler::Stats::opcodeGetSpell, new OpGetSpell); interpreter.installSegment5 (Compiler::Stats::opcodeGetSpellExplicit, new OpGetSpell); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 47a653451..0fc0da9d0 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -391,6 +391,8 @@ namespace Compiler extensions.registerInstruction ("addspell", "c", opcodeAddSpell, opcodeAddSpellExplicit); extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell, opcodeRemoveSpellExplicit); + extensions.registerInstruction ("removespelleffects", "c", opcodeRemoveSpellEffects, + opcodeRemoveSpellEffectsExplicit); extensions.registerFunction ("getspell", 'l', "c", opcodeGetSpell, opcodeGetSpellExplicit); extensions.registerInstruction("pcraiserank","/S",opcodePCRaiseRank); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 9bc256413..671bf2ff7 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -369,6 +369,9 @@ namespace Compiler const int opcodeIsWerewolfExplicit = 0x20001fe; const int opcodeGetWerewolfKills = 0x20001e2; + + const int opcodeRemoveSpellEffects = 0x200022b; + const int opcodeRemoveSpellEffectsExplicit = 0x200022c; } namespace Transformation From b4230f716e41eb0cb86cb062cd61cbfd4a19e2f8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 05:19:10 +0100 Subject: [PATCH 335/889] Implement RemoveEffects instruction --- apps/openmw/mwmechanics/spells.cpp | 5 +++-- apps/openmw/mwscript/docs/vmformat.txt | 4 +++- apps/openmw/mwscript/statsextensions.cpp | 20 ++++++++++++++++++++ components/compiler/extensions0.cpp | 2 ++ components/compiler/opcodes.hpp | 2 ++ 5 files changed, 30 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index f231a558c..21781c530 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -34,13 +34,14 @@ namespace MWMechanics random.resize(spell->mEffects.mList.size()); for (unsigned int i=0; i (std::rand()) / RAND_MAX; - mSpells.insert (std::make_pair (spellId, random)); + mSpells.insert (std::make_pair (Misc::StringUtils::lowerCase(spellId), random)); } } void Spells::remove (const std::string& spellId) { - TContainer::iterator iter = mSpells.find (spellId); + std::string lower = Misc::StringUtils::lowerCase(spellId); + TContainer::iterator iter = mSpells.find (lower); if (iter!=mSpells.end()) mSpells.erase (iter); diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 25bf3429f..cf27b7d6e 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -364,4 +364,6 @@ op 0x2000229: ExplodeSpell op 0x200022a: ExplodeSpell, explicit op 0x200022b: RemoveSpellEffects op 0x200022c: RemoveSpellEffects, explicit -opcodes 0x200022d-0x3ffffff unused +op 0x200022d: RemoveEffects +op 0x200022e: RemoveEffects, explicit +opcodes 0x200022f-0x3ffffff unused diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 6aa19681d..b1c9698ca 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -475,6 +475,22 @@ namespace MWScript } }; + template + class OpRemoveEffects : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + Interpreter::Type_Integer effectId = runtime[0].mInteger; + runtime.pop(); + + MWWorld::Class::get (ptr).getCreatureStats (ptr).getActiveSpells().purgeEffect(effectId); + } + }; + template class OpGetSpell : public Interpreter::Opcode0 { @@ -1155,6 +1171,10 @@ namespace MWScript interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpellEffectsExplicit, new OpRemoveSpellEffects); + interpreter.installSegment5 (Compiler::Stats::opcodeRemoveEffects, new OpRemoveEffects); + interpreter.installSegment5 (Compiler::Stats::opcodeRemoveEffectsExplicit, + new OpRemoveEffects); + interpreter.installSegment5 (Compiler::Stats::opcodeGetSpell, new OpGetSpell); interpreter.installSegment5 (Compiler::Stats::opcodeGetSpellExplicit, new OpGetSpell); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 0fc0da9d0..c113a3275 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -393,6 +393,8 @@ namespace Compiler opcodeRemoveSpellExplicit); extensions.registerInstruction ("removespelleffects", "c", opcodeRemoveSpellEffects, opcodeRemoveSpellEffectsExplicit); + extensions.registerInstruction ("removeeffects", "l", opcodeRemoveEffects, + opcodeRemoveEffectsExplicit); extensions.registerFunction ("getspell", 'l', "c", opcodeGetSpell, opcodeGetSpellExplicit); extensions.registerInstruction("pcraiserank","/S",opcodePCRaiseRank); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 671bf2ff7..4cd9ec3ff 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -372,6 +372,8 @@ namespace Compiler const int opcodeRemoveSpellEffects = 0x200022b; const int opcodeRemoveSpellEffectsExplicit = 0x200022c; + const int opcodeRemoveEffects = 0x200022d; + const int opcodeRemoveEffectsExplicit = 0x200022e; } namespace Transformation From 8201c97abf3a62f662790e556c44877541ad587b Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 2 Jan 2014 20:20:18 +0100 Subject: [PATCH 336/889] Inventory item template. --- .../opencs/model/tools/referenceablecheck.cpp | 64 ++++++++++--------- .../opencs/model/tools/referenceablecheck.hpp | 3 + 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 2b796ccc0..0b09f1f5d 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -186,35 +186,7 @@ void CSMTools::ReferenceableCheckStage::bookCheck(int stage, const CSMWorld::Ref const ESM::Book& Book = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Book, Book.mId); - //Checking for name - if (Book.mName.empty()) - { - messages.push_back(id.toString() + "|" + Book.mId + " has an empty name"); - } - - //Checking for weight - if (Book.mData.mWeight < 0) - { - messages.push_back(id.toString() + "|" + Book.mId + " has negative weight"); - } - - //Checking for value - if (Book.mData.mValue < 0) - { - messages.push_back(id.toString() + "|" + Book.mId + " has negative value"); - } - -//checking for model - if (Book.mModel.empty()) - { - messages.push_back(id.toString() + "|" + Book.mId + " has no model"); - } - - //checking for icon - if (Book.mIcon.empty()) - { - messages.push_back(id.toString() + "|" + Book.mId + " has no icon"); - } + inventoryItemCheck(Book, messages); //checking for enchantment points if (Book.mData.mEnchant < 0) @@ -1009,3 +981,37 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI //TODO: reputation, Disposition, rank, everything else } + +//Templates begins here + +void CSMTools::ReferenceableCheckStage::inventoryItemCheck(const item& item, std::vector< std::string >& messages) +{ + if (item.mName.empty()) + { + messages.push_back(id.toString() + "|" + item.mId + " has an empty name"); + } + + //Checking for weight + if (item.mData.mWeight < 0) + { + messages.push_back(id.toString() + "|" + item.mId + " has negative weight"); + } + + //Checking for value + if (item.mData.mValue < 0) + { + messages.push_back(id.toString() + "|" + item.mId + " has negative value"); + } + +//checking for model + if (item.mModel.empty()) + { + messages.push_back(id.toString() + "|" + item.mId + " has no model"); + } + + //checking for icon + if (item.mIcon.empty()) + { + messages.push_back(id.toString() + "|" + item.mId + " has no icon"); + } +} diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index fcf774f14..43bb54fa7 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -34,6 +34,9 @@ namespace CSMTools void miscCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void npcCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + //TEMPLATE CHECKS + template void inventoryItemCheck(const item& item, std::vector& messages); //for all inventory items. + const CSMWorld::RefIdData& mReferencables; const CSMWorld::IdCollection& mRaces; const CSMWorld::IdCollection& mClasses; From 24f090ca985643f488f49457b3e45e22ce726b67 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 3 Jan 2014 11:31:54 +0100 Subject: [PATCH 337/889] Finishing stuff. --- .../opencs/model/tools/referenceablecheck.cpp | 565 +++++++++--------- .../opencs/model/tools/referenceablecheck.hpp | 10 +- apps/opencs/model/world/refiddata.cpp | 20 + apps/opencs/model/world/refiddata.hpp | 4 + 4 files changed, 301 insertions(+), 298 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 0b09f1f5d..65bee314c 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -167,6 +167,46 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str npcCheck(stage, mReferencables.getNPCs(), messages); return; } + + stage -= NPCSize; + + const int WeaponSize(mReferencables.getWeapons().getSize()); + + if (stage < WeaponSize) + { + weaponCheck(stage, mReferencables.getWeapons(), messages); + return; + } + + stage -= WeaponSize; + + const int ProbeSize(mReferencables.getProbes().getSize()); + + if (stage < ProbeSize) + { + probeCheck(stage, mReferencables.getProbes(), messages); + return; + } + + stage -= ProbeSize; + + const int RepairSize(mReferencables.getRepairs().getSize()); + + if (stage < RepairSize) + { + repairCheck(stage, mReferencables.getRepairs(), messages); + return; + } + + stage -= RepairSize; + + const int StaticSize(mReferencables.getStatics().getSize()); + + if (stage < StaticSize) + { + staticCheck(stage, mReferencables.getStatics(), messages); + return; + } } int CSMTools::ReferenceableCheckStage::setup() @@ -186,13 +226,7 @@ void CSMTools::ReferenceableCheckStage::bookCheck(int stage, const CSMWorld::Ref const ESM::Book& Book = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Book, Book.mId); - inventoryItemCheck(Book, messages); - - //checking for enchantment points - if (Book.mData.mEnchant < 0) - { - messages.push_back(id.toString() + "|" + Book.mId + " has negative enchantment"); - } + inventoryItemCheck(Book, messages, id.toString(), true); } void CSMTools::ReferenceableCheckStage::activatorCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Activator >& records, std::vector< std::string >& messages) @@ -226,36 +260,7 @@ void CSMTools::ReferenceableCheckStage::potionCheck(int stage, const CSMWorld::R const ESM::Potion& Potion = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, Potion.mId); - //Checking for name - if (Potion.mName.empty()) - { - messages.push_back(id.toString() + "|" + Potion.mId + " has an empty name"); - } - - //Checking for weight - if (Potion.mData.mWeight < 0) - { - messages.push_back(id.toString() + "|" + Potion.mId + " has negative weight"); - } - - //Checking for value - if (Potion.mData.mValue < 0) - { - messages.push_back(id.toString() + "|" + Potion.mId + " has negative value"); - } - -//checking for model - if (Potion.mModel.empty()) - { - messages.push_back(id.toString() + "|" + Potion.mId + " has no model"); - } - - //checking for icon - if (Potion.mIcon.empty()) - { - messages.push_back(id.toString() + "|" + Potion.mId + " has no icon"); - } - + inventoryItemCheck(Potion, messages, id.toString()); //IIRC potion can have empty effects list just fine. } @@ -272,41 +277,10 @@ void CSMTools::ReferenceableCheckStage::apparatusCheck(int stage, const CSMWorld const ESM::Apparatus& Apparatus = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Apparatus, Apparatus.mId); - //Checking for name - if (Apparatus.mName.empty()) - { - messages.push_back(id.toString() + "|" + Apparatus.mId + " has an empty name"); - } - - //Checking for weight - if (Apparatus.mData.mWeight < 0) - { - messages.push_back(id.toString() + "|" + Apparatus.mId + " has negative weight"); - } - - //Checking for value - if (Apparatus.mData.mValue < 0) - { - messages.push_back(id.toString() + "|" + Apparatus.mId + " has negative value"); - } - -//checking for model - if (Apparatus.mModel.empty()) - { - messages.push_back(id.toString() + "|" + Apparatus.mId + " has no model"); - } - - //checking for icon - if (Apparatus.mIcon.empty()) - { - messages.push_back(id.toString() + "|" + Apparatus.mId + " has no icon"); - } + inventoryItemCheck(Apparatus, messages, id.toString()); //checking for quality, 0 → apparatus is basicly useless, any negative → apparatus is harmfull instead of helpfull - if (Apparatus.mData.mQuality <= 0) - { - messages.push_back(id.toString() + "|" + Apparatus.mId + " has non-positive quality"); - } + toolCheck(Apparatus, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::armorCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Armor >& records, std::vector< std::string >& messages) @@ -321,41 +295,7 @@ void CSMTools::ReferenceableCheckStage::armorCheck(int stage, const CSMWorld::Re const ESM::Armor& Armor = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Armor, Armor.mId); - //Checking for name - if (Armor.mName.empty()) - { - messages.push_back(id.toString() + "|" + Armor.mId + " has an empty name"); - } - - //Checking for weight - if (Armor.mData.mWeight < 0) - { - messages.push_back(id.toString() + "|" + Armor.mId + " has negative weight"); - } - - //Checking for value - if (Armor.mData.mValue < 0) - { - messages.push_back(id.toString() + "|" + Armor.mId + " has negative value"); - } - -//checking for model - if (Armor.mModel.empty()) - { - messages.push_back(id.toString() + "|" + Armor.mId + " has no model"); - } - - //checking for icon - if (Armor.mIcon.empty()) - { - messages.push_back(id.toString() + "|" + Armor.mId + " has no icon"); - } - - //checking for enchantment points - if (Armor.mData.mEnchant < 0) - { - messages.push_back(id.toString() + "|" + Armor.mId + " has negative enchantment"); - } + inventoryItemCheck(Armor, messages, id.toString(), true); //checking for armor class, armor should have poistive armor class, but 0 is considered legal if (Armor.mData.mArmor < 0) @@ -363,7 +303,7 @@ void CSMTools::ReferenceableCheckStage::armorCheck(int stage, const CSMWorld::Re messages.push_back(id.toString() + "|" + Armor.mId + " has negative armor class"); } - //checking for health. Only positive numbers are allowed, and 0 is illegal + //checking for health. Only positive numbers are allowed, or 0 is illegal if (Armor.mData.mHealth <= 0) { messages.push_back(id.toString() + "|" + Armor.mId + " has non positive health"); @@ -381,42 +321,7 @@ void CSMTools::ReferenceableCheckStage::clothingCheck(int stage, const CSMWorld: const ESM::Clothing& Clothing = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Clothing, Clothing.mId); - - //Checking for name - if (Clothing.mName.empty()) - { - messages.push_back(id.toString() + "|" + Clothing.mId + " has an empty name"); - } - - //Checking for weight - if (Clothing.mData.mWeight < 0) - { - messages.push_back(id.toString() + "|" + Clothing.mId + " has negative weight"); - } - - //Checking for value - if (Clothing.mData.mValue < 0) - { - messages.push_back(id.toString() + "|" + Clothing.mId + " has negative value"); - } - -//checking for model - if (Clothing.mModel.empty()) - { - messages.push_back(id.toString() + "|" + Clothing.mId + " has no model"); - } - - //checking for icon - if (Clothing.mIcon.empty()) - { - messages.push_back(id.toString() + "|" + Clothing.mId + " has no icon"); - } - - //checking for enchantment points - if (Clothing.mData.mEnchant < 0) - { - messages.push_back(id.toString() + "|" + Clothing.mId + " has negative enchantment"); - } + inventoryItemCheck(Clothing, messages, id.toString(), true); } void CSMTools::ReferenceableCheckStage::containerCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Container >& records, std::vector< std::string >& messages) @@ -556,7 +461,7 @@ void CSMTools::ReferenceableCheckStage::doorCheck(int stage, const CSMWorld::Ref const ESM::Door& Door = (dynamic_cast&>(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Door, Door.mId); - //usual, name and model + //usual, name or model if (Door.mName.empty()) { messages.push_back(id.toString() + "|" + Door.mId + " has an empty name"); @@ -582,35 +487,7 @@ void CSMTools::ReferenceableCheckStage::ingredientCheck(int stage, const CSMWorl const ESM::Ingredient& Ingredient = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Ingredient, Ingredient.mId); - //Checking for name - if (Ingredient.mName.empty()) - { - messages.push_back(id.toString() + "|" + Ingredient.mId + " has an empty name"); - } - - //Checking for weight - if (Ingredient.mData.mWeight < 0) - { - messages.push_back(id.toString() + "|" + Ingredient.mId + " has negative weight"); - } - - //Checking for value - if (Ingredient.mData.mValue < 0) - { - messages.push_back(id.toString() + "|" + Ingredient.mId + " has negative value"); - } - -//checking for model - if (Ingredient.mModel.empty()) - { - messages.push_back(id.toString() + "|" + Ingredient.mId + " has no model"); - } - - //checking for icon - if (Ingredient.mIcon.empty()) - { - messages.push_back(id.toString() + "|" + Ingredient.mId + " has no icon"); - } + inventoryItemCheck(Ingredient, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::creaturesLevListCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::CreatureLevList >& records, std::vector< std::string >& messages) @@ -625,18 +502,8 @@ void CSMTools::ReferenceableCheckStage::creaturesLevListCheck(int stage, const C const ESM::CreatureLevList& CreatureLevList = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_CreatureLevelledList, CreatureLevList.mId); //CreatureLevList but Type_CreatureLevelledList :/ - for (unsigned i = 0; i < CreatureLevList.mList.size(); ++i) - { - if (CreatureLevList.mList[i].mId.empty()) - { - messages.push_back(id.toString() + "|" + CreatureLevList.mId + " contains item with empty Id"); - } - if (CreatureLevList.mList[i].mLevel < 1) - { - messages.push_back(id.toString() + "|" + CreatureLevList.mId + " contains item with non-positive level"); - } - } + listCheck(CreatureLevList, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::itemLevelledListCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::ItemLevList >& records, std::vector< std::string >& messages) @@ -651,18 +518,7 @@ void CSMTools::ReferenceableCheckStage::itemLevelledListCheck(int stage, const C const ESM::ItemLevList& ItemLevList = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_ItemLevelledList, ItemLevList.mId); - for (unsigned i = 0; i < ItemLevList.mList.size(); ++i) - { - if (ItemLevList.mList[i].mId.empty()) - { - messages.push_back(id.toString() + "|" + ItemLevList.mId + " contains item with empty Id"); - } - - if (ItemLevList.mList[i].mLevel < 1) - { - messages.push_back(id.toString() + "|" + ItemLevList.mId + " contains item with non-positive level"); - } - } + listCheck(ItemLevList, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::lightCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Light >& records, std::vector< std::string >& messages) @@ -686,31 +542,15 @@ void CSMTools::ReferenceableCheckStage::lightCheck(int stage, const CSMWorld::Re { if (Light.mIcon.empty()) //Needs to be checked with carrable flag { - messages.push_back(id.toString() + "|" + Light.mId + " has no icon"); - } + inventoryItemCheck(Light, messages, id.toString()); - if (Light.mData.mWeight < 0) - { - messages.push_back(id.toString() + "|" + Light.mId + " has negative weight"); - } - - if (Light.mData.mValue < 0) - { - messages.push_back(id.toString() + "|" + Light.mId + " has negative value"); - } - - if (Light.mModel.empty()) - { - messages.push_back(id.toString() + "|" + Light.mId + " has no model"); - } - - if (Light.mData.mTime == 0) - { - messages.push_back(id.toString() + "|" + Light.mId + " has zero duration"); + if (Light.mData.mTime == 0) + { + messages.push_back(id.toString() + "|" + Light.mId + " has zero duration"); + } } } } - void CSMTools::ReferenceableCheckStage::lockpickCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Lockpick >& records, std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -723,45 +563,9 @@ void CSMTools::ReferenceableCheckStage::lockpickCheck(int stage, const CSMWorld: const ESM::Lockpick& Lockpick = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Lockpick, Lockpick.mId); - //Checking for name - if (Lockpick.mName.empty()) - { - messages.push_back(id.toString() + "|" + Lockpick.mId + " has an empty name"); - } + inventoryItemCheck(Lockpick, messages, id.toString()); - //Checking for weight - if (Lockpick.mData.mWeight < 0) - { - messages.push_back(id.toString() + "|" + Lockpick.mId + " has negative weight"); - } - - //Checking for value - if (Lockpick.mData.mValue < 0) - { - messages.push_back(id.toString() + "|" + Lockpick.mId + " has negative value"); - } - -//checking for model - if (Lockpick.mModel.empty()) - { - messages.push_back(id.toString() + "|" + Lockpick.mId + " has no model"); - } - - //checking for icon - if (Lockpick.mIcon.empty()) - { - messages.push_back(id.toString() + "|" + Lockpick.mId + " has no icon"); - } - - if (Lockpick.mData.mQuality <= 0) - { - messages.push_back(id.toString() + "|" + Lockpick.mId + " has non-positive quality"); - } - - if (Lockpick.mData.mUses <= 0) - { - messages.push_back(id.toString() + "|" + Lockpick.mId + " has no uses left"); - } + toolCheck(Lockpick, messages, id.toString(), true); } void CSMTools::ReferenceableCheckStage::miscCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Miscellaneous >& records, std::vector< std::string >& messages) @@ -776,35 +580,7 @@ void CSMTools::ReferenceableCheckStage::miscCheck(int stage, const CSMWorld::Ref const ESM::Miscellaneous& Miscellaneous = (dynamic_cast& >(baserecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Miscellaneous, Miscellaneous.mId); - //Checking for name - if (Miscellaneous.mName.empty()) - { - messages.push_back(id.toString() + "|" + Miscellaneous.mId + " has an empty name"); - } - - //Checking for weight - if (Miscellaneous.mData.mWeight < 0) - { - messages.push_back(id.toString() + "|" + Miscellaneous.mId + " has negative weight"); - } - - //Checking for value - if (Miscellaneous.mData.mValue < 0) - { - messages.push_back(id.toString() + "|" + Miscellaneous.mId + " has negative value"); - } - -//checking for model - if (Miscellaneous.mModel.empty()) - { - messages.push_back(id.toString() + "|" + Miscellaneous.mId + " has no model"); - } - - //checking for icon - if (Miscellaneous.mIcon.empty()) - { - messages.push_back(id.toString() + "|" + Miscellaneous.mId + " has no icon"); - } + inventoryItemCheck(Miscellaneous, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::NPC >& records, std::vector< std::string >& messages) @@ -819,8 +595,6 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI const ESM::NPC& NPC = (dynamic_cast& >(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); @@ -832,7 +606,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI { if ((NPC.mFlags & 0x0008) == 0) //0x0008 = autocalculated flag { - messages.push_back(id.toString() + "|" + NPC.mId + " mNpdtType and flags mismatch!"); //should not happend? + messages.push_back(id.toString() + "|" + NPC.mId + " mNpdtType or flags mismatch!"); //should not happend? return; } @@ -982,36 +756,233 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI //TODO: reputation, Disposition, rank, everything else } +void CSMTools::ReferenceableCheckStage::weaponCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Weapon >& records, std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + + if (baserecord.isDeleted()) + { + return; + } + + const ESM::Weapon& Weapon = (dynamic_cast& >(baserecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Weapon, Weapon.mId); + + //TODO, It seems that this stuff for spellcasting is obligatory and In fact We should check if records are present + if + ( + Weapon.mId.find("VFX_") == std::string::npos + and Weapon.mId != "magic_bolt" + and Weapon.mId != "shield_bolt" + and Weapon.mId != "shock_bolt" + ) + { + inventoryItemCheck(Weapon, messages, id.toString(), true); + + if (Weapon.mData.mType == ESM::Weapon::MarksmanBow or Weapon.mData.mType == ESM::Weapon::MarksmanCrossbow or Weapon.mData.mType == ESM::Weapon::MarksmanThrown or Weapon.mData.mType == ESM::Weapon::Arrow or Weapon.mData.mType == ESM::Weapon::Bolt) + { + if (Weapon.mData.mChop[0] > Weapon.mData.mChop[1]) + { + messages.push_back(id.toString() + "|" + Weapon.mId + " has minimum chop damage higher than maximum"); + } + } + else + { + if (Weapon.mData.mSlash[0] > Weapon.mData.mSlash[1]) + { + messages.push_back(id.toString() + "|" + Weapon.mId + " has minimum slash damage higher than maximum"); + } + + if (Weapon.mData.mChop[0] > Weapon.mData.mChop[1]) + { + messages.push_back(id.toString() + "|" + Weapon.mId + " has minimum chop damage higher than maximum"); + } + + if (Weapon.mData.mThrust[0] > Weapon.mData.mThrust[1]) + { + messages.push_back(id.toString() + "|" + Weapon.mId + " has minimum thrust damage higher than maximum"); + } + } + + if (!(Weapon.mData.mType == ESM::Weapon::Arrow or Weapon.mData.mType == ESM::Weapon::Bolt or Weapon.mData.mType == ESM::Weapon::MarksmanThrown)) + { + //checking of health + if (Weapon.mData.mHealth <= 0) + { + messages.push_back(id.toString() + "|" + Weapon.mId + " has non-positivie health"); + } + + if (Weapon.mData.mReach < 0) + { + messages.push_back(id.toString() + "|" + Weapon.mId + " has negative reach"); + } + } + } +} + +void CSMTools::ReferenceableCheckStage::probeCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Probe >& records, std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + + if (baserecord.isDeleted()) + { + return; + } + + const ESM::Probe& Probe = (dynamic_cast& >(baserecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Probe, Probe.mId); + + inventoryItemCheck(Probe, messages, id.toString()); + toolCheck(Probe, messages, id.toString(), true); +} + +void CSMTools::ReferenceableCheckStage::repairCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Repair >& records, std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + + if (baserecord.isDeleted()) + { + return; + } + + const ESM::Repair& Repair = (dynamic_cast& >(baserecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Repair, Repair.mId); + + inventoryItemCheck(Repair, messages, id.toString()); + toolCheck(Repair, messages, id.toString(), true); +} + +void CSMTools::ReferenceableCheckStage::staticCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Static >& records, std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + + if (baserecord.isDeleted()) + { + return; + } + + const ESM::Static& Static = (dynamic_cast& >(baserecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Static, Static.mId); + + if (Static.mModel.empty()) + { + messages.push_back(id.toString() + "|" + Static.mId + " has no model"); + } +} + + //Templates begins here -void CSMTools::ReferenceableCheckStage::inventoryItemCheck(const item& item, std::vector< std::string >& messages) +template void CSMTools::ReferenceableCheckStage::inventoryItemCheck(const ITEM& someitem, std::vector< std::string >& messages, const std::string& someid, bool enchantable) { - if (item.mName.empty()) + if (someitem.mName.empty()) { - messages.push_back(id.toString() + "|" + item.mId + " has an empty name"); + messages.push_back(someid + "|" + someitem.mId + " has an empty name"); } //Checking for weight - if (item.mData.mWeight < 0) + if (someitem.mData.mWeight < 0) { - messages.push_back(id.toString() + "|" + item.mId + " has negative weight"); + messages.push_back(someid + "|" + someitem.mId + " has negative weight"); } //Checking for value - if (item.mData.mValue < 0) + if (someitem.mData.mValue < 0) { - messages.push_back(id.toString() + "|" + item.mId + " has negative value"); + messages.push_back(someid + "|" + someitem.mId + " has negative value"); } //checking for model - if (item.mModel.empty()) + if (someitem.mModel.empty()) { - messages.push_back(id.toString() + "|" + item.mId + " has no model"); + messages.push_back(someid + "|" + someitem.mId + " has no model"); } //checking for icon - if (item.mIcon.empty()) + if (someitem.mIcon.empty()) { - messages.push_back(id.toString() + "|" + item.mId + " has no icon"); + messages.push_back(someid + "|" + someitem.mId + " has no icon"); + } + + if (enchantable) + { + if (someitem.mData.mEnchant < 0) + { + messages.push_back(someid + "|" + someitem.mId + " has negative enchantment"); + } } } + +template void CSMTools::ReferenceableCheckStage::inventoryItemCheck(const ITEM& someitem, std::vector< std::string >& messages, const std::string& someid) +{ + if (someitem.mName.empty()) + { + messages.push_back(someid + "|" + someitem.mId + " has an empty name"); + } + + //Checking for weight + if (someitem.mData.mWeight < 0) + { + messages.push_back(someid + "|" + someitem.mId + " has negative weight"); + } + + //Checking for value + if (someitem.mData.mValue < 0) + { + messages.push_back(someid + "|" + someitem.mId + " has negative value"); + } + +//checking for model + if (someitem.mModel.empty()) + { + messages.push_back(someid + "|" + someitem.mId + " has no model"); + } + + //checking for icon + if (someitem.mIcon.empty()) + { + messages.push_back(someid + "|" + someitem.mId + " has no icon"); + } +} + +template void CSMTools::ReferenceableCheckStage::toolCheck(const TOOL& sometool, std::vector< std::string >& messages, const std::string& someid, bool canbebroken) +{ + if (sometool.mData.mQuality <= 0) + { + messages.push_back(someid + "|" + sometool.mId + " has non-positive quality"); + } + + if (canbebroken) + { + if (sometool.mData.mUses <= 0) + { + messages.push_back(someid + "|" + sometool.mId + " has non-positive uses count"); + } + } +} + +template void CSMTools::ReferenceableCheckStage::toolCheck(const TOOL& sometool, std::vector< std::string >& messages, const std::string& someid) +{ + if (sometool.mData.mQuality <= 0) + { + messages.push_back(someid + "|" + sometool.mId + " has non-positive quality"); + } + +} + +template void CSMTools::ReferenceableCheckStage::listCheck(const LIST& somelist, std::vector< std::string >& messages, const std::string& someid) +{ + for (unsigned i = 0; i < somelist.mList.size(); ++i) + { + if (somelist.mList[i].mId.empty()) + { + messages.push_back(someid + "|" + somelist.mId + " contains item with empty Id"); + } + + if (somelist.mList[i].mLevel < 1) + { + messages.push_back(someid + "|" + somelist.mId + " contains item with non-positive level"); + } + } +} + diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index 43bb54fa7..feeb32e6a 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -33,9 +33,17 @@ namespace CSMTools void lockpickCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void miscCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void npcCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void weaponCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void probeCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void repairCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void staticCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); //TEMPLATE CHECKS - template void inventoryItemCheck(const item& item, std::vector& messages); //for all inventory items. + template void inventoryItemCheck(const ITEM& someitem, std::vector& messages, const std::string& someid, bool enchantable); //for all enchantable items. + template void inventoryItemCheck(const ITEM& someitem, std::vector& messages, const std::string& someid); //for non-enchantable items. + template void toolCheck(const TOOL& sometool, std::vector& messages, const std::string& someid, bool canbebroken); //for tools with uses. + template void toolCheck(const TOOL& sometool, std::vector& messages, const std::string& someid); //for tools without uses. + template void listCheck(const LIST& some, std::vector< std::string >& messages, const std::string& someid); const CSMWorld::RefIdData& mReferencables; const CSMWorld::IdCollection& mRaces; diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 5af215989..65990c1d4 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -311,3 +311,23 @@ const CSMWorld::RefIdDataContainer< ESM::NPC >& CSMWorld::RefIdData::getNPCs() c { return mNpcs; } + +const CSMWorld::RefIdDataContainer< ESM::Weapon >& CSMWorld::RefIdData::getWeapons() const +{ + return mWeapons; +} + +const CSMWorld::RefIdDataContainer< ESM::Probe >& CSMWorld::RefIdData::getProbes() const +{ + return mProbes; +} + +const CSMWorld::RefIdDataContainer< ESM::Repair >& CSMWorld::RefIdData::getRepairs() const +{ + return mRepairs; +} + +const CSMWorld::RefIdDataContainer< ESM::Static >& CSMWorld::RefIdData::getStatics() const +{ + return mStatics; +} \ No newline at end of file diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 51f548499..f82d151b4 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -237,6 +237,10 @@ namespace CSMWorld const RefIdDataContainer& getLocpicks() const; const RefIdDataContainer& getMiscellaneous() const; const RefIdDataContainer& getNPCs() const; + const RefIdDataContainer< ESM::Weapon >& getWeapons() const; + const RefIdDataContainer< ESM::Probe >& getProbes() const; + const RefIdDataContainer< ESM::Repair>& getRepairs() const; + const RefIdDataContainer< ESM::Static>& getStatics() const; }; } From 7b63e1942c3cae9e4bcf6b5b0a522d49a9728ee7 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 3 Jan 2014 11:42:49 +0100 Subject: [PATCH 338/889] style corrections --- .../opencs/model/tools/referenceablecheck.cpp | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 65bee314c..3f4d80db8 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -779,31 +779,24 @@ void CSMTools::ReferenceableCheckStage::weaponCheck(int stage, const CSMWorld::R { inventoryItemCheck(Weapon, messages, id.toString(), true); - if (Weapon.mData.mType == ESM::Weapon::MarksmanBow or Weapon.mData.mType == ESM::Weapon::MarksmanCrossbow or Weapon.mData.mType == ESM::Weapon::MarksmanThrown or Weapon.mData.mType == ESM::Weapon::Arrow or Weapon.mData.mType == ESM::Weapon::Bolt) - { - if (Weapon.mData.mChop[0] > Weapon.mData.mChop[1]) - { - messages.push_back(id.toString() + "|" + Weapon.mId + " has minimum chop damage higher than maximum"); - } - } - else + if (!(Weapon.mData.mType == ESM::Weapon::MarksmanBow or Weapon.mData.mType == ESM::Weapon::MarksmanCrossbow or Weapon.mData.mType == ESM::Weapon::MarksmanThrown or Weapon.mData.mType == ESM::Weapon::Arrow or Weapon.mData.mType == ESM::Weapon::Bolt)) { if (Weapon.mData.mSlash[0] > Weapon.mData.mSlash[1]) { messages.push_back(id.toString() + "|" + Weapon.mId + " has minimum slash damage higher than maximum"); } - if (Weapon.mData.mChop[0] > Weapon.mData.mChop[1]) - { - messages.push_back(id.toString() + "|" + Weapon.mId + " has minimum chop damage higher than maximum"); - } - if (Weapon.mData.mThrust[0] > Weapon.mData.mThrust[1]) { messages.push_back(id.toString() + "|" + Weapon.mId + " has minimum thrust damage higher than maximum"); } } + if (Weapon.mData.mChop[0] > Weapon.mData.mChop[1]) + { + messages.push_back(id.toString() + "|" + Weapon.mId + " has minimum chop damage higher than maximum"); + } + if (!(Weapon.mData.mType == ESM::Weapon::Arrow or Weapon.mData.mType == ESM::Weapon::Bolt or Weapon.mData.mType == ESM::Weapon::MarksmanThrown)) { //checking of health From 55c5d7cee445bf7ae83580370f3b11c1694f44f6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 15:54:23 +0100 Subject: [PATCH 339/889] Implement Resurrect instruction --- apps/openmw/mwmechanics/creaturestats.cpp | 6 ++++-- apps/openmw/mwscript/docs/vmformat.txt | 2 ++ apps/openmw/mwscript/statsextensions.cpp | 16 +++++++++++++++- components/compiler/extensions0.cpp | 2 ++ components/compiler/opcodes.hpp | 2 ++ 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 6fc8f2597..a21796ce6 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -266,8 +266,10 @@ namespace MWMechanics if (mDead) { if (mDynamic[0].getCurrent()<1) - mDynamic[0].setCurrent (1); - + { + mDynamic[0].setModified(mDynamic[0].getModified(), 1); + mDynamic[0].setCurrent(1); + } if (mDynamic[0].getCurrent()>=1) mDead = false; } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index cf27b7d6e..40377327a 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -366,4 +366,6 @@ op 0x200022b: RemoveSpellEffects op 0x200022c: RemoveSpellEffects, explicit op 0x200022d: RemoveEffects op 0x200022e: RemoveEffects, explicit +op 0x200022f: Resurrect +op 0x2000230: Resurrect, explicit opcodes 0x200022f-0x3ffffff unused diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index b1c9698ca..f053e9a97 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -1104,6 +1104,18 @@ namespace MWScript } }; + template + class OpResurrect : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + ptr.getClass().getCreatureStats(ptr).resurrect(); + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { for (int i=0; i); interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpellEffectsExplicit, new OpRemoveSpellEffects); - + interpreter.installSegment5 (Compiler::Stats::opcodeResurrect, new OpResurrect); + interpreter.installSegment5 (Compiler::Stats::opcodeResurrectExplicit, + new OpResurrect); interpreter.installSegment5 (Compiler::Stats::opcodeRemoveEffects, new OpRemoveEffects); interpreter.installSegment5 (Compiler::Stats::opcodeRemoveEffectsExplicit, new OpRemoveEffects); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index c113a3275..ceffa4c9a 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -395,6 +395,8 @@ namespace Compiler opcodeRemoveSpellEffectsExplicit); extensions.registerInstruction ("removeeffects", "l", opcodeRemoveEffects, opcodeRemoveEffectsExplicit); + extensions.registerInstruction ("resurrect", "", opcodeResurrect, + opcodeResurrectExplicit); extensions.registerFunction ("getspell", 'l', "c", opcodeGetSpell, opcodeGetSpellExplicit); extensions.registerInstruction("pcraiserank","/S",opcodePCRaiseRank); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 4cd9ec3ff..a211f167a 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -374,6 +374,8 @@ namespace Compiler const int opcodeRemoveSpellEffectsExplicit = 0x200022c; const int opcodeRemoveEffects = 0x200022d; const int opcodeRemoveEffectsExplicit = 0x200022e; + const int opcodeResurrect = 0x200022f; + const int opcodeResurrectExplicit = 0x2000230; } namespace Transformation From b22dd40b41e5098c3c829603c538f6afedb18ab1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 17:06:05 +0100 Subject: [PATCH 340/889] Implement Paralyze magic effect --- apps/openmw/mwclass/npc.cpp | 3 ++- apps/openmw/mwmechanics/actors.cpp | 5 +++++ apps/openmw/mwmechanics/aicombat.cpp | 3 +++ apps/openmw/mwmechanics/aiescort.cpp | 1 + apps/openmw/mwmechanics/aitravel.cpp | 1 + apps/openmw/mwmechanics/aiwander.cpp | 1 + apps/openmw/mwmechanics/character.cpp | 7 +++++-- 7 files changed, 18 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 307ad3d7c..d66552373 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -517,7 +517,8 @@ namespace MWClass MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); } - healthdmg = (otherstats.getFatigue().getCurrent() < 1.0f); + healthdmg = (otherstats.getFatigue().getCurrent() < 1.0f) + || (otherstats.getMagicEffects().get(ESM::MagicEffect::Paralyze).mMagnitude > 0); if(stats.isWerewolf()) { healthdmg = true; diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index ec3b86137..108be6159 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -797,7 +797,12 @@ namespace MWMechanics iter->second->updateContinuousVfx(); for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + { + if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects().get( + ESM::MagicEffect::Paralyze).mMagnitude > 0) + iter->second->skipAnim(); iter->second->update(duration); + } } } void Actors::restoreDynamicStats() diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 39a97df6c..6f643aa68 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -91,6 +91,8 @@ namespace MWMechanics mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + + // TODO: use movement settings instead of rotating directly MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; @@ -105,6 +107,7 @@ namespace MWMechanics float directionResult = sqrt(directionX * directionX + directionY * directionY); zAngle = Ogre::Radian( acos(directionY / directionResult) * sgn(asin(directionX / directionResult)) ).valueDegrees(); + // TODO: use movement settings instead of rotating directly MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); mPathFinder.clearPath(); diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 3615c8546..5099625c0 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -161,6 +161,7 @@ namespace MWMechanics if(distanceBetweenResult <= mMaxDist * mMaxDist) { float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + // TODO: use movement settings instead of rotating directly MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; mMaxDist = 470; diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 08d758638..f56c75996 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -97,6 +97,7 @@ namespace MWMechanics } float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + // TODO: use movement settings instead of rotating directly world->rotateObject(actor, 0, 0, zAngle, false); movement.mPosition[1] = 1; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index f66f7ca62..93c94a3f4 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -236,6 +236,7 @@ namespace MWMechanics if(mWalking) { float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + // TODO: use movement settings instead of rotating directly world->rotateObject(actor, 0, 0, zAngle, false); MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 351e33f13..8a73c98af 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -955,9 +955,12 @@ void CharacterController::update(float duration) refreshCurrentAnims(idlestate, movestate, forcestateupdate); rot *= duration * Ogre::Math::RadiansToDegrees(1.0f); - world->rotateObject(mPtr, rot.x, rot.y, rot.z, true); - world->queueMovement(mPtr, vec); + if (!mSkipAnim) + { + world->rotateObject(mPtr, rot.x, rot.y, rot.z, true); + world->queueMovement(mPtr, vec); + } movement = vec; } else if(cls.getCreatureStats(mPtr).isDead()) From 617a65cdd22b8c93d44b8b6082f629fd2346736d Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 17:39:57 +0100 Subject: [PATCH 341/889] Print message to dialogue window after successful trade --- apps/openmw/mwgui/tradewindow.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index e6cbbf968..6000de858 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -24,6 +24,7 @@ #include "containeritemmodel.hpp" #include "tradeitemmodel.hpp" #include "countdialog.hpp" +#include "dialogue.hpp" namespace MWGui { @@ -340,6 +341,9 @@ namespace MWGui addOrRemoveGold(-mCurrentBalance, mPtr); } + MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addResponse( + MWBase::Environment::get().getWorld()->getStore().get().find("sBarterDialog5")->getString()); + std::string sound = "Item Gold Up"; MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); From 1c3296fb64e4dd792681a9d8756be9451930f079 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 3 Jan 2014 18:23:11 +0100 Subject: [PATCH 342/889] Some changes in weapons check. --- apps/opencs/model/tools/referenceablecheck.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 3f4d80db8..73f316677 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -8,6 +8,7 @@ #include "../world/record.hpp" #include "../world/universalid.hpp" +#include CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& faction) : mReferencables(referenceable), @@ -551,6 +552,7 @@ void CSMTools::ReferenceableCheckStage::lightCheck(int stage, const CSMWorld::Re } } } + void CSMTools::ReferenceableCheckStage::lockpickCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Lockpick >& records, std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -667,7 +669,6 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI { messages.push_back(id.toString() + "|" + NPC.mId + " willpower has zero value"); } - } if (level < 1) @@ -771,10 +772,13 @@ void CSMTools::ReferenceableCheckStage::weaponCheck(int stage, const CSMWorld::R //TODO, It seems that this stuff for spellcasting is obligatory and In fact We should check if records are present if ( - Weapon.mId.find("VFX_") == std::string::npos - and Weapon.mId != "magic_bolt" - and Weapon.mId != "shield_bolt" - and Weapon.mId != "shock_bolt" + //THOSE ARE HARDCODED! + Weapon.mId != "VFX_Hands" + and Weapon.mId != "VFX_Absorb" + and Weapon.mId != "VFX_Reflect" + and Weapon.mId != "VFX_DefaultBolt" + //THIS ONE IS NOT, but it thas to be ignored as well + //TODO I don't know how to get full list of effects :/ ) { inventoryItemCheck(Weapon, messages, id.toString(), true); @@ -977,5 +981,4 @@ template void CSMTools::ReferenceableCheckStage::listCheck(const messages.push_back(someid + "|" + somelist.mId + " contains item with non-positive level"); } } -} - +} \ No newline at end of file From e9bd43e2da541ba2ae21ac9a2ad803dce9ccfefc Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 20:54:14 +0100 Subject: [PATCH 343/889] Use GMSTs for bound items/creatures --- apps/openmw/mwmechanics/actors.cpp | 89 ++++++++++++++++-------------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 108be6159..f4cf1d482 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -408,17 +408,17 @@ namespace MWMechanics static std::map boundItemsMap; if (boundItemsMap.empty()) { - boundItemsMap[ESM::MagicEffect::BoundBattleAxe] = "battle_axe"; - boundItemsMap[ESM::MagicEffect::BoundBoots] = "boots"; - boundItemsMap[ESM::MagicEffect::BoundCuirass] = "cuirass"; - boundItemsMap[ESM::MagicEffect::BoundDagger] = "dagger"; - boundItemsMap[ESM::MagicEffect::BoundGloves] = "gauntlet"; // Note: needs both _left and _right variants, see below - boundItemsMap[ESM::MagicEffect::BoundHelm] = "helm"; - boundItemsMap[ESM::MagicEffect::BoundLongbow] = "longbow"; - boundItemsMap[ESM::MagicEffect::BoundLongsword] = "longsword"; - boundItemsMap[ESM::MagicEffect::BoundMace] = "mace"; - boundItemsMap[ESM::MagicEffect::BoundShield] = "shield"; - boundItemsMap[ESM::MagicEffect::BoundSpear] = "spear"; + boundItemsMap[ESM::MagicEffect::BoundBattleAxe] = "sMagicBoundBattleAxeID"; + boundItemsMap[ESM::MagicEffect::BoundBoots] = "sMagicBoundBootsID"; + boundItemsMap[ESM::MagicEffect::BoundCuirass] = "sMagicBoundCuirassID"; + boundItemsMap[ESM::MagicEffect::BoundDagger] = "sMagicBoundDaggerID"; + boundItemsMap[ESM::MagicEffect::BoundGloves] = "sMagicBoundLeftGauntletID"; // Note: needs RightGauntlet variant too (see below) + boundItemsMap[ESM::MagicEffect::BoundHelm] = "sMagicBoundHelmID"; + boundItemsMap[ESM::MagicEffect::BoundLongbow] = "sMagicBoundLongbowID"; + boundItemsMap[ESM::MagicEffect::BoundLongsword] = "sMagicBoundLongswordID"; + boundItemsMap[ESM::MagicEffect::BoundMace] = "sMagicBoundMaceID"; + boundItemsMap[ESM::MagicEffect::BoundShield] = "sMagicBoundShieldID"; + boundItemsMap[ESM::MagicEffect::BoundSpear] = "sMagicBoundSpearID"; } for (std::map::iterator it = boundItemsMap.begin(); it != boundItemsMap.end(); ++it) @@ -427,11 +427,13 @@ namespace MWMechanics int magnitude = creatureStats.getMagicEffects().get(it->first).mMagnitude; if (found != (magnitude > 0)) { - std::string item = "bound_" + it->second; + std::string itemGmst = it->second; + std::string item = MWBase::Environment::get().getWorld()->getStore().get().find( + itemGmst)->getString(); if (it->first == ESM::MagicEffect::BoundGloves) { - adjustBoundItem(item + "_left", magnitude > 0, ptr); - adjustBoundItem(item + "_right", magnitude > 0, ptr); + adjustBoundItem("sMagicBoundLeftGauntletID", magnitude > 0, ptr); + adjustBoundItem("sMagicBoundRightGauntletID", magnitude > 0, ptr); } else adjustBoundItem(item, magnitude > 0, ptr); @@ -447,26 +449,28 @@ namespace MWMechanics static std::map summonMap; if (summonMap.empty()) { - summonMap[ESM::MagicEffect::SummonAncestralGhost] = "ancestor_ghost_summon"; - summonMap[ESM::MagicEffect::SummonBear] = "BM_bear_black_summon"; - summonMap[ESM::MagicEffect::SummonBonelord] = "bonelord_summon"; - summonMap[ESM::MagicEffect::SummonBonewalker] = "bonewalker_summon"; - summonMap[ESM::MagicEffect::SummonBonewolf] = "BM_wolf_bone_summon"; - summonMap[ESM::MagicEffect::SummonCenturionSphere] = "centurion_sphere_summon"; - summonMap[ESM::MagicEffect::SummonClannfear] = "clannfear_summon"; - summonMap[ESM::MagicEffect::SummonDaedroth] = "daedroth_summon"; - summonMap[ESM::MagicEffect::SummonDremora] = "dremora_summon"; - summonMap[ESM::MagicEffect::SummonFabricant] = "fabricant_summon"; - summonMap[ESM::MagicEffect::SummonFlameAtronach] = "atronach_flame_summon"; - summonMap[ESM::MagicEffect::SummonFrostAtronach] = "atronach_frost_summon"; - summonMap[ESM::MagicEffect::SummonGoldenSaint] = "golden saint_summon"; - summonMap[ESM::MagicEffect::SummonGreaterBonewalker] = "bonewalker_greater_summ"; - summonMap[ESM::MagicEffect::SummonHunger] = "hunger_summon"; - summonMap[ESM::MagicEffect::SummonScamp] = "scamp_summon"; - summonMap[ESM::MagicEffect::SummonSkeletalMinion] = "skeleton_summon"; - summonMap[ESM::MagicEffect::SummonStormAtronach] = "atronach_storm_summon"; - summonMap[ESM::MagicEffect::SummonWingedTwilight] = "winged twilight_summon"; - summonMap[ESM::MagicEffect::SummonWolf] = "BM_wolf_grey_summon"; + summonMap[ESM::MagicEffect::SummonAncestralGhost] = "sMagicAncestralGhostID"; + summonMap[ESM::MagicEffect::SummonBonelord] = "sMagicBonelordID"; + summonMap[ESM::MagicEffect::SummonBonewalker] = "sMagicLeastBonewalkerID"; + summonMap[ESM::MagicEffect::SummonCenturionSphere] = "sMagicCenturionSphereID"; + summonMap[ESM::MagicEffect::SummonClannfear] = "sMagicClannfearID"; + summonMap[ESM::MagicEffect::SummonDaedroth] = "sMagicDaedrothID"; + summonMap[ESM::MagicEffect::SummonDremora] = "sMagicDremoraID"; + summonMap[ESM::MagicEffect::SummonFabricant] = "sMagicFabricantID"; + summonMap[ESM::MagicEffect::SummonFlameAtronach] = "sMagicFlameAtronachID"; + summonMap[ESM::MagicEffect::SummonFrostAtronach] = "sMagicFrostAtronachID"; + summonMap[ESM::MagicEffect::SummonGoldenSaint] = "sMagicGoldenSaintID"; + summonMap[ESM::MagicEffect::SummonGreaterBonewalker] = "sMagicGreaterBonewalkerID"; + summonMap[ESM::MagicEffect::SummonHunger] = "sMagicHungerID"; + summonMap[ESM::MagicEffect::SummonScamp] = "sMagicScampID"; + summonMap[ESM::MagicEffect::SummonSkeletalMinion] = "sMagicSkeletalMinionID"; + summonMap[ESM::MagicEffect::SummonStormAtronach] = "sMagicStormAtronachID"; + summonMap[ESM::MagicEffect::SummonWingedTwilight] = "sMagicWingedTwilightID"; + summonMap[ESM::MagicEffect::SummonWolf] = "sMagicCreature01ID"; + summonMap[ESM::MagicEffect::SummonBear] = "sMagicCreature02ID"; + summonMap[ESM::MagicEffect::SummonBonewolf] = "sMagicCreature03ID"; + summonMap[ESM::MagicEffect::SummonCreature04] = "sMagicCreature04ID"; + summonMap[ESM::MagicEffect::SummonCreature05] = "sMagicCreature05ID"; } for (std::map::iterator it = summonMap.begin(); it != summonMap.end(); ++it) @@ -489,15 +493,20 @@ namespace MWMechanics ipos.rot[1] = 0; ipos.rot[2] = 0; - MWWorld::CellStore* store = ptr.getCell(); - MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), it->second, 1); - ref.getPtr().getCellRef().mPos = ipos; + std::string creatureID = + MWBase::Environment::get().getWorld()->getStore().get().find(it->second)->getString(); - // TODO: Add AI to follow player and fight for him + if (!creatureID.empty()) + { + MWWorld::CellStore* store = ptr.getCell(); + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1); + ref.getPtr().getCellRef().mPos = ipos; - creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos).getRefData().getHandle())); + // TODO: Add AI to follow player and fight for him + creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos).getRefData().getHandle())); + } } else { From c0fbcf413f54d2cf75301f8c86855b0ad77a6f0a Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 22:44:35 +0100 Subject: [PATCH 344/889] Use the playermagic and playerfighting control switches --- apps/openmw/mwinput/inputmanagerimp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 1ad409515..9090c9747 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -676,7 +676,7 @@ namespace MWInput if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; // Not allowed before the magic window is accessible - if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Magic)) + if (!mControlSwitch["playermagic"]) return; MWMechanics::DrawState_ state = mPlayer->getDrawState(); @@ -691,7 +691,7 @@ namespace MWInput if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; // Not allowed before the inventory window is accessible - if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) + if (!mControlSwitch["playerfighting"]) return; MWMechanics::DrawState_ state = mPlayer->getDrawState(); From 2a7d610f87f40d56d28156603bafd2f4b69b5981 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 22:55:17 +0100 Subject: [PATCH 345/889] Implement GetSpellReadied instruction --- apps/openmw/mwscript/docs/vmformat.txt | 4 +++- apps/openmw/mwscript/miscextensions.cpp | 17 ++++++++++++++++- components/compiler/extensions0.cpp | 1 + components/compiler/opcodes.hpp | 2 ++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 40377327a..b4ac8e154 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -368,4 +368,6 @@ op 0x200022d: RemoveEffects op 0x200022e: RemoveEffects, explicit op 0x200022f: Resurrect op 0x2000230: Resurrect, explicit -opcodes 0x200022f-0x3ffffff unused +op 0x2000231: GetSpellReadied +op 0x2000232: GetSpellReadied, explicit +opcodes 0x2000233-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index ea4824c29..8d435959b 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -465,7 +465,20 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - runtime.push(MWWorld::Class::get(ptr).getNpcStats (ptr).getDrawState () == MWMechanics::DrawState_Weapon); + runtime.push(ptr.getClass().getNpcStats (ptr).getDrawState () == MWMechanics::DrawState_Weapon); + } + }; + + template + class OpGetSpellReadied : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + runtime.push(ptr.getClass().getNpcStats (ptr).getDrawState () == MWMechanics::DrawState_Spell); } }; @@ -776,6 +789,8 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeGetAttackedExplicit, new OpGetAttacked); interpreter.installSegment5 (Compiler::Misc::opcodeGetWeaponDrawn, new OpGetWeaponDrawn); interpreter.installSegment5 (Compiler::Misc::opcodeGetWeaponDrawnExplicit, new OpGetWeaponDrawn); + interpreter.installSegment5 (Compiler::Misc::opcodeGetSpellReadied, new OpGetSpellReadied); + interpreter.installSegment5 (Compiler::Misc::opcodeGetSpellReadiedExplicit, new OpGetSpellReadied); interpreter.installSegment5 (Compiler::Misc::opcodeGetSpellEffects, new OpGetSpellEffects); interpreter.installSegment5 (Compiler::Misc::opcodeGetSpellEffectsExplicit, new OpGetSpellEffects); interpreter.installSegment5 (Compiler::Misc::opcodeGetCurrentTime, new OpGetCurrentTime); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index ceffa4c9a..43dcf0ba4 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -251,6 +251,7 @@ namespace Compiler extensions.registerInstruction ("dropsoulgem", "c", opcodeDropSoulGem, opcodeDropSoulGemExplicit); extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit); extensions.registerFunction ("getweapondrawn", 'l', "", opcodeGetWeaponDrawn, opcodeGetWeaponDrawnExplicit); + extensions.registerFunction ("getspellreadied", 'l', "", opcodeGetSpellReadied, opcodeGetSpellReadiedExplicit); extensions.registerFunction ("getspelleffects", 'l', "c", opcodeGetSpellEffects, opcodeGetSpellEffectsExplicit); extensions.registerFunction ("getcurrenttime", 'f', "", opcodeGetCurrentTime); extensions.registerInstruction ("setdelete", "l", opcodeSetDelete, opcodeSetDeleteExplicit); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index a211f167a..8742ba946 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -205,6 +205,8 @@ namespace Compiler const int opcodeGetAttackedExplicit = 0x20001d4; const int opcodeGetWeaponDrawn = 0x20001d7; const int opcodeGetWeaponDrawnExplicit = 0x20001d8; + const int opcodeGetSpellReadied = 0x2000231; + const int opcodeGetSpellReadiedExplicit = 0x2000232; const int opcodeGetSpellEffects = 0x20001db; const int opcodeGetSpellEffectsExplicit = 0x20001dc; const int opcodeGetCurrentTime = 0x20001dd; From be2ebc5cac8a23b9feb18d6d374597a3c6b39294 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 23:33:14 +0100 Subject: [PATCH 346/889] Closes #1081: Implement disease contraction --- apps/openmw/CMakeLists.txt | 1 + apps/openmw/mwclass/npc.cpp | 4 +++ apps/openmw/mwmechanics/disease.hpp | 53 +++++++++++++++++++++++++++++ apps/openmw/mwworld/actionopen.cpp | 4 +++ 4 files changed, 62 insertions(+) create mode 100644 apps/openmw/mwmechanics/disease.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 57773507f..8c8a1b324 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -74,6 +74,7 @@ add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting + disease ) add_openmw_dir (mwbase diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index d66552373..08947c4c0 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -21,6 +21,7 @@ #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/movement.hpp" #include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/disease.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" @@ -604,6 +605,9 @@ namespace MWClass ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); } + if (!attacker.isEmpty()) + MWMechanics::diseaseContact(ptr, attacker); + if(damage > 0.0f) { // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying diff --git a/apps/openmw/mwmechanics/disease.hpp b/apps/openmw/mwmechanics/disease.hpp new file mode 100644 index 000000000..d3ea825cf --- /dev/null +++ b/apps/openmw/mwmechanics/disease.hpp @@ -0,0 +1,53 @@ +#ifndef OPENMW_MECHANICS_DISEASE_H +#define OPENMW_MECHANICS_DISEASE_H + +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwworld/ptr.hpp" +#include "../mwworld/class.hpp" +#include "../mwmechanics/spells.hpp" +#include "../mwmechanics/creaturestats.hpp" + +namespace MWMechanics +{ + + /// Call when \a actor has got in contact with \a carrier (e.g. hit by him, or loots him) + inline void diseaseContact (MWWorld::Ptr actor, MWWorld::Ptr carrier) + { + if (!carrier.getClass().isActor()) + return; + + float fDiseaseXferChance = + MWBase::Environment::get().getWorld()->getStore().get().find( + "fDiseaseXferChance")->getFloat(); + + Spells& spells = carrier.getClass().getCreatureStats(carrier).getSpells(); + for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it) + { + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(it->first); + if (spell->mData.mType == ESM::Spell::ST_Disease + || spell->mData.mType == ESM::Spell::ST_Blight) + { + float roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (roll < fDiseaseXferChance) + { + // Contracted disease! + actor.getClass().getCreatureStats(actor).getSpells().add(it->first); + + if (actor.getRefData().getHandle() == "player") + { + std::string msg = "sMagicContractDisease"; + msg = MWBase::Environment::get().getWorld()->getStore().get().find(msg)->getString(); + if (msg.find("%s") != std::string::npos) + msg.replace(msg.find("%s"), 2, spell->mName); + MWBase::Environment::get().getWindowManager()->messageBox(msg); + } + } + } + } + } +} + + +#endif diff --git a/apps/openmw/mwworld/actionopen.cpp b/apps/openmw/mwworld/actionopen.cpp index 728b6e32b..e9d8b4716 100644 --- a/apps/openmw/mwworld/actionopen.cpp +++ b/apps/openmw/mwworld/actionopen.cpp @@ -5,6 +5,8 @@ #include "../mwgui/container.hpp" +#include "../mwmechanics/disease.hpp" + #include "class.hpp" #include "containerstore.hpp" @@ -21,6 +23,8 @@ namespace MWWorld if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) return; + MWMechanics::diseaseContact(actor, getTarget()); + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Container); MWBase::Environment::get().getWindowManager()->getContainerWindow()->open(getTarget(), mLoot); } From 09d491d1baeb0d08a94ed4ce6cd74f25c838cf2e Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 4 Jan 2014 01:13:19 +0100 Subject: [PATCH 347/889] Move a piece of functionality to its appropriate place --- apps/openmw/mwgui/bookwindow.cpp | 14 ++++++++++++++ apps/openmw/mwgui/bookwindow.hpp | 1 + apps/openmw/mwinput/inputmanagerimp.cpp | 11 +---------- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index efe089689..c28ef96ef 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -44,6 +44,11 @@ namespace MWGui adjustButton(mNextPageButton); adjustButton(mPrevPageButton); + mLeftPage->setNeedMouseFocus(true); + mLeftPage->eventMouseWheel += MyGUI::newDelegate(this, &BookWindow::onMouseWheel); + mRightPage->setNeedMouseFocus(true); + mRightPage->eventMouseWheel += MyGUI::newDelegate(this, &BookWindow::onMouseWheel); + if (mNextPageButton->getSize().width == 64) { // english button has a 7 pixel wide strip of garbage on its right edge @@ -54,6 +59,14 @@ namespace MWGui center(); } + void BookWindow::onMouseWheel(MyGUI::Widget *_sender, int _rel) + { + if (_rel < 0) + nextPage(); + else + prevPage(); + } + void BookWindow::clearPages() { for (std::vector::iterator it=mPages.begin(); @@ -89,6 +102,7 @@ namespace MWGui parent = mRightPage; MyGUI::Widget* pageWidget = parent->createWidgetReal("", MyGUI::FloatCoord(0.0,0.0,1.0,1.0), MyGUI::Align::Default, "BookPage" + boost::lexical_cast(i)); + pageWidget->setNeedMouseFocus(false); parser.parsePage(*it, pageWidget, mLeftPage->getSize().width); mPages.push_back(pageWidget); ++i; diff --git a/apps/openmw/mwgui/bookwindow.hpp b/apps/openmw/mwgui/bookwindow.hpp index ef87dd9c4..f8821ab50 100644 --- a/apps/openmw/mwgui/bookwindow.hpp +++ b/apps/openmw/mwgui/bookwindow.hpp @@ -25,6 +25,7 @@ namespace MWGui void onPrevPageButtonClicked (MyGUI::Widget* sender); void onCloseButtonClicked (MyGUI::Widget* sender); void onTakeButtonClicked (MyGUI::Widget* sender); + void onMouseWheel(MyGUI::Widget* _sender, int _rel); void updatePages(); void clearPages(); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 9090c9747..da0a349d0 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -19,7 +20,6 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" -#include "../mwgui/bookwindow.hpp" #include "../mwmechanics/creaturestats.hpp" using namespace ICS; @@ -591,15 +591,6 @@ namespace MWInput mMouseWheel = int(arg.z); MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX), int(mMouseY), mMouseWheel); - - //if the player is reading a book and flicking the mouse wheel - if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Book && arg.zrel) - { - if (arg.zrel < 0) - MWBase::Environment::get().getWindowManager()->getBookWindow()->nextPage(); - else - MWBase::Environment::get().getWindowManager()->getBookWindow()->prevPage(); - } } if (mMouseLookEnabled) From 12691040d1a50f84cd23f61840bdb5f4587cb165 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 4 Jan 2014 02:49:10 +0100 Subject: [PATCH 348/889] Fix incorrect disposition testing and get rid of of a related hack that is no longer needed. --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 11 ++++------- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 2 +- apps/openmw/mwdialogue/filter.cpp | 4 +++- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 3951cedcb..b97986562 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -251,7 +251,7 @@ namespace MWDialogue } } - void DialogueManager::executeTopic (const std::string& topic, bool randomResponse) + void DialogueManager::executeTopic (const std::string& topic) { Filter filter (mActor, mChoice, mTalkedTo); @@ -262,12 +262,9 @@ namespace MWDialogue MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); - std::vector infos = filter.list (dialogue, true, true); - - if (!infos.empty()) + const ESM::DialInfo* info = filter.search(dialogue, true); + if (info) { - const ESM::DialInfo* info = infos[randomResponse ? std::rand() % infos.size() : 0]; - parseText (info->mResponse); std::string title; @@ -509,7 +506,7 @@ namespace MWDialogue text = "Bribe"; } - executeTopic (text + (success ? " Success" : " Fail"), true); + executeTopic (text + (success ? " Success" : " Fail")); } int DialogueManager::getTemporaryDispositionChange() const diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 1b7abed45..c9aad1022 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -44,7 +44,7 @@ namespace MWDialogue bool compile (const std::string& cmd,std::vector& code); void executeScript (const std::string& script); - void executeTopic (const std::string& topic, bool randomResponse=false); + void executeTopic (const std::string& topic); public: diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 11dccde42..9d08debff 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -5,6 +5,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/dialoguemanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" @@ -133,7 +134,8 @@ bool MWDialogue::Filter::testDisposition (const ESM::DialInfo& info, bool invert if (isCreature) return true; - int actorDisposition = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor); + int actorDisposition = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor) + + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange(); // For service refusal, the disposition check is inverted. However, a value of 0 still means "always succeed". return invert ? (info.mData.mDisposition == 0 || actorDisposition < info.mData.mDisposition) : (actorDisposition >= info.mData.mDisposition); From 557652112f044e1823fe9bd36d59f24e5f135d30 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 4 Jan 2014 04:21:44 +0100 Subject: [PATCH 349/889] Show the target HP bar also when casting a heal effect (same as MCP) --- apps/openmw/mwmechanics/spellcasting.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 7849eb165..52fb0805a 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -68,6 +68,12 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->getStore().get().find ( effectIt->mEffectID); + // If player is healing someone, show the target's HP bar + if (caster.getRefData().getHandle() == "player" && target != caster + && effectIt->mEffectID == ESM::MagicEffect::RestoreHealth + && target.getClass().isActor()) + MWBase::Environment::get().getWindowManager()->setEnemy(target); + float magnitudeMult = 1; if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful && target.getClass().isActor()) { From 710a1e2f37fa51614b18bf2352cc6ebf722c9f77 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 4 Jan 2014 04:39:06 +0100 Subject: [PATCH 350/889] Smash case for manual reference IDs as well, consistent with references in data files --- apps/openmw/mwgui/inventoryitemmodel.cpp | 2 +- apps/openmw/mwgui/inventorywindow.cpp | 2 +- apps/openmw/mwworld/manualref.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp index 672ea9c16..a16e67a7f 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.cpp +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -72,7 +72,7 @@ void InventoryItemModel::update() // NOTE: Don't show WerewolfRobe objects in the inventory, or allow them to be taken. // Vanilla likely uses a hack like this since there's no other way to prevent it from // being shown or taken. - if(item.getCellRef().mRefID == "WerewolfRobe") + if(item.getCellRef().mRefID == "werewolfrobe") continue; ItemStack newItem (item, this, item.getRefData().getCount()); diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index c14971f6e..70295c4c7 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -425,7 +425,7 @@ namespace MWGui // NOTE: Don't allow users to select WerewolfRobe objects in the inventory. Vanilla // likely uses a hack like this since there's no other way to prevent it from being // taken. - if(item.getCellRef().mRefID == "WerewolfRobe") + if(item.getCellRef().mRefID == "werewolfrobe") return MWWorld::Ptr(); return item; } diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index 1cdcd8484..0b21fd78b 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -64,7 +64,7 @@ namespace MWWorld // initialise ESM::CellRef& cellRef = mPtr.getCellRef(); - cellRef.mRefID = name; + cellRef.mRefID = Misc::StringUtils::lowerCase(name); cellRef.mRefnum = -1; cellRef.mScale = 1; cellRef.mFactIndex = 0; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 083c1cf0e..c23c63e2e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1967,11 +1967,11 @@ namespace MWWorld { InventoryStore &inv = actor.getClass().getInventoryStore(actor); - inv.equip(InventoryStore::Slot_Robe, inv.ContainerStore::add("WerewolfRobe", 1, actor), actor); + inv.equip(InventoryStore::Slot_Robe, inv.ContainerStore::add("werewolfrobe", 1, actor), actor); } else { - actor.getClass().getContainerStore(actor).remove("WerewolfRobe", 1, actor); + actor.getClass().getContainerStore(actor).remove("werewolfrobe", 1, actor); } if(actor.getRefData().getHandle() == "player") From 1b96c5d2668cdde9f3c84caaa61d4b851bfe8960 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 4 Jan 2014 05:13:53 +0100 Subject: [PATCH 351/889] Console improvements: Show scrollbar, allow copying text from the history --- apps/openmw/mwgui/console.cpp | 2 -- apps/openmw/mwinput/inputmanagerimp.cpp | 3 +++ files/mygui/openmw_console.layout | 5 ++++- files/mygui/openmw_console.skin.xml | 13 ------------- files/mygui/openmw_font.xml | 25 +------------------------ 5 files changed, 8 insertions(+), 40 deletions(-) diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index b8d20709d..c15cc7b1d 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -119,8 +119,6 @@ namespace MWGui // Set up the log window mHistory->setOverflowToTheLeft(true); - mHistory->setEditStatic(true); - mHistory->setVisibleVScroll(true); // compiler Compiler::registerExtensions (mExtensions, mConsoleOnlyScripts); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index da0a349d0..8f19fb02e 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -497,6 +497,9 @@ namespace MWInput edit->deleteTextSelection(); } } + } + if (edit && !edit->getEditStatic()) + { if (arg.keysym.sym == SDLK_c && (arg.keysym.mod & SDL_Keymod(KMOD_CTRL))) { std::string text = edit->getTextSelection(); diff --git a/files/mygui/openmw_console.layout b/files/mygui/openmw_console.layout index 7d24a283e..0c9a97d04 100644 --- a/files/mygui/openmw_console.layout +++ b/files/mygui/openmw_console.layout @@ -7,9 +7,12 @@ - + + + + diff --git a/files/mygui/openmw_console.skin.xml b/files/mygui/openmw_console.skin.xml index 219cce39a..470451a0e 100644 --- a/files/mygui/openmw_console.skin.xml +++ b/files/mygui/openmw_console.skin.xml @@ -2,19 +2,6 @@ - - - - - - - - - - - - - diff --git a/files/mygui/openmw_font.xml b/files/mygui/openmw_font.xml index 726bfb281..e4037561d 100644 --- a/files/mygui/openmw_font.xml +++ b/files/mygui/openmw_font.xml @@ -1,31 +1,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - + From af3569665c8ed1abdc7f61afba2b174738cf6492 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 4 Jan 2014 05:19:08 +0100 Subject: [PATCH 352/889] Remove unused font --- files/mygui/CMakeLists.txt | 1 - files/mygui/EBGaramond-Regular.ttf | Bin 405476 -> 0 bytes 2 files changed, 1 deletion(-) delete mode 100644 files/mygui/EBGaramond-Regular.ttf diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 70fdf4248..ef223a617 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -8,7 +8,6 @@ set(MYGUI_FILES black.png core.skin core.xml - EBGaramond-Regular.ttf openmw_alchemy_window.layout openmw_book.layout openmw_box.skin.xml diff --git a/files/mygui/EBGaramond-Regular.ttf b/files/mygui/EBGaramond-Regular.ttf deleted file mode 100644 index 3f6f6c191d9d47870201c2979caf8acbb41a7e63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 405476 zcmeFa2bfhw(l_3H^1XA@+&noaCg))?Fu*VjX&7LD0Z9TPIfD`vR20R;BBHCvqN^;6 zEFvNzqGC>%b6^lrL6=ojgggIV*SRRW>%L*%_y2s)_dMTRx=weWJ{_v7tE#)J8^#%9 zZUBWP_UhHzZ^DunG8p4Ih>h*t(6iSgd}l7gjd0t04;(PqKV|jHj7dK-=FRLqcu4nR ze_$D7fkei7of$AVzvz-%@7&DT=vaI|X8g3VGj|--lM%ig1)Lr~e{Q1O-FJp*X$SB= zZ{o~J(|XqD-Hq_Kj9D(7GY|BHkNd8QvGH@6a=dBs#bYNJCF@r) zwOM>$HW?A#R#!U0s1Hk>JZF7+#BF>#{D0<;GMVq=kHNp6?}z_!{y6*x_+j{8mzFRtEtQrsSz4w%$hfjo*~uj3 zFUnuw-=#bZ{~qO0`1dIX;eS&3n#sxue#u{S$DJ(f1RC7rmv)5!ocI-`7@c;ZA8yRmf53QtAH&6HuM|<*i134Eq&qt zyS(ISV`pE=ywfh7b}5UVbIIb1St6AJfVv4BGd2)3Ai|@m> zDdkBeQl(Tc^^%&U7O71dElrT7NHeAR(&f@h>1yeE=@x0DbeFVM+9vIk_DK7sr=;hl zSEaY452VkeFQk*wchVV|$(n4F-EvTlms8~~a)DeXSIgbyM!CP-DvyxI$P?wM@@#pb zyi~pt6tzQb2h0#qt|I@_1m_TJruY`}9~F?d6C6!3l#-7Se!1?~7|G0`oML_?m@goI zpZu!`?jyKBK)E~l?;_ZXAmzy~Q=UfeZO|I=?Q_B}ZMwjpLjH{+o+k*uG)8^%ykBmj z^xf(0x8%2y|0uyL2(}WeA-Ip=y8=pPp7a%eiQMxntxS$ng{^8urUNA9# zVV2IR9A%dj@DTZH=lD|n`t)jOH1aG96 z%Tx_mDx{c!1RVrz1kE}ND6trD(i)7>2G*BFv;J%pOJ+;)D`M;LD+WJZ4?Zj8Ln+&V zUm4qtUpadYzY2C7zpf-DDtS4tVpaSeeh;fx?pGdQ-IVRhcGg3A5YnIla^NA>6B6JN z)~K9Teq>F`8RZ=7r*tZvY!Gq%7M<&y4b~N1VXe9a5_yO*5WIf|YH1_s!$|JfA#t8$ z4w5x)$dQ+sM|od)pCu?CDW9-JjGB*R|W^!%e%t~9iu;7E8GaUF>n*%rozpJTL`xl?n<~d zaO>dK!)=0t&cW}4+YYx2ZZF&cxI=I+!X1Wt2ks-d4!AGjpmXrkaAzT|B{%~P5?u1a zMZ+b+rNiaG6~R@&)xtHvHDQd0#7JQ?Xf(wTG{@#N@^2#8E+E@Z{&MousAL2=jU%2w zZx<09O={KM1!P~7zlvZ6#q3i*Mf|lC(?rnxDvHKF zLH4$?2WfsJ_^om?%E~4|=sQ2vVB=1P}iT14pR6X4}nHUw|3*g1Ql-Y_D9z zB>52JT6b>cF7D^C=&xj+$@6##uY|vz_u|dGg}3q1d;-E#_)I<@&&%Of@~ipvc-{iH z5$-Oyt$Z8biRT`;{cumgJrDOPe~aOde*pIx|AL>y^E-Y9JuRY{1Ui;%lAHWNDPBsI zx=5g3sSK_ft~(s)SL!c;dZiJRZj3ZhnkvnfK)2A{m~^GIMgq-B>)|%RZIrQy3G^v}KBd!eXJtt?WQXjPqvb?k()@yudG=p_E?>$aYd4s4RX#4rV@P&83e;8g`0O-av4ifYJ(r)KY0J#S@0P`IeyR z=NEhxe%m6_%O?KKwwt(JLit>n{zAWr+b5~qu9Sm={8TPzP1GueU@JjtHR}$Ulg83n z2DHsA)`ew5m(OGQtbi3l?i54PltIE&u&%_ft0A8nSTEL_H9|%;vlcd#4P$MPQR^U^ zo`aNm5c>2FkSAwV4!L2~BXpZ?*B!c3cj=M3NB8PJJ)j5mC_P$_(PQ;EJqgkU+ADv7 zzo@*Tyvh`W#JCCK8INZw`a6XgfD-keA!^T?1jIZ;Xu69r-oc9@Et$}nQU4M^h8i85n(A84b8fa4Y^k@hnrT@G9Vd@{J-!F$9z-26^VAr32U?wDCF25q?045`Rkh z6lHSMMa(=oWax*0X@Wb+&&qj#9jMO)_B?x)y~RFYpRq3><-TKQprmVW_&UCxZ{nNzeSACL#rHzi9^xnv@iO7(_YpMxuaN&5f-wS0R8MaDt0+E6K)I3L=2FaQf++;E2~HsR8o?a`N@o5~ zkiTmuG&#OS_!YA)RG%<@y!6%k1gR{^Y$=r@QM3U=u_%mf=t>x`#moXuQ^TC#E`2ca1#gLm2Hb)a6!p(<+{=ink zT@42vf`4ea9iQF!9kB;&?6Z12nIa@9D;#IFmMS5KEc2#ID1#%hixLbT|iD_ zhaI69qCHMyA>6+vJ(#eG($D(!TXta%yXb8xL34Cuk^e&ar#>~mk__m-3iMYSS}kfW z{`R2m=W8y)zj8Pc659L)d=45>(2N#Zy!hj(<`X_}Kfp3GEHrxY_xHRIVd92({=yH# z>7WgXKa)4&35|%t#3RKEp08p>2oURRJWaVl;E`ad_7eQ1n$UTdXyD)cB*Bn|^(mgF ziIgeYD}YyM^N?n@hP5>Inzj=$F?+PD;IGlv!vBPJ7Vt8WC*P!93;%wC zXH~K0^_*G=cv2Pe%B?Iwnis+l`S3VJ)TdZT9bQ9l7QrP{+j#_sij_Iuq=*v75OgR< zk=qf<^N5&7vTs!{;Bg_z*l7X(6$Y4}@8l0-uJ)AjjPk7VobrP5lJYXeVVXSRN8TY{t&6d^*_ag29{D)5OWo0nU<70uGhYQ|yQW8e`uo4#ZF$*v-l^#7t8`C#a)U>QJ_^3)cxJh;>3P ziFHEW3>^o5SSL)xL#z$Th*7aVr$gtkK(mOzsIxN%b3((2gfesg+kqsT((jD-BDOXXqr2k^wJpV!M{`cB>e#LY;t(fkn71QaoVj2=F zro0!em`?vc(0=cK(Ek4p9)15y`S|~r@(KJ0`J|06nlqk#GJfpTx$N`tW9MAVz8*h* z+Dvw8{QQDK&H#(J23X8(fMwi0etuyg500N-RLJ82i+Jkz`4t7c3t%BHm@xH{NxW>r z^cmB5^~Bj@$Mf!!MNAXGSUzy-^m)_xQ0&c2EU}GMg`+AyO`%F?+&v zzIvwcUpMEH=@a=)bEb`-$v4cIgSy=bSj4vgmh$`O%qc41JLb$AH;3<@GjHY`zE5Pt z4-(~<66{7$15`0Otr(XM=Ak$-)8Lpf37AbVk6=DQNH@e65-cKEOt6BWA5fwZEm8?7 z$T7p122m_j&N!_n?V<73oTS>(FNdfi(>M>Iph?SEu*arZ% zv0kzUdjK0Dh407g3qX=vM-7zC+kw#!XGE-WJ zxyaShI;`|>#yrI2nH+0o)MBY2{KRUvsHfBtehxL;FHH!CSJIA#gqcdE86$^N%0uZ5 zmq&!-XNSY{!{O^GtjM>>Zz<7YRw&9*)`Xweho85EpBv4umAk@WA)!S217WF0&x~EHP`GSsM=T4?hovpNGQlUp2#7dEsZc2eKMW3e4&s zjt}E8Yjik%TsVHZ`OJZ=6XoaM6n^e7p9M>)9Hrp;;tl2d%0Dqvm!AngL*}zW3O_YU zEmc@6_nI*^JHpR{W_@ekF~fD&qX!&JQBD{IMxh~Utzh18IW%w4XPUAZkYf%ZRtzNN z8=*hJpO3jb-l`qqtuz2JHYTe~`APX1F*2*bO2%;b;^{k~PbrvXNLcF->l4_0C7*;j zN(=l=EZGdDy8=#JqLd`S6W1R z3;Y~q3OtB4k74j>v>z?-2)~>jPVs9=;(LbLi!ZUujJ1(JbJ1g(EcRGMoG3-q$1SJ> z&JK7!6<0n-$V_X-olh+=UrFhO{>gz4j+TGSkMN`X7(W5-avJ-J5_T3fvBoO-@&4Bw zVi6KKRKuAoG!l5zlL-C0c|_vd|9T#;W54`-OU}1j^vn5n|7W$(w0=l!wp!D~2tHr7 zruF!{kf7#Y+kGLPMf=Z}hiwW2`7uo!{A+B~X6dB*nfir#QvFUngMp`MHqEUCwRkO6 z>!KBCWm>h?U2D|(YpvP{ZHzWio2t#$7HUhiE44M+I&HnSN!zU5r)}4EX?wK;+9B;l z?XdQa_L0`1eW`t`oz~9kl5Sw-&x@75L_J;4(Tns7y;g6~oAiPDP`zCrr%%?W>vQ$R z`f`1>ew}`kzCpiJ-=g2I@6dPa`}BkQv-->W8~S_tC;I35*ZL{_M?GXHhShKxek0aM zHZqMoqr|8*>WyATGiDxb#%N=LF~yi^%r`DKRvK3u*BiGO8;!e+t;RNEr?JP_Z#-o@ zZ@g-}Wqe?KW_)3sG`=&=SeQk#*eq^K&=PM+wREu*SjsHbmhP5DOMgqNWrSsnWuj%O zWwvFZWvS&#%Nol%%X-Tu%Vx`cmhF~Zmc5n(mP3{oEr%`dSU$3RZaHZ=Z3$U5tHbKI z##_^^dDb#(t+m10WF2T7YHhcUvre{7x6ZdNwO(mmV_j!mZ{1|wY`xFA!@9@1-};pG zdF!jzx2zvnKeK*eJ!$>UdM1KJXc4vucSJBEJ|Z=uOGH6LSwwY2_lU-b{t>MaBO=B` zOpKTsF*{;m#L|c>Bi2N$i&!7ADPl{+wuoI3`yviTJR9+9#5)lmMRY`b8F4D&$B2+k zu~}^{o8J~|OSN^e71+vb)wb@oM%zH!P+PlgoNcmgx^1p)v2D3+we33FO|}iTJ8fHR z_uF>b_Sz2E4%uF`9k#t=`^eT|`_lHU?X)dq*X$0v-yUmEwrAS&>}B>^dxO2nKF~hY z-fkafpKPCQpKD)iUv6J*zs`P>eS`f@`&RpQ`!4%l`vLnQ`-}F&_IK9sM1xjuDP=jwz1Wj>V3Zjx~;(92*^XIkq~s zId(esIQBc9ay;)i?0C=diQ{v}*N#(;9~~j5=5#py&Uk0KGtXJ#taR2pdpVn(EzUOQ zXy*jy6z5FmeCOrPmCiNJo17b+o1OPLw>x(^_c{+a4>@0S9(KOt{K(ni{L=ZY^R)A< zOL7@5hs*1Vb|t#fT{*5ISB0zA)!=G!4Rj54wY$c-CcCD)=DHTUR=U=>ZgOpOZFb%7 z+UeTsI_P@db=dWu>oeDvu2Zfvky4~J(j6HcnHZTKnG;zQSrJ(q*(yCCO zy3^e`?jm=EyVl*{ZgLNF4|TV@$GIoFr@QC67rU3cSG%us-{julzSF(MeZPB$d$)U^ z`=I+-_si}#-0!(RbARbReGEc3im#5j&;u+x?=b7S}?OEtq z>RIi%-m~6wr)P`je$P(NUe7_#^Pa<=_dK6?KKGpToc4sgn%Cj=dt<%H-Y(uEZ>6`p zx5?Y$ZS#)yPV`Rq&i5|$uJ&HTCB+@J;p2^ATvu&bPsLm+wB`4&NT%e&18R z=Y6mG-tv9m`^@)+@1*ZL-x)v5VEi_}*B|Ro_ZRpp{SE&9{x<&@|78Dc|K(JP8v;_elBnS&N_+gs{_y}Mx{sLelrEH{>jg+#H(j2Gw z;}n0K;*V4OAnevktcBjT(AySz+Y*i!`M1&AHhSAeZ`O$W((YIab+a~(93)Zipqw*&JvnhRF{lHgc#A$EglEl+SU>Cx^;yrgC#A&s>oM>`6pV&U-@{Hd@y661A0 zV5%ZUOao?3V%&)MMvBj*_(qD)q4;Ks&!PBciZ7!07K$&T_!f#UrT8HfUrO;qD87Q? z+bF(*;@c>`jHq%b!E%Db2xb$8`VxkKRjQ}p6PZMVji5nbD~$b?F!sS$P3(!)d893( z?^-}5BCXjoE#aQ2rk-gRST+|dgl#lwV?epYDrI`_cZLUNvtOq zvLy{WZo&_1W+9`{&$5sQ)YBRCO>g=pgTCoa-(=7?z449Uv_h`+rV*W?yhZ+(kq5Z0 zBIvvkyBvbfMOaXm2p>hNOiJ6xMZaV!?*caR*C^d981qPnItVVEMRn*yb?8DEZ=$h~ zP4RsxKAY;+m+F>Hvac^mmTan9U#eTS@-}ko%SCN-sJvz>4|0~;44Fpt$|FkePwmg6 zeEUNM#gK z{2+=ir1(MMc+p3LD9=L5a}e?r6!{F{v<1{Ec#Q}@hbO{lzX+Rs&=T$g5f*(Q!lDm~ zsZN8bPQ_IAU@E(m{6kDX)nf>?wKUvTgs82h)Yc(XuQK{(D1B2#-wdU1%COfZ>N*rp zv7ZEK5DtHBhLI<|N1kGImQy~%Oh4r_jBrp+WeuaU$_WR<2(J~&>-egT3%#L&`n3)H zD*S>L+XzpPF4XR}FrFY`2v2Rw7epsrN%{;Y>C=_U98P6c5_cRyI!qZjP`QMCzZZ!pl?%Q?t!F{*?@gRK_4RgVK{8co~Ok50`cBx z3BMOKEz%zoVf4*YB8T>fMNX3w|ItQzQCA@C~yKjo~(kuxNt_n|#G=1Hx3McrVHn??oFj!)-t* zgy$S8=Qx#OTL_2Hx2er7;qpaTlrO@fe1xgZ2vhmuy(nM27v&>g z!Xa`N<#W>0c~?P4@MGQpKQwd>OI`A#{fI~V5fA+u@!-|)3yhE-G;R7t9os}ZIMF7r zqCAOa;TL&UQl3@eG$PL`%9H36evwZ#(Re%2cs1dm-HazZwG*DIsXyB3+Yt6_Bp$*l zpUgv8?-FaCA%lmX%#g!j^QrRJDXifMxk%rBOy7PS{`TYWw<0XQ5n=Obe*3XO-%41S z6H-xpD@o?Jcv86(#y1p}QO*%6=LnT^BwWssa5*9@@)u#Gl@!WXp?sBazDhV>5f=H1 zu=%7qA#AqiD3x=R$~hV?=V-Vb5fq!sXh+<%+N?Pf$50sGJkw za!!QH5n++P2n#&ADPK3`>kjAZ4(BVvB3}_UpJsd9;r5)O5>AD47Eh7$sc?<`l(V05 z_J?!!hjSKTk)sHkPcvtKIOo&Ug42}$Y4cltI$Vwji*iKRe43a#9sV|?hXkdQZY2r- zFFKjnLa_y=i^|fKyO9e zOrNNo=@Zl^MoK>AC7vQLJi~bjpP82#Cvq_3&il-~L>%RXQ9$*gC-M?cGcWQ{Ug0?8 z5RN>yppII+|xL)Fkyd*rs^%6d_ zUS^!AjTv{|XVy!^ne{pyu9tWsFZBEQdI_IdFEb7~&|9-!BF?Or@QJ(x|AYJj{EOL_ zCZ%Afh#hou6ljT+P`A@JF)|;?9_r5S`;GS^;Vmbq7S+O*xC%ipcr=ti>VPC^>+6kwUZ(GZ-r66qRy{ zN;yTPm~)tKkXGbP`Op(JG)gE}^r}cHdK@{4kf^<%@?-WUU4y+%C%ci|ihcYI*q4jO3DJ95BKsrkrn1>=!KKFprM z?*H@bL)Mdh0;}WU>}&Q7&SU-+wo((>&z!TVT;Ucr2Uf^&Y!Od{{oS=V4>5#o=EHb9 z+sQBDm#{s28cxSNiF15!;N|2SqeyT+$t@Tqj@CG-7VmMk{2l#NjmEK z7MrZDRW%76>dopcYQ5S`?XLDv8`Pd^FSWPYsP<8B zRoAP(Q#Yu$sTYeKE)w|TY)y?WX>K646>Q?n$^*;5F>iy~i>NfRH>UQJIfmb*K6lb(i{(x?6o%-J?FD?o}UE_obktNc~v-MEw*wh*eMn z`@dt+0~6UK*nG~xZwy`A0EG4YvtMD0+Is-LUJ zu)}s-{Zjo({aQVNy|!=EZ`IN2Md}!JtU68|uTD@eRwt^H)W52y)W2aT?tAsL`h)tT z`jdJ_{aHP$o>N0AjE*&~Nt&!Fnu>io9Rk;)S+xl4*x5CQ=G0tTB=+t+npg8_el37q zyeKVNi_v1WIPB*oXo*^qmaL^}l*D|zBEem^m*;G+SS@M*wwsNyG~oHU9a7M{mmP-o3xv?TeMrT)A>7XgLWHo zxcq;BiH+Lr*fG6ByHoqUc9(XywpqJJ+oJtJ+p68G-KRFGebr{QpW0s?pbk_AsV(YY z?T_01+5_4)?N8Wa{j;`1dr;e{{RO+N4{5u#hqXQ0BiMI+RNJRLrtQ}r#}4ch+Cl9} z?J4bP?8QE#J*z#ZJ+HlhUD=nkm$g^4SGCu$Kl{4&hW4iRmi9JwYTwn~)85xU&_2YT z?Z?_D+NauQ+7ayD9@ReAj%i3R$dSXcDi-4nTpaQ6DObVTH3Gb&kFeCh8W0Uw<)L zorEt?o+?g=pU+o>`(6;nS~l~llhsSqDe9%_RCSsN0h?xs0UtORsR2Qj>kse>8q**W{ zyBv9izy7V1|4G>*kN>Fb-)IfW<|t_uVb;wwqtqxf%8d%6t0Al`G${l2SzTe9HC$Ri z{UWRf97rwj&0!P6VH5Vxr-X9R{$I}tmHg&Op`Oe~>fc-^K1`_pmMO50H`fvj^CpASEA!75gsOxb4NA z1^Z#?b`X~AhuE`_moKuHVaa}&y#YD)4y@lkfUWT->@!#!e-0`7C8X#{$k4yB?{Oc) zPwZ!Qj&qIoRN_ayQpp3GBmjy@B%?m0MrSilQ; zF)xK3`a+=k)i*@h`Z3@Cymkxd#C@`yZ^WKZUuVn!rn~}b4OT_ z;SIxSKF%bZt=ovFrrZr^QiP`5Lw?cg8Z=xHUjhCAj$+gp!eYw&>Q`QC+IZnc4*bO0 zj@z_VV+|Oq93$>FSfz^*7f)j@5u@%8usyvOzcdwz6BdLL%LQNg#~SB~ zl*&TDG}N^hYXk@Sd%n{K{rEiBxf9y-W9&7Yp#PT3sGpMucsB057>zS{8-5vwqBKo@ zGMqD6AR66`9jiottg?#r61`L})5~F>)m87ISLxMy4J@_l^m@IUz5*6oJ@sCC zZ@p3P1KX{>db8e7@2?Ml71toWMIWrU>O)}9HB4{QhwCHsk@_fow0@C3Mjs0cukrc> z{bGHhJ_)v7m*`XUOZBPxG+2Gj&}Zti^x66x*niE_=j#jfh58~`f?cLxt}oG->dRmg zwo<=BzfzCE%>fqJd_@>G!)`cm!elV)zakB{;lX`0K3IYUjGz%^TnyWQTL_PxY$-HK<0Z(Q1qutH!DEYJ!?*^iz}6WHm)iRnydTHN%)_Oftq9<6%AK zHlmFfSeL~a@kWA?Xe1fQuwrZ1SLyLaijiuh8R>|ORgW`!SPEFED-*)h=hSL_7lhTpPNII(pa zw+x+OXITiuCgai{om(&l>=*@++{1ks3sF3V$MFP=*%Y3}Gk6w8M=sCD4KXD+!&)xv zKKX+%W5?;%)jy{dRqmeg`ZPf3M%A->q-f?}2UNAM~yIz50FnA7Q2VfWA%t zlfGU5Gwc-~)OYHC(Rb+&!E*6oeUJW#zE^(~HjIzy`}N251NsxNW_(hAN`G2Eq(1|@ z#^?0s^%wLP^_O7b_=^6j{+fPRe;u}tZ|ZO9Z|m>q@51Wwef* z(L3~`u!KCOf1w}Oztq2iP2>svr2dWmt^QY7NB&L!PXAs%t^WW!$)EHy`p^1V;}Tdy zUTRD=rWw_uV}dNBTkCGv}~Nj`)-f1cwn;ohHDa3{iRI03LL+mho!av2ih$A?)eH1q% z9m9PQ$8k#HE8LNEf}iBy;3W57Vb%OM+#vBiY@2`JKhpWUr++zd_dNCnU!-$-ufUf4 zHRUkbmB0C`O*t&izY#hj>|ygk?~T|q`8zvyMOZ}%n{GwHiWm7LNJSx4pT)@*7v$q= zaFhLzw+_r^;&CHD4t9|aK(6+I9na(RY!*-D1Z!6IBaZ>fV}Q-dKEOUW8{>j@nP`58 zea^o!8IpZ~f*oHxo0TU-smdw9p~`NI#B$t0Bl6e<*o+$3Fpr9b=8*ZHm4)(!to;Gs z37)HfcbPtw^+B#D!q)y`o+z|o1$TI1haEm~!p77RyoibBHz7vc5g^`_Af^^HDo)D$ z)@kIvz({}m5}-qvXOc(aY|SYAx?sk#6z6}$eP`mHo;z7LaPNDN?mo;pTEVGzVP>-j z{CNzp`wE*t^NjhJX}pD*&O7)m#(d)w%y>S-Z#m{19hmWaf!|7S=f7g+^F4m6(B2== zLqFkn6=oo3F%Rm*dfpn$LhNiUW*W3}%h_68q|TPNPcJt))I+k6I}&)(;Y_+oa1FTve8NBIiAk{#o# z_-b~XU&Gh1ulQPiJv+f~#!Wik@Z0#^>~EOO>||%@F1d5S{|T;O_Hv4=Vg|!C%wIy> zz^nx)C@^oaa2sYUPVRz*_&F|m;{W0O{{-I6k>JOOh~>9PpV3$lGsyop7XD9;h4TM^ z2VHn0!br?&1oz1y?vqR0r-Zmq4{)FD;8B9_j3vG^k@(IO;yafU-&sa{XF2%JQQ|en zv7RC{!L{HtXOWBGEE|Zk+)kY355!p>AkOk9;w+C6XW37j<$2mcc!_w*hs0Bk5KrkK zp7J^IlrM>=oFbm`9bXQfa+-L`PsCGxCZ2MRcuJ?>DY&X1ZUw6!CAOdXn5RD zoFzz{C5kvp3~`ot;w%ZoSyI4RLOcVUrS!jq%~tSd6Pvk&%{;8%9_@rj2jS62 zcnlC8gM`Ou!ebop=;ZO*X&r|N!_rbnQganRtPcqOAf#>+=G=p^V)u{pZgI<|nMQ+5 zcS3IFvU}Z#-fcs{_OtKj}`i;spA38bT`K#ff>b zeql#A89xK^q#mc)dg5n;+#i59qfx$_U5X#1B-RN1xS3-k+O!G#Xi@0lyRe3F59D<$ z=2-V5j|cHfp?Os*E^)_Vx`}k#JX7wRP;>Y;qVutl8 zG>9Yk_pl+jfov}uiu;51v0=DB=w;T1 zTRLB5!*Lf`2OEK#gg$2@r7xu~S-W%ww`z^TeVsNoTK36)c99$<$FZ?;lAOdQ$|-UN znnKkE%#>gJW3wLE|)KsFJ?>ROXN$~ zQu$K(QnpN0!}4aAy)9Tua4l3TE{;9-M_o?D5YhtP`PP^>Vduwg{oZCGi@V8gN6ki|w| zy&;#4#F|4sYsb1n5gUcIhY~g#^k2pOc%uIaME@5P{ZAzNpG5RO zndtu#qW>vG|CbW|PbK=FM)W_O=zj*$|4gF)Sw#P{iT>vh{m&)(pGWjRpXh%9(f>lC z|3yUqi;4a(Bl^Fb=zl5E{}n|4R}%eSL-fCf=>J-x|LchU*Ao3-PxOBS(f>N4{~L+^ zZzB4?ndtu(qW@co{?`-z|BmQ?1JVC&ME@I!{%c(p0YU$`Wsz1x!Kh@M`V`%NA+L5Z6}K#Dx{mXp>OYJNu`6-m z$<^)%OW_mQul`%B2LH|)-2Y^yK}eMU8+M)kf7o+EUx~X9&i5F@>OeDJC*(5rO=G|i z1LjlS&&I(lQj(Y|c2V)SoUwZcGH4Ouzkw5bPJrA_40WonoWot|dV*94MjF`-@8ue` z0gptwwLMq3!>K3e;J|MI4y;RlFU3d+ zQn6HyTk!Tv&q&WnAIbqa6QiuV+*4jHuaU2l@0MSd-%u`!dp-V+_@Cl`PVgnfCGDb|vgfcq-xbg!dD^N%$@yl;}^4PK-}XN=#2INvuq)No+{$lejG@ zAt@z2Dr3^UgYF%C@34D^-#haY%O|!^9G}F0s(xzu>YlIeJ^A#>=fC^uXD!r;+jd0z z#Ja&&)Ob61`{VpBtg1eYmD3+_duV#N#)nYjcVOIL#AjjA10HL7w{YU*{V*QTyXU7B)R%I|`m!I1A$--pgWTmGnu+X?<} z`_m+=s4^=4k98L-TIS#RXE^vz5A#czG!Kn*<=?tp>Nji{{^_2nf4n2~Ki~bYh0b1& z)v)fY$G^2NGMZfky?rd&By0heVWui<;BEo0-2m-<6Lw4Pz)s2C7&T(2vK>1mJE1ROk3?C5*@C#SZxY}f%m6NBm%ulRO$HPYeC@yfvoO zmnrgEDNUUWO*WUs*T>{y<+*}+qiOUp`7 zXr5G>_FJ(Td_hKOY1|wms5>}Eqe7=LO3Qu?%Un5~mYm&fLMY@(%J*dE*?i?iId;cy z#0h*sZf$~u>VvuMqP{!41Ka8|Q8mscIwB<1YQUsg)rPs8Hd#^l0J|)4?x?eH9ar4e z$JD2rNkudzQMuA~;dkML!|UUknwt7G^=t0ir+1HT^|h51B}Lxkw4~Iu6n}E0FB|{m zLU9qaCC$l`%7x$Wz)l<JZ_gduKJdZ0yJKof+Xp`1TGXecr>sCFnV&9u%woCm z(`8v>d)}Cl5=`M0=_$d~RiS6OxbO;8+pE(i2h?qthjha_dQp9T4;}cbDhd1HN=jAAfxOaJ zZ+7t9=or~?ao@_+nAkv!(=oUvWABJ(?oH`2sAXJyU4O-4L|aoe<&~S`bDC~9Z=YKH%OGl~%dcniP{)*mY2LQC?3Qe|lr%%$q(qdpfkjzM`d} z2cN)Ote5n3LVvDVJ_)pwQC7zR;TjVfd{>|oy=F`&RgENNhCR3B#LP@k|> z+J|f;YkDt}!<}#U0`mcT_ z*AE|FADfZxbW~TCmE>lp7iJWuCdbG6JdP-5RC=<_k!|6EXO#0=2|Xec1)H=fd;P^l zWu+zQDajiD7xBuwXYa}`^r>DsJtnCn7;URsu=9bX3+~=pG_kSI#nYP_#+(~VF`MuG zB}Td7=T%*sqLr+aw6ubASFfJB^3m;!X51D!);M8yzlNccs+d$$x>|$MJT^U4DY)m0?9I%3j16Y(nVsrEa}4(F3XNO z+_=MRb+GE-xTFWeS9FRS$y0tiITEq~!h9WTRwAijJ^ziIa(_g)xvkBt{59-~kytJe+DquBm8n&;+maPiWlL4-7 z5=Oci;UPMp zj-GXUXQ`!$3$ZrS@L&^6+7$xZv*a$|s^-ePb(nR3>_+iTkJ^Pk9XFX)FuY`-EiYwTR_H#epQv zO_3bgp0LDp=!C{nU@A|_n37Ri5vSTBytbevz=hz1DgbLnl9Q2{c^PGiT|K27;xfC5 zgr(r$oVDY^tv2;Jtc#YJe8~p(&4{{PpzeCNkbWFXis4wsdkn9);;up;!-9@%%jS^i z4lYXbdELar!5c)6K)-O;^71k%uFBKJIcR!&&W?S-`rU2zv3GOzade66}tH72)_Aj$8~A ziN!r%yhnOkUVe5@&7IA?f~1R!0zxA|*V2=4nI|JGnY~>Cc`1elRHqUl5Ag%bEiW@w z3jV2AE3BM8V$|sREK997GCH@LO~&Glp~=3K$h7jf!hxA-se$y|E?F@p*@oKNZrE&^ z?9KHB{K{+TV>2CYMb3SObVPA^zhrl!2M05FPGz3hCu!%CrB|eE z3|Px<6h6ww?b*DHdR5hg>QXM<5S^Zq6soT*%}kZDrI^Hw8lI*)<5K%60*{?P;{U!m zi5vK$>iy>j9;}h;Im0>|{=c~MwG>hq*&XlFt#47+#RO7mw$AT6H%m{d zn=m-i`tFDVw~r7#GzsIwE`~K-3pi0!ZunoalEG9=k&Y1p-5q2l?K`(As?cGRt-4g{ zas;D1Q4#9UGpo9K5)&iUtn}Dq3%VS#v9=d3MgCXAeHUc*&#VHx8Jy zQ=U~fW&NO~pRQT@+PrBmED3d6wD<7JerpH6foh;-37u!9fIJSX=vAhicS5HU5*xN? zru3~3s3gK6yUcpw6}=h$a8goUQc{K>xk$(kbQJK4st|U?aEmlW5%7Ycc3u|oSsCfW zdj;1zz1IcOl1HRxW~TS=6}-afU4r1i^b~1Ds6D%1M08A+%@&M|iH_)(&9@*T5Kj^D zfe2x}($aYrE5b7{qb{v4f_9Am=0>mz%vRKKz@RdKM6JeE^*l1s+(}IQxYeyI6Jm89O>DslH-i~CS*HT=txW%U7-(<*+n_GSCu{#0mei9QxkeZx(w~aiGljwOvjlgU6}wY z9!)_zVJ4_b?aqgTe~6ennx z`J#=%@qjrzw7WrX$H7w8A3Bo@BF^MHtcX!OB$?-Nw3-B75}4e`10;X=tnbE%vBV zu*;KGpHAzo8lRvFh6yAXsa|!o`7A? z*!n2ZV_@kMFxoE`Rc7@`2~mMa8EmiIh_w^KIVernFt$){(U4zQ7$}Q-;~}7g!~pAX zaOvBu;#V)F=Os{Y<#!SjH=lU#f@psZLWlAb#o`uZL#S1*lG{-G7L(5f ze;GCc`B;jlr67j{CLe#{E68EI>?_uZL>rj|1O+!rgwF4(kjt zM#?!&=8}?yJ`!e!aBcJr#T$4 z2_Cu46R%kfEB><}w-KeEt@hT&B-m=)L*(2Rx7VJZ5S18Aa*a<;C=xeV%dCIrw^B&C z0`sL9tTRHQOZQZM<6jnC%?8R$G)|B-Qcq;YM^4jkfWv5Nt)OPXO*x2|v z>q_(cExUQZlnv18i`?BU+-SPIrF`&`k{&&K;ef>@lWGEafik_cu^Z&V zdRSq{B^LT_duY|`Ypk?+6QimM zO#EW@+&ue{@u9Ah*X+Nue(~@H;{SEPeIT8ud8=FlJte@p)MsGvQE0a^rplnyu;{nj za7r)I7O)3Q)deGo6s&L~JQpky94)9DRFU5>xH9}a*<4)O&?CR1zciq^xJ3B+p;*En zbUEpD=yGX7%f7H&F;zKVJap@rXwH%nqSIp1O!e-3>4KOAyZlAHyRaVrtm=&~s71|s z4E`4?A3r8)+7PZE$LSNV(pCNx6kObQM>cdXI~Y0!CMrkk0QDY((GV!W;b;ldm4}l2 zk?B|_wQ!mV3l)T?Lsz8fuMphe>^vZyh>CR>5E5r^Y|V_7m%-=Fj5(KxRkzcjZMJ~{ z1DW5Jp4{EWZiv^=`!e~F)Y762lrz`#)^4}=qq#D1iWF|3#>>31S)KTX?YM9jOv$Y!Koff3WjlWejTvb#FEa#Y8voVmM&j& z&Jz{qumCCYjQFz5*t09(wb(LarH^%>oVN-Dxuwt~N3s#w%@&#@+bqBlZw}*#@wZiB zZG3xpnAF48EFY|SqcOjP0=tbJFg^$r)4C_ARQ!Gh-M+=pmy=M6XN|B zrD%b~NMB+~aJav`I45d+X`j@QEBYi|+t9VBcT;&`gLJGHrAur^!iI7zp#330}nxmacu&# zU~$l5Ou*RGRU;cm>tC;rek$`(7}#>e{a4n4o`vRAqF*57gaJ&O*6 zo*BJ*c#l9vAUeyDA@y94jlUI>NBsWM+8A4gRhC;?LVqpeRU4LG*RlMHPIlk#LuYPk zt@Fir`VFYMi)ZZlJ(so&TOSJDI(N@SQjaY+OdMfzc%qwLHzK>%0QXs)XVnZ?4K;%{ zTKn!u1QiFdeS_H_J#bU=<+z{(@J->-`FA8PK;+ZFOpLQbK&J z#|4A>X6UtPVWJib7Cb$l3-!*4;ZNF=U<~KqTQOZzXyzX;2QVT6elc}1Rlu?gIbB!s z`|a=rvJ)C?DG9PtWkiMU4_f-$-I?j>y}ir5v2FJF^rVKM%Vn{u7R4s>t`?VJvFL$X z+3IpyBX#a9sO#NQ<-6kC%@IoUnRwl~1ZTBu9+$%tP+ghHo`X?=k7EiVtO>3Zo6Q}G zdx3GJ3zruJtcezRoIf9&i%Zpx#JFxV`{57AxYW4y<7q(qi~|k)p8Xrd|zVgC*9BdqYcfp}2@)X3lO*EYZLuDmCYr zQ2irI@(iJ77ORn1Wg*2TS%W%#-tXfhJo}1QK0B@rvle6V?R~G@KH0y~(`8z-;?GNJ zu#Hg^yAiM@xpE?s8hMY}F`JgPSEme$pRsK9ZMDkYh?Uc)-4Ula@IL|Gx}bgC$ULj6 zN?tnpzFm**}d)KSlCoc?~T~yy=ThVKuPaRLqMF zffPXyKnh3#Izmb^6FL!eR7L2ocpvy=z0GtXN@;9knZyyTW~1!&EOjk zFDM#ZQI%Znjfl2;TcQ)vY7Y!u`SY`jU*IjuhZ$YC`ayI;ZI8Nvp%31e_x3$wqmsK9 zkBW^^{8S%~@SdS&2K+Ww%*z;53uSK)?y4@#H;J36Bs6Jd}&=EWEl(4~ZMmLR8xVN*ySz;vC40rj~} zgQzSuKZEA@-}!EMy>rIY$rHzpT%A=~k(FL(^+1@3DWg~&6)OpXB@@*M)l$j^kH$RH zoK%LF8O?zPZvHO@SQ5ztp~je`C-U&|eFZxb@&iFV+G`D1B)i>kxvUP!VvqB_9p`K8 z^>J>GoQx>1%=KhPTur1sGRk}I%+lW;eNuDbZd-w-+LPQ_4ts0Q?=qsKck6Jkxfbut z(#Ax(M*M%oy?K~jRh2h3c(x|9_D1(ZA+KP&_qTR2}%cpiX?WbtB+CH}Jt3qzx-`e|}J5*H|`k${K zp0;w&-skMS_S$Q&;kVW{xf5ouGa%a>R!`&?n~yqzt&KH0+39Ude>xnnz17|{M|{_L z+8e4O=?Z?U4#}tAW4-oL^q8VO2)SdSP+3Pth)C8U970Gq$SYFkuz2P{qd`)_$2Ghy z7WHi#&*2ym@Rhv_J7(684)^u6HP=eMj$^sv<_dMWE(kZ(YEfR^Euk!q4kW=o*mdD$?ODji3E==g zaZ?4l;;_}o=)mZQk(p_Lhm9mD{>}Ccgei6ku|14urs*W*ZL1G<>`UOnTqLaNU2dDd zw&Mryx%rior+<0J#;4b&EH(BDeaPv5C6zqotFCkhR)<6RslR#FvEVYMt`Ak;Gj>ZNR3DvlC$%Eb zf_8JmCbKSv2k#lXj~?yNfZMx~A`5s@H!1rmsOf!C)SuN9sh#DC8h2!~?(pk)}N0{fKat{U#h2CnY<;a1tK3m7w&4kPi;J)D2cb&j~jXF!-& ze)at5eJ`JCO`OQ3Yu5TB>p%9e{C!U}=yc+#4eDQE`7K(r-U>}RqO9-lwJFU1TH!Sg zLWsePgO^P^i>(JYI*xx_=RF$s-iTaTJgu}k*2-PRPI=`c{ zzp7*7B`0g%lX6#0_TK%WP)BQZoj)!`*1EmEcf{po(!vSr{1=^?W9A@VRi9<>s$Ja+ z0bj@36Gs-3t5I;G`*Kf9XH##iNv@t4OFpo|MqhDDly9Z{pMuU=F*3sC0C0z2hW$Cj z_X5(m5Mu`7f_4a@g`{b4J#(j2DBD56Diwd9duE{X*6J482wY##G9!}IK4uYIOtCA< zZio@_c_?kMj+#iND`JGwX7_g3tZry+|Gu);HeT2%Elx;UgIN|Mn~qp3BOQqmm($x( zlm3eDtu-?)P-UCe!i6Si5xVfE?hJIZQ^FHZw~87Qoo!^0X9N@7bxtuRioP#U?9Vu5 zF-u0HdC;Io&_l7$IR$H%G#4xuLQ_&7v$(Vi+-J*PZZgUftP?c{h^F`j+5wjzJ#zTK zzWF)lP$ZeHcMkcjzypHSOBAuN- zS)bFAt+Jy^W$)E0g9qXDvKvTzlEAB5u`QuXaDrF{c~`@f(kIuThY|&sPWcCP;Fdxc`iI(q7+}xt z?Q^5Ut5*#THq>R3U;q+<7nf*_BI%Y;A3f24%VDr;ttBU%#-cN`z3O@M<@TN4 zz)+9$U~>msGYQzy*R}QV3E1IrEyWH%gelkoSo%<55TF8t*)G~y6ofp7QbECnzD+I- zGUef3MBF2Po2YsCf&4EYdnW(tV_y(^@_+6>^0@=E@7q|@vG-&1N1ooRdGXWv-`@3j z{vV$E8!`6m*TnGM=YMkK$4?xYz3cJa`@i|n-m^bcbS?QenwNob-@$$O(E=U~;pS*VC#_krEvCbched>+;@2}YZ z#ar*0yL;r**}eI1+AjTR{`SkB`)mFMZ{3>h*NzR{(L2)7d-zlPHhpk+hvt<>^8fMB z$MgAFTb09Owp#RcU(B{Qt&OHE8mm9sJJTM{)<$+dysz7AOL)7&Yn`=E=-aP(Rb0IF z|9t+E^WU7f``oE*t<$|DU%BDf%a1H3}j*^4^A405FVc(DdF^JU4V_LQ6`GLm%({p5ZcyUZSkT^a#pJ%T z)sg&{dmqpL{Pv(8P{~ldwf^sZH9VMod}W4x$sG+qrM5DaIxbhlhfj`w`br?o40@_I zDnh>eTJ~WN_X|Ba_*cngL&pJ}Wei(Wa2IH`YCsv;>Maeh5nK+l5lPwoj9EddA~Kx_ zNN95{k&mCxYDkKyt=OEI6zR6&Pnxby6@lmtCnk0(AagO+(0=9fhZ=86DPVNhm0PA4 zHsrs*?VmI+$194^^YJ6=R4Dm>0fD~wiP^AA0iX9woZUIR`pvP+Kl-njD&{%{pCV;4 zZxSAyc#?3#mE>4u^CCqCITXMJND_1(C9i?&Jz+scJ7|xH5UtdPPDfZ&N*lZZ%@Atb zs=g%`We*V>+6tJTq($%*VYZSMWB{0*hg)h=9W|+DBh;QY@&>AbGm>S@i9%YxM2-OF zgd{~&^+JL>V~lGrA}FO;?61kVOBeK5U2c=cV>h~^ws2=uFTK>#7c=-=W~axfF}XY) z@ofH^R!gahPdX6u?3;3tn7J=h>8x0raIG>_R9XW@S+8-rv`(YXrt1oZ4;g|LJ9=uo zZ{}CzMp^^m!XLFZ=pZA)DAy}II&im!!TL!zz(Ns>Kj%l957lFXYlYN^;v-n>gZ+KI zJzaG}^$9&PW}F@Y(op8k5$r zK@RdELE0(8&98xV4m%QTr2{=s44dcyxi^vk5k&lvxL_1`3uRuX=D^TD_rQ)C{H)xmhRwc>? zVy~vs_F!QrzR^cJ_@c5gDBXm)I>oOiG%jO>?olo8)?Rpo+X%xQDl z^#;3EE-ZlkKIt%2_)W$m5o*w$XaGQ+8{PSprFeA<0%I|I43Z33T3$ zYVoDR+bktd*qVuzA1+%tgQ3#u(`wzd;f&3ou3oM9n4_<%-d`c>!!DP>K$f@OS6iQc z{%rTE_8_e*#cW>CJSXg!iAsgT-aTl{BnBfOo zk6bCV{P1P4STN2j*ayJ6P$(I&d^5Z(OChf8S*Kg3C}Ft>!9Cno-R+kR#Y2g@u;YZ? zvDXox;q={psQz`2aNtQvU0ga{PtvI-=swxu^sQesRfi$09VbXw*C+Pw zYCYF3no6T3syF(kJLzKGP5g$V{9aYk?KS?I1uCp^ec`MWaD+ zT1k$X{C^FlLgQia&8TZ3|JxG{mhO}x>NRA|hdRX*AyJo-bsm@BIC=h?8lT0ydX3n- z+<}$N*XT^$`pllJ^nC!pP=PlUxVkDUtngW?P2^Z+Ca-=zo~XAnYt1)v|S zj7M8e4>1N#>40;DjN~-vLNbfMX-+^*PURm5L4H=B-~(BDoS?o1Pe?8K8T;C;%2DoL0y5vlg$H)KqFG3`yt z!CFP$I5f{)_z3JfWIv;g3Z`SdSBnC$s}9i)8v2xBD%O1B+_}x0$HgOUk`{Hj1LSgZcR?@?Fg7_c*|gj}v4 z@n}h_E861(paF>p7jmwBe3MNyr&_O?T)W`C+~TqCYg=1cnT;Ct z$yRq_#<%DE{cFeT;$M$CGLBA=Uyx9#;`j0q?QtOi)C@4FF#^vc-&n8~&>}2Rks!|m z>rPjvGqqYLa_G27R;&%Dhl8jTn-0fdy4}qv!MqM4d6rhyry7v~y*O%c8Ea$LPwsnc z$MvsXKm6dL}>V+?799ah{P@0WD|ImPo*O_e`;{WlI=|C9H`GM3nC zNyz`>si$Ud+cN*y@!D#d1L+pOm;Q=1Ofoxl8QzECo-hlIhFh$#Aw-~BE5n3N#~w|J zN!Y1cC7FhRcm(-Dbeh|$1s||@-=WX!IQ;eF4{RK6N%oFwqUM0tGJCr1)W)s{#pu(H zlV3e@?ayu-?is5a*HoF@x~C8Ce&mYANE>kHl90IYy8IK+xf>ZAN1v`2^;*(vNfD|f z{sOu6dZxeF29d&3!fFT=2`C)|lW=ia!%~(68V(lw@L+#B8I4r9t!AVPbpwvZa7+?; zIt!s9ZgyhqEu0fAi4phYpSOh~0b6b?-h*yh?jj`V-bUD%1(h zxd!|Kd=H^_@K6f_T5%U*sfm8v4obGAu$C<_f+*JmhA`y_vP=W4D~<)8L*>H!wJegO zI0vOa^``PyR9Ll!zSHaK_YCQx&%E!*-%jVBKk%u%&E))#P;{bU;|b}3`rUe~FEf4D z9-lwbdg!K?uKcPf+_K+d%J^%$5inwTRgh9=g;C)lp3|4cu?4g`0n-4uFtW7=9+{`A z+Z5*&fES!Ol7-eX8fj%XyNcboql-Wq*|KK3@@6dP370$N4nh_7i$Z^IQv-ac*{Vpu zYqtVeJ}Qm^Bco=VBO0Q3v?|4pRt`7EF##S@;c%)}OinQv&)DDKB2uOYMCtDJAI~Oj z0iP|PO>fWr;-{N_&hXDM1}i{6A@Gb;h5u)Z9xi4K^2QKC^r4V zRD;eAW=2q!47Vp_g=DgFB>{>--jNZ>6(y_ zq0WdwuW1Mlnyg04YV*WL_v=jU)&7)hkWWtC_|hezj%_`|x3rlZ;noIkU{|f%kb0`~ zyR$U;k9Vw}0Kv!&2jVf6{-H(iHor)7d&E-f)UJ4R(iV z@BN4Fd97i+*&8)lta^*3y3(g#-yrtv`rzfq^3R>L`6}IB_A9a%<Cd3H4s;g#g*+kvXN9=oJ$Av77N1!V+&a`66ol2BZWVOYafMg14T5N_NDXOzrN85l zv#_`;3fNFyW_7*Csf&=Rh00WKN zFe}xFFd0w=g|Io0Tb_ma-8*+|g9#7BR4o`Fn>0IWQ{4Sd2+NBno~lk!eh6})JYxb< zf+6~q+6Z(qP~ns%FT!4v&Yl@{8TGZ9^zf+vszSzU2`@OqfvpuMt9<5`i0>i$4u>x| zQZb*nE#XYWyWP2SwX-bK^*AKo#HVguYt~K9*RC^rV#VCnGVZ8Iy!jQsuRC&knrb1K z0<0E7z>8W4_a^+tU@F$>&Rt?vQe(|o4YyBN4lmXSby7I>sa%tF5Ka)jBsnMhH%<`C zAi;!*u1|wUYLPXwOrcweoZl&#@RH$XUTNwsc!hjGx-Ie<;iT{dCB|$= zayyQ0Y0=0MbZu6Q)HY~=?m|unL#g8@TWBj-t;8gh(M!dRmbK5-QBzqf;EY|`)U2m{ zGiWivc>$jW{7T@gq$pf*`ToUC8xZ%ITs_p)(caq9lu1QGLBFp83gSueB+@o0DU?~$ zq)QQtlt~>$O`_-!J%cDBS^q8s5>ueP;A0Z}>^nZMd|EcztbXI(`wrccRb;1gVX$VQOPTqU}&}DIh^lPozTtR{-f3AM2RB~Cb z6nl2xdF5deT;8Z6xGpNr#B<@C)gBH6%_F{eTOfdQR+=8|G=sf61}1!UDasV`2F!;9SVOgJXdx9emtc zpfrw!a^k){9j&X2bi}E^ZPRC<+gLAJ8-T_4LjkuFIvi@qviLr-6e#GSR0iOxE(hvk zxBU9+qBZ}Y-@f+6BWGW_Y0b@3!}}VC&P~##xDn$TwxUlvMdaiRgy?^fUJB1G;f7LJE1s#xR*cp9 z;l?Am4;^HMc?thUrW;!5^{RD7+Y*}U^0siLWUFJ<@pMg%!Bgv23>3Cdl~M${6aTYd zdXm+q>`=A|nPysIjLK+m7R%L#jX@TTWVeKG&uzGK=E_%Zb|rn1)zH3WcEof3XezNS z_1IkRg`4KW9fv-#<@P^))MByP^Ut)^`XTi~ zK4&;+)@bu*I>+|kGuyFidoDxpFLzq|CB|9wj^2{j-U zkH7Kotj!;(j!@MG%XCJZ_etVo`#*o8GbNrrbVv96Q7#{(rx-uo&KU>kDoXMOq5>?P z5mEq-ce z>i?L5G(@UoQ6@a2X<4ppX-fM>0`8ukbpGr;t<&+UXyY=oyYk^R9=FW4JAtqE!mH$gCW(xzzI z#4qG9fZ)(2xEUg@xAqaa8(zcBkGt()ixi!yl%zBnlermtR9Y_=ANVgSdl$svaVo>j)5YbDwV=Kzo%q zaU8zkVl$wN5G0l1F{Utv(ugraYcsNMGl{A&qOk(XW`j^DvqkP<@&SPw51u$Ej1QTr2cEZV5R$|s+O+$MY-}rj-%Iz zuSq6Z54{$pLmXaL$QjZZI+DRaceK*!?rhB8c5qWFl0A%hd*CyA4r{+k_#VeI`%!KI ztIQlksAWmIKKF9KTR?d=o|5PCwBoD8Gb)?*R(%QYaIwDRqPlor;1?7xjPhx$McK%B zH8W$XzB(OC)sh=#spoL1Uy9;Diet*jLa*i8d%JX9SGFSYGc0mY!8gIb*r|VlJ1T4>R=`#h0|&A#?K#TZm24^t;1t9cUYs5 zj`RbC(%urtSFg$-@qTz~nt)g-e~%lX33feg3q6uv3D7DtQ1Gm1p(wCa zHyQuZabF_hvv06FR@;4{QBRZSVSRs{(-;1EU2-_+voBViaQMQb27~S{z0csNI9}nG zP1=whmIN=W*4vWb?|6Ls25HktiA77dGNNevgw$ccvX zUu*@O=j@ir@wH=XMu8o%VZ_@h>_KTBQeB@^rr}k^(#Wko58Pl=+plgSc7@t z1m{2F&f$8*0d*{P&i;l9Mj9BcLbieNR8zu zEU*!qH*T1jUf3yWvM1zo z9Im>a_*$=Z_;&1%`pw=@!tdC{SI=>o>-0^#)@`!IRCX3C+x(xnZMzXhbb@ zqG#OX4i}o)>tknbt+k+;CRC;D5Z%1*p=WR0M0?=;ui5?|5flkxP`dvO2K_YD zSqC{26C?zHT!8^9Q@f%ND7FHuD+w`B2Tsob)|Fa`ZlxQZAHqvyg~)7BCPRq8;tju0 z*>c(L2R%#WMYB79(%i7Iwy}F%!~WV$W71HO1(%9}BJEI)vA5-o-$qvTu4$+ro&7== z5^qog2mHA~o`(+>{7F!qOtCHt7) zNm~}*prrNsRJU{lEWq8-QYnYY2$0deg3f7vZgAdZwm435DsutpiLzG3 z2;65)!(Q>G<^vWZYEfBz-YR|m{gK=!>)V@bEg@rM#vl7iuKkze!Au}7jvx0#UaV=a za;D7=+qgUr2NiT7tyRwc8`Xz1+WcAstX+|}d3OXhq!n>=?(aQy0XlQv)b z;j^LjyPAzsuk+#U-LH)u+83}os|JpYjQ?U_^Qjoh8Ne+YJGHt0m*XQx2CAIaz`jFc zuXPWe81azbPdJueCHzS98f?>&xT;ymrQ)o@CLL<9!kCS5tSMwQGz4uX&ApYi;rhS4 z9!LhN=pAFA!;7FpFGU_f2MrLqfJ(qjqN*W4?`S3z9bY4_PBerQ6nPZk6;~)g-W7a^ zp}R3_NFyQv&QMMQoDd)kQ(|f5rFxUDdDr^Ttp$=C+$IA?=G%6#K$BZTAC-Ew8qdgy z!7`fsvL7@7JX2#bf+hpMD5J^thcV_2;wI?@X&iM&yO3ATaEX#29S{(!$1ETV0PH4L zDGNbr1VpweRn~5WA4h;#j-o=LIv(3Ji zSe3yM$z38I8m_Oeumxr(hQd{0s4jCT*hux!_KSmZKjP})x2{*SQkiq2n@jOQAzMHv z(+_M(J-Vk*=G>x`IUja3tQ~Ew9~#W>Z)m(qH1)ikP4CYC4a%I$zv|^>&XGuk8hx*8 zZqVL}EW&x=pLoJu7fcz1GQtQ%{)Ubsy@ zC%=e$mwLnxb1TJdK}C3>rNLi;%WYCI3AOPC6a!`HBiKRoi0qT4kCa|ynk;>!^df}S z(nqWp|5Yp>0W(DS0HZ7~Nv9W3c|Z@=(`&Y}U*sudtO@dJWw0o7iu5I=8Jw3BINs3& zkrzE{L!*8Nuc8^e-K%ISZ~LlT0{n>qQrSy(MivK8NOeATZf}{~!9=1ykw}uqjPKnj zLb)v{3(bxRl#6%h6+d;O2zJXTxx9k}gzZembspFqZJTtp9Xb-GOx6I+l2WijlS&;N9Op8d9k zeyj1Abo29f-Muq;-yiS4|BtlcO3smx5GOP|q$aE2NwKa4oQYJE&n)FqDL~vDwVhlH zDJKgo^p(yT^}P8F9XIFirsGBzDgMpATc7`tbdTl(r~^18+&S@N5)06d;Q+(G17(Qd zP-0#dT+>Qk7OiF*=_3#fl!DLV(5LDJrEz8@Lqaei@Wt#x)Umru;JzmtqZT8f!e+s*0wD z!I6c9k+CZu-`O>lP5X9K)pf6)pI=?Q`S_#xe;!@jzg9N8Mi&pP#T{%P=xGU52O_TN z=IA9?-SXg?`TzI1s|Q-4W6yMX_gwSdkF8$+nNQui@%&#KA9&yc+jhiKcRg_DPLL3j zZq+PE2erM(UUmv;a3`U8snj+87g`J=Oi*J)!EQs~T`QnKi&}&a?z6UzsusX(@Q0YM z8yJ{fy(<6E?5b6>gWBF)U43q7HkX?n8k(i=$T&a#q#f1#Sg;E*U@Ldz=8zzU6iQU* zLYT;)U$BU}{r$^qvvV)>+u(aJQye+FHP{Oh!-@GDhgP#o{5 zba3^*efHqN&;Hxh=YDm=4Zk|~kZ8PX>plN;`n~7Be*9bF!1IT$t%`V@-fENi##P52 z9KLnSO?w+nUXR7O?y^gczj*D;+0SXtZMfryzx&Y#Hr#(+jK6u`7}9zkx#s7eIk4;D z|2X@3G4?mF^xM`<1gkpY$&rVT?|AsoJ!`sc)1geJb^n(x-**2ceTXS93U28y;++&z zUUWM$nw!MO1;MG)+yttk3OH3YM#Tdx6LfR#W~It{#OPF7x>yMOStL}{)YWJJm}7C| zKjJRLU*HBq!u<)-XHn$p3m2Xf?x70kRILwgOXZcURPxFd&}Gp?NK~8bqnDyM0dAeT z2lqTsEkqzc>4pbFezHQgl;8n9N}%Ee9Qnzs`@mRW$WPLkC911VrT07z8}<>3JQui) zAEs(~$268rJhJS9gg-660D+Bb3m9-73~dGG2>E&tvM9%et@xp*$H9O zQk7$7MRoYtz)?s8{7tyoHkkQ@>jNomngv_*so8bc+LO^-YTBuD>UH0*_uJ!UjR~6o zITG;dqrQwMznE&ayCz5P&eb~;6=`F=)?=>lH6EVG`I}lZy;ZAiguJu_B`bWoqCR4% z&^e4wyE)*maQSR~oews*w;ObRT>_$ZmwXzPZK`2E{h1%=xL-ko0x{6&VEkza#DkFc zHb8!;J~EU^EDx2xKmjON=Um;WEA67Es>yHNr-2<1>Sy#a6paN?4`pu`mi7cYPvFpE zcRRpFC2Y~f`XTxR<5(T$l8%)61x#OCOI;0=%qo|Ig4R&8ddTMGrVsh(DR&nuq1QD~ zJ_!^_<)2_P9=w1_q(i|rrgr|~)_}!n8tbLOFU6&*U4Noz~g8U|X2qFV+m8m(Q9 z9va`ca9XG9o9W%QM?SsUR%dE3hx)7A0Ib$qwKkVs6Kid(Y#6XOb-lA|#wYt^>-NFk z-JM~=u_UFG@ORSdOg7+eU~ixF^$L!h73+osCBT0(aYEjb{Wp02Uo-~!GC-MUa_bSZ zhc{6Wb#QA5zyp97EDD4um`0<+4kOnUmay)waGlm* zyJ5VgE9%vlS6|W4x2rYbHBguaTR^Kyn$2MzK$0Z3db-nF zt%>#xH%zDZxN?V@IzsXM4|{7iDN*Y56oxZRSIO9L0ETV$SV= z55cAA2M-(?A3roMpPo24F>w&j!DHF{8^RAzB^&2j7U!Ax5{_@=zr)uo$|za`Mv`#$ zkz7W5+-`xhtV*nkCF-;Qov&~%-mIL9H>0_FF4Bp}bQoS4$W3o0-A4Txs?A*4xd;if zCEqQ+0-KxapC)rL{Ia5Fr&t6P+AK5txbKD=i{!{q!io)&TeZ5u#9K;hpay1@8mQx` zvKpvc)()H15~%yFrZ6jkI!Gl@M?ZuTsM5c7Pp(1<)E-7h&xO2nvvh&z=nG7n+u|0#efNk;MIb zckkRVtw3y&o_YVg*(katw}tcotPPFnjgm3FAgwQq=@5?;$Jv{w zbVN!cKw*9EeG9vG%uL~uotEatbi%#cw>#@JlYCOf@(Jm+0)4(EUQ-wgve*djPy+}N zkfX9-B*zupJaFWhA@0q%j*4>83CLtc31|yp9_mUO8_lxOjH9ExPf7E+Ts#tx#KQ*{ z7q)KhM^0;JdtG&4Uu0i}2i0T^qF?gcN@(^u>E8;cwZE+D~$ItWsR}XAVM+PoQ1x8MwQCKv5fN@=i0*e{8P4(#2tWfKt^NQSI`F}w&;+d*oJWMQNxy_zt0 zR2%mF0`7<8QDX9)K_A=!Y_uM^{);wIHX)9#)L}G~)nUZOGEP}hB*JL47{S6lyf56e z@47R*XLo(z#_O-U=IX01y=3QR6wl12+uBl#lzmGPKBOSw$YBN+9oY>cj4?v;KnEq~ z7-S@l?WN@y3HM12fXrjc4w+$eej$8?|KT90PF8IYnyZe&hP-eCzJOB=|DRl~B!j8? zHM{wNfPApzq%*$YnDiX04SwDoaJ>KpQ9R_UteB>Gao3a=43PH|HT$F zd=XV<-4$gege^^}l=#c4ft=M4devccn{j=n?6p;u)qK36SfVpNYj!6rg_4iYZ+?pk zoj3g06A3x;-*>n}E?r5XVXZeiDA%_)8fFS@A!)#WH zkHBBeAdD6iN6O`vy#W!yck>Dynv!_ajn`g%^0KW{Yu60)cC^-3S5=}uE;}@>H~^Hn ze63_IFG!BUTpr+aVV9sm{r(?Mg%hLX1@C$$&iCAK&FK?IdDYymtkY6etBier*%{kk z82eZG*heK~bfR*h04}qjJdy=b1NeWaI=gy&!WPH6z&9u8jHCctt>!7L3D}JnX9rVa zIo2$F5j5xj$(ta5CGovy&)jhJi5;7O7|!)}HQ|cZNU(S`+v#Xl*XGi)vu?03zmKbH zQ$8zxnwjb7duQ_+RjNQWCfTlT>vPfEUL^SE1Enrq!Np2@6JSJdxN>{He6 z(wc8&)VVG4FGUff0&EQTL>E|%QngT^o@Y~`q%|*At7Di{X3G~-19pIKqx9QARg#XRJ}LR8!{uylYOHonxyETF4J?O4 zLOq|KXjf^T$GtE+g#XOhk@*e)oz^r}jl1P!WFwM85C^=@q!UfZsx#rnB9gd9lVn^l z=;3lP51K`k8fE!Y*jq-UybiV|Kxk^2ObY&A>C(_8jWr{*)x*Z-BM}nLNP)gcbE2Y*BUxls$#xS{C2%Jw>MiQ`y-1Bf}m>!&b zr0?2_RbgA}fW_x;v%KGTG<-{sxg!5J=AT)^8)j`LC|uRXgC4_`K6lJ1e#B;xF4=tZ z?4`S`jha?R*N)*6vsudxn&&n3@;cem7YdCbM*p+zQ#EtZRFzX>YlWY_r4mTwnkRNR zt9rt+*y{}e+?GOxQpx|3%xj6epZNWn!I>kov>P$H3j(g%+ryc@J8SiskUa|3hOZ-6Log=wb67I<@7go^THApcfsR6x24WToE z)Y(`cuL@*?*`i1?sb?0^Md2Ct9hGN@E^CYH1i?c{Y&ch$DY@de!Y~PYyPF%6@lZ{; zrnmxS-})Mtf9zW?iXSY@2{-x`MI4(8m%b%)fuL*5q=J5D6|Rtks53HA2kPz+u9F^> zzK^{2R4yJvp+yZGLx`72AbQ!oLCKmV1r}2j)gf3Lvk3?{$lQViTw{JP0M)$GqxBWR z=wtUaCw$@Rd+)7J`YU6fdNdXfh9i$W7X2FPxIA`mb5)?e`o53USNr4iEE!JHGY}4j zzFMy_i+ikl36;^?Qd3>!Ho=zC!1YPs5E*7B7K?J55Z$1g8q&$?Y;A(>R#d&Yc9t(H zX%bZ#y-iDi0~WP)p~Q#RNQEp30Hb<_Gn!rF4RsX`UFh1;87k|Tf8&{XjmP9P+jPOq zrFWyk<4Zf9J-;Yjdg{w4?s)vn_o&okPSZTzP-S(6L)XlFXCKvid~jYGd2Qnq@#>#MWr zfRFD$uofQ&^2eBOi;d$2XK(-={{ zEj`KiDf{-f#9IsBzMg*@cOqi5!dnfcO;KWj+C)woZogrit{#XD#fEVI%UUD;gFU5} zNj9qLFKs>E0XL6J;_Q~qn>Nnm23nBhjeExn{vy@KMr-s8-*aqyFN(Jn#=R;CJ_|HPxpaNha-!=jtJBq9=X{0^} zzI$RAr6a4eF+H{{WXFoNM=48irQ*fpeBhVtkkl!{WZ@L<5dU%7Q!Gc?>}BOhS1J=( za?MV$y85n3y-9xFtkxur8*NHW(#ZMORxHT3y=KbDuF8PuLdPB6Hd zpvPG(ciG4=jd6j?z~=dBW6w2czG z-&zTBN{c;nZ83A>>Jk68Lgufuw{t_S!(6lZiic;9d_9)D*WqxsztUOzx%ZZ(!PIO- z8jQ_jG8%76R=4`Q`e!RE-lz3-Z#{Li@BFDNt)&UTxbdoP%&v30(x={cYU;#X>uBvW zZcoZl-TZvx`kp(VE=iJ^t+oUz{0@&vZ#Z6+?d=Tp?^<oIn=*_ z|Heys-cMXd-kV{p*;H;4>jrlu^m^p^L%~BmPvDb-Frgvhga)igm4VugRpDn_T_gbA|6^#3w}y^B1XK#OLtQWnaWq$~>S~@zUntD)^YiNb;O(Z`L+$t;f3dDT|sN)6$0@>sy z2-3o1N*6=y+`f6^x{2Xc9c>MDSw#K4p18?c+sOjcYK15dFJJ&Mm-6Vfr0l^&W#MkZ zeM0~i@~7US=oCEd%<+w5=I6QYrm4n-o_g`o(2u*jpRja65T8nDay=yg2P4~W`OCQent*uQw7PLg&HDWMo9J4pob=~4v zU3ti8w|6h>>Oi5K%C#L`UBO^`?^TmyyDH{Q6^?6`)T!DjTjg}}6!_MY|8Mbc5TAuC zqZp<5lJKe^L7I(X)gGOA(hFV~02&f0N2G@s^hU!1a*Jfcwn7}ZP*=%>Ew7n26QQX> zkQyBh<**t{U!X^R(mQ^`TrLy`)Miy~u)nJVsf5|sNPI-B5~~n$h7>69^c5vC@(JN3 z#?xb%2h|+|q!X?WNMk4;8gPsrs2x3&4cq>HD;# z%D0U8tRgdLeAjUa*Nn7TpajWH3I~!=v$agb;VMtMpa7#I*$VxUm6UB%`*6XA{54b% z;=K{cSl$Dd!}axa;9BK)rk!cKG(3`NyuJ%tA6DPx2^Ue1>btDtx>K-lFta76GgwtB z=_YFR)Gj;(5z8yR)BsM-V;l6Xb(|OQE%1WyFk7p#uc?PJTbiX`TlR3-*BUY?d0sX* z#v5#IpZ}9Ox6^!XIHA<@n0=0pUhV~AH}DYi0R6nI308tqww364Nek)^o?@zHp#>a2 zMwEbP08{8INl&=&I;*L}cmj4#T733ZbzGNnjtG=-03C0Il3IM=B`#J_pyY9mrlg*9 z^aSjTi!KzWFRTEJq>dQC0CR`d-v^({LFv=9e|*5K2rpvgKlJpZ2p=pwucSQJ<9b;f z>?T<|YlR91J%!mjXo1^hP3+r(zz+?&c>{L`V6Op^jC{(s?1z0{?1q4n%eiU8)Vh(? z1AU|~&^ZOQvoM+al<+&oKQkEf_Z2*W71QS-nWBOM_aIJO zvl-NoBn{R1VMZHaANUkoR_evtEB?C`dgeObQ70`nm?XtIjZ@lEa7;mX8{&C^Gh+NQiN-o(>I-LwPm+c$a(TcMm z1dI|`NL6@OY%`Zb5eLe2!8PESPzIaJP|$0C$aw@dMn-%}c%U$biQE{@P6?M=sp@Sh zk8qD9+cBy~l2lmIc#zna4QGohL|#JhBdyO*%Vhg=g>NnJog6RQ$>a|qS7F&lLc|Y+ zJ-cF)xtz=8tb;?)?{!wXDrx_6hoUTOx$q}>9@s9cK)0s%=DP5HRO~0P1f8sSGr}`U zF%AlT1?e7eesy|<+ZM0~3TR+=85&rtg6!aKbeH^H3IqNXyFPt*Q;+h$;uaMId}xI$ z-kBaMQ{9*DDNaTL zRhIi0VOnQoM^A0F&D7euAGKxfKea024_l+F7tX$P=_m6a5U-oM0tIHKw;o5anUrkl zsdalRTAQ!=II7JY8VosNo6kiSMZ&7?Bdmz1O);`kLC7mQs6ApDNs zO8GtS=&dB=-{D(xb%kN73Sr6Iu#d~8#&dqmyv9Nu(+Js_L$v!#3GJQ~ks1iCgJ>61 zX;&(Q-j;S3X}p|v7im~FY!I5$?jns#-^yusk%r}O1?|*!D(y(=Q0BHW?OYqCC)W1$ z(1q<(J{3wYiSQ!-FKZzBpc0J124~4Hy(uKzJiLzf5Txf;(Y zUv-hT`f25U2xaHL({~cp0nbs7l#;KPzL6+SYojQOC9TTGh>BQ>d9tiA6q~ZPe81bN z(RLqfX?^02iRok}j}-HqNY@;FS&PGI29mKuY~ER{waM z=~T>)c?Z?MvNf#qS1lznmRFAPz~k#>F){Tjp$0fVR;c||T8Ww!|3VeRBr8Pi)H~m4 z?Jvzu(XK*WYHvc8&i~G+Ghe*;*B<$i^6FoFZNx8eJ%+8#v&fkuc}#2bNxtV%4*^(V zy=AeKS5-wCOM`0_rvYqom{pY;r51RHBG;_kJXa3|S;>;o(6T+uhrF`WOposXF30X_MlgNQ+;v5oO5(y)g$_ zQ10s{=MR09hm%;MpyKgX{m%}36L`D6_SQ@)8lgIn_F>2Hl5Y|nJ|_LPK!@)taXzAh za#tBaCM6@X%FD37w79U3iWp_8hi62j4_M}<9hS9)8!{a~aqP(9`CYgXaVR&?-V#5W zKANnkb}M*h(wEOk*A{5;bX29qY;N;%=ck0wIP--Re9lW8psIZId8gM?_GCj{I!X1@ zy%WBPWHO#m{kER`@8rv*g9Up0P`ZxsH_-z=BErjOabdPbG-+lbo&}9!?ytaHEuTrGyJ> zxDD>j|3Ny+eTmFw`FrsZHojquZ%=O5Qp$jRVPV5tw2~v49PHFf%ne z+~3<+pFx=t+=o8tpF~WGlZg1V0rmD3%n8HRz97BI_6{-1bZ#9xJV{>S8Ye~EBCiem z79K$zQt}p{%t|nI84^W-n!+34aS!&lwV+@KY`K+!EV}3aJ)<6$m*UhQqVBRa9}bxS&VC| z>+1pED?Um#SJg*Jb49*J!AD6oIdF`XpnBOJR^n}-xFPib3;f!EaNcZ3H}{cOAy2;VE~Jn z+$acgQMz5DrP1Srs$5wA-xuce&64%so!dd{k88>hhU0mf%NHN0(Tgm86iU|k*kC`8 ziNs@-Gtn7rZ3N21h!iPcp#LmBBo*TXUlATF%#kolSaL*n5QF474<##2)i!Z>^6+P2 z(ZeB1te}t{pi6_{!3@_*6?*3piEYcWp zGcckQ)SjZZWxcR#3Y`$Ouket5-_u2aHaayt>nhfz6Kal?aN#dFL$?s$kdM}Ik}}i< zq9iiV2&Dsy8Ga+cOl|ux;BqGLG4FZ;~8RJFH5!Yj-kXSen$uul0hGo0p36g9O{A*|T0JX-D zFU-(x$g%kiUD>f4S9YwgtB|^>I<6=s9X@ZO*6++;we;=}*<_AiR(|E60?j_fX|@U~CZSQwvk)*E$xMo?=B;uorYWC?aw}GszJx+-N`}U} zc?(X>58m;<_uh2fzMWe)QyxMWDYZNgfoOK7RHr?n(rhHBPCyOs;hP2;IS#%S^8SKzzMGD}8~xs@CmqK$ICGrd+l--)B(sV;C7#)=>_RY{@PGF_XzoSk@BxdX?#LKCLbGrNk> zMAhlB=R^zVpHabV(OArdd_vfLI9+xS=%FmB&|+bcEbuWU72=ptG=9W)n8weM9c%d8 zN{;IP;p?!K*tnLy2{?gwIWitfy5sg+&)#_T!Zv_?Mu!HdcyyvlwJ;QnP5uw!%VqY3 zhUp-*4&Wb3ufPRkN(`FH2yG)1ncEo4V#+WtsGcOC3Sei&x(E^0q0k@K0l^FNu3iA= z%&J^pH!*$(nI&L+h41&5=`+O~tY`Teucp)~Qs9;d38*(fQoU0lFMVN!0VONRT-vo`{V1VPYO4c_!NsDnL<%BebPA+p%`=Wif+ zI*w7l!S${IkU_BxHGl!4nmv3NaFiO&$g?vFHmlL5;5sZ8dCJb5-pIRSTilBif!kZd z#kU|ITdPB*KjGcKD>sC-D#j__=0z)x2YWac3SM>P6_+2rWN~2)n1*T)u=saXa93y- z%rgf;b+~=@D>=u{OlF^bSE??o>8*TCTb1K($$Da|k~4)Z2Vo1jtRrVCThFGq?#0$} zQ?WCxr8f~juy5%;s_my1YRzzA@lRFHGH~XUBjo8HB`Q*4oeGb!b16x z7C_HT7SvfZp*)w#P6?BmDF+4&W6x$|xszVF7<0qQ3-nf2U!?FJ;oZCj8`kGNamCR~ z5A2)YwR)(pn`$j&QptEU?A_+uMhCDn#BIo5$*aoj;_npb4kQWrkLt0J;B#h3NkDlP zuwdLMrb`3JM*)hL;&*B;)?Q2b1RJrnm2MhJN;DbQPY!fs(^SF9xXH8$;a?>Vr0f@V zKND!35uXu$!(=<{m`6CZy9$GmVRx7)Ef~s}&R_z;4T9BTuu`6|giC9f%iLmNhoF}> z`T%VTjg}n2d^$)ud>1c(2h3u=RF)dLpgnkK#IFbr7L1T7$$0Ni4IELZdlp{q!qLf62-2i9TI4rnDCL`YFXG%!uC*eo~o zenv1I>RU=q42he&pWdk0;*?BV2Ok@1SfOw{f-=h409mUs+GcSl63f4{T2OgKfkZVF zo3qEr&_i;_l8(g=xamj|AN=V1Z-3v-M=m+If7{lHwPV9PH?DX-V8|o`&9EEHaviOK z8FsnmUvwl}CNSdTJc^Vh$qpwv!bc_(a}qMK0Rh51wWvT2{eS&WHtMt%%kNc9z5$$SO$@S3OB01ieT4Wc5_hoDnJ zuR^IB!6NA`TY*H9DYeFIDqJU?ORyhB9zVJ^Bi^ni!?5uj~dEzHR zB*M?bqWcgMijq`B3$X(U*~wD{TwbpFqV1TYj5StZcsZ{GF9)maQv&46ck7a@_VNe(N?;&W9!5R^^>BRA;ZQ_=hL=8qSS{i{b1qpV9fr!E6~3tWDD&b(!R}9M zzT2wK58tHuh01kVtN!&W|UK=aGT_k3JPVvm+-dDWTd;2 zjB8A*+`N7Qjv02}pnOJ1l-@Upb(N61%96b3zCj!ag?ujMzQO9^eS<81PnF`8l4oVf zTi+?%rnKa;LFrK8>N$Vn{6}h+UNy+}_n`cT0&ir5dg&P^k%Za%wW}qCA11cw%|rq4KbAS;+qANviM-`nJgj5wP8i)@IYs_ z75NA{^J%7wHsEkba12r%V*9uRLO%=T0=y4-!=Oj7cBhR5=o(}`WL010_3yLG`mp9G zoW8t0Nt1W`4T_vB>qGfQkRJ3Uka)?sa!ne_X+^C+;JWv#!+WbP1Ve(qTl(M!u0MrJ z3tJ}E4)k?(Byn#{Wtc1Uioe^Qe-k)phJ9!G2;UR$Wi$jDQTQ_7XTZInv=cccbvi4= z93^7l8c?o74w8qkU~q8py1autb&(E8Qd2#i^m3?QltIS=LzL8KF)_`v>?7pylclS# zI(72I704alGCsC?sG*KdT6Jn%@nbSjvmP>VfAL;isGb@T_7BNE%2a0e;x0kLQ$k01 zt8%=2E=>)z>T4M1a)3LeDM3eyD0dItiaWP;Y8~Yt_4UO1Qq{N@R{={E!HHFYKX{A_oZCq;@d_Mv7to#BG>B{1@ybP{wFf%Nm3awq~LIj2lm&&QW+)wBimmzN96M>n*K^NgOXpevvPF5BJ82Esg*SW zKwg%%Om#M9(~%(G<4|#l&e-FktFXt@;`dn2G0nE1;}+*w_RbIvVkO;0QEbZ|vrl%k zk#(I)6hEn~t^Akbyb5D|Ks=Ngrd*$l{p!H=cJONcLQ;Yk@EKTFA4 z_zq=}iGmIHi=RdvhMhn`8F#kU>NL=M6noweiT8E` z{BIambYGkqYJv-Wa4JI4oFXu0nA*I;0+lx=9CO)1mMu?NTg)LWOMCZh z-8{0IR9Et1lK-5~K=ol#{kkvl*fiT)D%OtH?R7qfRyC@;q*NSEUwr*73)l6&ldhWS z@``oSRKAuf*5#Dms8t7J*$7w9@(}<-N;it>3ol`{>XcW21V|0J0kt<57xv8W-Z?ow zvZ|rJ4lB7VmQ8yvSc-2HvDLu8w3etNO0lsdHoXZxc}3rVC1pZTnOw}^!GbrQZ@%o3 zw$ut86c>JJdzg{suPtx-e`)&?IJv4a-+S)8weM>$)mv3{RrS6v>Alix(pkDYYiCb7 zdqN-(_OOKzARrz#y5aV)4Io_> z6=cYAK)Kz5``1YewWVNH-d1$w5Cl9QyaC~)B)#W$GQt9y?n3|2JAn3lGeAWj8yUv+ zYpkz|d6pI-@rw8c`F?c1f%y}S!Aei_4?^$~Vegc|50L`JAW`@RGbSccjDQu;k@k_? z7Qisb{UuCWC_oE2PvJvgK8}^+$$r7OYb;3Nr?-``2%iyldX5kHl7>~*f;pkmBG+Fa z9g3)E%JGAMm_cad7I~CFFATcM+-1T-F6_4y8}e#81~0*$kWTXZvl-7!7}P6XiTr{ zwLw$l`-3$gj+nnoJTB*J($CXt*e`UM7+LOM-vpix$OPi4zDr^yS}b%ZNqOJGjHgd& zUfwqp$jWGcZ%12A6^a-(0kt*4S1c1Aqwy{E+tPmNID?){@uP#nnY|UBdyB~mW@JF_ zkO6@bM#*F~nl`%ZNb3~Xj5sd<{}aqK?ybWi!LKh#E`ArNn%q#@E&ksciX;qx>}pQ= z#fD;bdwNpuIzP%VIG6Dt!tu)lPR6g*f~zGPVEo_#CS7#&!105}DROh=^5LZ@{!kZ> zmW2ueZj=L;_OtzNd#xc-iDEk(r*bi{f3Ab2(f)ZIS0iewDX95M`lfrod1^-@Z%m%^!T#b}A!7c`c+s*O86 z?#)(T)E4vYlK45}pApv&R5q>;kNZIlKL^SD=zj}B3SLw#*^qN22@ zz&+p@h)3;ufH^_tQ9h-U`4BH2EYP*$XqXC{in@37Gf_&3$_;C!@0bAr>d>UA+9}d8 z|2%fX1oHluo|pgz!0%@sO}dr7Cl2iYLQgDpEZ{Jhd#_lDHruG?`dOO6k^)fMY+oT%Snaug&c@7x7IskVc6 z1BBtWn|^v7Pz2sYSZkuNwhP)8f6IO)e=*zs$6n#Q7~U2MXmRwW=~|WzOijh&DA@`L ztE!B+9jg^r01vc4lh?g=TG|E*X>dkP9eI5KSeWzykY=(!JxbmHCYX3UB=Id@wloy0 zEJ{>yG<g z9`dHVKBI@o8~B>&3^dubGre)|y7M`TP)LvX&)Ird&XKpLkCeCjIsLf<Y6Gh0gq!=sy2&eBwRt}SfvrlL8H-gU6 z%8}8GP#IF`CB-3veUbNVo~C4E;xCQ~s<(jH8kvW5Ysgvsx@yHEh7s$lkAjDF|Gc5{ zMzP?AKr_1F$a#m?tzCn&fhA%|8&7b{)r``2W@}nEinC`>_|gbOS4nzWtb{NL4Di&Z z)4zm87D9z^)j~r;xScR53Q3miHChjF1Qf~9dKe5~sV0*aH=pUnp(EeD13}OcN$~hB z98^I`i1I~;1_^rt=^JZG)*!Ulrum2?(0${AcX4^_1$G7JT^(52C{gXf|)@;iG$ijCBo)h499qDy`KXFTWPAbgR zga80sJwzvv;mU;22Jsc*0@x8PVu#CV`uBX^smamdr30PqwbikxccX8k&JVCI_8sAW z=Ii>V^!fCFG)e1sqtgWt$6m2NpkntD8ngE)o zbv6rsG1eJpk9859&?@f{{^D+uIt6a1YOoko&^+SO2x-}byg?ufYDO&uf-iR&XW(fG zR#KBh2|RS+?vz|RuP3M=RJ>_gK-;z=lc#t`Xa`)-paRS}lKL&qW>Z=w|E}~iel|n{ zj|l_r2xf;|atg$=nXRbOCD~A}i)!##WDBpsW04i0A|l(v4u3T6tX8ciuLjTPS;xWo z%gVV7gp|Lk#FkC#*RC8V`698WYLBSAE@E6nPT-9A4@nL`OP@r^M|WL0AB4 z2GfenMk^sYiu1Q9iX{W=NK~K$d}gvGG-m_K{!vMR#l|sOup@NZR4MS?2HKP?jf5H7 zxnup>$i+F(IL)>(=_L&oTi`8qG?pOMBb?dK(w5PQa&irxMVO( zJRE2NVx|;Nf?pU~O4-(BTgta&_#QWnk!(ZsTrc<@-^XX#b$VWxoB}m+=hMQZm}X}S z5M)Vc^QMVaGXizw_(mkS#&r>W=JB!3S;Q_}ARP1VZ|@&Urhhafp_QU&l3p>*2tnj)YDvH!%` z2z}7bu~EqBU7OblDm(z&OzQN)U5I9?7wdxY(aUBuY}CJzY*?tR1;b0IL1kHi9tbnf zbRe1l0^Pv_`*&?vJ2e3S#-8?8q|#^QEm2Sj(FW`z9rDdOZK%IwZ_a%S9Fl0`W^t^o zFu}k&0+%Q(a13!&2o6^urVu^DeNGwJkj%KOA|nckV>b|5^CG^ZAj|#b-&Mm_W;^xb_XP8dy`{L9@G&3Y%gcMU?E~aDy z5En=VF_;u%OuiwiE2tTLz!4FHWIz-UML|=)g&WgJMbnKj6q=zFbEcaC|9i?d;M>3bVDBN_}AQrb9V{u9yF zgW;NTLJ_(O!Mi8&Q#I(J>r2#=fRH1foxP9r#Lq7gWP(#i1}z8?kg=HdYgvM67; zX*R|n(ZD&5NXLE-Bhne4(}=i}m^cUip0^^f%bW#?lf>RltXek8?X1|llH$C*BR=q; z@Iml%{Cjp+`Wz37bJT3-U<|SWNs9yqVR+_gV0BJhM|j$4YUIaq$rvQpHTN?x;@4**83E-g&L_Z}hp3%Mlb8okjMz zbf)JzZn9n*QopLX3(UHxe0}0%T^rU-tRh^$I&#i%_&n`iJ|cgMWCDJkub{dc)hH&u z{;)jfuB8|qfDirc|Eg9@(czcm2JM zPZVcB5#Cf?9v>b0f`8ut3+LO`tYh1gGov$ zY-%7ssmL>zq}1=^U*Or+dC#j%;K37JKw2Z2G7e}FC}vxE?=_m3n<@{jT2azP$wLf&AcHp7pTmBW?QQ=^l-Uc^`4-}i`k z-uw56Fy7Dil3hA!EV4Pdj3#@P+AFnj{p4!mAB`Ct5ozu}l=gI9Q9}KuZ09mZH+J@O zU%7jA4Ju7hOfL~kv+To5#SsBr%%s{>6QD1J$!eq5<90eYim8=Ii4<-Zp%I*WA6|w4 zeKsMZn%#Q(=9_N3;o^(HXtt6~4Z&tmpqd(@;R{t(KP?6P`Al(EW zH03BYr@J^T5CCn8YTJX()OSYowOh?F9A|X|o!~S3&=IEQ`MsR^9U%A@U37Hcp4~IM zc5GNT4nJ2_ycUqIgn5yjTium?c1+X|@5P!my|Iey9at2VkX|OwG%|ySVA~d|utXtAly8_h9G-6jISBa1?916_BR$T~ zhM*IGdO`Oga|o!qgi|2G!Wrm6Z7_=1Y*qrUa;ITRGKYZh5iC(9F9&rMM<9~Zb35i7 zLVm9WI^YmIWEEdF8YVp5I&3~qZ0E9FN*>6m$LQtvhW7r5xGpdp!EOPI65F`go>%jo zhT3H`?V+v4|7J415UY|t9~gCndQPZ{y*=GaJcl{DuWp}@!LIW;;dhFW4dDeQa%TDj zEkroaAqo`n%qSGBlBolF3RU$1?+28Hu=Rki3it>LD%VQ=gd)R3GLU7k(=r_gk^!oEElWo*xqX)&>y)n*dWZv>SL4aI6NP z3yWigON7F7$?1x-g+Y}7m+Y`4AS7VnZZO@$7oi{?h$xC|I5{2R`tSit9;nFxYpJh6 zbcx@)!M6bqDbLfPeH|BhxqM&0!@kSUnf4XdBih#{D!a^g6ElEij|!T-%dR%e>mv4) z05}#n2JETBg8FNHh=i)FC@U_otaq%*V1;5&feUns-g>^LPqCi#o@#ucF@G;_wsobOYh}`-KX#&M&|v$V&5C;T>ikDt+Mff3!_CFmA)&^7lB!lx`&GNc#*luQ?z zk~L6yeZrvH;Ian&0S1_(-BVGZ&PDpcogo3v(V~G{iRy=0J@G=6ggARj_VskMB9Oc^ zw4-DP96!8(8P9W6zGgTjwNn*e;&)I-Uw-LM!m{KsN%A5IqfgJQ}f)>f451x)p{k3^Hz)I4eKV2+!kaH++qI9Vmka{vdicmdhTxj_?-d@jAELJmIs++}}!k zPyc(GgSp^>Q70gvdNov`6^~N*>eONb>MYRhHql_@f#d8@`@0y<;YFCNeSrR9hl^L_ zO9;+3@jeIU99kP!a=9|YCjVO_IvrkxrpkvDi5)nSWYdxJqUDPG;NN`uforb5>Wa%R zJ@39FABOb2b`&Yr9q_#XU;aSQZ?~F^a+y*F)j^z-X>&RbDZx6j&goU5i%v?+t+*)_ zbwR%FcH1VrPEFMRs@cez3%S`7*|KPa-4jk9;`x+OS-*`K+$D(-1|A{%0 z%7@a!?|1DEfn1Pt?5h_UF7K(6GJER5<;y+1NVRA?qe5S zc>a;YyKbL3y>;`3b<3Cb_ja~L0X<%_vlP(~F&~fl0LHfAq&RQF*k&EdYy4i0!I1C_ zr}i@+WI*WY58+Q0e`*<*n7QP}?6Zi#Zb3l~zm0Ms@VtAy?g`8ra}`wJ%TNJ?Ec<}R zf$&%-kjbO4+v}b&D^&jX$M5@CI8%j;ca2_qc=yYM{YKGx*H)oK)xob zNTh*NnNiNd(ZSTX4k)}6ly~lvmCp0%^LF&wFpNv6FH^4=Wq%|@TX19jO zsE-Ra?Yi>V7Q~q??a#s1LCs*pWkQGHb6=G;_oKo~Un8MDmrRq>4qWBp9v<3Oq+f-O(a z1IgtA0}L+<6|~eR5uM+H%SF8AsMcyz(y14_(Qzws}k7((GaO_er#aBi`&i`)@zf(6)Qng|800{UAh{5B!oYOBhExk<0DZn35m zft4=SnpsHUCC3i#hwl*KVxz-UKc&5umZ8-(qP)b<2%Vqqwfa0e&UJp8{0XrRL>x&9 z<=p1fTaxWE)mW#3OAcBO>G(AWsIw_la4E0H{KkkyC&m8gk%RlUZWbsz%?(iP4`o#Q zG!7)?=iMe`0_byyMd#T5!-|BjU&E)T{7|hHi-^0` zYO#=Bj-w*Lv8=^oNKt;4D=pkQ25phAvA3-y5i6@Nug|Pf!=u%5E_rPk4M+TY+$Kgk zI&^=1iJEPOTv>R(NXE*P+ARrX>f2)SqJ}QTXgn&;j{94m*XoCney;h|qPistrw0U^ zMO^fCS}X3UNAi#G)FUGud|dS73&-0D|19^?AfYVCVjd3JI+|-QqvhfrSM!=e#S4clhQ-rUEkL7nCYxfiPt2-Va`CW8-c`z z!#2qZH}%?hkseI7#s$R1Ar$sgM*x;Lbk|^kkyf+jvqOE z2#DkB*NzPhCLzew5U5~zS?1j#)bTVeeNQ=<=09&rFEDc+Ex{*BwA2c=3~&pm&(JG@ zriQPpq97Q>3=r4^eB&^DQo&U4ARSnV2X;_bTKQRfS|ah3mH$1WCqh8a z`X1<>wn|Z_v*wxoHDX3h=g=qjTglGcF3>B?CX1P_S?=d3{U&=?1WO7W*tcs3dEW>6 zcuiq{tRRZwOX6HOU8%>?boDRnA=#ZrSC5EuK{){g;n1<5&{~F>EXaujB_gAlvKY^$ z1EO81?R>74-#eRk0qSJ87G)-wmnTt0%uC~4-^=1%|0?}%ao#0n3El-!^*!;fbLcpq zcb!A0T#h1=E^uIcGsQH|k$1gom)tLrgFNda|2^L2iNyu)BK_`fgsjB%yO-Eynyhqn zTF<*p=y|ogLPJJYg5?xNOyDGSn5J>v!2qDVx)7Pd^@|3{rZ^OvW6xQ7Wpx0e1&sd- z6qL<wH3HBK-g|lMIRq_bhCz9tUH!ffn{ylxOyc7e_ne7#f{fhtedwip;yWe zD9_P-*Un-_dJ1d%jx7A_q;o@VC4WIR$2X?408LPeXs+^|qjeLe5!bpG?UU6UdUvQm zVF&lk>|DQASOaPj1-nDL7m!=U8Qehg)y}}lW-#AhOCOh-wNuyzQ>)ctv{-o<2*lpnBSXk;YHw|hMfJ_q<0epROa9;T zPC8@MWr~}xe=%Qm98-F zv-g56&zhNIfydqD0x)sn08K!ge~1SKKB*W;`(P1!wMNcZWh*A zsx0)7cptk+iJxB(h*3o$2L6Fv#+_TZ)(xx>qO{OLwQkN~1aJ+?IXirC@9yneH?Bt= z*Cv639V!a?98=CIu^7$_!8b%bG;WXLwX>d=GxgB!5F5^`nvvX@S3FA-B4IU1Cq#q! zrHf}ZQ_l2Rx@A?*%2Yxl)6VW)YghCk*RrXh>|pu9jLfOqI<(qO+}1(+&z?C>e4F+k z8TRD(=_dgf0tUgmEK&*$k{5aI7OXvnI$G%g-lYoK*@i&{vyu>DKAv}I@9t%&LD<#V z(MHvnE5ll>4plJYy!%hOO-!?s-p=1o!mLkhJQPbFJ7k3JYR?VfU2R$PW`A zBD<&h3$|?THc(6!o;?Zt0AEEV4@7bx^vn#NVaD@fGEvwdAq+a~$PLzv(ZW{xZhh3r z>^>+CsDA=X8c+%NghBrVjt>?XZeynxZPI~wVs7`wA`4EQxDGy(y}M_2tesjp9u8O4 zR29XXI^G`YeO3UU2W#W@)K_Jo2VrewPaR4o5tOJJP%?vzoMaCJrRYXZ5$KN+LKNuF z3#XtiV^zGop`rnI&dSQMNQ4bVcfj$4+x&Rlhuz*S|mz5M$7FJRiJK@4%evvt&G9cb?z6Tt0 z5IX3~@+FdA+lx)f4M;5pEC}kHnq(7Dw<*Pe2k3ImgOqn{j@c|;8xAHvfp^HQCgU-M zjSTg4HPpr0;%#A+cP7mYexM6x1%XkmH0iHx_N%k-4sH=&!#9J0#3$N4hhVY39 z{`yBy6tB|}O;+ff5!6f!l-teuHdQsih?=RxK`~H*E{Uf}eOa+J;As>C)lG=XvJp$M z+Y!-Zf*~3^z*y$*vdnW*$V5q1-=QKnR2{#Pf}RG`BdD;=s1Rt0pJOCW=+s;LyXbF0 zJUl=<=^p~qB7@zTJlBzMEGX|R2Er4b83%VwU(UNsh7V!2I1oDmc9^l)5J#(=SZ!dp z-Fn0Iu-$C|6vb5&mkupm(q4tchr*!O#a0={1y4ha8Df|Cz32P8Eo*;o5^u&J6@muB z5MDtA@+-m~9A>B~JhaRRM0gp=yS&~HT~P9Va`&~}ZkW*i!Q=Ai73ny~k?i0w>7jUU z(l`D(^APX)dsvZGuv04A2(PUu=$Xu02q4zMh8R#nGJ8Y%9ehDzCBmhzm41Xu$Df_} z*r?1-oxJKw)y55em%m z#m0g+KxS@SDQ&#Wzi9Of4@ic!zt-f))b>}M9EmS~t;rF=jc0bwYE-Q=q>ft8{V+gw znX^DVU7C8c2TRkSIAXUTzsx~rjv{+FB1G_u8Y0CH+;Zw9$(UPEUKxN#*N$B~pBzag zq_>jf{ZEKo5iak)Ape2OnxvJX5*poCSjKg~L`HKm@anBuKxJ8a8Hb$KJQ?{tvQ#X> z6d@HW>G%7lH!MriTMnghyMMa|ZHD=A+5Sf&M})thk21_#lkQ=ap+iCX1q|_&BeI~D z#p||m;IBoBw+^NIpGDJpmr>xL^LdY+k3beAVGi^yK>}udZ8Tg_7Ah1OB&guZ_gmnh zLq;)Q&+*XzH+!1zH^uU#aqMZop%YR;n_S#~c z01DbA$1XUcT~2h(kugqZ`G4Jwj;l4A$<0mEZCpg+57E^WB93U5|%p|8x3;zVw( zb~2BjxK}w%d%$gTuSs8LrPzbtNFPnx<_;#wD`>#CA-?4rNwemI!y#9$W)Y>mFv5O1 z&rn)Zy?fX6hKW_8YBtcWx7Td<$1CBobm=*6WT)GhjSH)MiQgl#)1k&NIf;g{HYe9& z_g)Njr26OcG7ZN0+=tne41Mm?cir)!58ig`wO1WIFtc;xx+PWdwzk;eDvyWz{m3?V zk+>r~M&(iVEq+JHHb=UOUaNPWjgHIQx=AkIIyb3V=Po$1fA5ZM8`dowP4;&$0bENw zk%;ijAfYe4rPn{<`qB$p{gV&j+dau8t4I2JTjQlZ+%13=XLiGAlmS>v5d+ZfM6F4e zl8jeYSE1;O2PHmfY+6@+&bm(Ubv=r)`;$Gxy=|?vk&su6t@Cltt@Ys{i(vRk#6BrB ze6+@2jmgvSG(O$W(~38JKYzmZaXO%TiC9s3zbffPW&4oUYaxb;i)lj&7`aEuR&Cb< z%5s}oY`{9u+GaI_Ik2wkO4xAr?O6%n8w9y6sk*rCVk|n2AUDj%%Hzi#r1`*?9%s+W z#Oc8AD1MA^6`%}20ykO>07yhuqn5UYMOv*Sf|C-JW4W%3QcCUeu^s^PQrWQRbmcU1 z`{{G~`ahMf6Pr+9*5XoV{a1PG}+(Y5;;G9e#~F(6MMmJ`m&C7A7{^%>U*(1xhAt0xhDPe2QyofvmXV) z$<-r6z1_ekucS(a!4(B72xuj1KUU<>Lp7%d3VKAHcj61lG(Ffr4;@bZ>U&(9O#9K0&UceHx)p*$~_gj_xox?3qo{Y2vJ#Mb)wSW{u{JleJUt zWgzU>9Ur{q=9533^TD z{rDb*xr3i@`S;nm9gMz6F`B!C{V0r+hg}`Jtm2_kVV2LWQcMZau|?Z}uC=#i?dhq^48F>LL55 z_}Y2i9AEo$bKhVe)z%ISED{Fe^S->u2GpFrVcstE_3&MwgV$;@Q=IB2#JTe~@+VsC zGTBu-$(EZr^t7!L4`ZgX299^Mzqh%usd!zY^+Cq}Tbp3D+Db&-9)zNpH3T{9A`5 zR}sYUuAt^UIb9b-HrK~l0MR^f#buXXf=anC^KjIEjbn2;#0y%T66fgU@4l7Jkuc1^ zE@sVNRwFFT>32dqRKj&8F%9we2B3%V=yuJBPN$@G5s^Sz7itWO<)-QEZpqej>mjP= za)TIH50ZZ?qZC!JVjMLWI}@?!=E}{)0PI@+2|ttc{U;g2oIVo`|G$qu?2s5j%Jc)# zS*+MlGR1Z5|W#nIKvMA=4d;ebe*LD&_Akn`~ zlfwlbT$bF+V`nJZyhd1`6SQ>xMhGI#f?`?-Brv*o^LYqgi#N?5nGB5QI5Iaioa@NR zhIC6^EX!FXc^EjxgGTVeSmz>R5l0Hl$Ypev4~x6T&+=tsO&yc+fE}Jy=tB~g@ z(I*Y#MrH;MYo5Kxe!{;?2-80i!H(5r1lCY)$RdYyJD%Uf z0idA>XI6<+0uH*DhK+vL=KQXtn^OcWL*+gy=SX!hnB*#bDz3pXY+I~Bg1*|lYI z)r#e0>}{+kPmfqUd4O@oR8M;Yd!1xvo?G>z^bNjtLYf8+4=trp5=hem`8y9_8W|LT zy+NolZ7s1#;p(E*xo1Rr;E&V!XOxroDCak@ zu1Fs|GtChGpjC0M&I&ijP9oG!a;g^RG!hhes-*+5CmI9=!c0VHI{zIKnoL>fJzBR+ za~@#+nfvef(Cw!$Lel<@t&^))EpKXUsHv{X1pDamNjA*arj*mZ+VGaEzc3O%T7lUf z6GBtEGA497*v8<+r`k^_p}oIsiYA7nTA1*LjC#cCKC- zuC5M;8xeBB;lFen%mV@!ira{@V=RHwLNaRwCdiP5OuMGY764{ZQV@`=RI6^Sjv_;u zK)?3nH_d7VQO#l5GQDoi*a%ftPQ*ZU>mBQT9uBKSa}hR|Q?x(aj`)=H3a2}=K#eEI z5V)$E%-jWxjNlpi6oRrSu0?>9qmmA8VH!2XZX>Aa=0;>K76twIOwyk8i;v-3e$+Ibq2UaYmG?I`g(9pMV*|Enp?SaW!C}Yc z+mSP_*(%Lw8}fb!CYDrm&t13QcJuXz5Q90jdiiK|Tq{^X2!TWws8?}AR?N;z;73%o zg)Ahniv_XhOd&duydhJkWuA?Ml3evz6o?{}2G7g2;I?TdDT>{~xk3g4c|qY37OISu zW#JL92*#+c9e8R-YjY!#!C3{XK#dlds<8&rX`dDG$Vp)X=^&@vC~6+aqur;U=ER0yrsdN;&1s!<-Wx=8I8vOj8T9qM zaB%(O8KsTuJhfT#%k<0+`N>Qbp*l2Mf_;xyme*C(&6};ZKi^Qe46g0ZY3b9@2foEV zo!%dcp$4^ao01|UwazF4pEw|tii~iCQBxFky4Fx;4mH(2m%^5h4fM4%RWGSof@s)c zluxblRf#BL9_Fj_pdTtNIuAlT{e_HjP5xbaf%}VS&&~+GhyZyRR#O?$hXTv10?RAl z#bgzhSK1QzBq^wf%1-dW7$>40u!~kJFGs^e>IbnLs<^P{oJOMPnt6jDM7Fw$O3e-} z?dbxletUI0C2E(3iVCp4ggYVDhd>)lT-)a}g5UFf{*K(0-rr{6O6pA20DLU~W#pE$ zwOXnZ<)vP?rO;aF^O&-IY_EzOf5EreV4A)b8WX#a0THv?;+173Ubn5#Ug*z^$!$O) z2SB5T%d{MT%zTHFOSvq`UjaRcp0omzYO(7XJ5wP-`8(U|Yb(kM;)U@{2tUM3?Vh}< z+>mwVCq?`|bEnVztlz?dGT)+b{Y=7jCQM&^OM(p0E8_dOj`R|<^8F{g%?pK&RL;da zXc;`?n&LFE77~*boCkLfSCLYLdw_~Edw^yD7!E=>#nDlx?4~5`vFQx}M4u7`5|?z8 zmz5R>nn=g{30zZN>j0RuarVScl1m8H^g3NGF(3e%YGM&#)0go6Yu;)>(iAD}M$T*! zm7o>4QWY4=4b%nEubD1zp>os`+ySivpWx)`@nxvq2aSKf}mvp=W|mvQ2y`Ej`nApDr?d%KO{xaQ>GWKif{{iPU3cEG_?lH2* z@Kt1kJ*cgS&>8aOJ2j;$(`VIA= zKRqUPCiVH*3#4*7b($8u+A^0fDUm~xhsf-ra^KtB1*!45|C9PE?~6at_o=xxv#nA= z5;Z^3nWR!u(j+$I_sF>EuxWRs*3JDqHOu?pPxSffTxfQg6h?h*9sM7+8hHgiOMPWF zR0)80`Xce3)bCPn%{>XP4CK2fCG>td^#-O@mJCS>1LBUDvBYt(-ExwGV$l`-UQAs% z_lr3P|1SPS&&N}b&fX!_C9CmF%g8te1ICF!%vyoDQ=BDJhGdp0Fk+y4KDQ%vM7m|vHonhNh&-yx{NBWT7dKF z-{@%3XHC`{W{$6~3OI~6*9WQs4L4)Z2XI>TNE6YCINOIqP@X>i0Dl@pZH- zZ^%aseyK=GBmu>lIRQ=1wJAv^75bcZi$N-4MFxAVk1q|I)zUBHzX89KVYShSagEb; zXQb#$EluUc2A6kyXF>7fMP+N1Hwrzzd)1OiiO*%*vr{JKh&3Nqm*KEqz!Bu#3mNW9 zz~ML)d?`%B8>3OT}|5LTK!s+>CAA89(18m9 zzOC_{P!hEgvbKZYmZ42&kc#id%%zr~-x!IvRce2-XQr1fold6yh5uoY@b@JDYZA6M z>`6$CvfB)&q-v=;8K=t&=s8j!^PC?r4Pz~sUprGY`E3frz zGOjEtEZ%siZ}UY(R#QCaD>J$6L9@#U6n8mN)LmmyzuQ(4b+&i6?QXV)Z9q@#Dl)k| zHZM?ltR}0?R8+CSi@oSlTiG3k)6n&TQs0V4sWJxr7`zO&CWY$@m|Q4Xh?NkI0P+rV zIo_r-;CZwwV$b=dF7O9i^`g{Yym{AoqeHuQG*m^REmh?a!|829s}AlP9_nfwOV-p5 z_RyKqnaJ{1!)@R_VX0QwToBQU&D&2W!=E02z~cwn+j9mNIgGC|{fg6jNR(vA{{cHx z6|fsGj}c>8`bg?IVl(=`)W&+7JMf=NIh*AZx6d;tZw^45smB*=z|^t*iE_PxoxAx4Zmv%VV*L|MFX{ruto-#A2|1 z@LqYG^UAOkO#1jz)6Zr+5u>H%bKqG157?ixS#s(Z+5gIA(Dk|8dZBx}AG4D9+eTC% zOZ1x3y~Q-55uLL_2El6(jj%t7iyLryPV5`rRZ|vJJFA+eYfA!aZXVv*K@pR{H3koF z#y#*6Khqfv!vlk8MuX&IKH_H@=OmU1s^@Rb_r?z2xiP$SaJ;m#OL=qq$o7whELEMQ zYXg?rvhoVrzs%Ty99{{!;bR9_fX2?Sntr*xjUQRZwPS}*Z1B3GwNV=0aB9cM&bw_c zb4h7Ka9#QXXC2?;lCsC=72D-BPy1e?tFm+)!@Im2P8>FzLLG!!OJH5Fp|r&8vfaIN zWXJ#f{2}9+|MF|;bF&X}S-?E|AY_3@BxO(^YP1O@5lI0p?%SU99omFBl^_?HF`?2+bh2_W~mm~S}N<5etlun7AtxoxLUEPLeudV2lyOQhv zb!v31IyKudF%*hzy6&o}Uwoir3_1|$1Al=mKdZ^|vtP#kK$geZg9~z&tP=~D=6QSQ z53`#7F#81BLw^wMF=N58K`cUBK>PmL81v75k?(*1>=)7g4BFS8RbtTOc=S!6@0YZ` zUqavK(6>I_cOkB&_bs07W{tD2@V>>fub}Va=)2~<%GFZu>?u|^`#c|~clLRV1Ks7% zTo1{iTUz{3t_~v=vo~0yd|b?dy#c)kb0B(JSgyd_SYXJkKR*QpX81AliXOmnbC zAMhE?_@PvljPYG3o=xCH0E=3IKafxb8%{Z-xNJr^}KN=%- zr~K{&MrxjY$YV0Mv4a>hLZF$D2_UuzLzt`pQ5NaY$@FME2Iw#HE19}!ra5_o)QIe} zpJRl9WG~g`L@8g`%#pYVKn4JvBbg7?Iw^z{1;C*zL)$O(?A^U>)7U7Gni?v@p0$N* z;}MiV#a!opI`RCEkB&7COAv$F~3M;VZ{KNbA zZr$A8R{4qWw$6A&_fgFK*W6#Q7g_6klh%nk@f1Zyp0aYAg$NBy*b2^V1yrilFs&OX zxWy!z-$FqP^1@|@(I-6zc5T|QdJH&!U7m1A%s}ij8_fPb-{*hF_n~Qn4tUWWh=I^N zwJn^gzcn67=MUDSrq6HW`{wrYWI-#_(^sOSPJTcr9J=%V`acn zAWvFDh7n09Cyt^TIZ!^Zp>azh54XtVu#>Bg+j2nLS}(zLINV8opPY}tdkG}QJ={8N zoXP5?K{n$fSeT=;y)u#zoJ#~w%5$@GZyWA{tUoBNliC4%l=hBbG6qJd7LEa8S3HH) zB&}Psa#=?^B2G(vZfVdoXtviHxPs4ZaUEQ^gidav_a6!YiO8h>M-75iB8@~)ZG*48NO{uR-yi@SB>MLY@7F5ttKW#UuR=ZpjviZh` z-*H-EUH{;%?Q?hvgQ~qP;29lbUzbhso&fVW%#|gsGBs8B&Zmz~*^R|6zq&K!x2slF zR?Tkq>ln^fkrH!nt1AtI(4I#THFtr=lfVkFR|-QNX&{awCL6UyVO5``r~%HC;s{5$;Dh&do63h7PhxF$kA(M?HWck>GBZn}ZDnh7VOe6hI(o~qjvF7HymQ+}wqMrM zJt6<8$GdxV^Tu^8wauxIuYAXB4Hny+qoc~b@M<-m{(5SvHpqmt&PR018ZRB<$D`7bV=>p%1C)hD_{o4j>+mq+-=D&CEMcR zBU1^Zx0Yf0*|e3}1sFbP6T-9$0nJ`VmM@T`k$rh;YN)6CbbZUmx@fVztA=~KZ>g8p z7#mrP*_;-irJ%57I`v9JmAYqk@5qSLRvfhQHI0lYpBNi(+Dn5*qc?F+-0BN24|O7^ zPo}I1^>;|nFO(7za>Q$K`wQ`77&;U@s3O~(q1pknlNC9@g%e`%`^W^M{e07zjzC8( zRLo9Bm!q>^fjZ4J^HzIXjDgKFaYtI(+m^|0hZ*o-a#L~0t6Ht>SF+dfm%GZ8Po{nV z^yt(PZ>&;&DB7t`msMCxyv$_}7nlC|PpZ{j6g9u3Y>q`|Pu6;ioP~DM9~(i_t=RV+ z(t|>lp!f}w0e){3-$a_)#A^%KtJzlh1c0=mP=ig zffI-R*MI)2#b>YZN#JUYfjDMY%LwwG*c%s{Tgu8wn4q zE=A~cw!l-%to-r3xxV^p;)sK}539OQe=9*9&gAIq?%cPO&t(8|=LtifG$XZ9Novf_ z$e_vryFx;_MA@S9t7DSZOirv^np^?^@rtt2kk>8Em}VdhG{X1HFI=jkJtD<(ZRp}ZV?&&=gZT@Ii z@9?T#z6a0{4)pd6jaB)hHg<1$B3jaO!Ryn``l078`_9)Lh3xlZV{3=4RykN`bA~(- z_KRrU<*DC~Z=y@)bkajGyi&P$w4a0vz~z+L?1RIp6Y&BE`*eAE++y)MgJ%3RviFf! z9fj}wVY12VEP$_AliRmK{^%7_M}VRNK=_a{xyTM9Hs?)V{1BCpNwt7-Ef~^QhL(mm z*PJ%Ms9mOT8C)0B1;c4e(1r?`BYQ`=5U1Ds1Qny-K@KsJ8Q(O%X&8_$)gvk*k`;QZRU&N**f1-g3|cG_ ziU5EY6JO^W(tt+zHPagr&kxE$jxpLUsQ6Rz^o z?vg_F)(2D1rH)Szj5x&;P+~C+jUW8sh0N0w4$tmyZvQE}lXV1H!Pg#2y?sMjf3I1z zE4CSHyDL5N6->328_J@NfXi%W)}o+$@I3eIi)Dq%vf>>*ZoeyJuviKp%|H#B`;Np}Y}JNUQCb+YnNV zDh&5#NIi3ZP=9IICshHl;u01U-rW%o zb0CP-v6Wi{VIHBPy+&9GW+a!N*&QxBY)f9S21VNFMxifyfGKdtM=$cEm5{B4d4h}@ zquO9HE{-s^yuZ~lGcs#L`i0T4rkzwMmoahE18H0h4~?vKGtAhb~-LZJJ+pMZSSWeG7QJ?TN1VdK4qPCYcwE7&b zgU39+U}>q_;Z-l$-rfrML0Q@gx$arWb#5spjS3rHki;;GK9Ug%DME7y2_b|iBTScE zMHDPa5j*r_)R1CQq@pkgb~>~|90c4x1QDnq}gXLGDr(Z zLPx**#0@KQ1tBJRA=__<95hTGH{2ag|HN>(mOfSgiK(gFU|T_(?P`D~MmCU}Ej zGGax(u+*XcKn~9SUx(w9!#&+M*TMAAJIqZFWQb6HUVT+}Z$^HCT+uW1@t2g}C(7Nk zMc0%FCP2Dkmi=J?nW_Z*v>CeL0Ql(==}A#s6HlZ`l~$M$(GOREaUqi3)HAZw6GP=K=|SWQJ{epi5Vy%$jnY0CFWnRj7-MQJp;ijzog{nS7|IgDX0>6n z+fp#NEnFE7uL_3?BNjZTZN!8%yH3_4$#fM*Gi|9bXTbo5t*4vS+M?X_^!+CX#)mII z7;XLJ671z@+w!4H_b2M^FZvaW_Z&TQQFU9;#(KklQoi)$LmOrrS50RmuaV`-&YA5F zTUk(cyztqbUpTR%$Z2xhxNOmiTD;$YuDwor`^*x;?1h?5QTH`4-WU;-Owa~EsGA|? z%fmysGY30og0UJ^NvOoP*yG0h9>{`IH|O*t7=+2JZuA~@yjwT@LmWgQ(>vLE_Kuk> zKgKWwu)i@?IuFP4v0XK>aA+hRnSqk(!wv_*!8i}2;0`j7G{`Waz|KN(Id}^pA>tRr zF|}|nV*t_$ollhHM;`ozlae|+@XW@$g0_EU>dO~i7pQa>89ZLqZ*#-5&~j<&{_iEs zwQQo_7fAKmte#`+#Wj1X?9M_5xMI?;sCG;F!IG12eQvzzWUD*$W45HkWT{F0?4JyF zFAVSQ(&%O9m3YD~cbVZvQ-RB9?@Zm(?+c|Adyu)BR|bPYJ7&tB+=ujDD=vV|?X>&{ zn=H55o7vc(a1yvbozO?NBMxV|bm&YEOe=auQH-2*GB>M`%mK3s?7Bd2+DJKIjseXy z3Gf2^+WDmt6a6^P6$;3*fiM6_*+^sm|Ta!h>+N!$kgPdzHev+ zAKiSW85^v7mGkMqCtVmXEMgyxJ~p zS?~jeU6WUhkDRJAxa3gfXVwl}uxe>_2}ZB#SiR{|Lu*}qWdTt0l;J~9ymoEs_K{7R zKwM-m`4GEcyFfqZQL+cUq7~C;}~2`B#SM>zDyf>q!A!std%O0 z;nl0gMr*@BImS!aPg|#(PY1fYI*Yvs(TTM;D2StyI}zn3v;ifBxae?bcpq^0(Ko zb=V8Seix!dm~Iq8#G>;1*}Fo2y7$>PW*=7$1b_U|FQ@(hJ@swu#Ydr|9hKIKD7ykO z+`u>qhdPS6LuN=52R&mFL+Xe2dm}{ubTIs;B;?fHyVkGjX+?~cEFEP>OgZ8_IgxbL zofuHY77U*flrU~UpLg4Y5dfiGxtmU048!?w2%SFNOn?VRJyQGm!k(eF>o&X@Gqe7N zW8K5Oh5yvvaEHHVINDkC182aRdehfZ1FiR(n&!vhsTz2&hedQ11-f4m3?a0N^b!1^lH%IvDqC3?us3$KbIG=@L;^r zV<@x-;YfyG_HDykkb8DXtA(F3r0q3aSDCcdG)se~Na?5$(7CYiR*XYaY}PAhX4vZsYn#%LsoYhWZb?6n)2?aoW@ zdDc-_* zi~+(hWmf2s7$jtcGfgGzwq!tJNM6Gv$W9+FyN$?6wh!=72ME|e3OsmV|DKw#@L|Vn zcx>Xb+gmB5Y6x&JbO|2_xD4KQV*vaG3Dyu#y1*TGAyM>N5I@jvLs39?{d z1e_~b(I70youB#EW31uvD}VLyp%*^ohViL=TkE>RWsx$IO}5t+s^$P5^V&W3lA{A| zuOVzY@bTnDkL=y?nZtdzos2ngXag6P+V<3z94HT%)xtWvY%`(r!40*O38zK<{5Y#% z`&j+MyFUH;RbNfLJO#5HvqanbZDzbck&A~5qIlv90v*mO<7yVY_wbJ%Kf3Lnr?-7G z_1MnP4vSNktzCQO@qStJ)DgL9ri*0YcCT zgj}9-0}0n^h8J^li<_x_Q`08#wKMToz!8uZsOoEgnxgL|17 ztb~O`XzE~6qL@sjWHlIpzP4w#z#qd7%+42GggqgYi6 zbNbX74>NHohjjZSNEi_4Naj&3CX33QNQhdP!DEI5b7Z~pFcm7RiT7N%wgHU}vnBxA zkpeD5(d)w{py&kx1T`qIL@YAW!u>(7Yo}JNh*kn#HC7#u5V9pd7mBIziZzIs8uoEo z16eYqmxM>584)84Z}cR+9>5WyDb+42sWQOGU#H%w~BKnhs4{@WCaEj)3SF zWMGjKG;5@+{s_Gi%cja(`Jd14oasm6_Q+7IGF%yHNFZtks|5{uQm?@t!q1Cvml2cp z0lrq)!lmDfnYLo4(5GPlOJ`qMWP@dfFOkkdFaj(R?h|sP1i2nUsr-6S3LwAoI)l%n zE0QV+aWJbFxfK`hg&i^N&+STyVLC|Y{Lk=v2&o8SyCWtT;5c}7Om>|RvvIdEwg1ne3^2}+j zOSF+?itjOkFfC+)PM5Ug901o#hsGluW+arlY}nMc`CUbjkCo~PLCZFgmQ5@KC#ZKr zq}{Sn${hxoiJ%`IX(vNI{Ur*$OiG=IW4ZARZa#mkXo}DsO%Tl~sZT`^0LTgkR|+K5 zAoHb3Bg6=hh}kS5Z4`ec_h&1(Bkj?`T~&3VUeJUpW8%73k|yMY0FS4HtZZfQ1dxx| z$R8#$xKVge3q?iXog$waULEKHkaZy=LtKzgd;2$CHL~}j9iLp&x^_w21)gP6y;iv3 zD}3Q-=LU~=Vp*V}ved(ZCr9enw$`2)XgQTOZl``1Y69w6+c7P$8rqf9GlCCBtW> zdTCO6`iu+uH1T**6cRN~3{)sWF&h*!?IVCNz~xOQO#~n&MCLN=rKpmi<89=1gdqiI z02ZbVklzgm9eF(vvPAi73wM)AG_b|%E32v_@uC=ZzQ6;uLr4Wi7n~0iHA9*wNo`~T zC08c)T;&!T{gf2!I?u;31{6qbIELdjn?H7F@Vvq3V7Xl>x$gSQpF97~L&Y9vxT&Y9 zE8dd`IgIjecC09Dh{cum<86Gv92vK%rhUbw0MRprp7UGxKfBcOxf!aWvGLqj` zFe{2lNw-Xf^BXa?SITNAVW+=R8IRP|SCTEU!^>^KAYh1D0sKcZj!+B>+(bmq;sQ|K zoR=t@y*_k9%o>OeT<}7dQ!Y2EQEz0dwa#B+b^p!Ak|i55`3gLuGe%CO<% zQV%jfcL;3g7+5kEMe)2Mj*UW;2uO!#x9b>8p=31Oj#9cPcvNq%muVrubW2sDv9hY6 z(&U3FiJL77f>KWs;X(#bD9ye~ZBZCUf$Yep(Id5U0r z>wa{oYV21V)$N<^vG@|9 zdwvbf+uRMGALj0#MPTm*TvoC#nH-&pWV56=M|^pA@BX=@un5=`pfrj9&Fj%Q(r9G zEidi4;pp17Z3%m!!&B7XaM`2Dp?y2T)!U*))k|X&P0?g1kqEi42<*YK)N%PC!&9)6 z0kw?~IpQRI0pbw+$v~-dAQsZ;6vCvzxx9vMS%19?LTZcyK+b2?<2VbyD+(XEv= zZKGJVuMM{bkuedz_KwzW6!?K1gg*zu%RA9yvko03P-nm_WD)w^a|U{jy?BpuUJ{X{{s`wuLYM()%e)z zo7p{o8}nK>tsB}Z8%*{jH0uqprC99>DpK%?)=`@f+@vyix_CjrNH>uaHob7JDwiz(K5@K$EzCN)M+G zCCl(L{E{Pz=#6o4^#;Jwj)eU_S*ouMw?*1Yi+vUT3V5x&vX{KcF}i?d5KQ3B?}#g~ zPhxLc4In-_A%cJ)*3fnJcE~4Ft8GqmN#n-u#>vjwVv}t9`^F8)diZCp>?>BM>xWCP zS=UzFv*zk$L)UI-+w+M0r0lZ|UiGO<6dyU-#=qA+15<+hwtsm+U=uQEpf#`JI2hpfpZWHou z`sYY&t&CJs!lNHI4NJrGF1QO~vvI{(YipM;>|+k|#>6lINIJ z3!#Q3YZ?oZ%?6vLd3Sg7x~`Z_Su&jPEI-Afm3C9-hUTKq@m(x_0i2PaPhBYceF1w6 z>EKq?WnwS=z*Jyx*{r=6U%S1(s>B090#DhjCDpVgGYYMI{BXcWeMEnJ0~Vn8q` zg96{_!VST0gG@rv`+=^`+Ujt5aiPN|O|dC(nE%h-o4`j_mg(YWsrA&pSE?$hRBB75 zl2rD6sce;f>vX5n>3vHtbklT0FEl&NKsShp;NYM*3aF@vh%}3WGmPV?*KHgJy$r*6 zmGLspsKfYo=5iUj`v1J&IaR5o6S@)g=l%T@1UmJ7r%t}*U7z=Pqll=vDb*v}i}7kP z&JHVV0G5T5t#j-Lj%;H?&MsKgkcy#zAC&Ool{0mkkV{IAG}dx2>-**)5ia=Bf1dp6Pwj246$%G7Bi#Lf4vKBbl5fnXy`@%iydS&ptB| zG9tPp`I^UYiQS2z%5YPOUGp-0mf5S`)7`$lTre7L>&X6F_6rk%Qmx2Gx=N?-e|WdQ z$Ok6&*&Cm@U;Z7*PI^)OQ*Jl+6PaZWT`6EJ&*yRPdNjq;U0^V=aD-Ny>x8W^15yk? zwzd#uBWo)Mo`0tgNyE{h`D!va5k#W0D^w(|{Fs$q14q?Hy;w5+u~1c6GF)D7HA1Rl zIhl3Fd`D6mEQn9G868~NeV5e;leV$}r#PK#YYOc(Jc|_xL7HF}@!x#no7WElaE;r) z*ILwhZ*U;xzh-9Z{B%p=@{P85$;g9~bpr$8!Bph?5qDE-TruNj&A)Ott*`Ulycw`~ zji@&y>YIE!yFIEW-`}@M4KR10DR$UVv%bUDBUSZ`oXRwf*46}zd;A@>rL7a&TH;$$ z_DFwa&bABJPOeX0`^qHTM-3PXZGqaM+V)Skcxb$ zkzsCt6EX;36IR%l|1rbF1VC0M={Os2113hzEC&EnENS!SSny zqr1+2ZTIjUU)wlz_qU42uD^HU%5R>UyyZhPSN!z}K09&Ui+4>P`!?PYwfyH_Q6DJgvTVK+xfHiYJ_9}`%4AQF z8j+E-A|nju8E!N!KI}6^kS+flAK{_k4qEm(#c-aAckav)g)fk%Iq zBRDWsSfmWb3)ZpeHG{_Ia0crJG~ak+{P*gD7-<>*M^; z%#l4G+fv*+Sg+QkCU+mqE==Bd&&Y<)oESEOb!vFK)@Ho?;J$zQb6IA*DsF;6VkkivW95smRb0B9|^cGQ~THS`yY1+^t+3d$D*KC!VU+>q%r#eLCC#>h+nE z8S+#X0oThMic6*vpDL0I7yHtcrcy}u-Mu$OI)1}1r(;yR@_XZ52@j*A8koF<%@u;T zy!}F>24?EQ3q#>-9TMMAWB_uTdl z{spzoB&gFv?$(hH+em1VPI^@=Mo=Wlt(PTc8Ob|b8fc;1O@YwFohdjBbgPs>xxfV* zyf|dT)A4fjULt!CC{8l?=|JP1w6&==Sz?8Z4NG`qfvWY$HgKSLi6kLz5sx@op+YG9h$Rb1S3no!CqRA@ z{T+SAbA8YSU7bE}nP`H;F8`hg#E?T^Z>GqDf*8}f?N&isqV|X^av#^Y$bG}_j%-U) zBN8gYnA0Cl=`E_yz5e>=D3zvlT9ZZ6BR~GP&%h@KP=R;f{x{Fv4kb1A{;zlLzPcBr z4p}Z7?T$E(eRILgtDkt1*IEW|A2SAgLDYY_f91@+nSD6>rmf|Q`86GgbIeJ19=r4J zEDQZ_jPbv;{P3R6on2wE|GqbaSCC(O?lJLCp*u&pPdq)^5EV5nCq)h}5SK{SGsGo| z^VA3$Ko%4K0?q{CCK0o8FRmah?gcY|OC<`v3#L(6tbF<7X)vqlhKUB?i}DSGkwj3e ztRfXHs|-_6j1CQ%rvx6VMwn-*+lx@PL&;!ic|^7$xeGjKa-d&{RoQIqa85>Wj}7te zt?f%)Uf!p92`M!l8Jq4*WA;D%jWFK)GDpBjEo{ht)g280n+k!s|Y zlY<35uXtaK|GH?CEPB0qO~Sr4+kfBCt$$Vq1VCs;T!wceC+7v zRA;fSZ~GOuG-nPSSnt{Es%mdcH65e47PvK@J^w52yFwFTRfyw;fBG|DRX8<}XON@Z z$dDi>O*Ng6eUOIKdtR%>QAEURNklI{L3<&i(27!X7SS!96%fHAaoejH9@zjBxq z_8zRk&|-oWf|OE0j8(isc^6Fq6eX8BWz!Z1(?O?I0V;&G(t;q)p*F*@iccMzD zz4AK|y}!IBdLZfau)FD<2pwleUlKxd0D>a7pvkdDVPWvOUR*#pgiTp84%;(7C*L>_ z!eD#w9)D<@JAZH(H274EUqbUFYK5-gUXks883zN}bb^Wj1w+JDXt|99YL2jK4jBPf z0gek*z#XN;!1Ar2^YhV<;?f?FZe}O1@Vi%-yE1K63D3#IRkJe_qk{uIEy+ZA*zG{L z1|>`;*wlbb5Q-S`SH@_IzhjaL;ZC#XY(RH2de9kN-Bd9~Uo} z?NuK~jm=T+MId4k4Og6@211zw$p+qeJ=(GAjc94Y8TRB$ZEz5boMc>9awCV&S}t)e zb`Y6gN{hjIkPE?EfbCR~%)gZHE}V)J;5d9}`_}dAW~M9RWjk3p4{E|!fx)j>&I2el zqU1OjFk4xvDa>0qq}BKr@Ifr@NuL1;B(RcG1^m&al3`aEBRo1&z-Kfy5si1Rm1};? zl2Y^Iy!P=MKJ$NXbcH;6yBIPXO;$5+MBQ^q(*KK*klt!k?Y`qH|GxRm`#Z)vLuJv4 zvf(Sb%8fVN-G7I@==(!s1$9Bg;)A)mAeWQX1w~aFqrRldWqx4DU=KNyycm$Yl3A@& z&t$(>9}=3gZ@zg;Hv9DTPM=lM7>g{SYfe9SedinhGi05WJLyha0A>BpI{tYn3`eI1J47 zXT=AFH3xFZCOHUL?i?wQfTP#5*&Z#~;NSgm&;F&H7L4m*WwI#sHAb`Tz-b>i%)RSR z+Ot2qMD8fU6a85ARn!a|;QsY2tbXQoQM9O?lk~U=2o8YtPNeq9SQLchk?R7W0fyQ?NuQ64}wm(`4Vt^@o5itocwrrHCRAu2&q2o9Ybuf}G9>cB<54CR0L zdfFhZSr^uXH4H;R4m4rJ>PocGun2onI+S_e!jF%9?OLs900;PcQ^l9xP&b^>S{pVc zhF?i+*dhROCGfHFh4)Rpe7G^B3Fr;xkf=55T{^!e$lv&rtNz!g_wD@5|GDF`Tcqnz zZHQQhfdBDmpa#Wc)C(#@pWhfAIjlCH{`I|7fE{YDs*0;@lDea7q6ao5_Nec^`%aD> zXxwZM@j=JLA)UpfH>xGkP|NRs;?3LM%WJ=O+%J?Nccj*74O*k>Ju^nTod-vPWJ_)- zGWyFIA2!dS^MbeyXQGEYDMz5I5TC%|AjCJs6HF{NIWIkP4yZzIbfAX@h&g$}8 zor%ER;p-1R`n{RY9KQWqPFJyA)v`8FAM&o>dG)T!x$)+pTJ*PUm_OFhbIoPFwN^*y zt`2MRwhe^#z!^)*mD-??IS*G`8_22Qs zx1;3I+zA6A_jlHLaekijS7sN41=NaFa8-z>v7|9l^wfd_r56B~nm- zYVUwTx<@n_?IUk}>AGikZt=!WHdqrQ|75eL&KeH~XHWUe8#ELnNWWT$$_^Xc} z{d~m$xJ??4P~<>_U&Tw=tQiuiW&&i+mM=$P&0jfa@2C`;4vWZZPX) z(tW^IX*CO|6gQ!=%AiG+h{3>3La6doI|WXiJqtW{#pMgf_8i?jH<9UTudfUilhiju z9&@|-on}ZB8FRsEb>B7786^P;RzL-V*qHn-#1JIJ4-2+Sbsby8JIv?qx(E&07SN!f zBK9PLkP@+%+j|I#oX5q>fa?{0Uq8I^|1ofg^GFzr0KvTqpOL)Yx*~qsopU8_Q zT+HQf&At|T47?Ly;9xIK*(=ZSqgNh2uz&03Sy0ZBfnA&oh1{0BkzHuob@DPsoQif= zrjWQq6C6Dr0Bpl`W+ph9A#}FXLscNuyF3yHw7U_CLSe3)TJKn z#;Eu#ld8bQyj8Qc1d7EcZ6)oDn+HE{{?a8Z-pu5`4Egf>>5^L5R%cf@m>EfSvAiRq1xdPN&vh9Sn6R8~k;WBhoxk)74!b zbB^|bPNK!-7w$5Xo%8qoXtt!$HRNE`Uj;)Q;wKYbgj@PM;MyE0O z!(X(OxCDzHm_(1dsCo`gdSHA3$_W2s`baaz&hHVb`=etbN01T6qi{Ch2cQMRNfkux zD7EcWO;WdU>ZoT#@t;z|m>;ZW1#WCGBmG93Wj2L+?a>7EvE;df3mewYqMcrUFZiPx z(U-WyMaZmGLVd(Y4AwSgBe1j-)KXqa6_{@1sUrVM76Rz?DE*3T^hJ(|{2cLqv**#R zz6~W&`{o`gi;JoirQofy%GzC0__?vAov3R7KL(fYV@K8LPxJs>E|H= zfT$sbrqbzDqxhGKsM1xseh8ad>^^!Yz0DKQKpRQow|NA$_j@-l@vgp~gbF&FTz+8h z!j{?b(fXwCkpB=8d1VG5?wP3wlTyZO2M*{Wh6ZBEOa@$l(13bk_9xU8F@rct zbPy!OR{=AVlB3E9#6*xk$5Uk6n?KZ54%BL2$G#&0(XO(7=GsG_-&@;ehm9y^A4?6j zgUr$QP<`1UM|(-E&T7zDO+MYv1>6o)Nx(O@_};MeC;K~sn~N)MbGG-*w`6~K>hU#$ zbE&e(h-Oo23Zf-rEvY=b!(a{{fAIL5JB|n4I)|}HBfPYGlTPKcxHaIZuo^8Mt6M56 z3)I=NK{XiA0P9on?LuwL5^Xj)Dg}+(Z?>O4w{Erj;(2P+bV?|zYgK%+=#{V_##=Iz{q41b6K5VigTF5z z7|#)W)eOwkiFYUz(Ts$mYv`?=nSLU#5K#jCNACVh=kAwdjs5CQaPrJ>pOcYnDU_^9 z8gRE$Yz++nAu`YngKGUHaQ`3|rGt$_{Gk*wA?i>$VUfJZG?{S}xFp6l`cA(D(!sK3 zYGf#sEQ>bBLkF!WT5&I`jT}u4i(tBQ6!(a~sB&Ia&T4g%^?*AcE|Uu>Av4H-bv_%gj6#Rfx~NlJkIW#)4}!%PVvjJ?=&RAt`e-~{UruGB zu)a#vGU^3JGc07vkwD1jB9=_;6^38j^`*^u`-6i|WI+NT{Kto+ygT z)Iz|M96od|erP!94G5~Z&D_3s+gNK!3j$cy(-r)lFISubkK@$vTYn{G1b1biH~Vs_ zKQ_2lweVJz*=^FpB{v(4*6c42`J;0$+;Qg%b1~nZ|E@Q=>-RnSQ@-uFjt=74(m~;Y zYN+?Wzb)zQ#S!s`S0-wIp8eo2KR|l#k9`^_)CRjKx2Z@Z{DJmqIREPTm$hF*4FBug z>rZ`Jk9a8saVW9VfjV(f4US`Y9E8BxVMKV+!U>jLh_QhZRnTyAWbLBC0*cT~-B#XW zF{1rE&r>;B&U#+$8JL5~sFOjVS?*~|p8%_OwaNGr3%_U8M_34%7y7H`pZnqykAM83 z2R?N3jW=9(_3_L0ZC*Dr)X|2NTx~2FvJr~FeySHR%4`;0{b+~N92!V}XuVt%)E)EO&BHfy3e;*je64ax3M@M=lbIwjNV5efd9 zZUp02@Ucg&Zhy>ip^JSrTZsWpqdeAAP{HDR4@VLbcKeD_>+k=g_QSZ@(r zC2ha>VGw?^5oDA)qp_xTx^b(cx=J7Hh*Y(O(jAqaW1ZWY8UUhkXhiuX{_l)%u z0@fZmfP$%@*2#a@(peyq$2`=I!`ECsO~KdCOizsU_r{|EpT`B<0ul|}JU>tPdfCH( ze}(S!P;>W{d&erveR8{|(rV<^N*sLe!Mpx+eDqLPm(K087-ALo z&rV!B6018RJY`OL{l?1C*;M)9WOFq8rmG}k89EwO>FT4N`jN|e())dK%O=Sd3^jDF ztDV1RTTcKX{nP{9M*IB!>pFH{)4%5Q2DazaCuIzJdpoyTmQO(>SOicw5+sXPkD|(q z2TK_8`Cwcky-oZf1o-6;T4W;tz(5dC8HZtP6ySE7z_(wOu!X9wc7TrdcOPNlAA9#P z71iF~eh?)W|DqVmHQX6_mC;yOP+}+`T0`L-LMTee12Gh}e#`^~qL~8Utq#;AGO1kS zk~uQX+mbnm@&inu%#?H@n+LShu|qqyZQZ( zsL8}V0q#Pt25E{`tH?~;5=xmMd18@rA^67Crh~i=)O4t2P#&{MdhyS%WcufiJz)Od?E&-09&7Km$J+n< zy*LmH)Qmu0jjhs)_+$Wefm!JSkg4FKCBNMw0}tx5G|Yj^gS zqj!E=D?V80ZBdjS8MFnyW$*xhE#y>t-(=tPG;{J8ZTth@M42hC*62-dsje%|yFUZ@ z_8cHLC74I9=~-S}Q7-I{Jvx5dJvz1kKl@+m39g^poHmxn0FE`nZRDuNmI_DF0R`zH z#+3__RVY1RTolkGW9RYbKgUcEa*#`!(~ICQM5-UXxuR+WH4wcZSB?Tu1YX5oTt{Kw)Pv*^4#C4g#9?T>h7*Myoeo`-#odx6C*3MUn+zj3TpW6jHaATD&4} z^M0A{diHK!{q$v4z0V-F9Xw(SqVZVSmKK7N?)cc>U$N_}kKQs~Jw1YR_s8BA)R_1k z^|p}n){sl>vzVNR7xPnRtnss?q)3L_~C zeMt{ZU&8Y!AOZf2wQCTN=%}wPvT^J9b>Pn+{;1zt-uQ>Vtt9bpEg>v&YtDZI{Uz?_ z(qKfJM>xSkTOe+UAK05(wXJT z$StfHSXZ+YC($$b*4%ddo+0!*@na3fxrwtG${@lppjZd`r=qfk*UV87xd%9^$6n*`g7rrh|@PW}51y@Wx zm^-rBjYr%4<}g2cA`jmJ4aZwMr@Hg-E#LM0GjYVwgG0BIJqp`f7sC4N<4V)T)FPGR z7tjR)$bO~Z>W>J6p9oJD&Zea)8!pLM(|}&+r7($!!E48Lk^_S zbQ*`Z1Gmtd437+ElP|r)m0V$4gtp09pTn4a{je`KmpXc~`kk)k;L4k`xw_6ov1-DJ zyaCM42)C0POAo{JlKbd@-hoGSR7*u^sDlET;V6u2$~&NC4q!W=*kE^7#ckcZcFjOv zM_YYOJm|EWP27kogYMCM{u!@ zbo+i@ht>soR!M*^u(YVU#9^N#4nT@D!+rw3j{ciZS5)3nR&*!2hNFMLD%C#AO&FkF zR~hM1VD>Alf0Py(pe1s-sO2Cfio1|H%BhJZ-7^)b@Rro6NC4KGyx*=b8$PjYA3oIC z4iJ=Z6V`jn@y?J_6f~+}M>kvVi*+v-KLk+%w1(Uline99VDm2B6lyFr^eC-=z1tLv z&bI#ZXs|)2V1xmoG^$FFGSTaZ zGC-@6km|rPD;~dE&6xXGGg^@Ut}-9e0}=$8;bqTRoQ;x{P4nvkcgXbjq+6S7Q}Jj? zpvV(7fOiY%JBE6o$N+i}!U#aTfa?B1*Jo!H+;W*QAP;CXk7KNe3jiW$o?JdA&t)8z z#1@5W05Pks7~6aIx@Y(LdkqGYT4U0SeuE^6&g-RHPJfmUJ+%3u*U$X>W!oGfGdi97 zG{NScyARLZvoIV*p@b;ZoouPtu_dx~RxRi(-pYpF;`(h}qrt;zK@S-3YwJJyp1mD= zi#Au*>EW;joEvU!1nX9r;D0*%x1T$cee>JbCCWMuO*n#lsbhUzN6+A~k3F`*cwLvEKE4>pR=)YO0daaA}F#%=PeH zNW^;lz&-3SmpGe1>}$HgB95 z@9u1FtcV3XXsj)8v-~XMGhEFcm329p+W>x-(KSU5l_4!;RG^0LD!BVuQw?w&QaMK6 zjE7lPrV%Hq-dnCDt@2xnlCO{I{hokMy{?{*nksy$M1z0mi_v+P*`QDKr4oQxcr}^W zmLrncwfTdSGavZUKMho`KU^{=tM+T3R`*)+E=+EBVTuN><>x(qgOK2h5xp50J=pz- z$1K_O7RlsyCOnPCJDbn=1(f<7_`vAG2iI3*Ke_Kc{uEF_75o9x@ekszP;ZwMj36@@ z-LB7ZSD};;WdJ}4sK9&#s|h=XBJzB@DT1kyF9dIgiONb)Fsw8|={2=k!-CQFM%9t)Cc2@uoGckvhjMbGp+2FSbC z1q_T^D>{GP6Yl7CgI&~RwjA8b|7~5V-di&r4mA`zK~7d)x4pId@CUvZw<0t*&=ejw z>4c+2)-~;qjtuP(q$e8No9*dcNlkR!2LpPGQD0Z>4wkw@quGH6WO+twc^rLm%DEQqnzMeWYVsnBaQ6KiMV4s67)vALA z^f!J;AR_!FphlQ<2_WIL;E+`O3;Ujb>?e(TKYM7ypX{pVKbAal%XnMuptnjTXpEiF z%zxhgA)YVY-02Gm*I#`q9d5r?`0$gz`t;_9ZW(XB?!^!C-+Q59C~Ah`WsHd6h}`ck zFR`EJ-f(?ly!7^wZ~yqZz6W=I{x}v%MDOu8#kklFj;DU~%f3eL^I8Tv23R$;V8m`} z1R#rpg4XNNp$^uKT$x2ZNdz7hKDp1?Btd2jCh)z|*OH2|$}$6X8(>_+18XyD`+8E9 z;9rV?Ji6cBkIc1IU>r;^b4WI-X!yxve}7dnDnwlHG2x-7UhsY#^NB__9| z8@1T1H5HjJOWmhp%WC=WsqDe_L|H?vzcl>SdVWDSdtlvmlS-rUCI%||wl=gm<@|Y(Ef@+3Xj#eGlrh&Z&0*e$36S3E^gfqe*A+Bf?eV&9&{-q{bcvSNT3vQY!qRATHoud9 z?UX}snk1`6QcG(4t?soGks+a8?qQ|MzABy(DR;P;{KGi0N5Kb!foR}(1EF9!&5%jb zt5rtS%P^);bRwD@=egOL@y(N)M~8dUZLs7bWuYJd5EiqM8{@}NtqyQ_bCVLCSo~eF z7<9n^ZpsI4Rm^(Bg5(kB@`~k-l}E;v`9DR+f9>|Jsb5AX(ZuUd$i3`tP(8cf@6OEv)R{= z0^CLRS|@rjyaHVj;;OkLveb%$hzo7K81{!pNemW}I@eMa4C7;4wA7-xk@yUQ&;yF9 zXV@H^vfMcIDGisQTX?LjI$Ry_Iql#^(QqL?MBF{Bs#OyX5#)r{l_lD84OzN)5B;1v z%hC$-FJ5zJvZgQl`GEKFTWbzPEbswTy4vw$eee6m{^zGAZ;n5*t;xAZoZ2(9$>s6a z-1UQG%2I4B?rMwu*Vm4G?RffVbx;zU+6EA-e)Ig#)pO`U@fiP~@^9VY#4i9MmrZWF z7CMjz{!N;5pdqVj(gjGi8R0;(n|hd^#@V5Y793E z^coz$>yn;GPY310RxkflD$mkp#WvMuq5q*&!C$b#Fua{?j1aDlnVd{&YVa&3 zQ|a-z1H!^MDU?KCraO^J4Z7#z;jXfXx^1Dl2|B6Ve5RzexT2|gp-od())|h^yZIlzFzI0`iV0`e^b2OQd17?E zbf~K&&|h!&NPFiEpp6!recuZBLS_|TYH(PpYl1(sSDH0OZT6Qsivbc|ERgUZWHFfC z7T&2fYRr}Pp9O2GEe=DeXmTV2-|{tsH&`&t@0C3E`u;#k*CA>BD1KS!!}{~D3y+8+ z+z|H#Ip3Fp0z@p3h_cYFl^1p)PX`KT6cgG~pfbPlT#HQfRzMZDmtIB~DLD#3qQudaAs_qLX>v8$%Jx4v_F zbo+eY4x`&sZyau`9Bn;iY5+5`BVdi%Y7Fj*?FTA*lZ?N({`{NxEnB(6#MMrc#buxd zmgWAD2Q8W zr1B9MxAF?)LbOEbfhrMMugaDP)sv7dQVn`!s0X?=7uYEmJVzwO-AwkuFZEuBGnJ?q z%S^*E@fQcoA(tC}-awX$)n;+IO3aAFW%p@JhOp$yeUhRcT}XsF@Pp~hIHo!feYJ`F zZSXq;;VGxjrq$$(7oD-pcka7RzeA_|NN>@hZHztOGMC+=xqUKc8?bEnR|%|4*Htyf$DW&!SCRnl`ZXv6`GlFFH|N6HhGKL#JCT% z1pn3}lFRxp$~#sLjV-4s7V@)J9-ppRVJN^)V45?)cr$ieOvIOfFOl>j`wATO7|)$N zas1fPD*+havUy{=tFxoMH4%@Yxdq`%;c^!7MK%_IFOagbK?#Wq6TtENI03#pJpRFz9)qwKM&0xAaA#SWrhRvHQ$?{g zWl|fodbM8EiJ${hY>-C3aol2#r^;c21jz>39CWQG8zikWYQX*BRTs;CAhST2!z?_k zxVy}ja8Lb(RkkG#cLxLg?a(NQ{~CaH%i9#*Z?W@hnoD6*RD5hGH!>pg zdNAjZ=hPs1msS?klm+QW%HnONvXWARH=<`=roXR9AMqNRVSPCV+h}0*2B)9o9cIO9TKNg3f_Gn?( z&aG=FC&pUH1FVmf*=$Ly6z{Y%V_8xAES!VTjTnQu4L=*<2p<1QaeHPt(28M<95Vw8 zA6sDA$^xS(U&1##x>Ss+SYRwj%2FW=xheB=ydtbLhB|+NmAM;iTx&!efc1#evEFENZj=KN1Me&$`=X>U%WB< zzF}v;p(%)UK>4rd_eTR7-MN2FgrdSkaj`eM5G+`C4WeNPX|SlsUmtIEh;Fl0CwR|& z(WP?nuNh+gfJLpg7a29C5VMir6n~2Izn{BI-iu<+1kj5~GY6wUvDv_~y=b#V>2no6 zH!$0cTICwMfr?|cn;vDuitXm#x3Fn`!`%9H{k=Wu?#?=jo|FcQL$;Wi?6({|iGNX`AIm<1Tm|@-xv)k0!Gm6k1 zDA7cpfQ;NIBpeFE5u|n4tQ1?D6XqyY6QZK*RV;e)sbwLC+gQTG*JeBo%OxN@qQaP@ z@I>~RGE<4ejiWJwqfrCl=5TqWP@};c)%!XO+ZW-A&DWpzZ8E>G9F@T+dv{ zR%wqVLIDGMCz0>Qwi-??NebFv06Zw+!}@Wt@CqySSS}wBKmnyb1^E%ohqPz&kh6Qt zRvpwzmJRnJbg9xyLi{_kyC0ai`iIw5-&CwpTSBgCRjMj+Rc0=cOf_5V{3&10NN0IP z$8y}=8M z^KpWZ$WeQk7BZ2l;*wEti>@K<26@&R;nO9q=H8*f{WX&x+}br(A?ftiEf22U{owk8 z-#GcnNvT7))725#e0pN%=MGi3NOmcMZ0`o%O->GF>uQwctUJO`;U zWRF<-xUaXZrJ*(+MZlrd?XZ~9-8#b$u=H`x8zq#HhK2UXs=7c<$#DmKBv*jQD6xhn zEK|L3*Qm*3)mqWA%Ib3pVx{llAPX#z9-4ueA@x*rswm{ul$y*iSv^*dX;iG291f#7 zZI85BO2XDyrsv#`oW=R50VIpw4rat)Pi?z7>b7Z0jzvsfheUD)HZJ7M9k92?xZ7o_ z*8pQWC(fJ>jzVv~)$bn|dSRHY$c~XoWF4jdoNSsnzuR(^0g4Lk~UGv)H?9<-UyNIpbkG}2t6yu*T@1~bm=zY3k3qp@}2 z#m z)pyfyPEI#7gdBbt;@xRDMv+06CAM~uc&BZ6ca zW<+7Fo2|AJ-@Sp?KcB3(nj<5X8F09Qo>8kk@wYc#{dd>R-rC=N+x+{k|7bLGs6V^O zT4X8+ZCS1M0Ds%wzc~7ryzS+VZb|gIM7_rrpe{?Ao*SnB3LjnfgX`0K{(OGt<0oFc zW9ZD`0XpB#^KYQ{XeWATSEdsbUnB|%Zc>kxX6$Oa!Ad~QsE0E3wKdgMNuSqa2f8zg zo`;PMDL!VQ=%5lCL}pS!bpmOC<3+LiT%?sn^x-T7c{e^Jp0vVGv>H4I+=_hRllZSh zDk8>1dhHo0kV;8Y_T;oVQB%@h^O#Bh316%vm5L5E7PWX%MI}Z4H3s!=t3Q>}joXtW z#zbmI&7bI`uhFPP`DA0!ZcoZ;bxB@_)%Cnp`i8|DC4W{W*}W!xUE51m8Kp93KXZGEXrP9YrNw>E8_n6Llwoi^Y5lshud0e*)9v7HK1}#bP zTvzP(d2AL?qU_+I2B@ci>|8C+nbNl8Tv_scv3H9}8MLcoxMqs>E{_beh_3jYw%Slk znNzEhIW4O@_n@PyGG_!z9+!iqQ${l*im_M5kAxI+aSZYW9)Zg%wM=QOdX-pN+$ASZ zyUY^_jeXG$Y-9%CVg`gcV(0#KVnT3VXbPTt%4nANzzn^&fIV2lZIo3Rx&?E@-8z@^ ziM2S;f*hkwm*rwru(WwbP7f8vTw;tOb`G?mOxva>skcUFd&pK%5wgV%2n?Zt=kmBY zTqX$G<|gvBmVdUQW}|^DjBuskEKQ|~J4eQfQ>h}qd!YB)>f(zyUg%|i@1|S%H=ijl z)?0B6=}>CTA>Tb+Yoboi_b@~3+d8`cCf-c< zACxgjOHV0(q8_P9&{Qjo2Z-IF{N-&%15ZMv^;nilYoLmb45mNNK*wbgtCRKsho3%FTCz9mD zlD)o5Tnis|oO^2Y@iJ^JaJh(P^9Xb1nXyy=d#V8TWKj*BPCd!|SL*dHpN1qM+o|Mi zm3d31q*E6e4af@BA>dnZ^QmP#v+BpujIW`t7Ok%1QLo)#e8cq;o#SyIZ$zl1sav?=lT-h5e}#&`zz!terC% z4J?Ak5#f0*!m7~}{E_ZiU4FQM!g%;pCr4S0@Q!4U3k%Dp$sfSZOc!5%K)z$cgGkf% zrn}l&D$0?`3x{k8i-q7}OVh3eH%&I5@+ep(h3fWR2TUjJZRXD%#67%!+fyBuEOI|zvqgavZ z#%;5^w_WOrkiH za0+6eI2Da;tpW~*clp>keHAo(Q#hv^xwB{Id7vhhs7w+FN)>NFi(opSOuPf>u_&#( z3qsY(jwJ9v^bA21Tba80gmmrlu?UDVVlz1wstEYPFTvv5H_W0*)6n1m0*T>xG#WO7 z`Ilj2$Y-Txjxt(BM6(!XCf*?5ie++2fsS0@OhDp8J2W?`cJ$mlv*BdN$%&1(pS!`f z@Yueqp50Zy=K7YfDKid7NgGZjbeAC0XjDP;-!{s1 zl`ONvz?`19gTHk#_Z#Y$w5)7Ou>_}T36zI)ims9WetmykU)S997Y({DLaz0@f*T3B z78(lzxmIiT*Q3w&GoQQBe|;?62X`(SM`5I?M2&R7971noFN&I20 zP3f_#(s~9v`(hq;X0Hrf?`trl9cbv0z1OHnT^nM_b1#}*Mk7*hvbZO8!K2ZDS}&Qb zdZwcphE`pR9%S{<(x>G(5f~Z~Kto;!oFNczkTWFVM72(2^-lu)Ic7B}G^iE?=wJX{ zFbAVh1O*3?BpaKJ&0u#0GhqKTHaP686VQ;@b_@cw(bz7!ha~ky8*^44^!JWc%!%tOTW9f`o?A*mU>DcAX(;%F)$kqDU!4Vi1?=7(%iX5ljolE^$=VSd3J z)R5KJ*{rQibzSvcl@+Xl#ci#%)lyqull6iQeeVdfENmD{r^nLS7sq;f#?pNJl2%dm zWlYKao&CFuFCvfLx*745`eBIQ|1Dp8sU9hsek6Z{E`M721Hw368|31!7)Ruc(6mVO z0w6z!^#DDR(pJ>mO(=S*41f%wD18h{RkN^s%1WbI&~wGnxGOyRkLI6@@`RNhieUn^ zDJAoW@N86`K&p4m)KDfI4MfUAwqyjVm!N{IrpRT>JJPurpU`Myph-lT(;*pK6?{Bno2Y~a@~-t_w8_iU_-7?MBX>#+)Q-3OB^Gu(~x zu2w@pK%fAwgf{h(03D0qoQN#_4%W0{jEQz}YLU{SZlwt-C@5tDqzOWQq!2Jmj{HH@ zPLB-_0=$Qi2&zGXU{zAm4_YePKX>vs zs235xU!|2K=WA0Ix7XwN{#!fWdHu*@ey*RdI~(U=cEb8`^1!no)k z=?XN~1f43u8_cXtU3GK4r*_P4ZrVJVuA6M_y{f-@X0W2zUU5uUVyYa)FM?V<{=2H5 z!AP%Fda0c?BmK&E`SEXnv`9Y?jKGWEpJCXEFN+M7yhxD+k(*W(<$b`)FZmQ4I!iua z@+Bu&P-kW`ma<5lvDQ=@0lFQSMzR9s_3_lXsDWpJ#h%Y&v7`*d_#GX|%8ri8WXB6; zyON_S-$)28B&fDo%^g?usBU5d+S^}|o}bG`S4c%Ycgi7pohoyCCEk@2&%cEFI;&!T zG;#;oFL>6D#2u}Qod+WjtqN&2A8DkV2|^Glvy9lFh*!Gg!VjTUjorCuRoZT|BJ~_9 zEwMJ*8p*goncedCztB<_TLS);B__p{t1iS6E8@Q|Wi^EJ&zyUbZ)WGCg4=#JQIM&3 zEuIJ*ecTd3a5I0Lj*E*zcYBtOAi#)2{yB~W(ob|{%+yx#sq$DnQl>((xM1^v5|Vvn z#OkOd8-0W+FvPg>U7EeQegBKD*h8JB61#e)p?wS6aJAW--}7^?WN*uU>Ct_U8ugM} zbRN8pe~5o9zunS3*l&T;oPSmI42#cfbcotF@YkZJ$P1q2svzC?Hgia;$`e^`tzaG!xacg1JXv2)B-$1VC1Q~%C2*GnaNa>T#(#Wrl|jCO6>(OEGQj?Bdq4E_Wd z=xv_K%5Y;-i7jrf^mj4*s0aAbPE;u>_|Yx=-KO%tDR196vuoqu+)W`&chy{5_ty4^ zi3KrPMvQ?%Z+*4Ax}hwdN%Vh;4_^Q%g+P|;7=F(7;95e*(B%ST2K*CXuE!2>f@Y3D zV+Qo85Ro8LfQs^(=Kn}1tabS;)a0=_GGwva!W#@KBV1$g*kPpdfQ~BZjd+P+F~zx3WI_tY0dxFrc~HMNj0@jQtRu4a@cD|lg6 zt3@721eZ;}P%vSYv2>8imX9?ekZu$K7+QQPT$*JM#2{qA*Hh*-mvlu3H?n?kVP~et zM>7NDd{pB9bYYRzgx10pRCb#vCSV!KNbPs%kudc~E(ueG;{FZrKGrg9;?&7Za+g2k zxg?Lo;H>avU$%Yr^qyPpNc(cpX-^;j;OIe*+uzoe-F9)8MALWAf?4Y>`p*{UO*hNj zBCM7Keu$MjBj{x7X{f-hpzO77lZltm4p_46LRywHN@^qPRzTPzb2p22TP8W`X2ky6 zfYMpPV6Yg-!K8Q=%&oj%BR>xf4~%NPaaaDiu*2TbW2wUA!q+T(tl21`o%YI)HBVYC zg4qlyo_kFyBFMb$Ys$~dH%yRUm;JsMK8?=l;L@iFI#A>k{MMKLxTVX7;)!KXV*3K2 zs#n`#JNG4ubQ2TC*Qv`dt%cwa~r7yKqw0$nS^Y>A!0#DOmb^dHZb~Ll6f5(+8 zIo+sXe*Y=O8chdDzRJ-=~A4Bjk>odMM!U&r~gZLsDpC$|z1dIiKTTYs$ zi;i4z=<dz zlW#i<{+wix){+Ne$hV4XUaa7}$Yi5r7*)Q~AOQf{Suj(JGEY>vf@KX94=C|A>{$NF zoMGvFk{zWyM@1sjlYJ->>1c&!43!oy)_{U!1F3i=dZ0)W*$sr{%fswPF6P}sNcnFo zg`RgWY`JD;ueYbWv9272x#3W#u8c~r z8R-&wP|NmA{zhLqs5n^s3v>HE|h4$pkHG-q0`(1fzR6V#8DG z;jMz*9mzBaU^CdJTCG8w=DC_`q8$o{iv2DJH7Wwyq=|2mDacq5gX$7+pcw}d&N4F* z8Ty1s{b1j*e*un*YPF-W@J+i)8e_pCz54xXOLd~%UscnVEbqMIU?LqXHA!bAM?BaR zOtvJ{3mxSOWYh3mB{8u>6q2VI7y}JUt+)m05@)Tg3BY0oa#f4m z9_~WR3N!U;?T(91MIy~tG&4Pv>F%seB@mkk1&bzp6OmZNZt^GPbE5R8Gj1T2N0TTNY_^KDnRV=-$Kk|x#uDhg97eJoK}k} z9ZSUGDW^?aLTl|j-zWrxwYhiJt>f=vYfbOmL0!9)ccuG)-CyyQt;V_8HB)0Fnf~%p zV~;iMwBwc8`!@2A3ZKfq@7K6D*|X_=!8l%(e_wj?MPApA*Ok|JgAiYQU)4Ww|CN8= zzH~bGzB(M+{0k#Lx^#^l7Lnh$L0;!%*wXLllTc$Ink5f?2iAFO!F$K}ziX!3L2uoi z?xDG4wb*aa2^N(_ho{p%a@E;ih_?ynXHIVJ z_jd)HLVQyU!(NilIqLnG&!eMU4#M(!7z2_4z0XX$I2d^*;|>nD#wct8%Y|8G)}T?j z+N@IVfTGq-T2M2Fvjt~)k~5nvbOzWdb5l%Y?j1fEsp(65G*YE)t-E*5&rMI#DTZP{ z&~_jcDT^e1rBf%)IQ4Hpe9?qz3+ilWf_01E8a$>1Pq7lXXJmzE=_IVz zJAWALm-;%peP()UVtBBpyS=q;wtm)LYp3EN5FDcap>l6=$NsNywoonztxPV!9A9K> z!;4X<96BT~6j(TlBm>398RH^J=;g!Ia@b0dAQ-kG?UeK+ru@Dlx67e#mTDCtg0p8k z|7+o-EJJKehVZ{+GGq`>zVU2}lHnr1m`)-4F7U~dD9JA~s+T~!Gl_B(#tX^{{zSmy zVO*>-0k5AQ4{V6hFg-akRGX?OFD>!n4GsOqey7bqGK0yZuM4xebD>)&0FQ<9Kx>7p z47tQ%`W_M)Z@W}0j9O`>Oh#gO11`A8>o&HT8Wh=ub!y~)BFiqePG93g>^*3mu0Pva zuudc?>2)r)OqHmM%rDbQ|*}2+UuwIav zTWHs4wQfDzS+H6$BuUwF87}oytvX@pdX?C$Dq(i*$AK85cN&+EgkC1A{?f z%#x@WSqn3Uy@Qeuv=X8?&1np31NcrZ{tQTx6&|r69V`p+{GQ$0whRuWyP6ungOLc$ zm(BB~d}%0LY4+uG$@z%zqk?_?0smBfU%@X;G9wAV76QD;KH;A{Sh#Y?AZB}EKXZ_A zVmf}i(a^IwggMkHMx&}J;1_sOcU6oV(?1;O^SB*0NzeNPA5I>-ueR)41@|M)e}{WA zFHi0~I|M30LKe`PS0Y&fsu+$Mz>8RFNn*eybGM%AT{Qr$+LM#Rs-N)N1Ql$QZ}QDd zca`H7n`Iki2>*$u*7yfdCq|RnWCSV#O=m*I$mo%~{h8+ulW0;F$y}QRDw7cv{0Q3GvZ$`lixAX4z-hTSjb-OpOUpF?~-@}Z% zaIw!Ba);vO7DX<*VWYoKyq(sKd>7R}^F_Sh8dLqtTrF-GUWi+lUQ_T|s6bNTjzAi6 zs9eHA=>ZvOtU0D$>Epl6n?ZwfU5ns-9XI8jJbx(fJ zUge+q|Ji#JD7%j8T=<-G=f3y$pw`@)db&NghE}UJ>$X~ROX}9#Ey=djmL=QR@@#B8 zVS{bJAErH4-W1aD6U=wsP={h^JQpMx6Fve)6h$6|;ubZEN6nWq44&MM{kHqx zbocEyUwn9Y@7B#5)~;CA(!3bJ=te+Q$(zTT4f%Av`{pj#&s@V*kzWTO$xU#o7BM=^ zlu}e!6RNifI5mQ4+m@Bc_ipKfOsQDn%IOu(6+YU1%iitM?n{bxyF#=>8j({$m|5)R znGzx5RLZ4PMw%1{!z6qeqk)o%NJ_$Zk%RGKequf;DYAa{cw>N;&u%ibJX!?!tvd0r zC!uhGjgv3;k~#Kle+RRb7enxVl)Kr{A1({?R@EPuaWF zyV8EUp|g$5iY#R8N!YLWu$)nwt(|e)s=^zx3VyCim`aX9v#3s10~Y|;LrF1=I5jXj zBSJQxtH?8UKUbB_9kFU_Ylf_v8i??Cyc$S0)wo7eDZ%BQE!kZZG-H#0h(?^3`6Y(I zUF!c5p2<;bdfA^Uo+|s5TxEPrlkVkyhQ{&w$3FA$Lx1|w2i|nYZP#COC7f#q_wU}h zZHv64n`X7u&cvyrEWGeD`_0bn`o1aH=dM<-=r4#y$MgG_>J`0PPaMD;=?a)JGXOn3 z3hP-sk%aC5$3l*KoVS4dmmYz+KzrG%px9-HaZ`)=Kuk#0ai~apN`?a{DryD1wDFKi zHhcETOOA~k+`nfK)l)XFTRppXb}y8?rly9*YM6e^{kGo!j<&xbx=0(Q|A3Mo%L%d? zj~&NvaHHZmogjWIFy-Oc%&@+_&eh3of_XfX$pMxbsc+H;n>OW6umxr^nX58az3#H3 zM~3!n9oVq8cSSdxp0zdlo~RnXK#&>kOO<+O-z4vB7&ENDP>8hM_%7o!z3*~z>Rp7; z(WIOtsC_2$+o*@(UI{dY*H*1fb$9y|fzTQH2%7_E)Zbj5G$wZ(Ys+PKY+HwD&fcDm z)~5RT$e7IT%k6`pQ+h`C_>W>coG)^1V!wLYnIrqqjB#}BG8z=pM)PleTJVp=h-?CmMG*lg}KI%S3F$vi4{^i0y+fS*x z28wdQ{&W{*Fw8&J!|Fw_4w({mydp+b3o12W!kImh&!r*Z_>95TVnMWJvkvTN3}1vD zP2N1S$us>{A{&n<0McW<#4(Vk%tc)NI=*j4k3gwIY$z=v3+v|2sya}8KrN_RzaM6T zpT6$rednmqoL9~b=LT5r4DSbq*)`xd2(O?i$;vsY+(;%380Z8!J`Q`CgTz3fx5zJH zR!iV%7i#adgDdFw-wOBsjTeoM9Nx7ZEU~hte_?G+eu1lRW)+XqkAPR#b;9F=81XX2 ztumPmCnLc-u-j4BhdCY=a3XccWW>@G(Fk}H#GF7&X@~&-!v}ZoLO5JoeSH@UMxI{L z>-;5beBejU12&^Rx$j7uv#9Fm(Wc(eglmQkCWbf|5BEbscgyY9T>^(O#O zEVuxDHT{cb&LCNooa%LEsQ0zd zihc`x*ZCVmllWKcM_hin_1vJg$dF%}@RWowPst`h1ISd4WC7C(v5%sVIKA4<(;~mL zAtf@WN_%)M@wR;byWae!dv3q%-1`bHM7U}UbTurCBO2y!tHo8Ws=Gl^(M;0I(1g7szo2+%rybpJ3Q z9M+I~Iys-b%RjISeZl3QLj0?jQ&w=Y&!0P~tszvJRzqMUQ3mcfj6%qtgHZ@i@N6|Q ztSbQV4`G3vi{UB+ub_qpbRiufuIxgj^cvk?tV+`xptwdp0!Lf9G0J`%xr!UYVM2>xr+L0B;0ue-)4SBMzJ^+=2*^wC{AyeUuIIL%XRWI`+`6F;}877``>x% zO*dYD`ci<~3?pL~I0J>|w%RIq1+_e=_tCb+{;Ks=S03D|sOy`cnplX+E`7=M-QL6^ z8wcph>#}z2#K_?-aOacv+o4i5dAvWq!X9#IScrdXIeDK!?^mhoq!ITdF#pUG46hby zRsdp)$5okZi7c9+m9qR3ilI1nL0!TIh)Pf0=z@dZ!zrWgxbeToyD8z^tK~w$e)d_; z1#d)FiKA5LFpnnKglWtJZZcj|^pVl1$NV&(x`a$g{luD}bwKk$=)MDa1=O4s%NiRN zE%e@3S!F*SzhHkf_V=z#{SthSAA@b@D{b`hl4fIyX7!Zqr@1#kM?iFXsB6$o+xVoWa>5w z+gYgewM6-Mejckwaw_Yx2Q?OEz=+Tl?T5;g@m=@Doy&;c-Qv3b+CM`|)3 zcJSdm7^7u^F48)U-XMv>-7kT+P7`Xnm)wa)pxYk6o<14+4WO3p+8;Jy^%u9HPo+Ggog!%{)kgOf2sQ@QpLoG_iS$p+=xm+UBluA+8cFQKHlVt{+!bp7l0 z?;G9)$mC_6?I6_58NvXv|Gmz9bwA7gKdEgx-2acM{cm=+TRvZkf@^M19a#je(&*^Uob1aU|a3Q_$Rz$lG3 z8e%{o^0;h<5Sc%Z7BjmB?r&Q)b4F6X$*SPbPq^o&U_W^S@5&)`?N_33rZ);IHZsJ4 zViGxy?%;TZP`>dR#h*qW!;@<%J^(@Hu1RbnxYr8trxzolC$KWaY*I?M0;d2KiQ|Zr z#mMsN!sYD57|wtt!j8^cBuA)+cA(y(ia88KNHa{tl@Z0?!)pvF8?#6Dty$jQx_H6t zSvA#)hoD(OU-&1kJ@!qbFW8?Rf5d(h^aYOw&|7#fE|fXN`RLMfqO;e<>nheq{+-kH z5*RTL8jk2@2+aer9iz@kZ{KnLvE$eU?)ycGI!nyHGu2i$wPfQLdK>RH^6&hNn7wCP zcVY3unKg_WNiN>jL<|A7$8D`5*uzCU;)P--M@B4N86Hx zo_~kRHSZ7jcl`YSOwz3VkXm~kk=*5lM6a{LO*YP)I^-7k{|c9+hD|mtpz1N6vuAm0 z0Un zoHyP5#yj6|%MDi{Gh*Murq-^S{>7~|Rqi_)Yrfwf*XI1c!sEkO^S704l&CKN_>P^b zu@PdDa_G88%o?)XQ1KcWMNFKn#B-ld>a?=Hs4eP6$12;Z3Ux%v6m>7@4cS!E8Bv_R zeui?DsRyXWadM`rLZoelHE}GKgc-h%?D<_e69Y6mOSZNp+fOk z3G6Dhn=l_lZ)!ew=k3>Di`scWJ{{Uao6pAeJ&nYdjSbR^TIV7zOk5C&L}m!Zn(&Y?uSD80=j{D41_t_DDZW zJI6+c_G|!vb6+oPB8w4iInNF1#pLA7G&$OaBzni!?dRNm`hl8zkCjiQQ+b%hP(M5! zA2Y#JTIB}Ly*RwI+6z~2RgN|?inDc7>53ZkQjG*oR;hGJUloTaZpgw7f%K>eKM(Z2 zlsf|dM|@rA56OlAuG`;m^NlA_EA#l#o!d8WLM=FCZF8ngEf6v$`46kL} zhZPN)sDwcAgle6vK%4>nusHI{Bxn?QZ5V`+SGQ#=bdYGxsP>FkjclsmG&GWi86FF& zIeqHr$c}B|uk2>XsmQ$sY&9b%aYNuCWZ~rx z>p!3WFs4Y5y1VAo?in2DU$wGpefN5#<}I2(x3+UmXNyGj z2@f%*aAWM#{Jz9(p0~eXZ^8F}Xno0IvS=v^J~66YvCS`i0ovxitL>a=X41qA4TN5dL7NK*U5m9Z#hF-d()V z7#~C*tXc^c<*lT%V^+=J&i+j+S9GlHTnl8$1#@TDw9jg%bG4GrFU*1QuwRe;v7xK| znDvO=6Qb*%Yq|nl3JE^(3jYieNP9{Eo)ISPcoK0u@;1)rD-e_+oSDlBXA&g>K^a4>wi#_)ShdRaBW?de#YYRje&2pX$49@_ z*66zzN=WP_RDv5uRws5C4Hq4&68Bwww zZ4JvCmzz-J3Zt`=_J8e*ll_6cNyW+DWjJ~ZXg0z3`B z&|~?S=C$)G_SikYD)!i;2fwB`AC9p}R@VS+wSQAzFX9}RH7qkB!1$WtDQB5)UFYpb zBGxs*mnL6VgIO1K$9w$#D*JC`e|sPgZcYYAcqTc< z-9T81p@VHv>lqJx_)&D9lICy#^1;mt$-o3dj#8_e3u?tK{KVO=>jQG0&NbSob^>%V{EJdrqw0`Bj>3IsVTxh`V39c#LuYsspZ=J#$`WP3MK$F;JppwEE+i>`%J%Jgcj)x)i}@lYGyXqh@;v z4NZ>-T2>A2WD*HrT>BRwA5PnaK}Y+?>TMu&eAGTJ_a^D6FlNs!&~(JV9v!>zcbsKR zZfYc?{;t|jgL?ai74^Dd%A|n_ygzgNN6vo@(C=eHKiGy?;fp%yN@Nf)!B_&jXjOqR z7X@Mv-&Ropu8hTW=7kCp{EL#!7WvaU)^XCkw9T+3jU5d>RCS=pb{<`L- zc`Z$Nc)4@EV4w8AyDa7JIfOy~Sj`odO~eWE^1*lFoNF=}N4}$^Y{!6S?(o>C*H1rgMmQ z7;QbUee4zqE?qBrAxW$aSD z_CEK01snfO)B;KIkEoe8`YEoQMdb+I;-?~;mZf*c#4St6ozHD9_5oC;tD|8_b!BcK zKQMEKlI@Dt#*c#B73-&rVGq#!A+-_|^+qgnGJ`B55IaxiiCc&kgqEPa7f=q!-uWS> z8|jE&N;Uxk*z9Hp=wT2c>;U4S{BWU>GKgqth}rOVo}nRW=MFrrRwDEV3E!UQLy`jI z++ttc+y}&?&S6xlw>#Qq*lqHY)DjLtN?XupB@~r1jmck?n}D39!rJu7Sb=D61>GtO z5AaSyBg#OJtJ_AU0*qvTmrJ`|9?2ONQeT>?Ug- zb$;NT8@!(%anH?zY7aeGr8!=|XD2y0YbQH6?l2K22hU;H$Bu8^GJpbE%&%>3f*o6X z%w+Q7GrPo`Bjqc4#GIo~sF}Nxi|G0|>h!vL2MIYVQIgAT8$L;TiVQN6upyLulZXi4ou!}!nar`4Syy4f@Ku-qBA)-CE}Zm2e7vE+NG zi;9uj8lFO@b>N<;$gr4WcW2!^WJ+(V*e03f*y~x)HCHHEKa2Xvqt?e|uMguJdysY? z!`mXp=Qqx0atsL{C7+^@0IPbR%4K)$+_ZrSihSN=hjYU|ncM)M<~@$YKygj!L)LfY zdtB2%u^6!?O5?~S<#Q}tvwZ2I1#@QQcIJ0V3OM<97QQi8 zWIj}~Amp0wDsEi2hC1}(g}HtCeIoVbtZY8m&GrWP!k<5$({#JIcnsfWsak#?C0fU@ zhKgi@m<~YENsj?v)hvY$6FE~Fz~5WNlEoKabYS1!-CH)VUbSM`;!{gbwKO(0=IZ!1 zARWIQ6ATv3>nAK?Ydm3n+~xUW0KWi6cd7zeLZF#^2pHFhl0Op9p&neSLP{w>+!8?o zSBTDb#^b%EP?0%`EEN>CScUU;Y0ISxVQEI$vgV3r{HyXKNQa%V5BunFoxKfoc;OV^ zUF4q=#fz|dcmW-#TgXF*0f8ck84-0GB@wdN%uGcpQz3h*VIdO8KTjlZLUd|aRl3;R zbm`d0;RE}2Zs(zFx}y1t<~dDGjV2DAXQbrZht3MmW*IipmuMs-%$mtEH*5L|P4Kht zo3(G|OvMGl%RDzFvLJAN4i{nFEw}Na2Kqi;vj1~tA3C^e$JT*$Yf!nVW%jW-$7aq# za*FJKBi?H_DOuM@9(>0BH`#Y&1Xy2GRfP)XpisEU>z0mUMlrJzFucsL{xdO1d6=6_ zYYSx%9K^!HNg36arb6)#$n;HHjELp{$2ej}MXaJZr)-qMfEHj?Q-f$~`CBpxtO~_4 zcduw~Ue(^b5(L(IbrtxlQ^{JDTV%aMWFHaPf#1X^lcjLwLyLx|7m@q|^{79Z#N=ho zB5EDPQR=csDBB&~0~=+>(h20u14hEM7nPaO^hkT^Kuxt-+5TL=h-aL^M(E3zJC~FH zxd(`Xc~<7fJ@~w?TumZCP)=zJ0I#k|uX~l>fqJ*8M|s`J-GG9$i6DHiq0k>r{vk#G zUbsYE`uExc_Ia*P#o}3yy7a$A(H}+G6LAz+_K1(gmZd$)tNUBK1XnZ)(LJ97Sl-%I zJxe=V3-e~@Hs?1R-MJC*HCK3ahrXrqU`Y3`YTnm|_o)%S$)%?0R-W<=Zd;G?rk6{4 zguF!ae5fU9zaVBG9Ua-feaq$|AU%MiG#}M(mCu5p3g9!u#@U_jJ^*v%r@WtirvQ*E zKDug+s$?6eQdQA7(;jH<^1bON`=A?RdQ`jy5G}s8uLr@J3+GpFtJ&sLyyP4-IXC+D z=P1osks0(6v7Su7bWb5XQxHemx>-H_!2S-HKU|(3{8=QNU-;eo8 z@#k#igI$e%JFc8Us3r_WhH5{g?|-(6F3cO#P)|yxo3Q>6{h#w*8c);twzb6AxW)hC4v}FSp)la7$E`|+ zbtma>sL~6l3#uG}DUHTrC6kWE2*N3hh6qTV0v}!nxHqs2a2D@C!XUL3>YKYkC07Pp zz%abWw@2TCMoVtu9^Iq%s2wW0g8&7nnh-B|)9?l}=1ssiO41Uyju4r6c*ES4fzd47 zNp_NcCq`&`ySE?#%on2LZZ6`{-^DOj0|a0&Un-E#;@`_>t@~w;+{=^7H_&ZzR4_xI z7?@;Z+b8W6-sS0khg&|q?{NR8`yCWWh{i)8Ie^on2@%a13u4akqlXUg^~>V!Z7p8H zj%)#Cjs6lEiQxT*1{KTn6N^eK{Oyg zXL!C^h0{<0Iq2t$XSDu6zGzi^QK01;QKyI!rj1>of5e-KBaR#s<4(mHCdfiVMC;^m z5Gcsa2%6%!-2bVK%YC2fd;u1h(1q@T3*@~wj135`Pf38wM4ljYmWXDo91d1Uk{pYemD^K*|;3sJ=+l{k(8>xI)EM=1$9j#-U5F3v?=W>WHa z)|dL5r5hq7W2vl@0?u(Z>PnagFFUw@8zQk*Ec3gb@mLgj z|5N0`ir<;?K7Jm^r^@gAF7Z2qC!7U}CyLaVPlGS5)5u7l!0)VF&XodiFpNM1(9{6b zlUuN;X^#Wz3sew=#Xl2cyb^OEhrQgn(DwdH%!NEWm2o0SiwlHER^ox)9z|M%xDg5w z&_nUDsW-_RN%$Vt<(ld%&WxQP=q$4mSFH?t`m2S9cn&_JY#wqBzGB~RJpo>N+P>R8 z2f$_EIcQOLF5jp?a$^8`q73UV6CO`w6DhGtB*5emeFN+cx7x+1xITJh+m_V`1#fDY zKc{9K%`*4ypHu#Nm^H*F)7fJ2JgJ3?P$q9+48kj6$p z3_(5zV`nl9sZe4tytq)83^Yg6oIE_uFOXfQ@da|!w7vlCHx0*II;F!O+&%Iw1EE2I zg)8|8x?gh9$l;yaH?C(0erJ0G-L3Ke8yn)zo`mo3wT`HNz6k@MH zU9<{FNTWP4935Z)Fo$Ue%E~}{gu$Q`Eud%dtUxVIn64^P_;1YZue#yklz{I5ruX$s z5h03lP;ml}#KvE=&pEeouHut@#8UVPqnPWvl`n7^63ZQg-#}Xf0tbv{m~58Ho2tqt zsRELaE@lp~d@>i^*9FszXX6PnH@lAcx(aB3=?E$AV8UBA_XDK9w?~CX&zo75uK?(i zGhh1)a2NH$A20bHq%Uk94~wox`W{yFU1X(!k)Kw0FE9rX0t0v);-V4{>|$7TnJkJ& zb{bht=|mdRo)U$jkZ6H=-W~nzNa03CT}Im?Vz9M6WdDgvt0$r7QV%ArE>W~vY$2}S z$++J&ajjUPcKtTvi%!Og5f+Fo$Mk3MvVkANw1Ydpe^Y%;YX>4FVRBIZM)K7K6(1pd z^<}45_-ZYNc+X?++}R9^8M8ddVC^CuYY=^4oz5d01=Gr$k_t{Tr3%u*9MLH>R&lyG z{-eZ!xDS{Uci(l*)sR3(_6%;{iiklV*mSfZUU2@rIe@gRb!um*cPRMFxyu(j@#i{r z;wa{f%xRq5M~Axr7>VEs`9XR8d7!xhm}BW9tN=|bgr<4mWMmOY2mc0%<>KNlGmRmz zVi+z~!EWqD`l1&sy>VcmxLxotz4CG;LHUlZX)Scr*PtMvB$tT3^M7J*_tE-G&Ual} zzxlCu>}c}Q+61Lch18R_btNaDB<=;7-+1TQtKfb{t?g}FP-+#kY;Uarl!Q9F&NqCt zeb|8$3j8<~+Jdl*Oz~*T5S4PY)r}))E4`*f+jf8o>T?7>j;&L8De4Y&DqY&X<*~PK zYlx*#;!p8$8$hujJ8&_2cNFUORpupSr-CxlXiLS(+rq3b+5_NDky>=uowr|g<+0Hb z7`V4=M$GFfQdjH)wJv?{bFOpG`(rkkNz?bsig$-PogB*&KFS{e3xvoYUUzNPb`DZ9 zmm-9jTcEHC^$7r_&qN~+Bwxvm7Po`9%*f`&F6f$xcrKpEokAae!>21CoC4$fhR2(} z?us)EUl=*OXAlVrFlUm}Td61rAc^Mn70Qk;`|*tPsJkC;eC*zh^L@PD;tCVWExJp} z36)7&`Xox-ZMWWn^q*5>!1u;k1df65c|m5V`TQpL@wc9BG>dmcu(s(C!QGdh1o{X{25#TFZjF*0Z78cCJj1)`g8yy;|Bz=Wqwjp| zE&U69v@8T@3Hw$#Qcm2^PsuxhAa(2O&s+uofT67en>Hv)w#=Mi_8NDXb64OF`zx20 zh-o4%ceQ9*@&}KWO#xbBw$Y?)k0vGCM^N$+ zz644+^YD2|WR4bF^6)g{2(QuuV&Qv1GEIE2rhCY$Wihx zz@#ZQv0zgp=!#FmoD$Oik+KrS#NP(F(l`f9f1?PhS46CbM zT7Oc{b8N@bS#V^_Jmu$%s>DiKFYx3RQaV8yQ40l~NsvRhmXbC}ngvZ8129EhTn!;n z+&MsLa6Gg{o`+;>gW_|qL+U$N2ZwMya$%v|E~Z37aS+H0()`;>ZVSyHvmSS84*U+` zo3n>DHpdakPMXWlFijfiT^|zKn1GJochRuaD2vsiD==dS>xgFN5hE?~EQE=SQCvX@ zI1U&CiXzf36V)N@bL`|LyAj;9eJcaDRxGQpY2m62CZmP(u*xr|?a#>`tp_iiF?ug} zmK2oh5zH=62xpjar@}8SG^J_pBh+#PajPg4_Q9*cZHdALDyc{XkF8i%^E%~qraZpz z3;UsfJYFpHMKmYtqmjO-<_saiSOa~Ae2OrF+K2I$tY&zA2esh@T)haeV0+Pqd8@eU zu-6*qX;Fge$+{grOuA;!Wsmbc-qX#r>-?qNEOgm`k0T$0fTe3`JcNVL!n>s+nMo3r z(^w*52_J$%ACyWawLb{cQ^jlq{$TY4ozw7$q~X|!>%kz@hR)8LeGmZ|G!rI+6zuoiOeXegJ160{7iKpB@*Morj63L%iqGSIxyTLNFZk5CUrU)g z6vz9V{KH2XE9hZr*d#zf4&gDS7+c^ig|WNb9!We@ldv%Bo{{Y#v#WDR#4Yw9vl`W- z=I2JL4;kNXE#7&5WPhCeB>VBCu6x7%c(d9MxG>TPofcJrN4L(rF zHUH(sTSiBtQlt$tyoWwhr5`O1cU9LJ(*VkK#CC-~=k-A<0nAY;2t(hecvsJ;fkkK2 zy45R}cXcePtJywdJ8v}=L!|5jpK;$ zDj>_53(8D>Q{>ZW@L|?%>@9siMf4B9qw0!15&eAS+W7s**B8oJkDWY0Na-Eh5C+@N z*H=qZp~<}A?$6H=VP zf>Y=N!CulFNs73bwc;b6z|G~s_TcUTMoKY@WHMs+Fg|tO923vQn0#NoK=*EJ!g!}t zG&F3Cum)$4*QRecy3%MchP_KEd5aNVk6iL#GhYjw$?Dw2$BrD@wPRT~k&qWJ!2LI3 z?v~lO|J?n8KJuic-iX|{oyBcH`DR`({CTj`#N4Y7$|HhcDS0#4IJX8>K1ZMNie*g= z3+B(6h1`k$e7}4MgNe+&;QT>sCeOd*dmOgUk3Rt(;d+4q!JNLOG(9^pi=2Wi6(sj* z7qTv!2yuhQ1{6$FCQ*@TS+E=YhLiOxt|;k}$;L8pJcfGuJ_gQj=zy!@)|;=t_OeR{ zcjAx_tnF*Lq2q=IR51a!7My~BahY@AJi@g~t;NT!cga~NT~8M;^?e-AHiYX2%PtzD zIc^kcrz{RcC>pn%Ppa4nABZXjH-KaTx+gkLbe?D}pe|X;Zg<+vJn*iLSH^Z4n#aE# zyWFZ1z4mMF|Fa$tpDt;8ril4ce@3zogpK8lAs{$`|KL%sKxDEg(-lgY zPD7(irvboUQIPqbxD)GFT#^X%_+eq_!@dOvE<_J+>Jt;L%WB?6jRrvP_FDGNP8#1BJD5i30goc>HM zPj^*1mqFG53CsYVTrPzg^{Ess8tJqGIRLoKmI22`8I5bNICE^2i35W>2X5JN%cc!| zs~AsWL{SZ9=*s_3#Qx6gEieckupSNWt#wWb^mX_&@Ni%%a6nZs8!Jwzt&DMR@~kgV zZc_yaK=sZ;1yyWrk+J_*c298tMA_*STWP3J37sCoe%!fgPKRd}^#X z({l#39GVvaLSR-cl~&Z$ndi)N=S&^sqspGbI~n#A+zY;UdJ~qW@}7&!1>7SWvoJ+- zzRZSUb|#*-F(lU zf(j0D&q9Au_7vQ+1^ZJcXjSoRG5=W^M%LppK&%p^Z}=jBqdOKqpo}YJ3fV)f`EF+7XriVf?&nH#KO@OW-Os95HlB}XAtXb+OFH7o z-`7)m23REr&QpcYNqzr`*gY=KpSQQ!GZ1(3;}*u9YytP2EM81=7B4@g*yXb6Y>p{w zscf2Y3`p_C8u)eCc~}p!Qo>R4kTw8x@8V07Ls1Vd{N=U(6{XnlE4c*=)9m~^j4xFSO4d3pg;Sa3OM9?s> zg|8qDar^pd7_TriOdp2tjX7REKC~@$_ipsxb+|%~yT=+pE%`Wd;{C!ImS@;qDPd~@ z_b#NDcZ0)aPsB!Q7`Sn!HcT;~1E>-5h!Ld7FEdifTyWvG@803q5B|Y>-gV!b5m$KT z^r>SbJKnqdy|81TBw%}+ciAG)!RYp}U54k=`>l^y9|-XLZN*zkmj*v}zLFVv891~( zIf#`4f<-|zZi0f69;y*&n<~h6H#~52zhmEa>$PXkpv=U+A@~ip_ustvW@d2`-q^cq z8HwTEwSOGXn!Utbd$0Aq;9gpvRx)M1i8l&exKrE$ARjo0sxkMqA-{WXhlVMof3dx| zK4Jg<1NYzihFdhi-tP5pDZT|OL+tUQh2Bl8<4nZHpmapR6#Aic!XEL{-yTq0dKju| zDt2^+d*N1B<*KS-T*+mta`MzuH!MJKDhPtZG*)@T0}_m&!BtfiL$!j3Qv*j%ez?-k z=d1EGsWE8n5Jr!ke#Hkz3g48*h1?RgKu><+DO0OlzRa<&y!?`j4j*LLBkNEtyKMPo z-JNYM%mWVU2QxiB>P2l|f9*TmNg@3JSth)j2;JdiRkbtqQ1od9v{Z&}rDawY88N(w zqWhI}fh=9#b=U2;-2A#L_6yiO(C zABSG6pgIup9(SPdL7#v&ZoCD!62&_$iXw%BD4p2^(v`JDV$wayjxrBa}8joNP#v>X3UwMJ!s?o&QYEm$O-vo{ z=+S`v=f8^m2Q;x4suMd1%n4)vfji)6xNM~-)Tul3Q7~!Q+ugw-aqNSIS$!A96viy> zi5?DWrLKTGt?_75a_slN=N)f<%RN_)9Y4Bn@6K%m7VT*DE*)R}%b(jH3D}fWZa()0 z8Q@HH(WbL>%)#r;WPq3&m^z3PMy04Kn2vq;;4o6q`EXd%x6F5`n7kYA-!&@MRo)6u zYPsutw8(+m_;nHGM0Vb#=70z+vphyZUGxd)8-D z`gW{C+mtrQTY$9wmXaTUQu15fs$s9oCme{=%BoaV<#8(u(6=n~4P$*naS@=p!1HgV zvnkwmRaF|mh4dz{VvfLy2PYgAkJgtmEX;hhGd}qKcfa$2d#}C>Z=rp=0dI%G;~jo8 zUyZ>9qAla^0(QPJZHxMr*!j2Vdu*@Knl}XMu?S8RX~`!71fRO zM1KE)eS6?+MH$^yV$L(NkNkbwrF|j(or{&FG=lQ_EugG_%xk+tV;jW~_>Qrs4=H>{boo4B({Za+O0c zspKk6!owwjuZTVHmHkZd#H3$<5$@%zgbE*xh!1_@PyYDB58m^J8?V3O^vR1+{(I*@ zG0Kc63ZLQu=Q+O}(W%yUMDOffS3GO%h`0oj>{XXvcJh*=BZm&`8``loY(I4GKc3I41NOtc+J5NriS-XkHZuEKJ!e8( z;ehZVb0W63it5&s7z7X}fsuXN^fHIWhSBEukw5xRAAIlc-+cD+(-$2*w13wQPy=y> zrB+97n0sdZHpf5HHb-y2@EVmT1)ii~!y}&RFv(>z6&cdvCk>WNnX!eq7& z^z$YZdn6>X?AHZ*z^|uU?-D(|6Z`asN)9)nT%l1mVg7Obw88R)D)aR=T$p|Lz{wa`?jr{S}ton;pxdz?vVV(Sb>DnlO>Mk@fJo|uzugo%I)8R&;#G> zF(Xekrl-}75-4^|(lX%eFV4oaP{o6)jc1Zk@e4Qu_9j>tjPLspc>%K`O}8jInMz6U zRw90x5fM37O7j0&1xn!8qeaA^c6PKk<#y$_qF9o|AFEnS5<4&Je)4L5VK=^TP_26b z^dzK4!MvuQZNbGZiTZPJg%!wA>H;dzjPzpVlLZ!%^J(WW*QM~n9N_TatH49GmWfFK zyvaN(2XV@hlogN12yj{zD`Exh4NFixp)OxHb4E(mz%%nV3a3H*=1A1L+On#3mB+Cao(%tM|GN$;+-A+dkt+F|$E;?CAcF^X%+P>sTbKvfvAT1$ z$B6uz*akt9c!Sb&1{K#o(MvM52-xF4tVnk zt=qG_eND$2zj}`%=jy&fXs+Lf|5l}AA{fY^CjjpB0bC>b3st?I--IjISv&@Fd_f&qIGkkPl&DFc<1-Mqws4Io-YTmi189 z`vXIWay-p3aElhj!+ps`hYsx6wrOv1Zv>V13<{#!sN?Nd6bk7t1VW^__K&O z@@;eFTwg^+3b|o)W+S*p3~G21(v^Mj@xw?D8yMa)99BV9>^Ax7e<=M^&drndhqeCc z(Rr+R3Dqd&x(1~}x+W-4DKmy;6DiXnx9pApN}S2 z7SD*@heCs4y-zuu!}?w>m%)xpeTi(=sONMl?-|@W0IS8Oo=s6|o|zNxg2$DfC-nJC z8+4(b=erYzq2g8G7D~n}h!VqcT8E32D9!K%Gi9%-Pgw@ND(pj9PHoyESj{kipGU~Ry=bZqzznf&XLZdz+A z1lLxoFUZ>dNXgJX*1cBg@eYy?Jl`iGu7ShG!r5eGhLVA*8X{Oy1R0h4rv z^>Scy8?v!kaC~s*=6wVE@apJq>-TgCCd|9E^2Hr0T|)Q(G7=L2c9VoxA}Jn`tDY`l z<_y9m5^;bdOG1%1{q5VjIvx8Yo;`a8w;kAiVC9O=tzBC^^}>^p-&8hYEhDAQh|rHU zF|>I&31l4vAYeKakdv&wZ`DG88yr5k8wGq|a?s)4=%^?lEFYraqU_f6+3VO?0(4xPq3-fk>! zgnrrDQkiF#z0wtD)#eM81xZ#>It^qU&caD|jatUZ{tgt2Xatff+$4qZ6guHdr-3Zt zS2h>du131a%GQFGA^Y|J?Y_gt{*8;B>M9*&Dwgnh%$XCtin$;E1{r&h&Puh+0T?Rk@i#mHqK9dEe zAhM8Gjkiop)pa$P3(AaOrsr#x7&ZU`DwkpkBhkq_$w*92m~^CK!F8JjX3sz!DN3s+ zuTV#_&0R!gLkV2tHzN+sN0m&H{WxsDC~p|<2XH^B*LC=uKMCJ^oJj*ED0*rOJA+*i z#HnRbWS`>7aC5=w$^eXy*1lKc0?R4a83E*GqKXf=ONfN z>ir|wHIG}*nSHl!RqI>f`??C4b_LfYm~P!u>KVp$duiVz!%Y8saQK`=`cO3uQAvSXQs{gt=@DWh!_No-T z*kfoI2{^D%WqIEN%uBLTUDg{`{7?rl9)DxSLx3v*4>8SQ%to;CC8*8%YB z^q~XW2R0XbmK)_qy|utU&RpkHl!?MW58Hc1?ytjXgME$X17;Q*MR?<u-EWXvrt=M^UQhQM^IS$p;CPM z%v*nMNv%RVz*}G}(97d3l$HS8IkeHT5TblpOw=bVk67PmqV|E)t>crX^9{vY)BA?v zE!_v=Ac{dsAymOzrik(8pvl5wdz-{W%?DhAJJL=M#ymN6wxSv8g1lA3_FUaOrw{Kt|QA_bCgNX~*XcM*n zyYIU5j@#dWX!|3F4{qNYegU}l62-If2Jqjf!n1#?Xi&s_j0GG|82u2a#5+oo!=<=M zB4H2d-vH+W2x;`!g`nZqzl3pI3={Av<>o6f7s}F?I~Vfv%b!bedkIO>K)_{sJ8$Lk zB*tA5ps>25oA~Iu@6C7JaqZd5E@koI1N*`cFL&Q5S3m2!tDm$5uTyv*G5jh6+eZ9{ zjmbEeCf{C|5DdD|OLdv^ z0O}$5H-ST=^}`;*)rJ~I5^^3MEzGGea~@>Umpu=1>C2i&akEDXKWCPqgO4peqH5mc zv-sv4ueqAB`2@rn=np@QeZ1KVEKT>$H*ZSeP3ZXK%_dLz!`9;!8~qUJlIvZ8+?8mJ zs-yn}Qk-9z4?aN5YZn&=E(PJg-hlq7-<`sN+Z6|*)}&0SR0Wb7E<$@OOxPo=J5FzW zGSbw?C&x@@{35UdwANQ{(UE!|DShU$6Bi>_3f`x6YgVrCQ(E2<7o1KX&fb@8RTrI@ zHrFa1s3(GeQxQNn=wv`2 zy)HnxK`7c_y`bE*wiH+hL+I2CI{7g#uax&N>2yNTX`$w>ax@a0d>>)lbKwX5QH84s z0Nsb|^qQ-Xsx6pGu?zp(nH#_jdtm&};4>v%;QyZuenP z_7c+XDRrK^C(G~5!{f&~)^3#5qN9qa_934_pOR;E{RRk#E1 z&?JzpQh4a5P|(_!2IBi1)V(DRN{e58)$6XHXk~eE?9fura)Rt`@QLDI(es3VANS!& zAg7yz@AJ(-*-Z?-gli%&EQB5OKps$FJKVDbD#9C~6k0-7aMhKkPmMxVJ+P17qgA~t zS9k^nlb_DL+U$IvXHe`5kiFuA=3b#rDDQ>NkkJ?uqvBGI=q`M&*q9Z?QME!Sjwm!w zZSn6g=9{n$c*jn}mTcLKdf<(ojoHjs(OhiIpgDAvTj{Ci$v~bVDc&mq!X%z@5rpU+ zK+yHmdtIGkj5aJAZr7=R-t9VmJXu^f&bIk0c{uI*d(v^6y>tV8XNv^>&T37ynw$pMh}JnEd+F) zK6wc_lCdC+cW){b3P9`=eT(}RT<0~iPTRN6&ql73X{2Rl1%zjY6^RFM3hU%ZDHxX~ zSf@8)xpmf>b+)%QH@7t{#S+aO#5JDq?PbjQ@uYi6)dyx&Y-pJ^PI)iAk$xq6*x?Zf*~|YK+DkP%<}Q?3r$_loc-9-~W$)#DvzH#vwlvo-#!4lgNAc_qb2jbg z?5FI%!r4rKXSb=dX&*Va+|B{cbMWCjWT?s%@zOw(@rrozxRpVic}7wYkq_%?5LTkb zjsw<*QOK3Z+-8&txHy|l4KoiKr?sjoh3tQ0Z>EqwDmO;x1Bl{%CB9HRUS@Dq^I$p2 zseU7R0Z?$-7oy6{4cF03eDacGqYMDpxW2Eq4FG)$mMvW7Rh-c|pQZHwccFiB3iMZ& z17Ie_6J_b21pO0I*6JnD-}iy?^qL|Hd>$;#R8ynmuc_qsU~; z=B%2_f2=)dge5qM0>aNFTY{+4W;r<kdfKQC=feh6R2Z*)T@e-`_i>U6je)%FbOIzI5`%8wCVhc}jg9i@9> z?gFmG=?bjn(ULv+xY-k*>Rx6o9v#c=N%=9t{N;`1U(4Jue+BQECvngGtMhh!&lK!0 zKK_}Sl>NN*0_4-*sQU$F7E^W#YJCbqWiXM0=%RhoOD2SMa|nhe91TvvB=smG>MGTl zTGO<81FBoTIzdMyxjIa8^?ejdH!3dO2$g&6#PQLCI91y>0!pcEftGF(?=T)-^AoSq zH7m)e*8CLi+Ru6WoOV9<0yRSGGvoip{kA`e{r-{K?+pa<&ZGf%1hi@*9AqFt22&KB zK%HcA0U-1$@UG$6>J$t;%q3_?rty5E8bq|qmof^lKV%kD7$9rq;@KRLZQMzK z59Yu1zI)#I`kSshd&Oz+0t$T&Y+Np~wUYM#anj?>A$kX)7+Y zuznfA85TH$5P3lT7tS#H;Z$h>_7c7PrHta^jLD`EO^ayGfP@EUP=^@*YskcJ`|0rn z>0y6E(F3&%tiKQO1~7pfj~-C<#YZ+>-gs%F_;_Qo0ix-#z~c??q^Mg$)fuh1@Vxz} zN{6jcI&3X%>Ig5gtbbR=^yPRG>#`TD%!U_(tGd#39*1-gdp>F?GK_Avl#pgX*3wW% zv_l70RrL*l%f;7?9*A%^XuGL1W&5%+2VEwC8L|he>c!K8=!a~o(odo~90dteH&_`7 z)e+lJTA^9DHQYReXdHHdmi^ z(cW1ujaeMp$WPP0s#K4g2j$P`#+)sMJjqZ97%wvdFi6o=3}Eh9!T#i|^8N87$ebxR z2w>`g0AKFY($#`(v|iHh z{Yk&M>iq85){i_%Rvh;pTkbg)|BBqB&soFDzhd{(zry>}MtvkYR(6X$4hYuTrk9K( zoAo$MconHM%S2Z zmo_Noh{>iiK-v$?lRG%YA#;_vGmy7BpIcKPI4;1BiZFShbZ>}~rK^q{&B~`T`P~>3 zrC{__D=DW@G?s?WE6$v}gh7GZ1~#lifwL8nhR(=+zgPKnCfax75<#$b*?mu9)x1ik zy6?U*ODSMTIr*T$eV^8#!Ts_0Gq~>)4jI@fGuhy{!F`|pXw%&HNaHWw5wJ7Fp7q`l z+O~3@5qQ>BD(+`dHj|6t*t4J>UXm>E>&A`cSi!0x)F6fiC3Tlz^Ei9vlH&*Wu+sb5 z)k`}YQ5t*ELZzmlQ_y0*ny3n4LS7&mxjd z+aGd36V0VYOi-89v_lYMWWxj+BF+f42~|uH3tSfGzv=cHue$XG=dq15urj z`j%@Oh+h<%pRo;`w^_;Q26uK&&XL){X9ht-dqA1lMK+9_-7If*ZEI@k>q_40-}lYU z|5iV5(NpPC&H_O!MLBb}qvsagFmi6ulncI{W9R(y^SzzVTaRFVZ!om8KoXfBg6#Br zxy<|`8%EA=fk!)UXMJzk@o(Tvo{Mku{A$>>r}4T^d}9mjU}}c{rtV-Bt%gbl?j2mk zJ6O}Qy3$F^(D4jaC{|xpF(L(I6Uw8e7P($7pU@Bf^8h6 zDZ|~(FahPoWBDB@ub%%1LBl$R5JsbzqwFTSvDa?2IK#2KI>^T+Ixwa20K)Tl;@370 zCDHXf#{Vzq@jdlMkeoDUoUvfAB3@3f8|k8P$(Y@V$AM(XCQx`Q0Zd)(dta}#RUbRw1Ce{x<95249(9CN-C7M;6{Zvvs&4d=n zsCUVp%3F&3W(5<#1?!}w?u93h%E?l|RXQ!2MT+Wf-l z_Vg32lmAfsoh`*CXpxpZ2tzR#+ITV@do3P!&03vKg2+m@4~(Pq=_!q)1g9PcekkWB z-h4v3@DIf=*FlgburEtR(E~Rar!UTRb`7CY##&KdQ*dBlZXENop)rmA`a_$(tyW~K zpk5pbRU$V8%GV(BCVV6A>w17nXKIn+Cyuw#H=bYT{Gs@VI74&5_#o#DZ96|z7ZbX;6PzPv%Q=>ese}Sx5V>yXEly2Y-A;}A4kZTW6HG0ggTQjK6mR38shE~*pU(LAyTQORLbgu+DxipY8U^{q1VHYWvG zO#XS}oDQOVt}gHwnzye{VBRI20`o3COlsa%BkqWg`*SVqYVix@u*JHzXT4wh@nV<= zlSAAZ$)i||yCa@p&{9BCa$6uqW-g*Cdr)n+e({{vxvlD5o0j~b@Eo4_HO)aCVs8$Q zs=HF89biL9Y1ccHT#~n5}XoBI)L-G#&}c&dqC#AQ{k&F|9^foM3JcP`D<#7(&%Ci5}#^GCzC8OX`XX&`iWX+wtlngGo7h;AiP`x2A7tSHqGyEpuD^=Po>l zC+uI(9Q@}_&EdZoI>4{^8I)4wxqHZ}*7QKmE$IO*Rb8OuN<7DSfx>&hNN4l2t}HGf zcGLspzD08gr$K4Ms~5RRTRP6;%y-VP5*@(7coef#!fuB)yEO$!j)R#7Xn^ z(LHI@Q-HJmqH|E)alo5KKA|nxIL-{5wcFMC?{Xsxc+3C_NzM{<5e7aWED3LZ?`w6% z4!6BO?7-j8YmGDL*c;a~GteJ;VCsbj=k~;Z@Z8kd^yYR-$xqTP?buiAJMjg%6G^{& z75z}T(YxjV;>t*9Ph6+d5?>}r$oR?A`5Dqt+OgERE(3VoMvM6a`&tXy!Cql<*|^Wa z`F=#p`?B+ut546@ll!IfWo#s0lJ0!{9nE}017N$}|Xb0x|9v^*U&cgEa^<9Tk zps#Pfr5(+DLmLz^UvVG@=Bs0wg&$(h{u25E7mN2Ps!)0sh3V0^w4<5t1oWMGU>`F$ z{SI3|rRuYPDlRq#Is4C9ztH~L7sOu+9=2=Mdt!&Y4VV#a-V<5Bzt$CofN_xwxNKJO zvW}beN=|5B?arw25O8OFwbK|7i7B*QgyuHUSjyc=0B20nn;HxdfV+t~BZryER?g)~ zG?XaX*~_%ga{}6#_es|8^E69Mf_7qsm^$rz<3+4@3IqCRH^s50LA%m-m7<-*Cx6c| zxjzN_iN_zRN!l+WTKD>MJM46f*_Vv5U7WOIX_y02h=XSAKwN^dAukQ2L$Sd^>lkv@ zB3kN3$U*1p>d?Wxy9fGLp#pqk{hVWSk11KM^2r!0dzCjI*eK4^dV1GS;W2s9dJiP| z9p^5BVq)gi0)Hk#t&#wH=JS0plST1iyyYt-ToyT>e4yrN-EBh%w+)2cQ-I2KY?g^G z>7d&pwxX-^$}28C32b1LO9l${wO!Xrh>x;)oX6Y$o4z^5z9Dmb;iu38U&IXW3eC{M z41vzg588T$&a;n%EK(}!E<9a%G2oV+6_3kwsVx3U55LR_herCBUu|Zi}YZw8ekhp>yopP&sT0D z?o&=ImnslDY7ms3QRiw3qfqanC#rNR7$u*H=Us%4sKLx^vHjI>+4eOTQwBn}Hlf>*R(cfSnaMlHE6dxBG#ccc;_&2<73QT--GJv$E{-*M+ z=wb)UW|FX>U(w`=SM2)F|X*cwSG~ z|6%iTk2>Wx=QZei_d51LtyerJdK>Ac^|qxx^(zaPd=?pNu3A8!J6VzL4}?r9XU+<* z2mX#2@&v?kN!LIpD8M(}aMhKBh(R5n9sQenmP=rywj)N&_pMs5@aFqQwa;e0RKLte zUA17s`JxB@ju>zMeBG|0`PQiUqHuR-yBC81$@bE^*4x)m7A{PS?3%KJd#{%?F~Z3GT~t z*7vop_MGTy;9EPXbhUobhx`#?e%r5Qg6XVEc=anQj?krExk*B-h7o3@okA2GDrq(} zkY`w((n!TEQL{;(!dENf>4+FVGp)dcAQ~ki?G3kFcgPmM-ge z-1YlRmf}UsZz@@byZ&&0z$7Twqu4M@`f>eJ=u*1=IsWzE-QKufZsIB$d)%|^tvzOc zZ1TO=I=FA|ZSK8m7S%tiH4K&ReMx_)lHw*+5zHn1Xf0A|zxT#NQ`>vo?q&8q zW`C<>@4pq^dteCS{o~(ztlwvT)jyZ4!|Z)Ye?WkUOIL;dmGtBK!=H@kQo8;{{=N5j zUug?f1`m~TZb$qgzQshIKj+M|zXv&xu>Z+Aiq{yra#d1_Ni1s()$GLD(8PW-{EFVQZ6ms}zSOx!XfMfcZ;w2e(fv}-ODTufEsZ{6lw z!#J!~8uzSZ4W`0cnJ>$=Fv%L!8z{lJxtF(wwnBru2KXNBF|`KzhEo?1*~ExA=%Ciq z3YTK77F9scDT6yBJK#lYc)&5*It@)oZmrT06#UGH3qK6f*6zEmJJx)LC4BuME?GKf%m7lVpikDlf?9_y7EjNOit{-X#em`%{ zby%zW%q{tRxU<*Kzt)yQgI+6hK;NR)%G@#R>$hdCu&Y`>QT7c~2(e%SW;`vNo-v{$ zsa$Msf|#euW?`Es+c*wXu(L0xx+Dp^e6MgiwswH^=*=(vjC2TRV@oey9QmEw+Y3yJ zZE*J<^l)N6dW_q@S4s~M10*S-2e*7OdZ?WPLU%cOjL@s+<+3wKv|I0YX|EtXq$?eQ z+%ZrbyB7Y8M~~emJ6-n*(4*k;#|*@Lep%6D2IA3E7vd%k78Q(WtRSMe61yfY-IKFk zq&W^aSJI$2iUQU&FNs}W>}w4~r!f8y@Thb?9OzeX$!;;dC5hUM2?>s0X<+my4>6?GZH>Yu@uXIkB5T@nLNoZ3# zCmp*}he$j{pQP{Xh3Di3`lvbKw7GMlJ-5oYPZ#WKWKOU%G6RGs4nez`PmH&Kbc*yW zG<(c~zh_WXPAI8!%)=EA;#2!@xI^oE)#Gu8*88f)q4t@96it`=%_Fwqp7hY|6-;=FX3CiCZ-0v_Z;1`Jr$U6bGpYDY{mUg?> zuoKH*C%#6f`E%Wi0E~ineklf^4?{6Xuh*~?Un-X3#B}thZKMmR0LU=TkxAYaCz$rD!aSA(q>qT_2Ai*$ax42;DPJzv9G{2jwuoNWKi zWBfJDMU!WyfkuC;F&Fu=h-b!ct0BJISD{Y*24FA#hs;T-lx{tUau|#u zD6@R8p5<>SCZo)8zUI8&U~ERtGxm>qo{!*!{KjH5mdx}uti~{&rHdUjIjzin_d9~w z7|yx-B6IGj6F4)+FNaNrzWd*}oWTFs`FU0d9cxfFH!w6$H*89a@$=tJ7eNN%4YQ5^3oKkLKXx+cFIffxR{7&W?hG)h|HZpnUsj6##b;G@t>UICKu5@XMbz_ zyE+E_|DKy|XC$RxmCa6OGub2oebQMb@gnCqMU<+9l}#qHM`E^ZXPM*!AhdKkjy!un z0kLXP9BJ1I6-dSU_oF3Kc_toi!hwog^cV&ff0D7H=K}{>?a1N%!-G4wZIS%Orly9* zyvmnjj^dQo5PoH@LQC3 zYX7VKpWS`&ui?IPz&r;8sA97ouxA4Nfz&=$&S27c%CQA^2^$WCY~JC^qu4yP`RPxxUIy@e1e3 zaxF8QJ6)PTXMf*v^jY|;a|^VLU>c7qM@RU?RLlZ2Y3NvLNg>d<01%r*S`<~nzc*Sc z5laC{WYR&)oc7|9m~-**k;D6jcJJD@W$o%^-EA!mOXkn2uFR$!Jk{k%!uX-c^@jHD zRk&`6q5WCxqugiOdgD*o-?6UIF~Qqikz0|@q$_X>K>VjE{*fpZ&m=~0jF2dm%*Ntz z)D3HExcSDjSB@OnzIDr{-W3g}+D6 zHq#lqM z83(3gf}b?sL4MxHt)==qn^+1O#|3zgmYu?XW9BQWq7c@kJ~OxGvP&---M$TF&$^rH zYYxmkfOIm2QOKC%zjD0s$DPB$@o^f{L_(jWq|z&`C(&eaaRD%9a7hp1AIl!1R2gt=g~=CFn57NQ+w9e`g)#fP@L#Wvi8$e;AnB$$x zgqe54g`@Ts>m=#XKwkvD`0wZO?_*Z|FAut3-mK9R0TLY}Ah|(rJc8ulL;|?h25BUo z7>GG-t;m!|tr+if3X?f9haBJX$E5y_=91fQQTMOZW%bkJ3+m!6fL~P3Zwv=FHO2$w zpg@e{2#zmq+(cq_f~YQx_iGBZMmuz{9Sg@km&!!7#yt!X95LsXbU=D-pyBy%$F~>_rF7V^E?v|Dm?&uU$CFM5?TPGv+Z8B^QwMS z4%9a|wbnL{>Ns zOh#2GUxaEl`*-gmT=w$r)m2{;3(#_&53UmHI&N8+l*Ls-A4&2lG}Tp-D9~f3)mv6MamOjFvQG-D zV<}iCv9<5^t&{tiw9nsVStpgYu{fJS^(*?&D_~!UDiPcmZV_1{xaRVcqlfoyudioK z**Pwq{}tbNeBU2`fpk8P$@#vIy9+2K6_`Z1?+02;{{1>d0mzDG@B7Wre-MYXR(xQb z!9GQSc^Om+Po2ib#sFtN0;n=Tc8fk(S6t}*4Z!Fl-~v0hDH&(2VCahgOc>d>`VPHj zBI$^(*nQ#sKs&vOI#)Np=~(p_zJq@)>%+hNis)Q*XxLa>f?L1@F95L9!AlV51$ujI zfEdJagO$7C!uzfBYP<~`FJ@h%$J?OBvmV#uDKasJJUkx!=~rwaDDx5BdKBr%`&DRilVTLn_v()>0wuNbC=a4Be;m#G|S z>~AG?KwCq`F+|l6ap_kKaJMm&_V<%BY3a8<-m^ zcFNLKf+W-ETD1Ush+3^)QxBO2wRd86bK~NL^X3?P6)L1+4xhptz7bz&-aWu?ire=B z`#t`Ng?c8RRQiSiRdR6TQki5Xl{^Jw8md=O*@Sf=lY!hueYasePOH)s+nj%=k5zRq zv`5E`16ymjPoSzQN(ILi)?|11)&xAC za%=J!e>!U_>E)YU#G1V3(_GUm1*60@eNy;a>_Wod&%K-aAo&}SHl*}2dF1gSrp!Tx zb3I+Dgm+c3L9dIp`M70ljmGC=;^OR>vtk)Dp8eJweFizlug1P+cnP&n-)$epS$ymh zU($2^h??tKnJZO3^=QmwY2|hwk3^}A3nm!&N~&rQq7v#s74RuxOJLa$uX8q$No2)R zp76n42FCEZi!x&>x0b#~B?j559sh-WD0YJDsD&(vKeeF)ch3(&>q4v}s4FQ`t5`-5W~1j3scoHK{lBy3LMzFT{6rGs8qN}rg;VflW zVHr;DJMLlofBWu1rC0q>^eWg6%o4`4v=Q_3-$~}V6i;|}QWAHK z)T!{_Nv2`KyOV7>| zOU+Do%iR)dqGER6wtFS-7j&F?-cYdR^+~&8nSItxk%v zA#q+umCQHzV37GO>A2l(@h0Q^9#Hc$S&MR$B{RuXCU}!cHDOjNRn*L6vco28(O=ms zrMfE7U%~4vji*K$wJ>D>uN&SHJGO1w(Axu;NOe)Ew}iqE^UWFfVddxST_XC!f!nuq z&O_duOS(=lXWcC{XIw;j&g)^9_^zs3vYvLqFWS4LZb>ER`+h}VAo`(R3A8$*&?OSo zOi{B0x`7m(gPF;3W<&8)CMlGo2L@$K7dg?L!cB{fkhTttn&q3u3D7aqv9}JaU)!_1 zy|sR^TNP5|Sa{w~{Kn2(#cYWIS)aG9$_^yw?KvA}Ud_)BDtfEilr@8);o}xHX`tTC zUn&*cT57B1HZARJq(W#Ae?7JNknR0+?S#!O+9x3Vy1p^vVZr^IQ(xge!eW9kgsVKV;?eRe9(Js`5qIVem{$XDZSW z!+C>@78_!Yzw%nCmcVjYxSOq+cq7`eQQq-0aTegi`LWfETmD6?>ka1)*cEQ&sb>6! z`oc&i>%>vp38*?sOir5?~ZL7)-CI3Z(Uedb7;mP<4@w=nYvuu zJ79i3GG1;r7G?wA2EPF?N0cq2tPjryr;lbEvaig>w487@;(Zccnv40=I`8Iw&81v@ z!n$|-^PtX8zzN?`Ca?~atincQ(o`EEVB!B05i8PG{|}y@#%k7ttGEHj3RToqQ}niv zxMTQQ67#zd-4Y1$)Nykt-KNIH6DaX&6tZ@Vy)_qVnDm!A~d!WC%P zP+Sa=00wkUsXNSx9l)+8`jl_1aeHlXO%yW!Wcgp5lZt z^~U54dfBDNj}GtMI#ArWcJ=b5jr9R%g{EQ2oI^DHO`W@{GyNdXvch_|za%j%tOI^j z)h)m=#?ug3#l!_hu^Oc;dNsabK{YM#H#8DCRloY} zOOD5BpccX!~f0f>C?a8fsM@tJM)Wp^>9kOjeOAmV@-oQCI+u|Te5qF_)i zRT{eN7N)6PNNXs}n#GrO$k%*&TjlDz@>`g!M-Cr4uy^;y^=E3xwKVV|7jyH-no6*q^+-3erYf@oN5R_$WVz4Fx-pekEQm$XA#0n;LL2Gasgi9{#fHi$;+skj3nzAN% zvdh*~c4roR`xEsoUwUR)7_6Xlw^9@b9ji&A8`NsGY7iSKcS>5!l7sx^xst3U5;Q!~ zRE2vRRqDl;HO$Aa+(vPB>FL?W2zfzd8n(QqN(nyrYjDb^cGwli_H(I1=3|G&_Gm|4U<&b*KBUol$-iA8Fa%;-6N zifJITisF=R%qqjJPkZYsMssmTTP5?p-WixBy8``K0p|4o(U_Y5&O9}Ap8w~H^IXns zG(N{VA$a|H4<^p2W|H~OC4|Cs;gM5uW=>97vtj#kBU(T41b$>ydkWpm2Rwn9SLMiC zsqbeK;TSqq9=D4`cvFf2hyX7$00wwX`PZpBpRB3z7^T98bzWJl^%x}n?m)_0`+t7z zj=4jEKald(D9;4X7{4%&`H5y9(UZut*~lMa-q*5!rBVfue}wPSq$A4W7!`7XB$4%T zJ|GzI+Qa>;>aW#0s$=2VtlGy{!@^MShu1Hk{_U$h zx=e0v#N=@rt)i8yZmn&nw3K9?X4kR*gU%Dru=oz*`zo`*9|X*+@<{?^ERa?7n_rtSG*38_-?qQ!$*38_7u?{hh(XkF;tXMu=HVBYAA4Cv7 z5eqa2n-$0v{lhBH*f6_CwU5YJ4`dF1xHt+RsH)CaDw9BV8GS&08AXKYeyHTJh_A`t zq<~bJQ`Oid?suyFWUMNTr80AKNL)%~CiUoML1o66>3p@wYjDdN7CNYg2xs(6-s8qK zSxhM4j(K7>i|jJE;C#`u`l27Sk$F*d6Z;@tE1se(0EVD&nRmY4@(x!$}9O-ksE2i5c? z7pp5-Ly8e>PKT2+q)0N6tYxx|xeg{riO!}mFwB!$?eQ(5LF0-)@}$8j?E20Qms>FE zmn>pl)Jodp-_z?QfBeaV+Bh!fn*G%SuAr#5-LMy*z4Dy!GfDZuevmnlDb6NMm6XEL zoSGUj70MkOFu4F@Co|Dd4nStdyrk70*(S;ww=$>Q7q(+g2K|zS%zc>Ck-wTWr~CF} zPO|NWJ@^h_UNu%=d)Q^;_`@>~Q@r8v8kvH`w7@;aJiJsYT_>s=NSpwELMlY-(!qe$ z!ot6sZmnxg#sjs%TDQ~UxB4ZKF|%fBD3Wi0<7rrm?-WNPY?yu{f^Veq75D~8fMitg z@sI!nQSp$!x?S9MQ+~x=yV?;_rY7 zoppCteO-TF*PQM-ExEdUeZDw*3A4u^^d5Up-O0bJny*@uUx|bfQKJOWSQu`LaVx#d zAf}^IV1%EWD6y(R+zX49lpJUXsWGZ~gR}a3r*}+i$)#&ZWY6z)Ijm-*EQw%Y z^O^Z3B3T1W372cZNCi!}frwhQ-R3Ml1(-}j2S0(|1gm0+WE98`X4^*4fBd;A#+~T- z+96|y&0JMs|Ji|W{CkXfXHjic!KGmyCx7qGUAovXuPGO5wdp*vb)F{_zwOa46@K$- zWL~e$)?U{oSo-Xi_PUzs%t8L$WEG?Sr6+hYKlrn6ro8p6SkX%Udf-RSU`PEjpE=~Q zekU81GdthBS*LgTYxL=~tatjW-^BF~oGLi_Hvc|%zsd~<&MuYo?2b)qrkhzVmVz^Y zh(;(lOjEW|p&BYkG+HK+Vj_M96mGYn4FE!0f`qbMV~N5XnFdfhB{Bh=z-gr*Vgek5 z5s&tuKD5Bd(Fk*?s==xC)O4^pzk8dAXpP%{Twl!aHzrAIH2Nzq!gBv)Z*HYpbm2OayaV3jMP>w=HU2U+8Jh*acmF zm@Ooo%))T+`t4CiM`f;Xg4t(FpWkiucq{dKm)Yyl%}p`yWajA|Zj)OOYuEK%wL~k) z91E+eA1X1Y(M#@_Ll&4gTX?K*>9jT^IT2mMU-*!3SKk2mEmw(8*9WZ(8%sGAfOK$^ zVW)8tK1g^V0S$mh*bir5ff-D#SoQ|Wr2v5OnTDJdKy7@aEv}6TMuS(&wmOm{R`cS4 zIT#S=$tXVxUwC2&M zQ0cVQX{79u9kdndlJflKHg71rQD=x6ObfrQ8jIB1qS`w?n)CGo zb6P6=)omNT_+Lw_s_YK8W$uBdZF!HE`}NU>+WW76yfec;*>-Gj`nu|d(0bFrffq(r zX{?1e-#_xZyLuZUH5x-pW3WA%`s2g?P&yB0orMoVxm;kL-myQVR`G!99V{AAx)_p@L=zKoMyTj06ZF8}T1No9h4$ zf_gIzG4eP0O=(00r$z`z3`kIk(qTc!7}LNqfHxVCk~V&!1Cbx2k*wRs-B9JYFF#)z^R*0_%eS`Pj$DGxVK^M%42bcUG0wBV6K!I$)zJ=#0| zgKS1~g}(-y!GE)=%3&Yhj7#og54F!Y^?0Yt_eQL9(=@sp>#!ToligraQ+MMJ5BtL% zrQJy8uo>Z0Q`7inbn2Y;xUw4+xd`12X7%`Pn6VoV9P%=c6?Y?q?NG6W_eOutZRAd> z=BQSv{y=?hC}z@gOox~JjXSEBD5_Nsa!pb(%OL_qTu=??3raHeN^$}XBI-#>h~ld0 zL;%P%agr*DJ(C*%(H-9D&)Xk)C0FQ5BbTJC5I+o-N zxJ9n1ow1q;l@koB<$!ele2dTI^;FGamexm8(;QD2v=X!cquzHWKYZJ~tKR;CCFW#h zk?EP!yP$9)yJ)1>Ho7w&T^fI6N!y(5X?>`EsB!w@!oLRV{Jq^BT!k+$8+|UXu2*f9 zZE>wy@-=OJV9}Y6zpU5G#>cJR)bz%b3zEa@H2XZdH5qPC;bg9V<6R?7E0^VyNZe4d zW1XWvS4XkuM%89URQ#-lZg`S2C_6C0p~rQ2^W+C{;s;;>*~T^$XsS_>jH))s7Xj`! zbc2pWQC`y6C zk(}k2;cJ%dBMeks3>a;($Yb2X;##{++-4prV zvkQaF8HzP-kFM7lnTj)oXNm|@LGXp(hpJ}u7yLeM5Ab79^*0LnXH^ntL|U$6)LKSB zFqBkUf$)gKO-Um_N2C?8*m#WwTP}(YnrukQTbh*4n)RQvcfJK-JBH)o1(w@vlbvb z`-2FIu4sL8oEe<8F>h^rVt+2q9Nv0M%j%s4YuI+=2iuSS!}XpRF7Ay-BA4oHo>X?k z;PO}ci4LeAN@gh!Xyg9Q$zq$J*VK&uRJ{ZH)UO&*ty8_4x1m&BTnCe437oaPO;L_l z@hAi)TTp}MTI6k#5G9S$Q31I z@k@?e=VX~j$`N6-uwh`90z!MOtiWU?ys%Y5nl|+?`ajA<=Sp%dL(O~gE#TC+IEvl&siBzut zV&BHY>q2uX>bBI2vZedbNmI@_y{^j}uCfX0S;tqd`1mT;CP9y#YuJBvtE%qq!KXCB$Fi)x!{{smTOV6%e)E}1_E>8X8WB;e{5n_Vh zAZ_6lO0ZXdMErQS)0R7jyW1i@w8WSWv^&N=O4>QlPCQ0^Q1=p4&g-|Xo8MU*_c@WC zLWU)CKzrub${f(1%;7=mpIAUg9-7%QF$d%z6u1thEy+P(o@9|l1wR{{`LLr-)X5Ax zF$ad$rqVeaA0JW9OC$5TYRC+D%mDeKSQoxOWnC)e>pM0s>Mmc`jPZ3PnG3k1n0#Al zIU`@^)3HTZshnfq=dWj8Auwn!_lt zMm+dW+^fp_D0xrfVZy~uUFp4%EFyQF;I z2tSLx?ETZU|6F;zf8n?6b7k*I)r0ixu0?&*YIUVGk};kwYeNDZ!o@b*_!!tLb=mu8 z=^U=Abm>Yk_#=Bx$={$bc#`%{ILrF88_N1$r2SVn+lny6_mLm)t#KH9m%i_4aOyEW zXf!x>NqwvO6g<7RFz`Z>LqeKWqDg`cN{W(AqS{3A={4kPP((8Y0UkEq3{RT`hY!gP zG{|}+S}N|r*%u;ITu$kahO`eOLFt}6*5uwK-@HX7XiZwdw0`O=t~^Tl0MM-dOkZ}= z3_u60zSE#bIGuPmNlHlttGfB7h5U&dV2Aeihql_~1qP|7d>Y^75?P@t{eH$1!djb#*G?XB~`BNIP zG$7TxAo1h20Na!aw=x|Jh9%~Dqhi$C*~bbWTkKjU=<9LCTzieSn-q_ZR1veLk9)6r z<3Q(dC&!z-(y^CjA5X>%Zl}Scw)9qaZ)t?vK{(}f`K;%{6^W5P*R4j|7H9iVjj8Yy zD{%0>2W5i>M{eK{4vnv`W94@CEw!xE^K2p8-K@2Rt&RyF&s$o(Xp&D2`3j{OdwDY& zI_N}A%sAIslDE&AHEU$n;(3EJdQ;(WMHMRe%*3Xn`h1xUrtDj;gUg!31S5!sPk#F| zoQI>f9x>;sO378uj*Rc$KJ4F8n^Vgcz8Y#-8jZV-OywUt-#p#iZb_J3iNzZZ>CGnf zO1m@sU-wV!o7W?Fs(oJOPr!h^W{=nGn#vP^0hwDqye+I9{SU?ys|)8&uKcml$SM0Q zq_NLuR1Y!tD%1_D%!ZQ3<%kQ~VAU#FiFUe?DvJdn24cCH^sFdygH{;mhE5|WMFMS$ zv|a{=I@#7d8ODZGn7?2gBF+#;oIDP`NxrV;iV2fbr*_$F2-L9ySLAQyoN`?JeFm|*@zv{-0aLiPb@W$PmX(M(ja?FEfu8oOu2bE|Jg;!h7G$zo z%?2xRcY%YNOsYYfLQh7cu2a^sYPEULVrR`}12U>4k)$ArG*k#$BFr>9J^<;<*dS*y z&S&UM2=OoaI-$T^>FZ>QWVWoC)^%LIl*=YhNUv38G+9kX>-x_!%#;y8{h!d8yTTlB z1{DIhoq9<`e3ZhE5ZjV`!+`^g>fU?Ko<4BhfoqQ)*}Zf7)-|gZ49^=JnA6dgsjsUg zwMS07&0;np0b)P1pVFJtq4;w?EC2sX2#ctb*2z-hll4vho?N@dI7Nmmv%0iQ;l6Oo z(n#EO)M(o7B3k61A*-Ths`Z)={pYxR8GZee2rR7SE*CxSTDO0a==nSpMHN+z*SLA~ zm2$!4yE1uEUGyXVA)<>i{Ok)P6}|~cdDt|RR%m$$<$lu4JIzw7+S(i>{N@AL$$ zuZ1(sHS4(GoeS3=MqaJKl}KxymZW6Ua|ZsUiu%If=)rPj1>tB&@9KV|V6`L;-gI#A z?xltQ84Q@Mxpd+5<^^$o#kQSCnfF;e;ejSuv^b3+(Oelh8j96HJMr7KRI*J9PS^?f=1Q&P&XV$8Cot7tA+mco%tyLKeB_o<-CX;LlL#NDWwG(jz zCEidLdN9&sN{Ih+_|wPMfVF81il+3LN6rs@wn0VAk`bxvwKQ+&)>oP=a3sd4PM+Aa zbHlpTE0G+tU}$cCUu#Rcrm7+ouvrXx!0c2h>qANWAg)p+5RG_d5b-Li(UN2$lHNi5 z+Dh1>R*1sfB}Pjz&>tX{^QU7JiE%3^h0rWfk`WML7P;EPwUS1yV|99i%V`~~%A~Td zwIv@pv#KH@s`;qXnz1DgR?pK&X4xiLn6=$omvkEg|NPBmU*A;o62Z7v!wt$$)e zDaLW4!D+JA8X}Gc4Gi1^d$*sj;P#*|uQye-nT!TUz>~{HGs!Pk_qCqA&m>ENer2?~ zqH3TsX$m-z8K9AS(>G>j9F1zTzDNG8@5aTCTmc;=P6Lwx_~;DUDg`m&k8ZyCn>)f_ z10*K)Q^@^a@h_=vQr!U`->qjlSma8?iw+5JcQ_4_2`&uRGu+XWj7aiQ$ib@sj|72u zn2;rHM79QIWH^F^2^z4bfkY?C4IoAbwj;$jEtz;jhno0b;oVlFAHvtj&jDjdCP*Zk zPtUzzccy|p`e=~jL-PxdTwfKKok;Xsud(@-$ieDRkKSmrh$h~uW=?8YogA>+!u_mN ze^{T^ioL8}AF8WAQ20F}uU~#7oe1b$CXFK@>tha$!P2GEH3dJKvvND*Hd-uj967bJ z7THO##c+}f3bV`sv9ETe&(oLUEan!g#r;;{5{txB zzrD#8gxD9)-*5I~e3|6tNwsSq$G7 z86rV(80RE4NQvJ5cTk;GHPw#foJ(Ds8V}BIzK;n$ebd%Qm;buMx#y`(%gz&2_6BT4 zR4y{l%TRgO1Keh9b!9_`@5iei+?)0{ZaMJ19l!Wd*3K{SuK)U?&w;9UZAVrQ^*8dr zhWu<&9S5GS%2~(pw4}ZeBm}yG^08>(6@3K8X#{HL+PQII!roXrC!d1R%0^B9KdHS8@I@lLho7#}Vl45EOctDK~|I33)2mv&;k$DJ|9oV?i+ z8FmqmSm!KG>$18|j62WDm6ZOrI`s~r++X6b1|8Pj+`fiRhd-(;YLCSc)aqr;xZ7kh z1WY8Ynd+X3uYNn$*lHzf1d}K^fr~p#)=<6@^Y~^XL!Iz-{NELv&{GkF5~H=)w}W9{ zH&OTRs%=w!%PlusgY4)xO5xU9jxR_eRU~y_vM2e{`z4KzYWv0Zf)#M0Jcjy|;;M6?hXWN(iO51~ z-4t2NC;2SY1P7+9o&58^1LP9lD!NSI>nI~B{=Kqv8Va9V3?)};p~tzI_#{e@`s|?Z zKek|;@R~Z;)D-FN-69L3X*XB!No*#J%5Y#Dh?dW_GwsbK3T2mH-FfN#5`_|+B{{G^ zSDyo2<`Ct-8qu+x3wzsYB$B&0c|Sfw0t%4}Fj6PwC&e%4V2UzX1UHC#3PD)e3uU0c zJd^2@QkX{d6Ws0UFJ|KYp5`L`Pc=~r&u~wTv$rdyIgO{R&>SaTJt@Pfx7;#WHaFf< znJ%%363Q2IYQg#_u5&M|_o?E@fD@l?uXpGsct2z(sQoe~NO(Uuc~L-2G@|T&D*s^j zRL9a$S9E=!XckK=9ddRgECIQ&sO(+6%MUR)dz=aKr@c+GDP(eSg4e$EdC!tXZXzoymb~ z7Ia4JYFm2x=Hrj9Nz9q;%UreU@IQX*mi4{is^!WBzb{)0yoJ##lulzwq`~|F08YEySqyBVWa|uDb22ZH4C! z7#u+oD+H&}4Ae5ME6?rTLwE6SRm-A|4G--dh}i&UA-_Hor_rN7deGj`*xJ%{3%q=2t$x5u_2H*?bTO*@ncO?^P z5+Sa%y-H0r01_Z zt{S&nG}>-ZKPF}DhR7Dt9dO%>;nQ@?(`=h?t13jsB)TW+F-U&5pyYeOZlUBCP!A5g9|TzJuK( zypFxVQ72EkEs!Au29vh~x#z_2Eb58aAAw2gC9*?S?2vcEH(Z4eWXJlDH5S~6{c@@A z@R-Sdd3{!+Yw-~)jPr|W{DW*BQOPKbUc`5ED8>^m;#d#Bh~^*@rcI#J#(K4u3fJhz zlGP>TjM~t2lh)He1-2Hd*Vi(nj-j|?gz*!9L#>+mcoJ|8ny-I!o{>txRXU)@t!pkUQbpS3_gPOnv^rg5uFhdPZE2Av?#h#73X7_H`ZONo!iTX;E z&b&OQqjzMWvuDv4Q<>~=rlGXv6RJzX9_*KJ5%tQz`{EuAukG(&+b`^yv3|yk^~JWM zuQR(;CdhH&B96O3oeQWgqd|EOF~W+8f#Bf*(0Wnu0cUga1Nj9SlP){IW&YA=eaa^D zu9oUhQ@UObiLSth2J^cov{v4^s;hqav{=F$jvM5Pit6ctlkUmr8_Y-eq95O+bZMpT zG8P{;@Nkt8TFB9Q>a&>D)~*@5 zTAF6Sr@*QOUH6^XJAI*_Svz)8^;u{(ZlyF^=r7W&H#Rg*v!!wVEdKyL68=%>r~G3m zRzyKcJB2;$#<79Pw8MWY?L1kc8_*Lr2{?kg3pQdslW@fO8FA#q?2bN)B6p-3n+S@4 z*A_OiE63KVynw%%@N_eCH0P>xsY(LiYS!9Ff?>)}BkPXKYo>x7tl*E3dD6hYV9TwhES&=&{J{@qF)Px_E{Dp8|6lI^o zNi{n_zvPomCSG=o$XZ=oavlpt`|EAq!^4#}kI}x=DC%P5$GC7I@l&H@aOpiqyg`RM zWJx+B7I*A*hmrmiCi7N5&I}9Jfg?c=Aac?QmLNdp>~Fuk++PMWYP#ZQwv8 zR97Q>9p7V8yEOF)Jx0Eu_R;SI_zT9MK2fjPWfM$k)*;JUqjW>*L#ILMvV%<*{X-J( zR^6?Bkn&pzh4|Pr5Fo)eQXT|w!4Qy!!)B==;ItW01<@Ux9tvb1>dd6sAxmmCfeVtI znIH1+(=kjGF5{>M93yHLYD#x4|4l%w4;y_Q|KisAjlAfYWme11Hog05uU?CdD~;_tBxblf(oNw^!}zPAcyO z#ld3ehswlg9eF$M(GS7|NhwM>)`2=s7 ztW(|1{6uSs`3n6yi-%us)JC&Lt+R{vyTJDGkEraJ3re>rUn#|RO8O4)=7PMsrERUO zb{V{Pu3LM(*Wlw%#Hy=fCU1SmnVUN6ePj;1xZg1Y{Ldj1p_fm>Ijm;a^i*|kZ*^)q zzgqcwI_Ax)TDWJJbNq)WsIwDYOQyOObfeO@ouv%^i164o8}{ZqH?2tHny>nS2>;=N zY59#S+NU*Cwl&65nYu~)(L8m9hLhHCfu^bjqu-fP15X= zEBZl;9&+F!b)r#xCxt0o)DE}7=l4rmYP+`$&1|dFx-4a>Sa71-=X6@!ZCR44Rq~-z zufbp7iZmhJO>NcNCejm$yWO&f+n1i^%TMvP6I=`;mcJZu?o}Pd{%sq^iLl_&4FM7+ zEm8XC3Qwy)zekeWp?6w5azKiz5to)2#HBqB>%i2ww1dQ@4^70S*L83G%IJpA>Xqd7 zusCT`r#7+(OD8(b27}Y@ZTbvh>1}u3{CQlGpdHkouwP(bRd64*EmxD1d{r?}0P0FM zt)6Q|h_+Z)qKb_Dp1nb7i~GP5A}#_!TVg<=Z948kzL(M#zGT(d@6`tNGpam%$vC$C z*|{@&yDS{_CEHZQ!AG*0OM217F#zzek*G(<_-pXCiMLbjXZ~p6gI{gGe`DTVvo>7g ztz6K*BpJTdalrE}IXHPY#V<(dow^5h6yt9E4-MGVanNq0SXm(*!ps+M3!go|#Rxj_odplgl&ntP7h124O9(XS(GERxbh z3;}yUwMm-bL3kof@PJ^z9#X2C)HKT;Z>~tk9lkw-2%VbD#=J(f#Qo1XbS4&#WOb}> z|I$SVnp0tCWTX|bg}L2}4zhRq+XOLaMxuyW?}&`<3%m4ADP%P?*|fSQIp}Xn+`E#w zbJ%XAaRs{pZ7vP$UP{yotU(z26}N|5t7^ji_)wGH~N(m1FD&pGO)|ydW~Z6Z6}Q`L$OP+{V!w#kdR5x8(jMahIt%i#Di`(1JcS zF-@IjA$l5N9AE%o&id9RjbUASdarBgcr>h-5>T~p@BJe?{wf^1+hj6l-^!)FedDC0 zv#R+>Iy2ZLt@gTDWyaOqJ`%9sq;|F}KDcw*=)tRGTX>?jW^#_e{w0~d)H7CF+*Dcr zVtSzUwx`ST%tk8pZoA85lQiPKP(@qL+rF}USG>2rb1vY+s#cEf(I@%0AXjnz^wYyL zQi=}5TkJL#o>se!cPcTpQltTAK!N!QC=!4*A2TDf07?0TM-vb>)lNu&?|SK*_*8=t!2t|fQ$eyd_#;fIFp zzbu^D_1vEdKXO#hSaz(x>z20OrnYSlZ&+|{RTKZ#gN1+p`uRd(#1Jysbh2Jk{ZvJ^ zZe}p9=Vez#+q|r=BIRFk|ArQvDC21M%`~UJqsbn9n^}LuAD-AU`or0GoISWSGt}1m z!iha^Jh*n@X+(Lz8&$X`e1@W{(^^$xUI`Z+>Jo44AG;(DBgT3fzq%FvuPDw%Ytmd) z6^W8OE@g=>f`5Es2#-W72@VEWnCJ9d4mdRkzakbgIs-|^3&O``A3g+C28Z-;>Z7Et z!Dy1!eP!!iY$BoLpxi?0u{wqj*;}!Vt0SSkT}Im(630u1;)M}ezee8^s1N zTeJscH?A<^E8AKt729i4&FqWV5Ao@nkF;8pnpM;V^PSvNIEId#3$<)$I`NIgOTMx3 zG#>aLNt#a>(JJE2CfdA!9V)#CvT`^A3Lb7^Ejn>}7KxwHz;d#dMNfe@S$$r)a!cP; zm9j4#^;l~-qrLE^Lnl}?B4D2O$(2j{hVZhE!@-p`QS<+nwG3Myu zS%Pf)jS08>rkhFFC&$?gI$4rrom;Qfx*VZ{TET5fSzIP57Sx&S$~%XQOlHZONwV=k zy@Rcfx-6qDk;=v(d*H6G%c5X(9lOPD(rN)2PSuV&=*PJ8sya~cTb7}Q;88)5ol{kk%XOIft%BXFz?wUw8^-_@ACju+ z`ZC;ODwGCa#4Zv2OL3VfJDRNJKrEo8%eHvR0yFHv=9xYYNxC}OV-#g?P3TAA{(QYp zPTn|jCRw%?-|a!}F4Pr@M^_k9*pVkaan-6?15#ISz=X;RHli!zAAFziqo+Hm@mWoP_!=dQCF*rhs7K z`%s^F<`G`4*Wo5@uR)B?j+WzzX=R6A7m)U`ZNL!I=r7Dh)SPm}NvOc`C~$@3pspUE zu#6$;bi#d1_zpC1N(TzRCpi@WtpH~wao$GB@pQ^-n%HkA*hTD%Pe3asyMY;sxmk-O zXq_5&UBi#DI`RHMNaidOltEM=um+0(F-Ey@*tWt^`+D05BGQ6ZZ5O?aQ@5k=Q0G)k zVAR`L zZ!Y@#Bva|oXq{3)<@0Rpo7<=nC4*S+?i|o*jW|%#tPihbAH&IxIFPbA=4dintY410 zj7F4b+cienZy4=}^F@C!_yW!!Jg?e`y$%vTAJOSc)Lv4F!SgHKBt*Ng3Xo)Wuq_;7b%_Ik|S!d#m9y5c=KUp$ptd_cOx@@dfZz|@@ z0zcS(;0G$>FXD=$SSnVMjZ+zC+4hzp3$uGN*ChL_Y*{|1?5S`CUa9Pz)SJiVDIB%P zm-v&&^E!dK>>zQHcu`y@OZ<}*2H`A6u$r7^&1KpvVCyA@bBAOk`Uz zIzovYl@W}CAA&Tf{65ZQtdV`3PO$P;UM~DE(Wf!Sni_m^)%s9%QBq8x=~jxSHBCld zXOf+=Pq=Bdz~h`#R5VB&hT9dZ+My=sUzv|pcU{cNHpXxwL(o>s@CKcLLS`+ijmS*9 z){-m~T56?9D9xtFQCFmRFU+0xoS&Dp7RjOBu~la@qs$D4CyRp4fb+I=1{bGsIs&OP zS=m*1iyS>e?My{ynhE$Oh>aE(ptAp2od}x0Qjk-oHZ>)w%wlSI0a>~R-Jel|^s?7o z;VRdprpp%bO{hY`Daj(sA}XmrRqYA*>pgCDnc8GR=u(kw%Gxqx@2VF8k9T8jbAyD> znGh*0t*snGOvwbq4KOB|pc)?T0EKVxvX-D-3)G&;yb>BB)0DGVacpC59CJYX{CN}C z+)B`9N37y_1^8O7Qee&6!n+2$+mw5*4#g;p-N4CVHyWjoAu*Ifqorn7bFPPSx_m5J zrNc=|X+e^NBgc4ygcH37l3%b9oSA2~bKb&}g`d`>bQ5Ty!^yOLFS|JV=;m;IKl9Rd zRKH8VFMMqE#9iu%W|kGVFwduhejU}Z1fCy7LAXXnq_IItdi(}XAx2q(hmKKY#(u@$ z12`@K9P0@`K=nO|e^Qz=6&>P)+{!>oAq79!gjfL;;b72m#j=dh7+w{wsx1g|PyR3UJe+D^>_hHx?iE#qYBh2+ zrDv}>)Y}*_Qr=$_@Z_`w)S+SdFuZ7B34*^Es!Bw0lwz)tW_s~H$O;rZ8Aya0#vNr# zO1Lw?`_RK-g;|U$7AhQw0*lJF(uh#8lnxm2aHy3>q!;-StWl?R8Z|6fgS*kAb7&+E z{$ztr$9ETQWB#x#EwCD1)PR6^a$>s0$+KqD)saGX)i#ZYbU(Fd$32dy<@6r(*4V;A z%qFCQYP(i%ceXHJ%|@*zy`-)-%6b{r)AM1E>~q_sp89TA&~B*7x^+^;eQ|p{Y@S(o zc_SmMWdX7k_lgK8bQKwG_oh0X+h((C`MH||%^Zi&g<4{f%Jrbx925LTt#bz-g zjz2v5#Yxya*+jZraw@inE}dm}hux+1;XKsyimgQA_whe(7Lk>e*gQdXV)8-CGPh1q z%u04%6xQI%FKzh7O|DE=@0=scmfnB4wl!|oNp<@d-Sl;*!L@H_?G0^oj{ReQ5dJ2HN_J+Z<$LP}sMRH@FWDFKp)kb?V7c0DBkNje9KG7hsZZ~=;T)D#7&@+N~$kpP4EkVNs$F)8mtcD>{IZD zN(7uWdnHU`#fMKQqlL6^yges;x^GKGgH# z$#b=7t+lf7kN@kLsq^Dhd&b`5PjJtuW~c^K3&FRC250s+TEMsC#1{*%4t4%aRrFxl z(nHc&oWFPll2>P~X6TV{c$n!~&1`Dnm#&>N`|7VOT1f@<#&~oi)%8{`{PH#Q_xc(l z_%}u3`w696<*>n~xo7`fcNYF*_ZQD@)zl6gADDM)aieIfHxpq*{XVc<%=I=+dhpoX z!BZny(G>J*AhHdzY=Bn;CvRwFvyMGG`aRv(_8s`<=xc1J_T2X4TN@oXnQPIRTn zVz|zD}zTJx=xAf#1`s;^)oc zV~N>{zBovJYTwxO49@IiKhC7|CMUc=3X9VO;-P0HR(zqcYW!!f9ubs~`}dNw@N7+m zeq0@N>YbVQ9MZowI!a2UQ!h7n|C7ckl=R+b0I%hM*PH;+X>5+NqEZkXk;zxaT^vI| zJb~baN(u=u#_7Nr$x}5$0xzNyuupQ@TRXf5v$4FxxHKD|v2UppIPR5`r$-cnx8J#R z-;8*6nbFY|%UiJZ?dAJ`?v^2U-QwGl}^y>LZNWq5wIadE3W{ zxfKgf%TlEF@X|LdjKTcj1ka^r0!%CgXO@rsk^d01vKlZ*5I;bihMwJncds0|ZD8R>lNsKh!rQi44vmP~cCS&d z)tLm7VANX7PQ$|@cUS$I8yBp6aCd(F_pkboyTHpM3EqbD6olHFyf`sgEgJl@we@fA zS^LD^tq(1`@#P_TIWBA0h+6gPp^#B@i!RZsft8OWboiB}u~F8_yg_215@MiglIkNq zy?kDepyccX#zvXfaHkyJ2grMJFOBqUe|Ja5!IjQ)ByDbEUK?*?K^u5i_)=bv8Sn2= zzF{U~ko0%)>FT((cpf9^f3fTvr0PPrxMr}_zXEwd?=U|n<5!UWcKW{2UV4xI?Ao&a z4*DK5oKjtRYM(te13SjwkK!bkx5nR-bWh}WSxWD1fMKh$59HiVb(FrZ z8t9Me90!+EasvTDng!9X`@L<0kfK;k~M!;f^L>GqUrTmdwVx7p;DD z@6N9+UH;%!e3en{13W)wUsP3N1tyvcOk`D2+bJoZMTygtfdM|DeS-%vi$olv!srwI zaGL76<}?kpOD3bP%O1{G>&<$xcPsmpXwolkDO{KFN{mgXw+ez)5C4eXmI1%6*Dz*L zGP9Z-nG19qHiO*0R?H=?1wSOYpB#$%WdLCa#s`1MA{_Ii4Y#ar zTDrPxa0CrVTN-R?o@_w z_5yM_8-PP`GtK2>8iTL`w{SF5xZMaqH(|^epGrx#ipe3Gb+P zpxI+7>@~3lQw7Dhtz#cBm)M7qcZqx!5;Ka!YA6YEvVpZc$yq5*M9Gkb5l$pLocEV* z->}1Aw%BXubjQ81j;cOgu)!QTcb&P?UHdgl*ng^WhmkAXHm_N)(c3MRbFzVkfJblU zw2{iwl{g4Q?%{-(-J%Jb)9Z*7VO3esdky;>@?>Lxlh=x)g)k>2L`>6NlSSQ)&Joih z`vzT&d9HI>^TNT7T+eTMH_Y2~)3HNGZa(+}K}yC37fkoteTvNpf~Fy3(k2h+C7UtQAdY^_>QSeL zn-$oI4UQEK`D?#ogIKSOSXdW0TW2nbQd{Czp$axofokARqK;4CFzh`=WiNVCh*nif z1}1tKGf)|Asi^9%@6^^;S2w%9v}WW6PqscGN|nBh<@$vK`zq6wI&NFi)6vw>*3l*- zZTw7ldTmv{F45RoueH13or^5pw|XGbQX8=GQpDYoi%cKr?~3;%eWIo-Sl8IGtf!%F zc9O{EAaTKU~;hD?HFWJCYB$6vFkZ}XX7^=?>@%P#By%8)oX zH=FVEkJ0<>@NCwo^8hW0dArOdIoHfBRsAP_k+#7z+H}!2sA^zKa=^wdVXXY)v<;ro zrn}fCA(h(Ha*I@jVjDc8&GcfM>hYXsk(tq{oT>BsS zZ_ze*Mw{N^IDv5;$-^yROnmWHIq9mmWKGhY5tIu4%EW=3(GsJS%f zC^x`pi{s!KZTe{&>Dhd9g~K3}gUcOZ+J(l&TT z8$cCpCgCqMDJyp?ZDV6w(FXWS+mzuin^P5vZED#Z+5oz=O&R{Os2$~Qq2q{bE!qIW zbUtPH%hsv>$lXNSG_rMQ0|?V`%J7%XGA8bJ+Q!3X(FRbabSylh9iUCWSAsUHS^z)xkN6*=q9O(O zh8(&wpDNMj)LEj<5f3$tJ8J%M%tzA}Ra*s1ym_Rha(Z>hB5@yVcxY=&bk^p(MmBtN z3w_}CEW79Yk{JhX*}k$N(Y9>KzNIm}r8`YnhC`CtPFa7m)O%IlQA5=}8R@8|vUi*5C9f ziwYr}wkNhOcU+cJWLzs(5>T3wTvBu>_TkFHTNa%O(VWedvpqVG$s`N@d9HBbf93}Y zztoChhKIVw?B1|^&HRPu!g{~m)x7qB6{laA#S|I}?;vZ3WBF*%5V6~|nf|BrdXE-o zJ(4cF*1dRg<=N#uzOc_fvNZqtE!*!Qb6-C8B)1fE$Fcht9V8}d9XHyEzXARpB8C)U z4(*|MP#(l2`HdRyh;-tg(8D5$wb>P4Th@1U{+87|$Ey!MeD8nV)&9juDCk?ECvjYE(t8RODcRo-x=vwpS!M$HH z86CcA$rjCIXI1qq>+cQ21Y=Yd!25Tw*hmbi%Yr$Ri)H{_qWBSwgMb(FhC7!8KZusE z_z_7)JCs49tddXwIot0OaED{py44E`_iuye##LotyPef>iy)7_&*A#;R?(!<`<9sO zlJuO`5YyZ)%IbL5m=9N%VO1n=jJcEg*Y6Z{2F57xY#!Sb$6-#fo*`*+^i zJEzun;Wx~Qo*jKvIJq*qlu?;ca_wlaY2$l&wahVxd@)Yjw(cvd_x#{QzuOo8J1_{$ z>qG85|28-|(aqCIyP~_0xgc;PfbvQnT+Bvs3FVrQ$q7FYHF1DBoLHHOuN;l>HoZn# z6WRFe;op2`)q^`aHh<%lHPZ_V&572VX1OV`gYAc)J`kgLZBctZDyv&Wh+v0F}!Zpk<7_ZfnDjbi`&}ou3i#}$ySG)NO z)_@?1hFSIj#i z%W-S7rBCBBOYAe%ht|*DzqGGnW?eoIF{EAQYIk+4J{#C{|Gci#%OdSH_5PI2AzEwm zBWv0go^0>BddS-lOF0tI>n-LJb(?3by?x7UcU`pFQDvxA6Gs+sCN=grcOQ68Le)rc zrqVdB@3Le?f?`5Z6K>qv>|k&vUny*4U|I2j|9is&AMDrH`>K3Fm)3n>crhl zo?Y{={@IbA9K7$Uk^UR@)@xtt-n|#|TY%Wn>!?#OK~EB&_E;64g(BHXyD>hUTKpog?EG(BJMYMw z$M*eX-{GJ9;QL38zp(C}!F^A1dpft?HSg;8Z@T*D2X?%6ywG*{`M{)+yWqtLd zCL-z1XlE$ms_w|JUJ&AfWN56hW;U-qnC_cV*R1G8o3~{AME5*S#Gl z@%~iK5iFb!M`cq$%lu8@e%TQCji?h4z}N5^?!c+h-+HXcSmW@kslIk5=yv#RlEw}gLIV9pIk97$|IjGU#D&Aq698P|{2=ZR z64+s0y4_-I4LQDPa(KIEbaxdVcTTrE>*6ujwb7ln`o#QrTB{DnUrM)fBYMVVaxW9r zdQnPQ9kaXIF7$u5@CS>z(iUkGg==P3&mYPSg(^heAE}Kzl_Czwq8z$`C+FkT0fyDF zGC4#9qM`v3AK*AY1qRTYDoS$5z_1@d5N&xas&;C{hOQM)UA6W3ZP)(Q>eXNU-ny>B zuC~2Bnf|!(u|4%2HvZPQzQ$7j#8v}bMWzLJE#CUdiR<1yv+niB&Za}7n%Q68-nM@8 zv<(j|*g|D@VFB01jZoP=fvWuh z6@En|Py#pB#|;Kw(8`TW5ka;NZ&2TES;*B4TWqo-f=ufo!3N^ZCpjq}u?7A*oO$Mj z?k7E)iMovv&F3z$*)+fg4;vT}QYLUxL9R(d^$n_95jYx2bg$&1!126{Fw#A3Yklji z-ae*dSv$^Yv9@a9iTnNGRD9#TBOA{8e1UzpWz%y{+%dTIZmq}EVClx8&$EsV=jI=8 zoZj2VitSrAwL5E_O`2?d%e&hv7Pbzb=)8Snpd}R8bMF6P@6E&HD2}}0uA{4}tE;Q; z`<}jLx~J#9PiZ8LuF+^Tx<*0>A%P@>KnM^Lw=u|E3o?hnUAi!Y1~80^JhE@LjU ztc@`*VAgnT)@zn!yi~zxS{2@nidOY4X>RnURs15s{e@-6wx` z?^yctyW;H~i4N}o-7myiU3p!&6>D_?FM}|5igSAjT&%UNglQqYi2iMMpt_Z?wHw2p zx%-thhhE-S+e`3fV9}}5SKE0lo@)2s^x(mtTy^!1nH~9c1FH`pS?H^GZ+ZPk_ss1j zfi5XL+;T<~7iLAP7=8G<)4#i|(!cYHM{k{X&W8Q>81ng7+v%^i)13YSx%;c_^jF*I zueQ@)ZKwaA*-m?B--q8aZn41!tvu*gDSjYQjF|^8i1E+-NtAS+E_d{NX znbIQAUO1MaH&*1T8|BE!G{TMY^f8JwQ5^v%Zb31%$jQ+`qm#$U8Xg2Xd1k|nas~YA zd;QifctrXpWV_&UOtntR_T-v=YY+R|XZnRkk1q9&d!nw<)}^wBhc=?(P{w=3Zdv7f zPrIo7;JeRzHTg%#`JslBocJxPT^i-Z7yY{YBjrWp?0#0%17%Kp0J&KN#JNP0mS;Sg z^z>5Ow@FW8K8L;hgs>g_+JQWrh>^T=S;Elw1gVp}k%he9*PDQ{$NWaPqD4*b%CyMc zxQ9cRC*hLl>GrY*k8Jbm>86XybeW4CXu?jxPngb+2dPrP7Z8|lf;kMkd%L(TO@h8+ zd}V`>75aT$!K5QOyl06lXhI69KXfIw5&>Ufo!FC{(=Ir}skW#fmtE7F&fBZW)&@o8 zMZp%C@`^rHl)_?US`Z3?Rcq>BwwV{D3^!4E#bI5#rs+7G-wONqoU@Q(^==79qkCoe6;6M_%Wvbe>Yr*^rWx<;EX-W{bk4p$`k1Ij1c6Ws!wivs$bWL^^sj z+!Y1#%#q35^QZpqH6qFT=-mKprXK#yY)5-jq0|6ue1o3kCA_(Ml%a^}R1 ztJdvO_?#;zIrzh`YZ(RSa95r>vi!2s7A?LweZktzS6;Mf?G;@AQGZ%@`A6>x2|vt0 z{-d|Ocs|Orxg=QHXWtXvJ-b_kc~4=-F8fGh@GRt#l$l;AKp3)co0Gj&ptxG z;sp{*%fpPjR*v4f^coIGvI&^2K;|_QhN{;B;$g+l4R00Z1iY8HI%xQgAtu7pYesx) z7rDH_Sfsr->+jYbircPrhgu`!ll>k=9Sn5mdK2QYm%kPD>AD(H)tyVEv8@Y6{0{GV zNtf>e7d)!)bIak$Nc1>acOKpwYwnLI8%Gyz>v14){Z41bv#hs}>zm--QU`tO7dc(= ze7fw8i-OfH$r;TtJRO#8L%t$jx@}nTjpH@1Q`b_VjC*J)Ki)gAE+A>XGmAn01s!;l z5*B&8BDxewAGf*MhdNVz6YX7UFy429bD@;pnH_3Oj;>|?6+UhG4%R6a=gUuVuqZ^D zxi;xw33S1AyfhSYiK^_B-JBI4{IH|h=AMoOn;X3OLcHL~33hhN3ug_L54kPy88Lbe z!|%bO+v3p0gWqwtF>|91TsDz`0= zoSL$X+kNdJt~`m<&5U+CXm1s^Q2a31{}_KP$81vL3IpD%;FcmaI~YK|nlDc(PDeyq zdf@csN*woO)?UwTysmP|Qbl(O3%RMAZr~cmtybBlO;!Gh(e`l_ZcJ#0pIEv-O7~%C z6$$2hdvHs11TSe|uOYDuLX$x@;F0}&+nQA^$uI&s$@xiH)U$zLw%6u5L2t&@WSi{f z&qv-QWF9;q3yq!8a9We_q}7?6Z}+Tenh<$cI#g!$PR@S7KhLj&uMv0gb?VdH5dfSj z=pAm2V8Sw=3Wp;63ro76dqnVR_$jh+>|pEH+3rjgYZ4(%A$AepA%fn%sBZupN%%_IO5!gd*{? z)}N)RUQrop53rkP9PHVbxp8ii;?hwp;vU0dz=cXslRGs{$y~HSz>VIQmC!IJy6j4< zpEJgBdiH(G@6nE0Ojz1~F^r#D*wV7FC)@Bk)FFTLRSCD$5%t*c!Mtzz0QVM; zh#g$5yBe4F5^4jQQELgei~eQMh%+7%jTrV`kAw7w>TkUyGv;|328=XPQ(`i=X|{9XJgjgynbd-PiAc32_D1iUxbgNY~@RPj}H@{S}^J=ovYvrO(DrKh>6g~(bUc3CCLPYM+e*m8(kGMA1@o%E-4xI5T z55m2}{CK@b#Ns^SUnAUB{v_ER^c}v-h8K2o-=gciuoBD!Y_8@vgGw+9e^iKFfJEEd z(f044?JN!*#dGC)(Ck7UGl)8Yc2g(#jBKs+CRGmaX!xb>Nblh%t(wb?^J3u#dWVSQ zqW>uuR4wp<5R{x{rX=u|jnLh0f!-tI2A%R?z=-`^9X~Jmzv)$kZ0z*xG>17>XboqT z-pob@E{dkmj7Y5Y!|V3U(+dvnYJVcFW3* zH$K>dn5fhoe%Qolm}wIRnB83G|L9LS99FSosWDG z>?@jN6}<{BUyXM^KXvZZEw66*9$!qC3-)cv_%Yr>X;s68=UQbR>X+YR3%Zeo z``vI)ODX3%Ihv1e(DS82Ow5Ra7Ql5OIr#1EE0;UZogqgh-54oShHL9QPa z3W!C!n9ju3PmHD{dLNB8vr%WKUC`a7`p-quu+~*Uy*P8Q+Q~E3{gM zFu%Ku{T*Ib)@)ph$HvIUlyQUfD0aHaBg&KDi=)5LuGntP)<|U2Ngv;=Pbm&~`;o+0 zRjhJ{Qr_Bk>Dq1tRB$1ohRP9@)ut$HFt&F660iJGv8#gTzUDCZ~mts-q<(DpMKxA&pyY? zh=)b%rVH|QuaL1E2fi+7sp!xStQeBu*&gJ{|i zCtxu?puiMfru;)1In{CHsF!z%RwvSJI_z%V(LUwM7I1;1P`W~$Rzpb~$Y=r4xv}z3 zBe9u$q1@TV`n;ywHRc(6#^nj%z~7?@9xXX=pB7_T>z|LdTV1+5VAsa|ex+zdJm{5w z)86V-T*D5$S6Jm!6vmLIQjH-9#MnOJaI|abpf3Ld_K!IDa5nhB-l{BWXMDhVX)pjC z!GDP*P%&yO!Ky+%XOoW}JR5&FdR*%`L24CaMTYD5`b7i=aU}NWv67AViY`&q5RgM} z*LyTanc6sb;OD}xw!YlsqQ@y|7 zD_fduE*_a_k^F>9HUyea{`%Hyf8LZCRXenVis@_(b%xkLfpfdCTnc>PWI3gk6=!xXKi0<)}i;#kuK9U8jy? zSUVx@2)e;?y;P?&%CX6U6@KAiKGae!h<{OzTm&50_=h^;>_KY>POf_ zWUZb=J3C8VUDSuvj%E1d?<&&zPSq><;^9}CO10)Gr6wAK+3(DbbN8TbcD5g*x{YUY z)m_UFB{vC_&VqWdL7}|~(sv)1>nKzGjFFwq2r3gz#wU}?MM>M@Y}n0%CBI$p=dm!_0bq?{p!#+?`Sv7YpTcr_Q@TadU&MEj^`tafdsq z4W$aXtgrHpPn9Hbx|Ay{3Z}N@3gz}f-sre*bh6wMmYmMWp@D%Q|DUQC`>F1gyu({B zQXG}Xa6+#*5HlWXg`~LbJ}&KxY`AW-R$T|=QVZWy$00f2^v}BHzof4(C=*}UI`{#` zrvrS+u{&glLtsA0fcF9&0$13YFCV?mvGv{*9nRG>@`T*&iiX_IM1~ig$5nnh{6H?z z(;e@GM6ZrfpN0nchYvs2ltC2rWI7kl?(+B`+<0JCEb#rMaBUE6c*4ZuOa4}z!FX*& zyN)ZMU0zHFl2Nw&kerat?Z%6_LW|Y##bN(pC$!!G!auQSFz~jrn0idI*{|lF*1jCneeWwrRN*#zRP)zkbcim=hjI&w!bh$J+FW?&~zWWN1nuu| zw(g|QtD?^y&I)Ci-&nc)JYKhjx&7yaivlJ$vnjD>J&Qfd$fc7ERO1+6SkX&EZ))K! z(D>kiV}s4Sw|VZv&6f=?zwS8e#!E+=CeASTF|A9b+Ey-|bD(@yd_=U`;pfjJ_l~wrGzU5lf9E&{oAAtb9k0A3 z-UHj!NjcnxBV>D%rN>5_UGQt+1SSA@{DI4obW7RFE=p1mZkDf-t%YD6{AbEl-#Oud zlb4<|QQUIhKHolPZKZ2ledo?#?tX^1bH_Svwu2NGaec^tG!h8;0PKgBI9Wy!NIh z?{G%en_Z28Lb^Ga8ta46_h`h)cXV$~boZuG>6ydlEtL9PoteRO!W|SjUHi$gG)f;E z)00ogt#WsHc;BeMJJOPB@4rpSPbS^BV{CfAONgxKV+R6VW8URMfl@itia~MJP8iY- zC2(qk%jfACEYjS8TV03>YhcMr_oqq!Vybg(9cCAj$xI;qk?s3rWw4>aZDrHqfDt0G z6|JiUuiLN#aF3qM*4;a>?rsnG;tSXNm5f7GVh#j=LUg>rIQK|=zy{k}3n8YLY?FDL zfES7}&+d-kRa36&!s=Yb2irxlpr;Wpxod6IscJ3{Qc1|6twFqf<8-{xbqlZ>ao*Cw z)`2vwKN)%-=%RSX@HLa(i$@9QM$HVbDN|Sz*kog}IcKD^$t7%*B8QuS>itP)8azS!3_sX1X47=1$Vco_M>Z9xT$agjhZJ{m5ce9<+JsGI_bzAOoqbn)9BpN} z7be53t!&d``jgOWo)A=3MbZ(Ol7{`ghc(!2uekwAh&By7#Ms@Kc|u|Z)vdl;jQ?;M zBoV&{mw(y03(jqh;0hK6gyLPom1Vn4W#K#U`m4QwBqxko5?t=ms{1IlWm1^ z!8hK#0OPbQ_lg>oR7c}>KLkePrx#hhvei-fWoEdP^(_v>j?oI`s8tY7IaQIZI}~-o zwG{3o-?C6}siV-#zsjHD3PfCu@dgn(f;(z+Fb*k!_`s%&R{5Ulx{O-~MZ;blXxFia zzZ>rltp2bK!H8t(frlL72L9$T7jc})N7f#dl}hDp2XBAZ<*-G+nd?@KLfkh)PPo|* z$;Vb8{c0WMya+J6$TaPVFDyO*2C~?jWwHn@f^`e> z2yq+{$w-)!z2fvJLUn4wwQv*@b%U3(K2n`mv=#@F|+&cj^A;TSc4ABTF5g#*9Dc z3FWC+(BUsVd!H;%sh=^`QgL_?D zQTedjx72+*0xnMaw&zxr7tPBeR+ea2Td&`q&Mk7g8$C%xa&vmMwWaG*TON=cqNClj z@5Gx|O0qg|>@l6f$N(Z$*sp-UjJH`u=hn!lTXqg@n&}YmSeA!xg>Pyqw5{p%AuWmM z7M=6_*M92xy-*@08rvIYCD_25DYuoM$vg5jF$;lg2{IAzFZxHm`v%STp3O15Uw7-l zt5c~rLN1{-7}Aws!1>X(CT}3FlNZukJs(%a|q@mLYD12Nq4J=mM!c8R)@YGt1o za^!8WRK3DFn;ju2*gGkS+laQX+-CubvFoOnINe=n13kt!>>K=xL+HBK)9I^%Xvwm@Px z!Z9W{(e(YSIoz@Pxis-qIX%xA~%5y;g^lUpR1FBZ8=<^Iz6nDtkD=BX-^ge2(W)H_KDp zg~LyFKENEPn4rCQ46h@qXA8P&c9+GMUb+(_Rq75ez%}!a2h|He5a{=)rq_B zO}*W-eyl09_{P>rekU8RFk(r(23|Knu99rtU{_|ECrK9eTCmi(%23tU(6;bhXYT5e z8jzaQ-_bU*V&g=yKO#!@$d%K_UGm-;UmC<|SlN?~?p(NWPZ&=WY}u995I^+U_xSU1 zH>VpKFY-_Kq}aJ3@|Hl+7D#SUGt=8Kl%(4lhdy8pv+!vcneqj%JzKT$Z#LxI(|Gf3 zv!xn4!+S2h{rYgDpO?yt!(CWeWT}<$fZy3Ht7>f3!2N;d*7&u>V^(+;%aYIQxa|1P z+@>gro2L;p!sZna>R1vAE7}kLVM*B;jtFo`bFX=u)(-q+QQxgS2=33b_jue(;8hf- z*2;rfxc=FAlH-)I%L*3~nhCE8xJRPdOAi<5+)(n|b;juNo_OUyv`tGEcc`vl%L#+q zt~!I0?Jp?px%QRspK;s7U{?e0Ub(MZjoI|Jme2lNDTG21jQ}wPW$;jyEiDm zEeE~!o=n{8Q20!)FuZCa-`bP%lzljO_m+}lg3fYI6nx&TTLcX8%}u1+fcw*sDJP*=y%ENyvxeeXKbEq zufcF4M!F$q_#^aKvKCs*00wNgVSYI_x6y%uguNIqUszuN#H$O}^7ZAyOre<6?b=z* zLxVl--2=Df+I^Xz94*8y+mb#(9iDVJ8Z+X-V=|%iq9T|6#yP_$O}CBzdSzcSE;|b@ zJ?TDy|Mpn9cVhlf$AU_$+}ap$CmJ|caxlC8m;{I0bE+-1;ZusMB_f8-IPuEBNNc2< z1s6bWR*cQ(G0&L~q208D%z>c^ZZ1t#9l{(VS^^9?@4*rfBss?n2>AKYfn-R2T@EFY zhhlgtjMos#Yxq|`)2#=$@lcPX@w1j~-`?mzv}>q7E{DDNllI9)^Ii`6ts1su_9sV{ zpIYJI5vy3xa*B|c7_59Wv3VSFoI|4*3O?*>{gC615XH}^Qv*4B8e-)vqsxJ_{->~b`lSc!+?c$15>prCp8s-6e4ep|rf zLC#B6xkI$d*N6NP`$JHX{Xu@24PqsTe%Zy@8X?B$8(cG7H`W*ySBP@Y^nQ{Yz!o;d zfGu+VH!=D;MnB=Tzisy>F#0-1f5aU8fGBSHQzOn#PHN_WD{SF4+8`qL`zw6Vat_*X zBJ<!>#R5fca`zxpG9=3qKWcT^(UUu3n z`&_t{Cxr^Wtjh761;MmH|n$wQQwpc(hOWD4ISoCkTEKa+Yyna*( zBKSHrv&iLl&h#Bhnzp}Tk-l}2R&LEq-<8ozELmWnk`B0vICc0yKf z=Y6zibo}XcsV-z+b-pugip?yxFtL>k95Q&rPjs70Q}1|hwj3_S{O00>L~c0$-#pAq zUX^d$-?!-aoh4*nHP#}n^m6xz-4h>v(I2lZP}aEreJoH>-rI(2sq^d#AMc)AWa@w8 z*fZF@Q{_Q+)fD?I`8cr=(5^v^GE@exectfu`J*G#r4w$x{^FCayOVoz_sW?IcduG= z(dc#C&baf6-KXCLno{L0{tVuQbC6Nc9PBRGKr=|kf6OsO&ca$R4Xv6y&qqt1moyP$ zOZRV1#~hB)3tbm6zpZ5VetT^=SoSiXJsCY!u>!IJTHE9Z~SJkAH9k_kjdyX*TYp#G-RnE<1hUC?*Bo5m(;iZ{Q`o zt7Gv#dfL5A@1Tp=#?n(2b1;P2_gH~Ocs2>hTaACQO+~KmudIOky^zG!IvH|o_l2d% zP|ILM3Pw^9SyB3q*>TfD7nE0Dzv#HF6TM3obFbR7I)9Gm@Ee-TE?5QJ(Q-->j^cf? z-*c}Y;@5E5wgX>@q|aHt_JU>IclQj_S^~3$dzRPfJ$j5{Ycf|uZ5A7Iv9^>1eX_4k z!OS$gv?LLf{{?%V$K&IMH7(gb##ue0m|nOb(U$YN<~vT{TA$CjssbiTKId;Hbhdc= z3!K7F+9SQOpuZoNA~;KQ_K)1fe3aTkaf&UB8O)o3_JCh(%^qQo{7AZ(!^q%SRSV8@ zLSLbKrm5-Dnaq^rO2q`RC!1_7O*gk3Sdm>Wy9>I@DsitDVujpLw6CyiO~R3Flw7#5 zUCJkVV!g59<^`*g-a_7{hgpC=v`K<4xd$pZdutjX{|q&Sb^|y>4F!nJIW$scH<}FA zQFKjq8o~}yxjox^`qzoLQ)d%ZacP54bXjeJEq*Edzo+Ni`G#1dOUFU|Sh&2Pi_6I} zEOLa4(-ko~B_kOEx6T#T3zGIhq1%Sc8ZJAv>jVB8-h&*h@JX}%&5DCTY-@~9#-d=T zl-T1!;Dr)X)&H4b$2@2r;Ecv>6B^_c&e-a$C+@ux6ehD`MwKs*h9J zo3g3UU^wD-!SoVG`g%lOjp8tz6X4sG=S!lbEIuAl>eYN)ShsxqUs?kQfu{HoAyiYk z2489!Q(R45Kk$TEIBhS&!Ac%5Oq%EVLnwgUd${YR%4sEovl9+C&wdZb-yi9Hs(UV{ z9hl1m6Mh~68E%oxj>fTUaM^ z&&R^7g5*fPb8 zcM7-59FY(^CQ{XrpkTWwGz=MIt1=v!Ox3C`E}+%R2CJ~knM#DDliV4n6pqNEH685Ao^9i^iSbYF}x63LG`8hLF;W71&4QlS5zWs$;9>4Jyye z0gpW@&Dhl`CCHSLyVz+?zrXUof}_#av|f}|TV_}hKdaYEs4eBto{2>6cZCbmoQ%5L z4`13D_O;+zR`2z`v{U0IQ>@+0!SzdCq&ZX$vUao8h;5%x;n^UvWt@$o@q8aKN4PwO z3jbyo4A>A~b4q&iVnNANKC(wY8!I+=H4m;Ru z+bf^9!&l~N1e#!5Z{C#b?E1G*&q&3;$86GX1lkH#aqxUA9<^0)<EZS&Qtxs6w9%KnQ~W5D@!@$>dyKwJoy4sZdEe^(>^X(eVRS}IujpyMBG-oQ zc+sQ2_BVPal6G`vX!NQ6{Z5H%>b?YU@*IoiLHS=Z`4*!OWxI+ z+~H%_()?$4WFII-j`s?7<@5ZqWo;qeD~Y8umf;qeO}7@$0(s~+b9^##m@Dio*o$aJ zCbNRXG5WW`wk?apU>|oWcQM#kIX(7O{LjVyVbIG}KIDDEB<|QQhrZP6!)p-G!`Y-5 zB<-<9C`jH8V;RAMYC5@0Evm{+^Oh1B3`}B}KXKndelpw}mmR~))TwNXE%Lj>e9e>f z+to;euP_*nFAW~MaN8}*nil$k_CR4U+m&rv={w}e1{0qB-0R0)#!tkC%f86K#NOrV ziu`c6m7C|*Ja%o78uFJjvDWCIfBLqQ*FSME<#)AsZ0>Y>CO+J>*njQC?aRi}*~L3( zJRYdLCd>%8Sc({rgp=*jvvFv zef~hXyHVDHnZy@#m#=c{|JOUIg5f`eTsm=Et{1+ru9ilR#^NDW-+R>9M=Cp0PXD}5 zu@c*n^z{3^Eg?Dd`~Swj$nQyC>T*Of(VIQ!&1HYu+eq^fXDMvmV)GGe1lb$4>DVpI zeR3OV9eeZ0)l7JGZdt?n++Dek-y?KlHo~u9t{>=};qR+1(pW;6&H^dbt<2eni6>Q; zzBw5t4V|ro+;q5<(UkP=OgI@0W_G7lhtMslnnRW1X)885WPk5=S~DjVf{A!2y`#bA z%an!ANVw(77Fo|U#-fdxkY5fjQmytte&s3mpEj{<$KUQ=T}-IB?6fefv}8)5NHL?? zIxg-AGNqlx;(3_gj((hpeym|<*||^X1a$!o{?lzY&MNB5X}Ixzlx2n2%x^ed5T4lT^-3O>W}aIj%VT66VAqTLL%s|pGyOW}n{RZ5?l zDHk%QrtNM9*>77Amg9+BD4a{WI@WjiL*?9F#3f2*qTy^JYzr?`rDRKR04ertqait# zEOca(&W?2*{y-^rT28Vhhvy{`7k-{9MT>w2Xrr*$a-Z;1!r&X2OHEA3Z5IuUonOfLgy#u^Z(uGnF^d-9`VrA-;Qlw46y6~W zzJa-%FzQ2lPFkL{3$>V~e3^R`d5I`yDPIQe3c}$v>tJUwmk4`sQe{P;Z?r$ecX7WG zzD!tr1AC=O^UOUbZ0ZYFX|g_SIHC`KLpbb^wfON7?)#Rn3%@2>_y(=3Obo8gn{mZM zzXTy0`}b1~wD9?KVn7YU;ZJ)m>Q#Nei@Nhiz{(Jd5Ew}N%Bn-ZRxz@zsQG{7nBgXhFlMcRtxz5B` zYZxCNLJU!(&G-i9dJ}^iD<+*H=o}CdR2RO1VPUYDtT=2{S1bnp#2pEv3Fop`lFbV8uB+N6VTU~V$$cxq$X zj5a@O$|b&mL3l}ObFkLtM(!zI7w)9?;2W4Pn3$EzaZ8@nH9L#CerU>%h3dM+#H?MB zaT@KBfLSm6i0I%ObiPO!MABO^FD=jIAb`wXE4)Cs1j+2Jgu{t{Z9KeuKliHeEMf2s zTDO_?wdIpxqdo=otrUJp7<>bByGdtZYn6{WFx@5w-@tsy#Ef@TF>%CneZp)HzJa;J z#7r%zVv_s-ubOo54a}V;roT33h)-+zxrs4i)!t=d;88MUX!Z^6ZOcn0#=^Y~I$t(1 zh%{%)8Gg$>X3l?u&fQgvy^3M|I{k=#{jG_~d#f0M@8FXr9eks%drZuh#wtcZCW1eh ze8x90UokNwwRxBVe_k{BgKuEIN*KhN4OU}Od(oaP=GfyKn6H_1Lbcou4ctBaxG9VH z2IlJ~osno&roDU{_p=%u7K8U2CT4-xVXn17wAm*-N8^rfVD2^R>e8$I;^0xdMEC_^ z@D0p2O-w_L&o1hhAwT#A<~|eSuFZK1Wc9n%@giCMRuxmFgVX;`e!7NX@qWK;Vpc}X z_E1KMADaC#=Fj~mW_^u6_&axXjSl1C116>~SM8Svm8u^f^ZMj zg>PWKYhskzT()q3&n@B*JsUB&@eR!POpIK^M1*CQt4$fhH!y!^V)QDXEn(~#n$S;l z@D0o(gi#*sMsNyaP0;xs%=h0C2H(JZ-^Adph}Xbqh&%m`NeAD+JZjdJs`XvQxc8cS zHNJs)%%n5in>N>98|b`k(!n<{KQJ-5WY}zv6LRP=>EIif$IZIhgH_r1i2E91K^y(D za9;!F3Bsrkjh7HRp|);HptaEK8@_>i(xlZ<n!b)PH;f^OV`9PNZ2k`fuUB zZaLS);2W5yO-#0iv7xRiAMp*$56!x=wK21!t{ct1;TxD|OiaEei#$KXy(KK5_TU?s zADNi#wY7{#T}y;96T|2{YhpTU{bFMuJ7VmgBMj5`tMTUr?mn(Vm`8N*4LZ-8n4z9V z)#$sQ`zE(Q7$gk7fqB8i^mf24M*FIae!YeFzD!>64a|#Xdk`|-lzRu?Z~4Bkh%opD z=Eo*xF3*er`M6&=*=P^R$4e##8KF%&0{S&ym~Ud3O#i)!>8kOWgG|STF2dj&n3qiq zLhqY&6v*pSCI;WY{DX<{*4oVYEaPq+>leO(`H6`csEvifeS^zZ>2Tiw=BFm6RI3YN zFf6|l28a&6fqBKmw1ledadE%2ydnIKF!%=MXC@|7lZ|2I$k}bKLHGvdRTHDs_!9t~ zw@o_u2Il7`rclcTkQXf6G-CR*e&HLK*Gx>X#>0=-^}h(i;sGGpCXKs?`#v&UzD{)T z4LYxzm^Q!HW?*K(pF2e4*FatP2IdX3uB5BlchXD5V+dpDC2yK^){nVNI>y>z)Mcz4 zzaWhIPwkj#BG+tU z@D0o_P0ZTr9%bpRJY_kESD1+FkGw==2~k{s(-NY1^o_GWv)qRa^1vWR6gU1{gJHx;dB8+JX5vHN?87^r#i(wiV29^*ob(Rox`&8u-?oqtC2Zr(tzzPE9 zV^$E+3ss)veuX%#z=jw-SV2UuW(9#BYx{<}w!;Df#j&i_lz#(JnyPic@}Y9Fd{C~=K5h9T-iM$ZIhn}np>nc%P@bg*ki<5?!1Mxq0I zhcI>aPUUZrkAm7t`6^)T5Q3~7w513d2WG#)+FE2%4oe5+HA{zhu2XvnqcaRF9m1HF z4q=?gi?M9>H;j(+H^9KsG3sF}JL*Zz`nc^zJt;;5Ru0iHtsHY~pXF8?G(wC9tQ?|2 zRt{)1qpi;x^484w4hx59m=+GvXsoQp>23#W2gxlg9Kx6u4#UjOqOCtPRE{x8^BOL5Lh-ugDe}+&{5uPl$$CM#fH=}gS(}PA&hCQ5GIJW`mpj~EQ5^KuvVxZvQ|(JS9yWE1Tnr)j^O@esZcpt zDkyhRUq}{QtQ=Mfm6Mf%atq|`yLDp$3&kim=Gg29l_&V=x^h@2R8H0j#_}-u@I#{y zG?uVTsGKYlqW>&+b=~h_l~6fZB`Ejc_ZL_;v-W#f`(Tk!IawqqUs{=lES=8Em$GtL zBUDb-2+F-!i~h@4i)b8RiBLINA}EjG_g^u7PwO(Q5Gp4t1m(%uv$z60)SyqY3=4$H z$pS&SrE(m%sABk$ER{>p0@xpfsIxz2U$b0o$P3j0>w^$veSk(7YqvK0BUVTFN1y=9 z1LZZ#gT`5}JdHVdfMIA(!tx-DX?YMvLmr`b3_2R41IvTzAVW|vS&NVP3Td*_;V_F(! ze_p8a&Bpo+OM~hmO9S=TQO}J=Ur3*Tl|kiXWuTm|JkPx~`wy%g{6FCLurR2cEDS1t zfmaWx~3kaq|m4y%I7 z$*MrPfU&=S_Cr=Ke28*b6jV+Y1(iR;&7Y;*k^-}|VNFmuSraJdAfxfwC)w}GqK73x zFT3U?Bi^#2E?yhUM#4k!VHuH$V$k0%7XTNPzidr_|*AGqku~1#GAP7@;Mgq)w(KrK?2m=cOm^urBWHAAmy;d~*C-_Ev zup)pv;*3O?;a(O0*&NpyU~V((t2-k>eJe%dOi!WuU`Y_2x-$}Bx=oBu7+4d8sXHS< z-o_`)_QZ)tuqX&qcSa&?;8l}OlIXyyAWYpEiLk-)a}#6aql9Han7T6(fwR11Vk~&~ zF0(Td!qlCSpv{k&^WUHY3&X(Fd|BuVclr_if|Ws-x-$}C6`wTe2*N7V1xtf4b!Q~P zD((*^pB2Ku+8|8b8439Fn#rFO@dp+MFg1$cZwwPm&Z`1{ggXoaOLF3-QKf{lk zvWRbBV093kx-$}CCHJ!$9hPSkRtI6~&PdQ^pJ@2OgVbhN9aLA{8HuooyF@%p^1}(6 zKnG_egsD3tp?(?i<7X$KV8F20|P6BFm-1n z;Li`uei`!zmIz_$&Paf{x<-fb5Y`A`>dr`jxj=l6`sE=$!y+L}-5Cl0w&mwmo-j*! zb`A%tgfMkyBv|kM%UtigwBEroAxzyF3Fv&qq!S@Juuce5cSa&2nxbf&=O;x8b-_X* zOx+m?|8LwPyd7eGMGOCLz`#Z!Or4D)o@BY&l(C3-5-_k*2vc`Pf;~eMXNXRi_6%4m zz|<@iT7Qvi&Z-bb7uendONB6XXC%li`;JLRqujEvRH&}HGZKt@uen#tv_`>NAv$$u zB%t%QNe5Y;Q5UQg!qlCSKn^`79Vf{ltQD%O?u>+g4*6bdr_o_OTS=n-5H6n7WtA_6NV$1hGj#Tx-$~U>r*C1A$f&$LzucV67X5ZeL*@S zp?<-_AxzyF3I76@to1poe>xPj=XLogn!?%+gyW&`S(#5EFQwtosodf+a{d=(Sg-Nn7T6(G09Df=TcpHF$p@bddr{`N4RDali(i#29^+E%rg?)sY7l+;Ueon!~#aX&$+vS?3prq8%!_!kTd|d z-Ek`pS6Xp<+I*J7O6hJZyVT9X#2Np<4PQisy>9aM#;hI=>S1es{fP$@B`6&XhGO1p zQyVvCa-lCceODlT;KqET+L-mWo*zB+73;x${e@n8$di|QFWUU79dGxq zi$vWaO$)iB5&Mb~_o|J(ugx{vIN6S*al4jp&fWyHRdW8)*6ck{1W9d=FRE8Sy#HecEsIGS(SvaNH+ za|a6vN7T0SjKRhp;(0i+@%+)->$zS{>e!SB>if9oSuAW1g5id9JyX`chjHqLtkL^cFQT5(lYnZD zNAT3(I$s^PDwn}hhV*Xloi4fTh1Gj*pSj@r&Rl$9U&C~0U~LhPyEcr6u5$X!ER{U} zYisior{wkI_g}GpYS&DG;uSR1=m&TBh<@y)e!$d3KiYyu zKls`AXFn1PIKx~5|ElufhUN1m$QxpxVur}oxEOR7A1%g{V|EdoJ&^e%Ko1^h;AT5= zHwRdBZ6niS4_g6ACNIPbfL$Ec*Ab|1y zorRYeLG%H(#<-1p%H3U=25z)C(+4K#(Qr;x-MDKk3S7eyuifD-M+%QNFF0lIMBA|| zysFhPbKrz8Ooy**YE5@`W&9_O7Sjv+2bVoPjHmm&&tY@vb|;JS=dpo}a#rNNJJIO1 z3SwAy`&_nI%T&*j#z;dF*b2%nRkN3kcyVby%rT=T@aKrPux`SIJS zpDo$GW!qJwFCN|egu*{w=~%FC|9v~I{b3htKjm5%#)aE0yTQW- zdW&i9ET1ScSf^PQYI9MCAhG#nK6XR^#@hPLF#054up`CY z)qb+IL5-=%j-}cWV#=<&Y}KX4`{#M4s%l(Y=BzR;oQ!~HBg=_rBtuh}hrh)*lOe#E=0^L&FTK;6>KK{@2hV(!y5?e z;iS)f!MHJ3d)AgV>E3d*>69f4Gu<9-;#NIvcVckOx!GZB3icf+_O$dEGq*RlyCD)0 zBf8tGs#!fEhKG%@6@Q7by%Fv1VlgPuE+etHjyh>XBq7vs$!cdILmVUOs|exYk^>CddUO{Xr|n2DA>j%7OBJrVBqLVN)hUaZ7T zYnSY$5<3E~%N{}IRJ~{t6-SRR6(^dji@4J*^L(_jF2Kp{9{-=8+uB7O?vG0E1V zIel0r?qMZI0MNg1%60T0QW4lwV*-aH7T(&F0vrDHUIyOo6>g{Q)px;ai$WKBd>L$w?cI42O z*Il)K;=1v2AQx&1$6FFUJTr;%ch7C79~+9pcW(aH^&93lw=B=M=0oYBw#k;-J|Om^ zjn|-!oBeDquuym2gz|~16Vt5_mX#FrPXK)KI#`pQc3b#A%@Rjin+8;SR#02J?TsQynJ>gJOX?I8t!O1z2N9k+X?*bWmI+gq>n*P7s?PeORkx2zRcXO}`6XUCsr$m-hLOyo%0qvF;`T8~!=!Ir)9Q}p zbB@Iu7HDjKx58Gu68$nxlP#T$fm8X7^3Rj zl4wswbcfYyi%G_U>ho8!+b-em>oQi|m@_iU@s-bQ+n%+@N^{bOH4`}?-xVjoV-{PU z@*$Y<@DI!i6 z&p*C9gSi0X!t2o^g0{S?a!u}5-*schbC1iOQ15$e6VV;9Sj}{*YI<&aSWVdqR_^@m z@%Es>vX1V`n5afn(ZSX?-U1y(?1YZuM0Psm!MM+ev0jU&Zed;n0bT^GxClRCcpvzw zPrY&J%=OQo_Pt8w(s%F3Ek3Y(!O8O)CO)@$<$-?T$vw}Xf7-LxuI23CzxwxwDvMU# za>=TtXZ_8RZTIing|j+y9&Z=!#mzG5{vk8}!2e_$1G_1Px#8V`8f_X1y1?D~KiJT+ z4-QO!;nL|Xm(L5OHQmv?=%gvH3vohV3@rXX+ST;bywjJiIJC;+bm2+6cG}uwH}Lid z+epCYiX{I8d}f(d5$CW2dA_ihbXZ_rB19*mSy`E@hgoCH+RWy&Oti$KxR0ek=lOGg zz`0ZN_bf)PRH5+Xp2x2J?%Kf(oqA^9m&Z@3{KqdU-#X{y(fKpS_vdb@JXxH&WO~U- zec{H5bC)kXbwNgWvhwdgn7sMWo`|3-yzj)`)#V1n)=tbXuGo5gbD`9{aLeZN$FKSQ zWfkB0FJHEL<-u#Gr*1rV+4vb(l1=x3xKFsta-wA~`f$n)pV0?eO`!H0YKWoCvIy9j zBCqpoKl`7IEf#Ho#c?$3Y(HN<@U7!kh5hZj#@A%L&5@A$*~0Ez=dG>fKp+J2c)0OD z7;&uWH}71qboZ{sse*9V-<-DQ{D9On6Bl*ZE?5t6dW$&FvWaKN12} zuUxh4)TIk%GASnO6SME~4`AH;AnP$hcSEpiX7hY|GLXSdwpkc;_v`lNwS~g|%yQsr*nm z-O-wg&A;H(^%wNEhQiwtna-ikss7YpzSSStc<-w7hGp>(kb?S_QHU}-TdSuB_T&ERrRd%}0QTbr~%w3KwVjhDEG+cu5ZOPzj4$kh&Zu(dqE9g+ zGIB{Qez`YNj`#=t{%aDcJ3XNs0w6?Le|EF}EIp2&s@#~{J=CHnlRbq+KJIg0$5 zamkhKu6%yMV(x@u_gr7_>i!gnoY4o#hd%5_A8>mCGWYT3F`byAzk5GH9mY^VK|+{& z1n?8cGpRG#1P>^pAIU8nN3ep-2m~Zga6M3p^8CHomLvPP+E;X)ol0Ekix{1pL?``` za>Mw{_*GiW8%_s2nNlFK(P~r0jgnvUx&u)J1oV4?u84A_qd3!ZB)vhe_iV)%vYjEh zg+k0x&lY3?Y^!{J$pm*oQ%^l#yeC~!xiLA@-4o6oJ1~wYf_9|)dB?XP-gSh z__@#JmQFeAskt&eh=t>I#JLmPjl@?U=JF``x(9q+K>1=?kUEMvH;+_ySgzuaa~!*o zlVe(9oo|@ADDZQ@S^i^V{W{VYfIWDWS}wjlE(i_P#Olg_!Xw8{G@ ze~r>*FW6(c*EVNT$0bKg%isQOYM!lbzPLT}#~-WwCQK zdW1_kmC?I{zhyGXgLfKux0A^v=_W=*9i|61^z{F66N%vZpnS z)U#B#W&26K#*pvuR^d@vds>*?o|`kQASv1EAsjR_7_n>@5g=3){(_=CArY)GB{6-G zx|Dw;x716%%5h(BWqoJEIeMrZ>GyKChV4!72Qx8uM6ubuT5HxBNJ_f~xXH+;^;l50 zs+GuC+rjlMo@hQ7`?;=n1swT>5k8tiR+DfmGIMGpuIn+1JHe^HZ)Q3(!c6G>EeH%vDYwEBz#2z>jYv;}Q4}@*!{~vbd zM08cIzYcrGiN~Zgu^!vXKVHMyaWFMU_ss2^(vIqzRH8eNf2f9aa`770F;CsKX*$S) zBZxTAs`amxr~Av)4i~qp9=~_nh6EDy*6^0LSsUMg7ZzxD8#^m|p4)5*f**E}Qyv^k zYvx2qT_=Xojtj)}nKumU@Q!?Jl0f($Q#9zYMxS1nxcD{z=?lSmAI2A9$ z=I9N@aAHXBSXjOH8~rDo6ZxI1IQ5}Um`>IzuPh9kuGO7alw}O*Hd^SR7Sg#I!yT;Q z3X0A08qusG>)V*CwK1=#HQ*(C+!*%;#zVX1 zJ*qcAE$qRl8+|SS=P=q=Fl2Osa6#bIhg$Oy^raeigkdkO!`islkHXeu6!{B2;0s27 zh_1@D)?s^F8qlYDx;dk*gtc2fsKfTPW}-D2CAvLE{|W2F%Lv8+)?UbHOD;@IsLCk# z@CSa}XfyFa;jSZmocORNR+S}&`*9u4#%GSeRmX|pPBr>P^i*zt9d1*$DoYG^fzeLF zIk}BBochq}XuT|PkmKb>-3ZPH8R2dxES$2C_vP7Y-MQHh#2bwHlLJnU3JdWLLz1I} zTQ?9l#+2rZR<=NskGSQ0Qqaf_+MxAiOqHN$!LQy*E>DcJ~3RU!B4`e+#uC& z;06uJH|cfp&sWzDMd$9R!!;Y{>?W?2|3eMu!3!KF9|palXmxzp+VOoM$#68+gNV<2 z3U~=e8RUN5%F)=|yInSiZ!-7^Y+lheGK$7()Q;0&%%s}fyvg7t;T*f>aD5?!vZ}*< z+2{+^t#e^&qd~D;djp@Cl@O=#>1rFn?<ZLmaCT?74Vf`SS zkGsKgJ~(L9+nCbo*7MVh_EA4n?rfsxqV`P|s_k=NJTk_5;b7xo{{!Q*v7!!Rt*G)j zNNY6BgCLFD>vOnvBl1pd+|u>q_U;_6FHk>j5;s#n9+s_E$bagq=A# zd5*iwVgX0IggmP(&+UV}tBON?tQ;9I19-(*YPnfKH}c3 z(!oCd9P}DipMe{U*42kN2KK7_D*rEg?*d92_zxGki;ZhMMXu$iY-;zVv7}(R%%hHqEbtHu%)MHX~mXSthB|7r+7+_ zv_(rTCjaluvv=4;)SkER=l$PK^v5&LtTk(`Su?X{p4rbO$%v5!=ymu?8DUQURdZEm zaO^dHtlQn0tMZcrdNZkKwWC+XRP;Wl^*Vf`G6H&)ycB0V=-3-;Bn8PEGbqrHRR4`| z#Nx3)3IBZR@QxamK|cxR{hWERufN9_O?`5iqo#}< zJ~%Bd%yi}`XK&Qi#NJJkTu;1juIKhIb&cx3e}WwvcHW(vN5+24&;LQcWUIHG`gcv# zPdr!n{a-Je^@b~#5atI<*Rz|d_Ewio5AnFnkortxtp5f8{d@N32l3k7DF5&O-cWah zz5(DmZUD&Yy8$4>e*?hpXAXUJk>0JnxB4mbV)M$K^VPQOtdN+9l<=$&<2$Kwk6*e2 zATDEH;K%Ii<07tj2SA#$yBp}AZqujw_KmZCx;8toj#TTNxz1S9V=mmt4&s}e5_l(+;+yHjTFn>1)1s;-j=TlFz}%wv zTAgbP%@^&T?-if-`9+!P!XnTniuYH}c+l6yHCb^`Pd9 zlGpn9ywA@Z9K0U%^H8hB|Q)i;WPf|%)gp1lItnuONVbWn$){P?ccQgdgP<@Ajo)_cZlfoni`)Vhqj4T9#z9&@mvpUvg@MQ1(cd3Ms+ zoSrT7J1 z#Upme0`J#yuIGa4r^YS2kA2UHsd4Lb=q@EcL({o^)$e~ju7l`mM7y6E-}jt@7yrgN zbl&^o4)JMvdEfi<^mupZyx+kcqQ*Jp`~0KE+F?UK^X*e~HOAb}6b9Pg8AGNXBf5VK zhVFg7bxB=HbYT7P5uU6*p76kXMg2U#RPChYWNRM1pX2h!IzP{YeLO=n&*~M|^6TE^ zJooqUPv@J>eKF?&Q~N>3i+oL=m>%H@rtA7? zy84xrw;#u=$_3^GJ*IbN#q>nO?@KrI6G?9Vf%kEgF+Iq|`% zi?yXj?Q-iRKwsl$F-kulzi^&YDN<=+a-*hEj09B3J*>U&ibHqUQ4H zj<0n+)vt!0XRl5Vv_*gVL_a;y77kta2VH)Wrn64*_Xmfr?b7tg(7n&sr3JTzGpB1` z>M=e=%c#x{=-^cSVRM*uaU{?m2EuWd2=n3>OKhM|t##x2tQF|u5`YSNM zvStmQU#DvxwT&7V5okj_e>(mtQhjL#JnV_a1#ENZy8fD8sp+MCebJ#mrtHx4DoxK1 z4_rGrbp6f=O`oah6Zv7OZ#JR@^>o!&6>j6{y zN0*rek#E28YWzN#(6w(geYU2LONsOZ)3wc-K1b6Fc=p3j=Xx}FO;Drh<5S}zg3IeV z>GE?meNslCPI^rc)aPq8eK_A@cFH?+ZJ+MT^E5qYV8A|yuI|4aEy z)34U_p@RZzUA^WAiXZbeeNJvbKd0&se_o^g8IM1QntJ^4J|=g_0*I`U7=4%OJ1EHC z3pIUYbg;h%1^IiCrjJVs)JI+42d{BHqv_+5gZ-Ip-RkrKu7M)u0lCP!^K+W7AUv?{ z%;+6wE_LQg?e`^`zN9qQX%}a$KZP1zhbGTuPq3?D^K;S!$EO* znWm3P42%U;kKpy}a!oHyyS)5w`ucK%rcb0NJAUL;{o(gU?RV!?oj2)oCnoDSp<~Gk z7(CAwqy+S<{fZQ){+iyb>BR#B`knH+Uut@brjN}G)c=ycm0BPDi2VvZj&wh|9tQ98 z`B}-dY#=^3JeT@vtL7;l&MlOIIOC7aC;Iy1DtL&^WhHr8>G8aHLWLu>hpS{g8no7H zBir+A%d)`rhhFOi`M+K3+O#2uXcVwVwPEmjutW3QxhX$|kH+<_iPiP@rSW-#=DGX! zBBD-!N3Rx^32KdTr51`_ejp-%<12rTKil=3g=-FovCZ zO1HJ{8yg&Xsev)<&~-a&`i+{ta8O_jJM>?vHrDiw(7n%pX0TeU1$^r8=zgGiZqhtW z1*#MD@z8$LgZl8zTF)Xr!0U_=y`FZ)uwHN9qUm*geb|{xb-U=fbdy8R5A<<|uIFP- zzZJUo`Kz*m=VQI5cG}*j;vFkq`e9i{V4OL09e*|bc1<6j6o|hLUAL{KZ`SmgIRP7V z90`h3cR=SUJ?9V{(zM;#D^|ZY2nykMs&K>5LzS*JfHqny&pO2qEq-gw*^4irkafz18hb#Ugu;l+8A7Th>BJH0W_zR}F`rKDXmdCv9mQQpic8AJNXiB!+#I6ii6 z{&j8~!rOa&d5vk=Q#UM_lvg|)IYTn0WQIn^Uq5H^HEAinEaZ5tSB)3#SNN_F-*XD; zL)>bkTZ{W{GTqgY36U{tkF1M{OpFMtTQk?X$Tu>pZ{0L2A#M2J(OfdH^<OE%^z#2?blQCwF01oV{;MeSTO}e9z46?COj0QDON_BT|sd zJFd-Du6Rj7F3*nPYyX2BiGjzw^lgUhf7eDv-Itm2_{gGxsjjHlnM;#WpH9h`XT6gg z6Ze44Ms!?MSi@4Y6qz^Lm1dLuBZ)w!Tgx2eczlqSz1P^qmy$AiZU~D_wto~II%HB* zXzEpzyTuMSN4t`A|6xIdEg9-TNdJg(Dm!ZALC@T(yJJEVBctzbTHX*7nP8tCTC{L@ zdcxc-w_THvu6$)z@(!(`5{iD8(jTkUE9ILOPn)`As%vOjLE*G%h3dkV-xAX89J9_P zpVIfgyP&LW!IYlg>w`;3PtnOKEG11DXOA+zz`MG-1^MuPeyS96U02V5aNtYp2a>X)2#Ku5iYbVZ*0R&^DRoV&1J4t?&IF5bOND5oqy1 z5Onp=FjnR!guB;et6o(0RL>#RrvhhoAyG2ny)xaWeo+;4@=K?WPPE>nkKLsEg44(J z*WvX2d3c#-p$WNlL*wiN6Ryn-mMq9-CXVfDu`{rF`iawMl@isn&}| zv+H&|9f&jj0j=(X)1Tu}_kqSIo*h1V%&?hDpRCJ@iYQ;4WiK5yctAnN{3TzD4~eiM z7Zndy7ud)Xz7g@ht4@C}?Fw-VsgqnfbOp^7CZB_}y(W{zel=O-t(!A0v@3}HY4jxe0J#WcZEaiuY z1;vBaW`vgcC36w)D^al^%!vg~izf!|)AYxJNTW7=)WY19$5RH+wxVK3%+E=EJSCxI znDwQMh`BYRM#n{kPnk4g=rCl~_KY)@_TH|2o2chYH4yy$SKWA~yvxsZxZw53gt+V^ zBO)X&bK>C9<#uFxQkAFYVorKwRAiiINZz2u)w!dJl17IYBR8J8=DdATBKz0hfB*Ku z=pEDJQW8Vwghg6EiheyhVQ6;r#RRo8&G!ebZy4e)%NvMj6PiNbLh0O$x?o_WcTQ+x zN?gSb>qi5l6D~$)4^4=EU6=J*_Zz>kf1~{#roRyw=)*h|#?Ww{5#TBQ-`zVPHNw5P zAfM29!0?17XWScYkG^X_cvR#a6H2qQ|2Qx#G-SYCQP|_P9ybf^RXVm`9)tTlrTPjn zI43gd3xiYlr20mt6NB#=obq%kA^7p+nAjV~W@g4m#ojh%s?(ND)>-pLyY}N^@DAfh zXzbwhp31P;RJ%4hG;?@VXu>#bX|ne5{;kKgEm3;zRWaCkHcLGZ!SAG_i*eJw$ojD* z4Uv2-bU;?=wN2NSW(|l;vv0{BJ2pFdz=*W+$-{9c5Ry1SZ`in8!eo?|`D^K5d z6`eVF$So6w=S`fLH++Jo46IpYhw)5Q6v*(b!)O!wYRjh#~_uc#{-nv*+r z_Hg@bUFnpjYbTbD%$+(uYxp>|8>f8iw406@+J?Zq!aeTHn>upvt`n^&1ge|FIv|1^xc6MM?Sn3va69zK>XN~(mYuvtX0Xp6J|Fvt};J*C7a=qns z-EG#o-_Uc1{yuh`{zx8S-FY2{UgkP;o_BI?)}%qxN?W?ptXMnxTN~S-b}ufh$sH0u zJvt-rhGk)qgV#Q?mc11A?99#P%j$YOUbr7%le!y0)Wpip%ZjD5^v$Je=LKGMU4ECt z%kSAkJX{mQt9~_5JLuWTm+o-nWl_E7U6H)+ZVGQ?so~0bR#in=ta|r|5u@I4!LuX! zS!d@;!+$%pT2aI}&)W5o34@oY{jZhm8++b!l;FJf3?xLsiq(#EHz8flEadUjL#wj0s3F!Xl3tP z))!nmWin3>d7hn9J~~$2C!;^rsNQg42BIKK1~tL(cy!-GL);mzZi~?qqrn%hA?tM& z&q%4;YYgKX5z~BQ2KXYjH+9}TwK6@$bh|wF)`k^-eq2a~r#N>=_4L+^NtMgT*2J(eX`_Y|k50elrU>q$2#<(0 z<2(ho8UrjVePBZI6ygX)9NGAVMY4s#u0Z#$& zSS7WJb9|y-zQO}3A?ne}41+KC+EjgTCJ$K*HixGsSfTgvO=JFc@}=|an(>()yD%)> zFhip}38tZ5H4|f34gYp1Z-_CCs8Oy+w-xICogL}sO){}5(XN=NxG+`^aiM$;#Yhh; zv^|;QYX%MHr3hi3kPuH;&-@WFsWr7-3x+0*y?Rnwp%EI!^SGMb$IR(pi|Sc1;VH3RZ)`$zXjoL*bYoYH#}m^tKQ<$g zSM|7DE>BdbLylGLtLkO9Q?CfVkKlQ>B!j)xtl?Su6u%h3QEI~CUJ9oJ=7nKiMcX~TQodR$k^o|r>@im1-K+!2Kn2YtjF zcSX9dYQ6K(?DCHCVN<71hG~R(=hGXqQbO$GL&uLW{^Hav+C8WwJg8zR{oT@YcZkQz z-_V`^nK>i0Ya-LRrBv^Os{O-H`@Q~S>5PwMz45O5?LywXv;_av&MKdP|FSdH1({yp zJ1g=e9o&)<)r0Q%kwu{MjIm9Rh~))T`r1$}33;l7AQ3x;x9r&L?eQLH{ryw5cGX{Y zSDrkZU62ssnzm}(%CF46`O&%&b=mO)`G&H`%!nH9&R%uz)<50*XwP51{;(1ANYmf0 zN(oPE_`>&QKiBi}!5fbm-Yi~FesuHif%4Hg_U+tr#onxzLq8st?Egj=axUZB?B{d#VEufoKl8cg zDcf4B-zAkAs;*xQD1IF>{EeFOt-A(XL;{ z#U_qP%%3!QWMci`nVl&M>Y}1XkMDZ)n&of2x@SdNd0~FJFEunRE+l{B+_B3m=hv>E zUei82B_uS=v!KcR_RSFq;j!K*UtC)D!pXk8xXi?1*NkYoZJ9eFBtEsYJ3lHV$(=Q` zxoT(XlA=NRcUOid`0^V{$JPzLZ)HSLsK=8!ZDieT6Gt`P^_`}1D`$@wnvv)66iuzT zs&vuHMWxrz8C(^^tmUambS1eqJR6@JUp^#l@?zeNv}Ur$ojW}{IXTk9izA*`TpVgy zE^kTcNOQ`J#!-WF3WI!pliY>RH{YDA;;u{W5VJSr*4I!h3oh*wDSvRY1=(*kdp+nqAJy=MCQ+WD2s$IjiD z9}<@qn(8ahFDy@6vFFt{mS6K|*Z9#|I2Nf*| z&l_1<;&oY8Xz}7F5l@4%BMWL0rw`>co|9%ZPbsTJ`Y-nEcph0{7(Q~e|Y5!ecT6llOH*=Tw{4lchWY0bA z2R`WeQDWYV&upwL``m&Dox?e6*G!@GKV7DW1@-9TXNH?j4sB5)~5` z>yB`FL%d-jDZcq?8TkKK-KKhT*?{cj* zR=R&8+2rz2(dF&JOn0ea{QJaeGrqu>kdi!L*20O)#tiX!L$VU%hr|v{Nf{J3CeP)H zat|MpGB~R6>IoS$-L5p%G9wa`28Mfm8S#VXyY%AJki}NB`HDSU?>p+fRbz~rZ9`mc z723y|pHCe!cu>!j@uPFI?BP5PoHNnLc15LU&9Z4y>XvUjZd_-E>bjARaeKIbhdOYb zr0-q)L^^$<)|ziUX}ziS>)(Ei;GdpGSq+5)igG_3o1HjesP$%eQpU`P`;N>uNP@SVOC4*wqrmS-hBU-&mMfIxQj9 z8#8E9Zr1p$@ezfQ33Z>lp=`|yD;HV=!bW@3ZSRnb52u>zv*SjO&#f93JuoYxu=Ih% zq?|c-EnM})Lhir&TknTPx@(47%W}6)j`6GB*R5S~ZdWqiX2)bqRz_-KY}lpyT9V-} zFdG$rvf5vX@}Bo|d!Nq^JmrhrSJjuE&71vl;X51--(aMApC6yz_pEL2KE>x+8l?X| z>wupV{Z|!SNx$_uzy2Iglz5GF_|<)--sgw#8neFVj2#;sUR5_wRPSNUt9F*X&riq* z;^ke$7tA>GX(gBMgt>XIqUvpvh6V0@PT+a^TUmcYPoUmhaY{^x%r~9;l2*>h(ofFu z8ZLO3Ncg9GdUm>!%wI@?^V?{mTdg^= z)i_tHM70`Xs?p2QxS$^f?fmQRS#Zbr^um(D(M5|YmKRN)TT)m)@9dq6r#*3VYS^f; zvocE;ghrMYOf>#bJ!*7$d0~mV=~DiW>*KpWuiN45Pk!RP7*?0FU${hX_qm=6-u7ed z_#!f1`!pH$L|ynp?-gbIKg15kN#9?{(=NkRv9#Rjqk0VOpZ(bfuZt>u&kXZuwxcs2 zOHv7r$vrRsw4wGSBMPK^YY){j5^@Y*QM1zp4HYo-bKfs@icf$ zW4^ggR`NBt`BsHYcc)67dz#EQT4kjg;u&OH&Q{leCw{>7p*j8yd{G< z=9?R+(_!9pv;f_sCCYfc_aD@4psK6;W@M6Yy-m5#i<|e!<(r4$YnI{W1C)E2-)|QQ zuiS(7u%xK8)h#*ZKV+0qCO2@b#D}%=56AcRQRemE$ZSpfCbGUNqwqzoD?=JotDAFs z-!tF9@A%W)D1~N|ETP@9@cm?aAL)msmh)W2uYEc~;_>N65R$BYT!4S$gMokJgMsep z_%{OoX7-0=%3|$z<>O!|AJZ20O6p%BiB|$T^YLvwNDPMZ?mfXEF4AqdUWq={^KeC@vqJiU#Rw=pSi|SE|T-xd%vstAN?twKFBY&GLZhP zc%nYx7zsU6wWIQ7|KnwOw6pTFYU|)*h)F;AgXA-|;_2V<{g3!KlRlsDaYx4DV8%n# zM~?Ix)pn}?{4+;2j;Xt)#xY|_AE|dXV=Auy(X7I6{(1!Dt1;y5miW*@iRaGvn2?DQ z@4nyhZODfb5i(CA-2U;R`c0r6`pXH(`?#ah)4$I#=G0hA^!JVD`}&B#Edx3NMF?ux@&j$>LO^Zh`bskVAHPodO!tmm}xSZ@ayOZr{#6Vgkesk>a%Gk)qhAN5Sr zjX?dW`;1kdq74A*_NM?JN&TnFsz1d$upg8eYTHNYS4VE{kSL&JdO^UpNu(80L8&@Y zQ2lfv=FerUYaLHf&*=S)dqeLB<^hhWjGuStZ;wh2UtWD0`bFlx96M8TT+LEW%q+Bj zF5}!Aq}pA`oOpozPMNIuLPkrDJ6CMt`UK=GcDG3^@pT0Jqww!oK1%$(+~0oPJpD zV6Gko{j8c>tXPRQe+PXO<%Sd2o8?C2a~~afGdOP`4#ktc6@KPR`(~-6jxQ)(lo_mb z-YkQaJ=!K!SAgvUb^SBBo<6|(fjVn6^>&4PU-ChxJ-7pO0_Gw_q=-neo?-= z2j2z!_kfI6@y=GRRXQ zISSlYo#TEIJ?L^}$`Vfo$6JXvBdF{5L7R*)kMrHsN5!^MWuj{?ZT+r{vPV0xzR6nY zIrb<-Qm|{3^&b99Wv(6~IeZ(l(i+E_;8x<_lcYOnt2BK5MPl`%*bUU0!G4o+6G0*J zXK=iqZ|tE<;}Gi*rGG4K_(hp$yeA`!_j+G9_V*s(`+9EbILi7v?Smc4CanwobWyz~ zP%)16Ry=E-H~Sy^=Eh5NVPHX;HAZR}9eb+hf{Zk;HHDiSPX2xN^WB+;aBj^3%R{yw0|BGkcVFiwXxOwGc z@4JEc`Dw>MeErztKRI`*b+D!8O#d-3mwwujb#pxZFud;=n0r5F-c{=fHP@?oTIo}L zjJY@}5Zl!;FehF4*n9TM`BzxS`RAJc{GZkbKaKA6vA*?JRR6aA-+kVHZvCgv18wm! zN9Ai3|33+8p86!Hx#LQ3jp{w3@z>s$T;B)NKw|H!YM%KdgjDq&QSf+sUkW)67J>ZU zSHTGdHAgwYr%%_v*p2}`A9wt; zcImU@W9t7&byl=My{~k#M)_zNFSztediVw@rcZpiER{ zR9^v4fv5d=g5wivU8G>m>wQVB%iu4doG$Z`KF}Zg7S&|}npG{4E(k+i`+3+n|jzRn5PiaaTD*`MZvSM9$jAg5LV zS+y)|7n}3t2IEa5-FQ<{!Dy?K^~SxDs-W~~oqv{L?CTVpuS>CAA;qjeeyTA?hV}83 z$uR4P1D^R1o}U?LuaWeycv+xeeZJ>+uB&<{(?0pGFVbF%^gf7XJ=yyKYo|o=6V+Vn zq`6Cr^Ux+q3cE{q$-fL{Ef;0G8GA>VFE(=gokVf{73G@1T4*2VioO?}Gr%(TTV{b~ z(yxPeg{a5FEaMoP-Rw88&I$0Ty$8k1_4`0%4n$`mI)@@F3Z5urML~~3Rur6w6w)1FR$NzQ(>^yxy;x$oDmGH1FVi1c1-=7;ECAKxDR}xxAG50kjO(#7*D; z?enhOpsycT`?1ztOgS&|GmxKw{21hCAYbioWgw#z`5DO1P<+TwL4FGIhbS8IGmxKw z{K3c{jQn`yk41hId|u?|vwt=abb+C2FAYS4m0&a&40yszrh+I?0^&gd@PJ~F3Sxlb z^@0Dbe0DO|L~rA#w;01u;*TtewVK%XQhTazOOo}VOy@deI6nEO_Fn&4dAvLA2IB$t zaoZ%xT#AlkGL^C;43quEhh?3bRY z?l`=!$s+n_t@U~Cbp9*PV0P2)!nNLC@%LBRBR(h-?71@8I8OQFz0X;9v#09ed9{_| zCO$;l(a3q5IfVChT3@AHn-p3;ezEu&&$w(PR{nsmob4sP@C*d`*~Yu_B{LSA-lZKM zkjX%dPSUwD%Dh+7jdvu)yj3ENH<)w(ojx#@U&kJkaAPrL*-5~rSn83)UaU>NZLXsn zUtcnh%5ZFqG#-)x#snEUzpRLl&>)F;C*{|xX z^s?`Klf^eb!9@H$7JuhbJ`8_n;%^`RPR8H4_&XJUC*yA~{vLvF!|`_<{?5hU@z^%M{2haR@Bh#C?|)|Q4)CZof?8wgbp>k! zl^=Y)ZkP3*vmPT}C(1JWVaCusU>4`L)VPMQZf4)f{9^9{zLP$Y@jKCVn7zF@{QkB< zYM8So+D}RibMHic>#DZPNWTY-`P!Yq++N0<+(7*N1M^E+?}aNt*@NyP^kxJDy&1vK zeB9HF-kkmj)D_*Cm!UxnMRQM{51E ze`6l`o46EgzU$8QhSqtd_Eve??oV&?L}gn*@0Hs8vbI+B_P6(CdV|~8Utgs^(6+(H z;5Jt2pmvTAw6Q+opWrtB@3t@ca{_Ivj=^mk@aezOzQ|AaL-)0y^KjCEJar9nrQcYB ze_sjyZRHlqP7NwQnzVwtE>+iigFxVVsEM?KDx@6=-dwg6+Gz7z(#x(&5lvl@)J7k~&_i!x@&R1!5zkvct z^*d7l>h~e9|JppCqgn&@U7uTvo!|2N%L<&Ecd!<{Lz3+Sy*~%v4&-0y*vGe$Yno%i z17LlB7g!nR_pY>FuwF1`%8Lqpv|>`hIK=9>fyl(sI)zsk<2~Blvq>a07aXHH>_UV! zgX3Ht#{+)74u?OY()o!j@(e$bU7)*gCq+z$NNf?yO=QHKr|A!hB%m*WvWdt@JSUR0 zRV2BDpGEfLHr$iO^>Z3B(%~Pto34^8lGz}Vl_!!7Z3xIk#?UDu!z)GdE{f#u5GmLv zQi#nXuz4i;qmEKF^o%|xGIkGLt43t}R*?yum$m|UCvF7TJ}Dj`Z!(yI4O8GRJ4+Yd zB~mUT6&$A@5t)$(nnkL@!CsN8&|7^{WEOd|(LaYe*PIfWdzxk@zYe>vUMe!5^97_A zZWdXzU*t3Ez(KZWwuoGt?8k1A>o$qh9}-zsBC>oVpj=}b==K8}nvl058k`qt*#?la z@|?)^9Iq#@wHRy_Syc*-iL@S7!(YI!o$XevCg?}C8 z){(cKy58s$QD0=+xC5LOxruT&LAx27Z>IjYY!lf;dee51TZ_Q~k=v$-+`dudj(D&c zoEN!sm&jeY;JC;ZY`goE$Y=M6+~WbwB3n6b<@{dC+)J6yr2)=AUnjDy9P9+h`NBGI zTI4>;+z;>l$oV3?Us@{i<&z>0L<7ow1>3&bEwa-CHi|q5?}O<5S{~RZ@{kW~5!n?E z_KQ561vq~M{zoYHD0O=DkjU;Dk*}W>`35|XQRcBTB73SuzG(w=KE74tTiCZ38}@Dz zd7?z*$vSXRJx|#K z>qNd24>pRtK>iB{M7~=LpdCc-_u&6N`VRTP4&l}}fXo-6|A4YTI3w~y_62Rk_WY(p=rqLUGI?pNA&;kw8)eSpD!lL>EWbSuxz( z#0Vi2hQR0H=uxYMgJOhI#=A?5uo^MKPl*w+S&XP?a7>JtW-(%?h!JJRj`=6WC_sK8x(e5cQG~3bePWF80m}02Ph;dB zF-8f%-ci_591V_$F&g@q1~JB#1J1{hKW>*8XJbv;JogT7*~5hIoJZwr!e0KHi>afGdLs0f?Tj$jD^&9kq4X< zV=>2T8^lQYsB~*v~9@ShWz_-#kimH_n#4C`$aLnyjYAK zd15@!AjVftiLtX%j0Z_Scv_6Fq5Es_JhV-WU6k3iM~sI{#ds8+uY<=Hh_MGe-YUko zc8ReUIeV$s6FbFt@|+m^$bb5f7|-I%=PCD{tzx{eLyYep7UO%^_5HnK{2RIsZ5HDP zMPmFYSBxL0iE#w}m!Thp{!?VVf{tSxUtKK5&(Zq}WE@BCFVXcH`L9#%*ZakI#P~gO-f9-(?L0C5W49P*_KES%MKS*96XPs;{)DXmIw{7x$Hn;T zHZk7wi19vlUO?VIz=z20*)OIPfz#|%akNXsbd`(g-XUhl1u;X{iRnEcW_YQX5glSi z@h#iv^J2zs6f++F1b7oqh?%@e%v7J41D1-Jwp+{$$_*rc&|xt%kBT|?l$cp(#mqh? zW=@@$LvzLCrxCLNnMKGR*&ybq<6;)?6?1g8m}6`}-WYU`Md#RNfSi&%F~^~IJl_Q# zzemgol>nKg$l=x}bK-6>Cm~}p_D%5tXe{^5shh=|)(XyuS$;^&iYzgwhXe8}v9+o} z%$d>Pte97A6SI0Lpq{gkGaJ6yFa8H;8u_RT8?r^T#m74zy`G3QhE zni4S=a9o%r=As2+eulE2*(K&;0XxOKmi%jXi@5}yOUS>DIxfY|dK;V*bJ-~|mmd+c zVZWG-@HbV8xdPcMj*HoRNX(XvJmj!R%!5`bgifCddjRnF6M?Q!mEn_yc@#-vTx#e z6X!Q0{}$4>oE3A^W-)K=7V|db+@1xFin$rtcOd6Z^6uIp=9WA$?}q2IC1P&H{(E!9 z{M;@vKVL28Hgs-V2QG;Dg}q|l*C6Ko9&kv^?agdR)PQqhehK@&Tnf&JxdXlj>csqt z4^Zx_o5b814UUQVAmzTcRLqC4{UP{vEdXc5eE6uCkAO$_iMbp3yH8$@uT$ph(7$m& z%*UpPxhEW;^PAZ4P4s^&7eL>;SIj5iebNU`i}}>a=&|C%!8Eso&qxd4ZcJ0yomf44~zMOgJS*=-owS< zh?qah1L*t_W&eE&pv;f!z%en8;GZMtdMQiHmu;|B%%3EKy<#3E@8}^he~PU?-7euOHHi7>a29zH_`LrTHx%3inOstG%u?E6FaHm*4@_fg|8njug%wlj_tie0P$|7$_ zG(bmAIiLNCe zKBI6-ti{N`HVqsSYe}!yWmx|Rue!~&58aIj6g#H!h#A-ez zR?9K5R-O>+`jcX{o)(KZX|?YX>xLS!h>2ELu~^*#kh2+-->a8|7K zg9CU|d+2heXL?>3INcYs4;ZH@+e#kylDI4#zl*moCl zw-kX3V%=Q|HiN@rebxqbV7FNJOc84^?7)=LHmLa92D!m8gNpq z`&-4@u7Lb6BIk=G;D}gX;`k+wU*0Iz4$?dJi}e7yzXH!!PKfo@ZQ!t2T$}#us6@sV zfISbQ<3VhF5F5G9wZ7I2c8T>+G*}FFgL9X#i?X}0aToj#d%#Yy9zn(<+k@WC_W?}u-H zE~o^}U<=p-j)1dbJsS?N{aIu`+X}XdbpU-YWQlb!T&#avEEX}^I($eh;;_b%3u3)= zQLLZTh;@{_pW^eMq4$-uV!c`|*3a_*>EqCjcZ2O ziN!Up^&08d&Wm*dIVZM*Gh)3S53v1p(!^)$*WKWNSZ{bhGdL{PN%Bwb0Op#)+N9cb%Cf3;wvHrA8tp8HjC)PP%5T=m+Z{W{0 zV*RBEoDl1;_~|?{-=qHTbNt&Du`a}m^>=Le0KR|h5Q}SXt49F5y^F;b0jI<^c8hJ6 zi*1#PZMTZ;>JZz#4(tF2!AWpY?2t523KoM+U>7(9kim7j?ePKRaJ_DOHiO;ZFgPuC zs13MtKC}`vgDv2^*xpTIhmjw4RP68uKwbp&2+Bn66*~%AR5ie+sO{hYI4S%v4zfTc zfEEpnJx@FO1h^n}OftaM7;KF}N6ap87@QG1HXIazI3_9GjhM!qxOki41IKm*kh1AHd*YFctH8_HrOQg1mu?b0DKdV zi9P9v*psJ#b7D^+uk4`MQ#Xn|trDCUyL^|}(~-m8tX)aDD#}+a2HU|Iv1cv-+rV+L zui7bgHL|Mriao0pK%Y(dIh)0_z*;{tS7GDSNFA&~xokv6nOe=v;T$*PR!8DfIfiVlS%$yTxAKDt5y* zupgWN7sYPO0ySVAKu+T!a9Zpp>eYn4CTLCFU(js#&;Yi8 zz2F!)FLrA(C3UDrl>qu~x9#ptY5N#b6WI4UT}bVz)Y0``C-;EdR7 zJRlcTf@ZK4>;=cbd9k@pwAUhgEwa~kgY95HI1bK>y)GUUgF4U+c7Ou_ySc8k*ZV*@ zKsM_rdp)w(WA}RO-ay$6l-)qt4V2xm9Z+@yWpAV`d;a!~Q@~QN3G4!gz$vje3h;p` zU@6!Hc7em-jMz7MKpv<8$i4~LH|+-}z(uid_JK+O?PmOS^C5u0ZlT;Ql)Hs;w?Mmv z`rL95oDzEzzTA`xszC=p<|gdjguS;4fOadiTcObTcOJWmU>7(H&WL??I4A;jU>(>A4uR8R zf7S!?0JP6S`z*B2Li_LF9_+l=2FSVhs2}G!`tf;muy0^*gYOIFVzXyp-@ird?Nh}5 z;xVzmv`6eOSBt&lgxC-46Z@<2V(&!O&Rt?ZxLxe8QKyHtioL5??1z)VX|W%n%x+|U zy;SUPEEW4PA3(-q)Zww+0J(dh@1for$k?+9P;QT+T@?G9S%5O%>;}|Z<1oi_Vn3b+ zDgm^|k=KW#q|b}}E$H8Z{;du_c5qan{I@8-7y90E&~6I;MO za6-5l0_1`ku+EQN9FG8GK9%eTWuKz#QGOAiV<|^5d-7-$ur_ z;dvU`(>npOpQb*~VDB^7`waFzgT2pS?=z<<@ve=%4L(7AsiWVNvA^p9xu6=ff~{a5I1Vm|eJ~AB z_Fx_82HU}YZ~{R89_7E62WkNH??L|_^zR)9=f(bhGC=S3!vi% z)bR(@@dtasQE*o5ABKZGPzn|UeE7qi;1D=1_F)@P{%|?K#=~2{9&iMl5&K8Epc%mb z@5x{R*a1$8{bS1gxE0`&A72prND0^mPK*6g8fXC6`qBxpUq;T$=zST!pP=g}b6AUFvwiv8;>fbL%(5&MnG z%W)DuuG#G09{VVMN8Ty$CS~8G?C;U@`(|)T?6-Vi3RnuT`z`oRd%%`Y!2#%$e;eMn zOFjkE=k0A^A2{KF>p=TI(EA_VU=N^qz#FbP5!h=$9T8JwO!cQDB{lXwe>z&iV)+$EYmE_a?DPI~tR%+1=%gWr#J+}& zhHYVaczf%OJ@4h(UMqlvp*dp z@g-cPYnicJ>*i zb{x9`$Y);{o-sO|KstJyQ?EpwU#s&|NJoxaqSFI3PpeL+YaXuLkuy-Ixxy#SUN!uV z{rNimh|2e9o5$_bX-ED+l@53EkLa|vf86nDosIR~jSaqK>wPmC+dEhI%2zivwKaD7 z*0yvv`=+GDnQY;0TJ?3>fp z=$ldB+S*dz+0dBhTT?Kips2_M={tPVUu+GOn zYkiq3W|U`EObr-YRFdyEV_JL1`p%XW&E5VAI&Lg02zW9tFVB}-x_0f_f)&ca4UT^c z>bnaYm#qj?eWPzhfuAyE#j5(2)`I2jtA^%zbt}|1b~Sdc!TriFzS;HoKB&G0UTD*DT5WY(0}a~U+~}KERqdP8(b(oNS33-OzJQ-b&|r=* ze_17Fd3#5T>L87+?Q8RV^|TY2^{rj)zWOy7TEDEd(djJpzUgIief8brz0KX-9peiN zyOwvhbaZzWbWt5@UN~oZwb$#-|6l*}YWr&{X8WeknO*0ru9{XcdtODb`M!ML$T7a@ zjmtV$*LSX04LJ5+Qs}Lzttgu{wYmbgH2PMwQ*U2;llJ9D{OHT2f# z>W+@~&Tg$uQo*x{@9HFY*}XlwActKQJW0Bpp}>lvNCwaqOIWu>pnx2k?U4e#q}rcw<~+pkhW zIDxaHzO$Q9(be42;dIn?`dDX|?vRZ>oIy9C({$+y^z}wZGS(n?HQh50SFLWKi{<%N zwKudhsm`V>LnAfmY+1Hi#V2KO>v~^3-MYPPg*u{(bDZ7Q-tFsZZ`Co3%vD{Dt!o;) z3VgJfS1VINXL&0+RKd3OK1M^!8pr9X()ee2eVa0886%}t8Pd3FSz|+kO7$}X!wTCw z9d&waDtTRjLFe}`zHYAX)~Y)FL)Y7ehZ#10b5*@n$n9^5Hrnrd)n=FIU3uQ-_O3QdZy5(YoFXuXbSjawY(}tL~6gWp$VGEzbALL^vlcoxv5LTa;H!ubN#|S2bt$ zJa6Xo;CPjZ4NdeN)k~F_u0}mVnp#@X-&adKrigtJ*IP-mH+JTB4ZV`>%7M$7WIE|L ztLi(iSN*EX*R^_ivvLQOq<4Fro`J&l)t$>RUn$I^Bec+;{P9cmMjYW(S&u)elKIh~ z99#$GC8x=|IvSVzJGGz^J)yoJ%-K$lGFF!vJEPRl3glT8aAY1oWCz=Ks?Zj7ae;3dy>wU|c z>)UY53i?^+s(KXxJ5{y{lYt(1xieIc^|m#x^)xpOy1Q|Cb6d;u`d07S&KA{*sCNcj2c4J3Slir=|J(Y)mcKh+ zW)tdbDr#p{&6~$S@D1@zn=`xIKPYM%J6E-I=`{=elVI9N<BI z?9xKZFD)>2T;?=Ak$wDpq5GDako^-zV?7hEY7hcLn`%sF=46S$@<8P~%S^&C3pX8= zSbz7e^K^{wf>F<9fxy<_^ZQeUzHTB}emxT?E0o4{-GOM=yn0oATRv-x2ECYTCZR%c zd#7@yiZ%rQ&i0PZ7PXpLg^`SmOYEx}yD8Ysnxdt#wV_L|1ywnv01?XwW30rSNPgM$ z-Hx&TGXD9O3xXDc^_rH(wa)ygl+#-~X`NAYjrNZ;=f}4R#kB30#c8ivJUi0`alXsf zxUPd4vZdRnhI2P7A;wbB0Q8SM6=9s9+sS-RY*j(eJFTxloG9v_RN!Fr`$Kb^TCy;6 zs&U~D1-OyP2#2WYPfZajNOw?*z@$2YGykbdtA$kYP_>NFe9CP9knXQqlbjX46W4-x z)fnm)YDbvTR!6IJ__XF{Urs@ey|^lA2hRV}p$LUwD$s>Y;!0crP^%z8UTK=CUB+4_;~w zNhK}?C$$9B>s6Iak~rPknCD%!x=XL4f+tDZfkhGx_tE8$69jc3$Ay>8t`5JR(=fmD zvb7g$m@BWnh!_9d+A9$40}GMB+RJ-s?RA+aa8R9^WoDPD_15KAb;}xwm}+6Sn$Dma zVNH7rEBYqY)`312h&qAjtk$kxRcppOvdgRHO{*@enpIKjt*fkXuE*xhnO=8IS#5={ zYM!sAcFz2&@``d_X4yQ>GxL1cRMl0^xw_5=M{U{cx`kXI_{wH4^j%doyFAZZv4AVB zdGma8YJF9+YO1R$%JY0xv!_*GU0yYNhHon6X3tSqe6yTcry1J~^S95i3%^WTq%Vw7&c6Qb5>9uI7 zm{l>m&Wlyk=F}{#t(s9;mq(#GGV^?OwPocMv&w3(%2O83p~|&B%~F7s$naInhtD^! zvaGt=2i4mr!&f<{x*YDQ6#AxirePqQxSPt8izL<*^D5?EO$K~kzY5x*vO=4V&1L*QO?3^` z4zN>0C8usqZC#%!*Hq1`$n%xeR?Wjb-s!b-W+9xWpoD7lt8pQ^=wgn&s##Q?qUj6t zz77GGD(!U~S6)$8jX?Uv?2qymeEjA?(56RGiO;|N5|qgXQXtn)l-bfQow7>mrIpk& zS#Ly0BfG0@@(#E}nMsj?zR$Ab0CN&+NHqz>-cpG`I%v5A``DH1plC_!=t^8L{Y85me z?|)ZmC8b46&7q8vr)+7H%G*8q+@c-OF-JOUK)pHF5YP1)y@F`7D( z+!5>+E6x@u%3@W%m2;(|6*zWQV6mg4lblt$M#>LGQX;i*tw6G}yz>gR^`Svo;FIy- zlO*~2$#P1Jplm*R@{!}%``?i61>kCwr6xotk#h?Es*xV+31^3+IEy|@4`wR1KUjozTKp!$mH z_nr9G8A*ZB;bW|-o~2sl%I#Z#B(K!z7IMacGp>9x4_!^zyH;DOB(&&}qDI4VB(`at zs=uoK-=OuYc5l|&=1G-QLz{zl74M}H>mxr{g7RMf{-j2iLa=3*>kU|=Jko)@Km<|a zs~ww_R44WXIMoR3f~NA<0OiqoT0)J-;K)+X*>ox6cOAvq&G_=_e$kDd@w8qcQkLuf z(gC##E7dqqt>d(LA$6Wk9hG0b^tb;z{`1m$0sq&~^0RfF=5VYdRSj*L&Yj1VMT(RDH2Sk3T2+ zIku=i>&!{ZbXg^-MUPz{wg$#u2U6RaRU7GTs-LMI9k60GQaf~66|VwyyHr;7PiJOz zBD9w?6^E3iN>;!t^~kQ*)~>>W<>Uv(j2dli_)*QDPOTNyi9f3Tfq6|up5U?OSm@Zk zhO){ZN~)@vvdKA9@xqHWf!LsEPMmVQc|H6}zw)k{r=3|jP(NkAiixTpF4Gdb;Bs0@ z#iKwSR7(Xc`M)~54#2pIqy4r&or(*_6cacjOtUO>x|1c*Rc?*3aTm5sv8A(Y3(J_;EVV`j9aD1mAO{*B@U@Uj>~;yhvt z)t=(WC6HK-tI#S( z!i?<-_C;fx%rlKJ_P!d4qK0V{Pe!Kd2RH(Ig1;tfN3E?_`%ANOF|>!#9L^i!KaL!& zKehUhPf$6;WVC^FPjK816nA974#uHn9P320A?V*u&}t8i0=1jyfn`h3ZnZDf6%wrp zWg?1`-6q~#wQ$rKt%#1GUML?|k6NovR*QZ>BM&IM4F1-@A0qE+=+)7cNdDRJ)zQ!< zj>G2AF8L~APSP8+H5^645L_8>{;=G$=aSB(lA3)`r#8mTL}x)BdOPZHJhXyX_rDwW z!~)UV5n~Yh&>wI`-W6?wJRDD2Z6=^iT)PwNpqZq#I}^q`Su=2LML(Q^HZZzGD@ioO z^#xZ18hEyhB@-*9c}e=d99qD06tq%0o2-qRpR3=A6(msy#S_Vy z8(EWw{*aHZhE(lZNSKe&2I3`_Lj7=lG0r(K5&We;F~iJs?xq+0Cz-|3mm&w>1s ztP_tU>n}z z$?AyEdE9@qjAV^P;1GLJBITnW;Rs-yiugAMGr1d%;nf)OMVi+%!eKOu5g+!QB2Qd@ z(f-luv2HrrBJGp8d+u=}?eQ#Z6`ZLo1{}b1Gem@JbS(V!g>?UZNUow zT0e1SaLuQgBi*rwNnDh~feBWSCD2tew2x%oDEg_@8n5T!I)rvWwvYjiMgcJ-Y4iE@ zu1ccV2l*t@-NSJSt%7)jW)838Aypm&shwOUNXj7I zJv-8-wHeFeI+ymoDq7(Ab?rKdvT@BHuAH$Ybo*|Q@qLIt$x1D|y~x1~{pgCqI> zIln+#`EO?!7*(OqR=tiQ2J~_AOq|KY7VsL@c!>6^X;bj7XeA6T1Kljn-# zF;2o3(T-?@_K(|T)i@f zs~ugVz_l4?cUQ$St7CwXKcWu);?;JPMe~aziFQ14z}9iy!QP@oq|`UG$4x<6a<)jG zP}P8XF&tV(%ttG#MywPc9r)~f9NNXcQN)r~fN+#GXjb(!5`{PG4)5$qzARZ6h$&Xm3drRDyF>?B8zaD|#5lAXN)T zTdIxz$OaBH9?x%GNiFO(=})tF;E_h1ll3R*5nX$DKJP($dbalvzQR{zq20u8k{Nt9 zI^u{uJ36A7I8fw-YNtP#oet^anf4*ecI3DFi;{~aKgR)%DjE`g(X zwH0j$SD5O^r`MbPV;zcRlXV$e#+4SYPA79s_)h)CbF9^X0863Qi($pUGcxS;S&%}l zllx<|=|EtJDMOxaK`8}2tPhkKQa;SO^V{_uWt541?fj}9x>S~!X=E{_I)X1R@3 zP!8|C4MGmKjce<&z%7CD%b+dviDI;l?`h#VB9>eQzu40i;hr7JM9an+sKo&An8rKe zy)17T)Izy-zhwv4^P-r_aVWzfc|Ko>!)*aI9b zyxM_dg=fesKsJ_G1pQhQdV!KqJ1mK#vIdU#K&x0Er7j7%)H56toMr4wwH3sAluiA> zJLKnbduaKn|5EseEn{6=-S_VYUdLJvWl$=%-3ye)9!CxDNv21KX1r>D*z10fg8fCk zt8$7lM2)b=cv>_)ni0X5g`!!}!LXnBkm%6pF!qWO7x1My zIwqP2*9nh=Bfjp(ddNP?S^Ydr@(Var@^@&g>(N)EuYraCCi-1;6I?eN zg-CpRn8ka+f-Vl{M0ibY5~BXy(XZi*@iMT4%cCpcS*eGk```=LSHfQHYog!63$;HW ztZ*V0bs{6`qo?(@ zVwG4e)`$UdZuIZyspzNC&%|1>P7I3m;yiJ_xIkPeHi(U4NDPaT*d#{8X0b(V6{F%J zu}zFc9~9fg4lyovMn8{!AublX#Dtg>Q{ob_TU;vkh|9#~;)UW0aizFQTrI8<*NW@J zi^TQf2JvEXqqs@z6*r5Qh+D+1;x=)+xI?^D+$mlrUM^lC?h>yQuM)2ocZ=7E*NWGP zd&IrsK5@Tzy?BFoqj-~evv`Ymt9YAuyLg9qr+Almw=gv_>1_f_?!5<_=otX_?P&%cuG7i{v)1| zkrYx&CABotN+)AkCo{5MHpoWVB&W&ga)z8K4~pI*XUT)*YEJ`mI$+P5QdA3|4m&$YGGPzu?kSpaXxmvD~1M*zC zR<4tSa=koHo-Z$u7s?HCqa2dMvLrXj5xH4zkz3`cyhv`7V{*IPA;;xTd9mCjC*-7@ zl9$Nc@>01+UM4S>FO*lvE9F)4YI%*kR$eDxB(Ik@$QR2S$@}H&GRDM=|PCh0-FTWtaC?A(!l3$izkx$64%CE_<%WueU%5TYU%kRkV%J0eV%OA)e z${)!e%b&=f%Ad)f%U{S}%3sM}%iqY~%HPS~%Rk5`NIt_IzyeQdQ`9KQ~j!_yjrBrQj68uYKdB^&QZ(MaO6J6xen# zZdZ4xm#RC}%hb!&E7V=;mFiXS6!+cgHR`qMb?P2Ou98dXIXqdY^hYJo)`W^&xl${3Gxr_#^6L>f`DY>XYhI z>eK2o>QVJs^*QyJ`n>vr`l5PVeMx;;eMLQ?zN)^azOKHZzNx;YzOBBazN@~czOR0u zeyDz=eyo0?eyV<^ey)C@eyM(?eyx6^eye_`ey{$Zo>YHSe^P%|e^Gx`e^Y-~|4{!_ z|5E=}PpPNXf7CNN(n3qEwAMyj?Q{&URm|vm-JlzFlb)uh>lu2cK1k2f2kY7T5PhgV zOdqc2=p*zCAPPB3XZ2itv~JeN=z02BeVjgCpP*0F^L2|}pj-7xx(yyqB2NAzZRZQfQrsxQ*p^qAhRcj$4wQ(vri z=?OgvuW-9W@79;7fWd-cuwCGZNZ zTlH=Fc72C_slHRcOut;eLf@rdsb8gEt?$;a(XZ97)A#6mqX(h~^?myO=)KYVqEAK- zMem6|6i6k~_51Y)^au5a^oR9F^hfn0`eXXz`V;W-pHJye>(A&%^=I|x^ke$-`V0Dt z`f>dw{bl_X{e=Fi{+j-}{)Yah{+9l>{*L~x{+|B6{(=6X{*nH%{)zsn{+a%{{)PUf z{+0f<{*C^v{+<54{)2u}|55)*|5^V<|5g7@|6Tt>|5N`<|64z$pVt4;&zQ&vBaJfJ z7-Nkyv8gi|Q*RnfqiHhJ%ycuu%rpm?S>|9f+Zd@k;_%gqbT z73NBFmATqnW3DyVnHQPs%?;+o=0w&na`Uq zm@k^g&6muV%~#A5=Bwsw=IiDg=9}hQ=G*2w=DX&5=KJOc=7;7-=EvqI=BMUo=I7=Y z=9lJI=GW#o=C|f|=J)0g=1KEM^C$CX^B411^EdN%^AGb+^Dpyn^OSko{Kq_FBP*=5 z3O>_nthLU@w$5g3y=}0Kw#iPj)9nm9(;j4J*@NwDdx$;M9%c`>bLElWd#K*>;<^1>0d4+D_YLyY0#L6nm;Y&7N-0 zuxHvH+iUx5zb#sC7umDyVtck-Vwc);>@vIDuCOcZD!baQu>+Kut8||Cyo9$cdTkYHI+wD8-JMFvdyKUL- zvk%w@?L+oG_PzFf_F?;e`vLnw`yu;b`w{z5`-uIR{kZ*v{iOYr{j~jzebj!|e$GB- zKX1Qazi1z~U$S4eU$IZvuiCHKuiJ0fZ`yC!Z`<$K@7nL#@7o{PAKD+;AKRbUpW2_< zpW9#9U)o>UU)$f<-`d~V-`hXfC+#2YpX{IQU+iD)-|XM*KkPs4zwE#5Q}${5AN!1p zoN&@9r=4-uITyP+mvQy3!8N)jH_c6VGu%vfkelTWcC+0f?ofA_JKW82N4OWbBi&Ih z>*l(nU9&sJ&2z`P2C%aSJsqQp) zx;w+2>3UqR>vR3C=)7Cx&T@<0*=~tj>dtY?+;X?Vt#qs0YPZG>xO3fFx6Tc^_3k`( zzPrF(=r*{GZpaP0lH24)+-A4MZFQsWBDc+rx$SO;8+SY1#cr3IaFcGzUE+4TOWhuK znY-M*&|TrKbXU2n-8Jr7cb$8YyWZX4UhHmkH@Us;X7>_zi@Vj`=5BX)xR<&+-OJp| z-7DN(?v?IU?$z#Y_Zs(F_d0ivyVu?4?suU zWw*~g;2v}jx%asDy7#$<-TU1K+y~u<+=tyq+(+Fb?qlxb?i22l?o;m5?lbOD_gVKj z_n7;<`-1zTd)$4=ec64*J>kCUzUIE}zTv*{@zURL0e&BxSe&l}ae&T-W ze&&Aee&K%Ue&v4ce&c@Ye&>Gg{@|W;e{_Fxe|CRye|3Lze|P_I|8)Oy|8`Hgr`>u69?ytp#s|f-;)CPa@geb{@nP}d@tpXG z_yzHi@lkO$o*N$>H^;}s^WtOUMJ|#XiJ}o{yJ|jLe?umQjzPLXw#y(yYpA|2T&yJVGOXG9mW%2TOMZ7Xz6|auh z!~^lU@!EJ@JQ%N!&x_BGFNiOUH^dv`p?Ekh#hc=hcyqiZ-Wrd_7scD+v3PsDBOZ@; z#uvxC;)!@No{BGtcgL5;d*aLD%i|ZuSHxGwSH)My*TmPx*TpZ2ua9qtUmV{U-xTkS zZ;oFQ-xA*%-xl8<-x0qwzB7JV{POq}@m=vN<5$J6j_;0N6TdcoU3^b`Z+u^Tzk|2s zw6*oL5$$bfnrGVKdSL52yg|YZ5w$(TyWo8+b|?_nGrk4()o*h{MC^++?#2jFTcj380;ykA?MVHWYC5y1(m-ZQCE2hDb_s7EqRuvKNip?jRqNJot)}%^ z!$Z6DR`{`tC*ch$BNpE50K_e3Om2{ zC8>@yY^!0mz)OO5jKPNH(UM&{RDu@=*|9(xmZr;%r5L-E252l0y%e<9WAI~_QT-iM ze_5(Z!;TbVmr?y4G{8HCc8nam!ixaaMjgDL)=aqKJ21CtV)S(*p*`JD(b^7BCDy^L?HF6t3y{mQB4P{&MhP>&n?W` zHB@;r5WNN`auO$UfF=@N<$@DAz`DR2Bf>-ukS7J!~mhM@)XX&1`_bktMXL`_*Xp)C2N!L>hS#Aj4mjW(czkFhB z2;OTE(&N>1=smmujwwlRZzJjLZ6v+Djik4?k@WU98vFJ(8vFJ(>P>qaY2V&P+PAln z_U&z?eR~^e-`+;rx3`h@?Kzg8WBECjpJVwsmY-w!IhLR6Y*?Lg@rH>MLmK6{Cpqp( zjwR<)<^qm*6WWhU>yCVJhg5Z)>j$>q;tz#v{-3k>~ly^L*snx*E5Rk8cZar%2TzUGh9D zd7hO#&q|(WCC{^x=UK_~tmJuC@;obfo|Qb$N}gvW53>R*On4PyRd_phxt*pk&jX+5 zfzR{6=XrqhJivJ#;5-j-o(DM31Dr3=K;{eFexaigUXurJihG!pp=kQMVXE4LZIY-Vg{O1Th1t!kOjUN48L*8KY3VMuG_vbT7Q& z$1H+ZU=2Bzqt`>00yzfWPBS^AQIJ^zZ@L)*+}%FBX-J+kC6`Ufb)$fOL9$#is#a|s zH>*eC^_FVQ(3Im|s1;jBRUiDVm>6xqc9I@U;{pkv8LFx~lF}fC4v@Oo|B#wBMaPq> zA=8BVdKikEEvSrvSC&ls2C8n^JW zuZX4M=G!* z71)sq>_`Q6qyjrqfgP#9j#S9eVo}J^qEyJyqEz5Tt-y;~AxDc+AxDc+ffu;~FLH%; zT9gXytbaTCOrf0?r2_kAfqk>UzFA=3EU<4Dcu_3yqFCTXvA{lCV4p3p&lcEc3+%H6 z_Sph2iUnR23wiS0LY~%!LY@}o0xy<@JnNrl{qwYt74ozw7xJusp7qbuqFTt)qFgAj z{sq>b*Nj4e_2;#tP+<85mS5oUFR=Uq%P+9}0?RM3`~u4_u>1nc?_l{IEWd-tzk}s> zu>Kt^zk}s>u>1~|-@)=bSbhh~?_l{IEWd;0cLe#le)5N0k^BH?=m*fy51^qRKtn%( zhJFAI{Qw&J0W|ajXy^yf&<~)YA3#GtfQEhm4gCO0{V0+jDt0MGKt4}fR+rZ|FJnK(>06gnYegHh{&wfx`82m6-T*&&8H#0{V)*}#QxKdhcC-H7tbaG_-_7#7S$;Rm?`HYkEWexOceDI% zmfy{C>2g)B*u!#rc-(tfZV$`tVYxjlw}<8Su-qP&+rx5uSZ)u??P0k+talH~?`6Gv zSw3Ah%N2WBelN@KW%<1^6Atbc$QD6_Q11zI<*I$<Es^D zv3xqY2cG5A$vyBapHA+9XZdt;pDX&D9V7?b69{=3o!Y}umhM@)XX$jR564-$C#jy% z+%uYcMsv?-?itNJqq%1^_l)MA(cCkddq#85Xzm%!J)^m2H1~|=p3&ShntMib&uH!$ z%{`;JXY}@r-k#ChGkSYQZ_nuM8Ld5|wP&>UjMkpf+A~^vMr+S#?HR2-qqS$W_Ken^ z(b_Xwdq!)|XzdxTJ)^Z}wDyeFp3&MfT6;!o&uHx#tv#c)XSDW=)}GPYGg^B_YtLxy z8Ld5|wP&>UjMkpf+A~^vMrY6H>=~UsqqApp_KeP+(bzK@dq!i=XzUq{J)^N_H1>?f zo>AB{`g%rP&uHrzZ9Su{XSDTltl5qpfGO^^CTj(bhBCdPZB%XzLkmJ)^B>wDpX(p3&Ab+WIb9WqlW|vc8K}8Q(># ztnZ>##&?mQdq!8!=;|3=J)^5A5_%6djw&nW8|Wj&*;XO#7fvYt`aGs=2KSKRQvqp4>!^^B&TQPeYvdPY&tDC!wSJ)@{+6!navo>9~@ih4#-&nW5{MLnaaXVmnJ znx0Y9GirK9P0y(587)1drDwGCjFz6!(lc6mMoZ6V=@~6OqorrG^o*9C(b6+odPYmn zXz3X(J)@;(wDff8JLl=rH&FH)#zoJ#=ouG1=mwc2R{WG z{1j;LQ=q|5fd+X%gP#Hocnmb)G0+eV0S$NzG{^-Sa2aTj3pB_Dn#c{&0DOms<>zvl z&7)hU;0qpbVID&PIK(1z3+?vN9e8JM014_qpG_G(FASekmbQUPJ+e_<3v6t2hpj57x)(PN4zlt2C0uAHt z3pD@W{$X5!hIs`V<`rnr3uu^EprKu$VP3)PyzMEr4vkH!(uPU3dBZLj?uu;~Wja<* zeE4llr7Ih!@fWSGg0As*lec zNoM*2Q<(%XfTn6&Sgxrm4cTecqoK`d)x&{rOk~i}YA4c13$1BxVGY+=!}Zi~eZ-l~ zt^LITGupal6+AfD+SjwjjNxyMStdp{(b$um&tt8|{yT z*3^aH?eLu;b4lRCFY=k-3!!M!7WgCpJW?<`Hi4dGS5gB5SV67XuoYYj|F*^83gM8N z48FD%{9_yZ4Z|P$-bL&m2_0sBE7NV$Q~nnkio*?y)`^nCdlo?BaN7bC;m!plGJNv_ z3*r3>pq9&qyBL^{cOHPo;a&zL>gaX`q|Ejy{M`|LFCB$6+;0eXYcP8!W>~n*0VEE$ zIFN_C96&OBlLB+#UI!Am-4VD6ByqTJfr)Sj2@EY`CLU(P2gC6Wn)#{5aftf`o;;Pgrh% zZW3V*+=D^_x1j<@I)po8z}Hf_c zaucJMhD2rp9@*JJ$r1cm92JDmwQyyYu``hY36imMq9SAGL?Q!IBm>Hk3@De#fMg;A z3bG7Hk&MwDn@JB$1R0P>WI&Q-gjA4$<$??>$1*U0Z^WQ2l2Mo0x2V>_m{2N@xOGBCk1FiA2fg)%}plo862jF2Q5 zpZEy;nBr%UA*6Dq2YaZ=z;2{6reSYV{0!<;I*0m} z%9$Rzn968MdJ5lu!{=_epUiPDiJMmGBk@g14-=OA$91`PT({CU<|{qp%1OV7t*3A< zSb5{cQ}A^WSU>|e4MukuK5hcvSqgmpCj1H>IZgw7-vghF3kRBpw&RzLVCfC)^w0`^ zXKajIrU^bj2TyyAY^LJV0vo=N6UsLZO~415Mklrfu5Rbp6nuyfd>Fp11NVMIvVQ9x zcutGE0kTHNNmM30M3R(-WjQ39Ch#+R;|W`bACRFk@M(`BQmz4>K!cCukT#$wvi(GgL_Cp2M9w0zn8?{gmJnG=+q{IxZX&db+dafx7DyB73|~EC1B~HmxsX3CPIyxFM!1Zir0E4Uw&J!*IOnhRCMf5c#SbB1dkBDRx6lCT5ft20rD6$V0uv4Uw(5VK`QGLu3;-3~6@5 zP$zN2aDd$q(@j-3q&nf^27Cy2$L6XVBA0eUPH{ zbwlJB4I!1FAtVzw!~*pR8e$P%<*9UlKS-wBFtkW$h-DZJAz5+5(1R)(!l8hMKoJd* z02(3zG(-Yuhy>6O37{boKtm*ehDZPnkpLPZ0W?GcXov*R5DB0m56O37{boKtm*ehDZPnkpLPZ0W?Gc z2!aIA5DB0m5 z8zP&yVMr%vh;gWNpK?RslAsjM*OH(VL|1}RNGEOxg%dZ#WD1f@VQCPjZ2nMA*lmfw^6bJ^TK)?-=U{DGKgHj+Elmfw^6h^-`D235T2c*vjM@N6bJ^TKrko; zf5)4X#U{DGKgHj+El;RMi zf>O9RKv5ZA8Z4!x!A?pVtfZvDMoJpQmNeK$ zNrQEiG}uN-gJqO7*hNW$Rg^T?L`j21lr-2wNrN?%G}uB(gC&$S*g;8y6_hmCKuLoI zlr+qLNyF@yG|YWT!_1fJ@T>u^IzcL2y@eE9<*n>N3kUcdq|{J(6dAJPu(bjbW(cdp zW;M6~3a8{)Bbi-DRT_oQStlFYphyE0rf1Tz{Ai^Uqm}Y}4u~7!u>5GMJY1{9QK^G3 zZo=jwOyQ@NNsIAH1LHLU>)`9RRJZ}YS6vZZzkOY31-c(I{ z4fyT(@K8P0+>?}w@$=9)DUGS?zfd52a~Qjt;cb9ei?#rUOxn?enWdzurquR7IM7(# z_7{qUtu~mV&3}}_{eO_k@YX-fq-}qYYOHSe3q`6TX_p^lHl;TAVcjiZe-_rQZtnwb zT6$9-R!i;W1J-Q~s|QSLGUOTFzy}BGX!9PVGQ4jOYlO@9U_`J>0-*@a-Hf8WH>I}l z0Xr?daWC+xy?emJMQYs7gGbW4^}>;4z#Q(=3pthPrrmm2E4@b#%j`&MS=f>X8E~aL z>|n!VXW%MMuBbXWDKiKG`9Scc&I zJ&!eM%N?W~Zn(o7+H41@4DYiGnMp6>u)z+prq}GM3&kt8(3ILo2S=Jx+vk97*poJT z+D8Z3jn(~gz|~!r^wxztF|g9~K(>bUu%uxbENNK%O1g+Yu%4DQga#!Ifk8<_SWwas z6qGcC1SJgtK}kb6P|^?#lr)3_B@KZRF!#kR>HQLM!sSIyq#>~E?N>ge#GaP8FZek9_ z5<98uPo^~V#t77T9w6Wc`^>uYjzGpDYG06yKYsb+4P3t za3pEAj+ch_Wc6eFqrbCI@ml~W^RVtbDiPFsd%^=B28A2tiY6@VPp&}Ur z_#8C@PoM1B)s#wC9-V=K#z$wWV76&wD)qzza@B<%xX>W%NNZias<(b(C)}f;aiRRS zZnQy4=L)HGZ#A7y<>yoR9ic8=I-&a;@I+F05(3tP@Gu3eLJ+vN<=~0{o|DbkGBr8| zmtMAvh1(<`cxEMs8i1@&Xl!IVhKz78B`FkgDupcG*KEtp*zY!MTQ2>4#zZMcPr%TS z)iCX7*7%4Rv=Wl=lo%u%s^_+Cxi~y%F}ic8EtY(1w|ZrI}>WWh5&gU^BN zKB#6dG7a4g5l-YQJC1>GtnO3= zZdQ7`Yo&>FCKQ^6H`o%n3>hJ0iK2m$7fcf#!6PT6s!{amrAl4S|=wr!>dTfw~p?Z zg!clAXP$vK0N4Ka7`%HI{`BL|JY^=-#m_wX^!HNfCn9)xY)f`iIlFOlb2(bEZe(yl zSwKd%{K$&3K6ZUs7tdQaw|VZIz3Z~&6)V=wEuT3!CtL2qRM+5Owwx&rZ7Ls+Y^Ink zw_&0UOFXh-U3PPJ@7^IOvSQsv$jOF6m|BRbg&XH=92^{+Q;y~j4zg?zE?ctDj-EFJ zT`+ws)|E|jZ`n5Y&Y3%Ruq-w%D65uc=udXjJ~P~##Ul@7ggkLBB>S>^vwNYreQjpm z-j(Y%u9!2lYH(fiARIe$^*T5(2YX*pQ@503XxqVGgnxZy*SB(AIqsYP0E}hd#@_Pm zkt3i-rUi~+r)05I7RBL>{R_%A$&2PU@3VT|#%ytK^AHYO&@!5XBT>%I0eOkQvYOXC z)DLxC%RXa@WibTlxCL~|!VvYHjiV2V=H9__J#wpns|RjDxvnMqu#SfDrvy^U4SgH4 zdpBmw4b8pH3(A?6eNkiIz`A{njeXE)Z@F&%ASxs0^|BuOGLYU^4?krwyEzLTfxbZr zJ+F5!j2N^SpE$P}>LzKJlTaVp2*(DYe~X}3i#C?894VxxXy>%F*c$z-y3U z$QG3Ap*yCyW?fWnZ0^l&gjV0v)Fj~b)V;lXH}1>m`Q_31bB+eB8lYhP`~~I4mVE+g zQ_DVybXv-t$K8VUwE&D9egIe}Eq_bM~#Yhis*;j{jc1zjK|6j^J1Z2;K zI){R6q=$iQq=$oSq;o(v(j!1N(iebiq(_2mq(^~lq*;)SbZ!f1+O#p-2cx(VhYtQ< zxvn{TDzMGvnIlJ_^)4tM-BO-AzdZNE1?A?JY<3aMdty?}LtV|;y#wp^&zytRj;Rc( zm|bo@u`CX53nMr$t#R$4V_UKv!CQ`NiOP!X791dvhskL6Lt&-pKfSqY-?8Fg=+*Hp z*;AnhNvVxs218v7$|tn69&+k}@`(pH3X@-g67ymFqS^DZt=UDvm*l*~d-pDCUer7^ zTo&M=Ckk=!ED+EFZZjL~4*m~E%eFXwWN&M8Hhbz`Xl6n6aJDrRfRzBprkiaE@p`Ek3euKobwMlbt@ z=0JMmAou_j8v?nm+>kDl`6Vwz)04E0qhGTx&fJYdt3-w@W1JB3S z;TM*)In8qit1ZIBwW2;*kYSExeVR`NHBSoJY}Pl!kj;nrUr=rf>O;>Yk505^ zU2FDKSbWK%ctk*aW%%YnXBNZJp|U8Jnulq9YUTlj?kp(hSXf^&q8o9o2L+NrZ*OVN zwxZ@9R-97@TlXC;W`RlPE7>sa)$BqgJCshU)|5E~L>^3*04os8{6Ved<6-tseUALo zz`n(-gUTmBnbTX!^MQ1tSR2#IsCIZUj@<}k4io5MtBm>0C#WE#;T!Mn4q;5sy1 z=U_XXPKNcIGMPhoZ8$qXKu>G<_>ai zd$6}%(4}7NEhhS~x0vY1-eTg+7PQdv8IbJ8*+>kdh;_Rm*JE8wEW)~&I1B4y;$)nF zxoBai8xK-t%vWLyCquDS*a9Y2V+)vAgDqg<3>@f-99019eh8$2{I~WVR`Ha zL~h<$o>hc};`ogaGY!B(m3VYnHupc6$rwHV!TRa=J7YHd4nCldkBh7NAg8U mcZ|3Zf|oOITo<@wk%M@FSO~KQ9~RGCU)HzmGw|hx=zjoaQiNsz From 16838595a3b124cb05303aded67a3b38bbf08ade Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 4 Jan 2014 05:24:32 +0100 Subject: [PATCH 353/889] Another console fix: discard text markup when getting the command line's text --- apps/openmw/mwgui/console.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index c15cc7b1d..f3805b255 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -213,7 +213,7 @@ namespace MWGui { std::vector matches; listNames(); - mCommandLine->setCaption(complete( mCommandLine->getCaption(), matches )); + mCommandLine->setCaption(complete( mCommandLine->getOnlyText(), matches )); #if 0 int i = 0; for(std::vector::iterator it=matches.begin(); it < matches.end(); ++it,++i ) @@ -232,7 +232,7 @@ namespace MWGui { // If the user was editing a string, store it for later if(mCurrent == mCommandHistory.end()) - mEditString = mCommandLine->getCaption(); + mEditString = mCommandLine->getOnlyText(); if(mCurrent != mCommandHistory.begin()) { @@ -257,7 +257,7 @@ namespace MWGui void Console::acceptCommand(MyGUI::EditBox* _sender) { - const std::string &cm = mCommandLine->getCaption(); + const std::string &cm = mCommandLine->getOnlyText(); if(cm.empty()) return; // Add the command to the history, and set the current pointer to From 18d4cdb2ac56ec7c5929f157ea8e2a34bb8ad618 Mon Sep 17 00:00:00 2001 From: Dmitriy 'Endorph' Shkurskiy Date: Sat, 4 Jan 2014 07:27:50 +0200 Subject: [PATCH 354/889] Fix for VS2010-Debug: manual assignment operator and cctor added for MMWorld::ContainerStoreIterator --- apps/openmw/mwworld/containerstore.cpp | 40 ++++++++++++++++++++++++++ apps/openmw/mwworld/containerstore.hpp | 8 ++++++ 2 files changed, 48 insertions(+) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 686e790a3..39fda7ab9 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -555,6 +555,11 @@ MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *contain MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Weapon), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mWeapon(iterator){} +MWWorld::ContainerStoreIterator::ContainerStoreIterator( const ContainerStoreIterator& src ) +{ + copy(src); +} + void MWWorld::ContainerStoreIterator::incType() { if (mType==0) @@ -807,6 +812,41 @@ const MWWorld::ContainerStore *MWWorld::ContainerStoreIterator::getContainerStor return mContainer; } +void MWWorld::ContainerStoreIterator::copy(const ContainerStoreIterator& src) +{ + mType = src.mType; + mMask = src.mMask; + mContainer = src.mContainer; + mPtr = src.mPtr; + + switch (mType) + { + case MWWorld::ContainerStore::Type_Potion: mPotion = src.mPotion; break; + case MWWorld::ContainerStore::Type_Apparatus: mApparatus = src.mApparatus; break; + case MWWorld::ContainerStore::Type_Armor: mArmor = src.mArmor; break; + case MWWorld::ContainerStore::Type_Book: mBook = src.mBook; break; + case MWWorld::ContainerStore::Type_Clothing: mClothing = src.mClothing; break; + case MWWorld::ContainerStore::Type_Ingredient: mIngredient = src.mIngredient; break; + case MWWorld::ContainerStore::Type_Light: mLight = src.mLight; break; + case MWWorld::ContainerStore::Type_Lockpick: mLockpick = src.mLockpick; break; + case MWWorld::ContainerStore::Type_Miscellaneous: mMiscellaneous = src.mMiscellaneous; break; + case MWWorld::ContainerStore::Type_Probe: mProbe = src.mProbe; break; + case MWWorld::ContainerStore::Type_Repair: mRepair = src.mRepair; break; + case MWWorld::ContainerStore::Type_Weapon: mWeapon = src.mWeapon; break; + case -1: break; + default: assert(0); + } +} + +MWWorld::ContainerStoreIterator& MWWorld::ContainerStoreIterator::operator=( const ContainerStoreIterator& rhs ) +{ + if (this!=&rhs) + { + copy(rhs); + } + return *this; +} + bool MWWorld::operator== (const ContainerStoreIterator& left, const ContainerStoreIterator& right) { return left.isEqual (right); diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index b34c71006..780d46199 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -143,6 +143,10 @@ namespace MWWorld MWWorld::CellRefList::List::iterator mRepair; MWWorld::CellRefList::List::iterator mWeapon; + public: + + ContainerStoreIterator(const ContainerStoreIterator& src); + private: ContainerStoreIterator (ContainerStore *container); @@ -165,6 +169,8 @@ namespace MWWorld ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + void copy (const ContainerStoreIterator& src); + void incType(); void nextType(); @@ -189,6 +195,8 @@ namespace MWWorld ContainerStoreIterator operator++ (int); + ContainerStoreIterator& operator= (const ContainerStoreIterator& rhs); + bool isEqual (const ContainerStoreIterator& iter) const; int getType() const; From 70233950124f9f53b1809eb565141120b57029ad Mon Sep 17 00:00:00 2001 From: Dmitriy 'Endorph' Shkurskiy Date: Sat, 4 Jan 2014 07:30:43 +0200 Subject: [PATCH 355/889] Windows-specific definition to omit inclusion of unused stuff from Windows.h --- apps/openmw/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 2bf48c1bb..3129e6bd3 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -11,6 +11,7 @@ #include // For OutputDebugString +#define WIN32_LEAN_AND_MEAN #include // makes __argc and __argv available on windows #include From 116f125f05b9fa12f76267068f169b66fa8e15dc Mon Sep 17 00:00:00 2001 From: Dmitriy 'Endorph' Shkurskiy Date: Sat, 4 Jan 2014 07:41:23 +0200 Subject: [PATCH 356/889] Windows-specific definition to deny SDL from providing it's own WinMain() --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 64f8121c4..5cb2fd5b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -183,6 +183,9 @@ if (WIN32) set(Boost_USE_STATIC_LIBS ON) set(PLATFORM_INCLUDE_DIR "platform") add_definitions(-DBOOST_ALL_NO_LIB) + + # Suppress WinMain(), provided by SDL + add_definitions(-DSDL_MAIN_HANDLED) else (WIN32) set(PLATFORM_INCLUDE_DIR "") find_path (UUID_INCLUDE_DIR uuid/uuid.h) From aa05ffcf60831c2bd020502b8433820d75e982e4 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 4 Jan 2014 13:05:19 +0100 Subject: [PATCH 357/889] Splited some long argument lists to the multiple lines. --- .../opencs/model/tools/referenceablecheck.cpp | 136 +++++++++++++----- apps/opencs/model/world/refiddata.hpp | 8 +- 2 files changed, 108 insertions(+), 36 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 73f316677..4b610d5d2 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -10,7 +10,11 @@ #include "../world/universalid.hpp" #include -CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& faction) : +CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( + const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, + const CSMWorld::IdCollection& classes, + const CSMWorld::IdCollection& faction) + : mReferencables(referenceable), mClasses(classes), mRaces(races), @@ -215,7 +219,10 @@ int CSMTools::ReferenceableCheckStage::setup() return mReferencables.getSize(); } -void CSMTools::ReferenceableCheckStage::bookCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Book >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::bookCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Book >& records, + std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -230,7 +237,10 @@ void CSMTools::ReferenceableCheckStage::bookCheck(int stage, const CSMWorld::Ref inventoryItemCheck(Book, messages, id.toString(), true); } -void CSMTools::ReferenceableCheckStage::activatorCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Activator >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::activatorCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Activator >& records, + std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -249,7 +259,10 @@ void CSMTools::ReferenceableCheckStage::activatorCheck(int stage, const CSMWorld } } -void CSMTools::ReferenceableCheckStage::potionCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Potion >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::potionCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Potion >& records, + std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -266,7 +279,10 @@ void CSMTools::ReferenceableCheckStage::potionCheck(int stage, const CSMWorld::R } -void CSMTools::ReferenceableCheckStage::apparatusCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Apparatus >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::apparatusCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Apparatus >& records, + std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -284,7 +300,10 @@ void CSMTools::ReferenceableCheckStage::apparatusCheck(int stage, const CSMWorld toolCheck(Apparatus, messages, id.toString()); } -void CSMTools::ReferenceableCheckStage::armorCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Armor >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::armorCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Armor >& records, + std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -311,7 +330,10 @@ void CSMTools::ReferenceableCheckStage::armorCheck(int stage, const CSMWorld::Re } } -void CSMTools::ReferenceableCheckStage::clothingCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Clothing >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::clothingCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Clothing >& records, + std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -325,7 +347,10 @@ void CSMTools::ReferenceableCheckStage::clothingCheck(int stage, const CSMWorld: inventoryItemCheck(Clothing, messages, id.toString(), true); } -void CSMTools::ReferenceableCheckStage::containerCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Container >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::containerCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Container >& records, + std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -356,7 +381,10 @@ void CSMTools::ReferenceableCheckStage::containerCheck(int stage, const CSMWorld } } -void CSMTools::ReferenceableCheckStage::creatureCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Creature >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::creatureCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Creature >& records, + std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -450,7 +478,10 @@ void CSMTools::ReferenceableCheckStage::creatureCheck(int stage, const CSMWorld: } } -void CSMTools::ReferenceableCheckStage::doorCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Door >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::doorCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Door >& records, + std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -476,7 +507,10 @@ void CSMTools::ReferenceableCheckStage::doorCheck(int stage, const CSMWorld::Ref //TODO, check what static unsigned int sRecordId; is for } -void CSMTools::ReferenceableCheckStage::ingredientCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Ingredient >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::ingredientCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Ingredient >& records, + std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -491,7 +525,10 @@ void CSMTools::ReferenceableCheckStage::ingredientCheck(int stage, const CSMWorl inventoryItemCheck(Ingredient, messages, id.toString()); } -void CSMTools::ReferenceableCheckStage::creaturesLevListCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::CreatureLevList >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::creaturesLevListCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::CreatureLevList >& records, + std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -507,7 +544,10 @@ void CSMTools::ReferenceableCheckStage::creaturesLevListCheck(int stage, const C listCheck(CreatureLevList, messages, id.toString()); } -void CSMTools::ReferenceableCheckStage::itemLevelledListCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::ItemLevList >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::itemLevelledListCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::ItemLevList >& records, + std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -522,7 +562,10 @@ void CSMTools::ReferenceableCheckStage::itemLevelledListCheck(int stage, const C listCheck(ItemLevList, messages, id.toString()); } -void CSMTools::ReferenceableCheckStage::lightCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Light >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::lightCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Light >& records, + std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -553,7 +596,10 @@ void CSMTools::ReferenceableCheckStage::lightCheck(int stage, const CSMWorld::Re } } -void CSMTools::ReferenceableCheckStage::lockpickCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Lockpick >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::lockpickCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Lockpick >& records, + std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -570,7 +616,10 @@ void CSMTools::ReferenceableCheckStage::lockpickCheck(int stage, const CSMWorld: toolCheck(Lockpick, messages, id.toString(), true); } -void CSMTools::ReferenceableCheckStage::miscCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Miscellaneous >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::miscCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Miscellaneous >& records, + std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -585,7 +634,10 @@ void CSMTools::ReferenceableCheckStage::miscCheck(int stage, const CSMWorld::Ref inventoryItemCheck(Miscellaneous, messages, id.toString()); } -void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::NPC >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::npcCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::NPC >& records, + std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -757,7 +809,10 @@ void CSMTools::ReferenceableCheckStage::npcCheck(int stage, const CSMWorld::RefI //TODO: reputation, Disposition, rank, everything else } -void CSMTools::ReferenceableCheckStage::weaponCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Weapon >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::weaponCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Weapon >& records, + std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -772,13 +827,12 @@ void CSMTools::ReferenceableCheckStage::weaponCheck(int stage, const CSMWorld::R //TODO, It seems that this stuff for spellcasting is obligatory and In fact We should check if records are present if ( - //THOSE ARE HARDCODED! + //THOSE ARE HARDCODED! Weapon.mId != "VFX_Hands" - and Weapon.mId != "VFX_Absorb" - and Weapon.mId != "VFX_Reflect" - and Weapon.mId != "VFX_DefaultBolt" - //THIS ONE IS NOT, but it thas to be ignored as well - //TODO I don't know how to get full list of effects :/ + && Weapon.mId != "VFX_Absorb" + && Weapon.mId != "VFX_Reflect" + && Weapon.mId != "VFX_DefaultBolt" + //TODO I don't know how to get full list of effects :/ ) { inventoryItemCheck(Weapon, messages, id.toString(), true); @@ -817,7 +871,10 @@ void CSMTools::ReferenceableCheckStage::weaponCheck(int stage, const CSMWorld::R } } -void CSMTools::ReferenceableCheckStage::probeCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Probe >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::probeCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Probe >& records, + std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -870,7 +927,10 @@ void CSMTools::ReferenceableCheckStage::staticCheck(int stage, const CSMWorld::R //Templates begins here -template void CSMTools::ReferenceableCheckStage::inventoryItemCheck(const ITEM& someitem, std::vector< std::string >& messages, const std::string& someid, bool enchantable) +template void CSMTools::ReferenceableCheckStage::inventoryItemCheck( + const ITEM& someitem, + std::vector< std::string >& messages, + const std::string& someid, bool enchantable) { if (someitem.mName.empty()) { @@ -910,7 +970,10 @@ template void CSMTools::ReferenceableCheckStage::inventoryItemChe } } -template void CSMTools::ReferenceableCheckStage::inventoryItemCheck(const ITEM& someitem, std::vector< std::string >& messages, const std::string& someid) +template void CSMTools::ReferenceableCheckStage::inventoryItemCheck( + const ITEM& someitem, + std::vector< std::string >& messages, + const std::string& someid) { if (someitem.mName.empty()) { @@ -942,7 +1005,10 @@ template void CSMTools::ReferenceableCheckStage::inventoryItemChe } } -template void CSMTools::ReferenceableCheckStage::toolCheck(const TOOL& sometool, std::vector< std::string >& messages, const std::string& someid, bool canbebroken) +template void CSMTools::ReferenceableCheckStage::toolCheck( + const TOOL& sometool, + std::vector< std::string >& messages, + const std::string& someid, bool canbebroken) { if (sometool.mData.mQuality <= 0) { @@ -958,16 +1024,21 @@ template void CSMTools::ReferenceableCheckStage::toolCheck(const } } -template void CSMTools::ReferenceableCheckStage::toolCheck(const TOOL& sometool, std::vector< std::string >& messages, const std::string& someid) +template void CSMTools::ReferenceableCheckStage::toolCheck( + const TOOL& sometool, + std::vector< std::string >& messages, + const std::string& someid) { if (sometool.mData.mQuality <= 0) { messages.push_back(someid + "|" + sometool.mId + " has non-positive quality"); } - } -template void CSMTools::ReferenceableCheckStage::listCheck(const LIST& somelist, std::vector< std::string >& messages, const std::string& someid) +template void CSMTools::ReferenceableCheckStage::listCheck( + const LIST& somelist, + std::vector< std::string >& messages, + const std::string& someid) { for (unsigned i = 0; i < somelist.mList.size(); ++i) { @@ -981,4 +1052,5 @@ template void CSMTools::ReferenceableCheckStage::listCheck(const messages.push_back(someid + "|" + somelist.mId + " contains item with non-positive level"); } } -} \ No newline at end of file +} +// kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index f82d151b4..a204334b3 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -237,10 +237,10 @@ namespace CSMWorld const RefIdDataContainer& getLocpicks() const; const RefIdDataContainer& getMiscellaneous() const; const RefIdDataContainer& getNPCs() const; - const RefIdDataContainer< ESM::Weapon >& getWeapons() const; - const RefIdDataContainer< ESM::Probe >& getProbes() const; - const RefIdDataContainer< ESM::Repair>& getRepairs() const; - const RefIdDataContainer< ESM::Static>& getStatics() const; + const RefIdDataContainer& getWeapons() const; + const RefIdDataContainer& getProbes() const; + const RefIdDataContainer& getRepairs() const; + const RefIdDataContainer& getStatics() const; }; } From dfd1058551ecfdaf34bf621670904f1029ad55e1 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 4 Jan 2014 13:09:53 +0100 Subject: [PATCH 358/889] Various style corrections. --- apps/opencs/model/tools/referenceablecheck.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 4b610d5d2..6c90af694 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -837,7 +837,11 @@ void CSMTools::ReferenceableCheckStage::weaponCheck( { inventoryItemCheck(Weapon, messages, id.toString(), true); - if (!(Weapon.mData.mType == ESM::Weapon::MarksmanBow or Weapon.mData.mType == ESM::Weapon::MarksmanCrossbow or Weapon.mData.mType == ESM::Weapon::MarksmanThrown or Weapon.mData.mType == ESM::Weapon::Arrow or Weapon.mData.mType == ESM::Weapon::Bolt)) + if ( !(Weapon.mData.mType == ESM::Weapon::MarksmanBow || + Weapon.mData.mType == ESM::Weapon::MarksmanCrossbow || + Weapon.mData.mType == ESM::Weapon::MarksmanThrown || + Weapon.mData.mType == ESM::Weapon::Arrow || + Weapon.mData.mType == ESM::Weapon::Bolt) ) { if (Weapon.mData.mSlash[0] > Weapon.mData.mSlash[1]) { From 558690b571f22b02df849046cb5401d5b2f009c2 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 4 Jan 2014 15:19:17 +0100 Subject: [PATCH 359/889] implemented list of magical bolts for skipping. --- .../opencs/model/tools/referenceablecheck.cpp | 58 ++++++++++++++----- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 6c90af694..b9adf81ea 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -826,22 +826,40 @@ void CSMTools::ReferenceableCheckStage::weaponCheck( //TODO, It seems that this stuff for spellcasting is obligatory and In fact We should check if records are present if - ( - //THOSE ARE HARDCODED! - Weapon.mId != "VFX_Hands" - && Weapon.mId != "VFX_Absorb" - && Weapon.mId != "VFX_Reflect" - && Weapon.mId != "VFX_DefaultBolt" - //TODO I don't know how to get full list of effects :/ - ) + ( //THOSE ARE HARDCODED! + !(Weapon.mId == "VFX_Hands" || + Weapon.mId == "VFX_Absorb" || + Weapon.mId == "VFX_Reflect" || + Weapon.mId == "VFX_DefaultBolt" || + //TODO I don't know how to get full list of effects :/ + //DANGER!, ACHTUNG! FIXME! The following is the list of the magical bolts, valid for Morrowind.esm. However those are not hardcoded. + Weapon.mId == "magic_bolt" || + Weapon.mId == "shock_bolt" || + Weapon.mId == "shield_bolt" || + Weapon.mId == "VFX_DestructBolt" || + Weapon.mId == "VFX_PoisonBolt" || + Weapon.mId == "VFX_RestoreBolt" || + Weapon.mId == "VFX_AlterationBolt" || + Weapon.mId == "VFX_ConjureBolt" || + Weapon.mId == "VFX_FrostBolt" || + Weapon.mId == "VFX_MysticismBolt" || + Weapon.mId == "VFX_IllusionBolt" || + Weapon.mId == "VFX_Multiple2" || + Weapon.mId == "VFX_Multiple3" || + Weapon.mId == "VFX_Multiple4" || + Weapon.mId == "VFX_Multiple5" || + Weapon.mId == "VFX_Multiple6" || + Weapon.mId == "VFX_Multiple7" || + Weapon.mId == "VFX_Multiple8" || + Weapon.mId == "VFX_Multiple9")) { inventoryItemCheck(Weapon, messages, id.toString(), true); - if ( !(Weapon.mData.mType == ESM::Weapon::MarksmanBow || - Weapon.mData.mType == ESM::Weapon::MarksmanCrossbow || - Weapon.mData.mType == ESM::Weapon::MarksmanThrown || - Weapon.mData.mType == ESM::Weapon::Arrow || - Weapon.mData.mType == ESM::Weapon::Bolt) ) + if (!(Weapon.mData.mType == ESM::Weapon::MarksmanBow || + Weapon.mData.mType == ESM::Weapon::MarksmanCrossbow || + Weapon.mData.mType == ESM::Weapon::MarksmanThrown || + Weapon.mData.mType == ESM::Weapon::Arrow || + Weapon.mData.mType == ESM::Weapon::Bolt)) { if (Weapon.mData.mSlash[0] > Weapon.mData.mSlash[1]) { @@ -859,7 +877,9 @@ void CSMTools::ReferenceableCheckStage::weaponCheck( messages.push_back(id.toString() + "|" + Weapon.mId + " has minimum chop damage higher than maximum"); } - if (!(Weapon.mData.mType == ESM::Weapon::Arrow or Weapon.mData.mType == ESM::Weapon::Bolt or Weapon.mData.mType == ESM::Weapon::MarksmanThrown)) + if (!(Weapon.mData.mType == ESM::Weapon::Arrow || + Weapon.mData.mType == ESM::Weapon::Bolt || + Weapon.mData.mType == ESM::Weapon::MarksmanThrown)) { //checking of health if (Weapon.mData.mHealth <= 0) @@ -894,7 +914,10 @@ void CSMTools::ReferenceableCheckStage::probeCheck( toolCheck(Probe, messages, id.toString(), true); } -void CSMTools::ReferenceableCheckStage::repairCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Repair >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::repairCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Repair >& records, + std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); @@ -910,7 +933,10 @@ void CSMTools::ReferenceableCheckStage::repairCheck(int stage, const CSMWorld::R toolCheck(Repair, messages, id.toString(), true); } -void CSMTools::ReferenceableCheckStage::staticCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Static >& records, std::vector< std::string >& messages) +void CSMTools::ReferenceableCheckStage::staticCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Static >& records, + std::vector< std::string >& messages) { const CSMWorld::RecordBase& baserecord = records.getRecord(stage); From e8607171058e3f026b6b89fe0b30164bc58b7451 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 4 Jan 2014 15:28:08 +0100 Subject: [PATCH 360/889] replaced raw values with enums. --- apps/opencs/model/tools/referenceablecheck.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index b9adf81ea..e94f0a401 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -656,9 +656,9 @@ void CSMTools::ReferenceableCheckStage::npcCheck( //Don't know what unknown is for int Gold(NPC.mNpdt52.mGold); - if (NPC.mNpdtType == 12) //12 = autocalculated + if (NPC.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated { - if ((NPC.mFlags & 0x0008) == 0) //0x0008 = autocalculated flag + if ((NPC.mFlags & ESM::NPC::Autocalc) == 0) //0x0008 = autocalculated flag { messages.push_back(id.toString() + "|" + NPC.mId + " mNpdtType or flags mismatch!"); //should not happend? return; From f4517c8221184dc85904724b97ca86bdc59f0a91 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 4 Jan 2014 20:02:29 +0100 Subject: [PATCH 361/889] For dialogue filtering, use the Vampirism magic effect instead of the untouched NpcStats::mVampire --- apps/openmw/mwdialogue/filter.cpp | 3 ++- apps/openmw/mwmechanics/npcstats.cpp | 11 ----------- apps/openmw/mwmechanics/npcstats.hpp | 5 ----- 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 9d08debff..92738ec0e 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -514,7 +514,8 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co case SelectWrapper::Function_PcVampire: - return MWWorld::Class::get (player).getNpcStats (player).isVampire(); + return MWWorld::Class::get (player).getCreatureStats(player).getMagicEffects(). + get(ESM::MagicEffect::Vampirism).mMagnitude > 0; case SelectWrapper::Function_TalkedToPc: diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 0a0b51270..94dd97186 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -28,7 +28,6 @@ MWMechanics::NpcStats::NpcStats() , mBounty (0) , mLevelProgress(0) , mDisposition(0) -, mVampire (0) , mReputation(0) , mWerewolfKills (0) , mProfit(0) @@ -318,16 +317,6 @@ void MWMechanics::NpcStats::setFactionReputation (const std::string& faction, in mFactionReputation[faction] = value; } -bool MWMechanics::NpcStats::isVampire() const -{ - return mVampire; -} - -void MWMechanics::NpcStats::setVampire (bool set) -{ - mVampire = set; -} - int MWMechanics::NpcStats::getReputation() const { return mReputation; diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 552422d84..586e06832 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -50,7 +50,6 @@ namespace MWMechanics int mBounty; std::set mExpelled; std::map mFactionReputation; - bool mVampire; int mReputation; int mWerewolfKills; int mProfit; @@ -135,10 +134,6 @@ namespace MWMechanics void setFactionReputation (const std::string& faction, int value); - bool isVampire() const; - - void setVampire (bool set); - bool hasSkillsForRank (const std::string& factionId, int rank) const; bool isWerewolf() const; From c4e4a8fb57869d74b7c4ac169fa2ed2debe48501 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 4 Jan 2014 20:43:57 +0100 Subject: [PATCH 362/889] Closes #1083: Fix werewolf change handling --- apps/openmw/mwrender/characterpreview.cpp | 12 +++-------- apps/openmw/mwrender/npcanimation.cpp | 26 +++++++++++++++++------ apps/openmw/mwrender/npcanimation.hpp | 8 +++++++ apps/openmw/mwworld/inventorystore.cpp | 5 +++++ apps/openmw/mwworld/worldimp.cpp | 10 +++++++-- 5 files changed, 44 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index a0ba01b37..643225515 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -140,8 +140,7 @@ namespace MWRender void InventoryPreview::update(int sizeX, int sizeY) { - // TODO: can we avoid this. Vampire state needs to be updated. - mAnimation->rebuild(); + mAnimation->updateParts(); MWWorld::InventoryStore &inv = MWWorld::Class::get(mCharacter).getInventoryStore(mCharacter); MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); @@ -177,12 +176,8 @@ namespace MWRender groupname = "inventoryhandtohand"; } - // TODO see above - //if(groupname != mCurrentAnimGroup) - //{ - mCurrentAnimGroup = groupname; - mAnimation->play(mCurrentAnimGroup, 1, Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); - //} + mCurrentAnimGroup = groupname; + mAnimation->play(mCurrentAnimGroup, 1, Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) @@ -194,7 +189,6 @@ namespace MWRender else if(mAnimation->getInfo("torch")) mAnimation->disable("torch"); - mAnimation->updateParts(); mAnimation->runAnimation(0.0f); mViewport->setDimensions (0, 0, std::min(1.f, float(sizeX) / float(512)), std::min(1.f, float(sizeY) / float(1024))); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index b1455f0dc..dbb5a0868 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -119,7 +119,8 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v mShowWeapons(false), mShowCarriedLeft(true), mFirstPersonOffset(0.f, 0.f, 0.f), - mAlpha(1.f) + mAlpha(1.f), + mNpcType(Type_Normal) { mNpc = mPtr.get()->mBase; @@ -157,8 +158,8 @@ void NpcAnimation::updateNpcBase() const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Race *race = store.get().find(mNpc->mRace); - bool isWerewolf = mPtr.getClass().getNpcStats(mPtr).isWerewolf(); - bool vampire = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Vampirism).mMagnitude; + bool isWerewolf = (mNpcType == Type_Werewolf); + bool isVampire = (mNpcType == Type_Vampire); if (isWerewolf) { @@ -167,7 +168,7 @@ void NpcAnimation::updateNpcBase() } else { - if (vampire) + if (isVampire) mHeadModel = getVampireHead(mNpc->mRace, mNpc->mFlags & ESM::NPC::Female); else mHeadModel = "meshes\\" + store.get().find(mNpc->mHead)->mModel; @@ -221,11 +222,24 @@ void NpcAnimation::updateNpcBase() } void NpcAnimation::updateParts() -{ +{ mAlpha = 1.f; const MWWorld::Class &cls = MWWorld::Class::get(mPtr); MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); + NpcType curType = Type_Normal; + if (cls.getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Vampirism).mMagnitude > 0) + curType = Type_Vampire; + if (cls.getNpcStats(mPtr).isWerewolf()) + curType = Type_Werewolf; + + if (curType != mNpcType) + { + mNpcType = curType; + rebuild(); + return; + } + static const struct { int mSlot; int mBasePriority; @@ -329,7 +343,7 @@ void NpcAnimation::updateParts() static const int Flag_Female = 1<<0; static const int Flag_FirstPerson = 1<<1; - bool isWerewolf = cls.getNpcStats(mPtr).isWerewolf(); + bool isWerewolf = (mNpcType == Type_Werewolf); int flags = (isWerewolf ? -1 : 0); if(!mNpc->isMale()) flags |= Flag_Female; diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 28bb81ddd..8edcc04be 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -55,6 +55,14 @@ private: bool mShowWeapons; bool mShowCarriedLeft; + enum NpcType + { + Type_Normal, + Type_Werewolf, + Type_Vampire + }; + NpcType mNpcType; + int mVisibilityFlags; int mPartslots[ESM::PRT_Count]; //Each part slot is taken by clothing, armor, or is empty diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 509c34afb..2944f00d4 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -139,8 +139,13 @@ void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& ite void MWWorld::InventoryStore::unequipAll(const MWWorld::Ptr& actor) { + // Only *one* change event should be fired + mUpdatesEnabled = false; for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) unequipSlot(slot, actor); + mUpdatesEnabled = true; + fireEquipmentChangedEvent(); + updateMagicEffects(actor); } MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c23c63e2e..621cd75ea 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1960,6 +1960,10 @@ namespace MWWorld npcStats.setWerewolf(werewolf); + // This is a bit dangerous. Equipped items other than WerewolfRobe may reference + // bones that do not even exist with the werewolf object root. + // Therefore, make sure to unequip everything at once, and only fire the change event + // (which will rebuild the animation parts) afterwards. unequipAll will do this for us. MWWorld::InventoryStore& invStore = MWWorld::Class::get(actor).getInventoryStore(actor); invStore.unequipAll(actor); @@ -1974,6 +1978,10 @@ namespace MWWorld actor.getClass().getContainerStore(actor).remove("werewolfrobe", 1, actor); } + // NpcAnimation::updateParts will already rebuild the animation when it detects change of Npc type. + // the following is just for reattaching the camera properly. + mRendering->rebuildPtr(actor); + if(actor.getRefData().getHandle() == "player") { // Update the GUI only when called on the player @@ -1991,8 +1999,6 @@ namespace MWWorld windowManager->unsetForceHide(MWGui::GW_Magic); } } - - mRendering->rebuildPtr(actor); } void World::applyWerewolfAcrobatics(const Ptr &actor) From 92234cf78301348c6423fa8791cf8ee8f3937f50 Mon Sep 17 00:00:00 2001 From: pvdk Date: Sat, 4 Jan 2014 21:46:38 +0100 Subject: [PATCH 363/889] Changed crashcatcher's uname system info retrieval and cleaned indentation --- apps/openmw/crashcatcher.cpp | 599 ++++++++++++++++++----------------- 1 file changed, 302 insertions(+), 297 deletions(-) diff --git a/apps/openmw/crashcatcher.cpp b/apps/openmw/crashcatcher.cpp index 666330666..d7ebc0d47 100644 --- a/apps/openmw/crashcatcher.cpp +++ b/apps/openmw/crashcatcher.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -42,78 +43,78 @@ static char altstack[SIGSTKSZ]; static struct { - int signum; - pid_t pid; - int has_siginfo; - siginfo_t siginfo; - char buf[1024]; + int signum; + pid_t pid; + int has_siginfo; + siginfo_t siginfo; + char buf[1024]; } crash_info; static const struct { - const char *name; - int signum; + const char *name; + int signum; } signals[] = { - { "Segmentation fault", SIGSEGV }, - { "Illegal instruction", SIGILL }, - { "FPU exception", SIGFPE }, - { "System BUS error", SIGBUS }, - { NULL, 0 } +{ "Segmentation fault", SIGSEGV }, +{ "Illegal instruction", SIGILL }, +{ "FPU exception", SIGFPE }, +{ "System BUS error", SIGBUS }, +{ NULL, 0 } }; static const struct { - int code; - const char *name; + int code; + const char *name; } sigill_codes[] = { -#ifndef __FreeBSD__ - { ILL_ILLOPC, "Illegal opcode" }, - { ILL_ILLOPN, "Illegal operand" }, - { ILL_ILLADR, "Illegal addressing mode" }, - { ILL_ILLTRP, "Illegal trap" }, - { ILL_PRVOPC, "Privileged opcode" }, - { ILL_PRVREG, "Privileged register" }, - { ILL_COPROC, "Coprocessor error" }, - { ILL_BADSTK, "Internal stack error" }, -#endif - { 0, NULL } + #ifndef __FreeBSD__ + { ILL_ILLOPC, "Illegal opcode" }, + { ILL_ILLOPN, "Illegal operand" }, + { ILL_ILLADR, "Illegal addressing mode" }, + { ILL_ILLTRP, "Illegal trap" }, + { ILL_PRVOPC, "Privileged opcode" }, + { ILL_PRVREG, "Privileged register" }, + { ILL_COPROC, "Coprocessor error" }, + { ILL_BADSTK, "Internal stack error" }, + #endif + { 0, NULL } }; static const struct { - int code; - const char *name; + int code; + const char *name; } sigfpe_codes[] = { - { FPE_INTDIV, "Integer divide by zero" }, - { FPE_INTOVF, "Integer overflow" }, - { FPE_FLTDIV, "Floating point divide by zero" }, - { FPE_FLTOVF, "Floating point overflow" }, - { FPE_FLTUND, "Floating point underflow" }, - { FPE_FLTRES, "Floating point inexact result" }, - { FPE_FLTINV, "Floating point invalid operation" }, - { FPE_FLTSUB, "Subscript out of range" }, - { 0, NULL } + { FPE_INTDIV, "Integer divide by zero" }, + { FPE_INTOVF, "Integer overflow" }, + { FPE_FLTDIV, "Floating point divide by zero" }, + { FPE_FLTOVF, "Floating point overflow" }, + { FPE_FLTUND, "Floating point underflow" }, + { FPE_FLTRES, "Floating point inexact result" }, + { FPE_FLTINV, "Floating point invalid operation" }, + { FPE_FLTSUB, "Subscript out of range" }, + { 0, NULL } }; static const struct { - int code; - const char *name; + int code; + const char *name; } sigsegv_codes[] = { -#ifndef __FreeBSD__ - { SEGV_MAPERR, "Address not mapped to object" }, - { SEGV_ACCERR, "Invalid permissions for mapped object" }, -#endif - { 0, NULL } + #ifndef __FreeBSD__ + { SEGV_MAPERR, "Address not mapped to object" }, + { SEGV_ACCERR, "Invalid permissions for mapped object" }, + #endif + { 0, NULL } }; static const struct { - int code; - const char *name; + int code; + const char *name; } sigbus_codes[] = { -#ifndef __FreeBSD__ - { BUS_ADRALN, "Invalid address alignment" }, - { BUS_ADRERR, "Non-existent physical address" }, - { BUS_OBJERR, "Object specific hardware error" }, -#endif - { 0, NULL } + #ifndef __FreeBSD__ + { BUS_ADRALN, "Invalid address alignment" }, + { BUS_ADRERR, "Non-existent physical address" }, + { BUS_OBJERR, "Object specific hardware error" }, + #endif + { 0, NULL } }; static int (*cc_user_info)(char*, char*); @@ -121,314 +122,318 @@ static int (*cc_user_info)(char*, char*); static void gdb_info(pid_t pid) { - char respfile[64]; - char cmd_buf[128]; - FILE *f; - int fd; + char respfile[64]; + char cmd_buf[128]; + FILE *f; + int fd; - /* Create a temp file to put gdb commands into */ - strcpy(respfile, "gdb-respfile-XXXXXX"); - if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != NULL) - { - fprintf(f, "attach %d\n" - "shell echo \"\"\n" - "shell echo \"* Loaded Libraries\"\n" - "info sharedlibrary\n" - "shell echo \"\"\n" - "shell echo \"* Threads\"\n" - "info threads\n" - "shell echo \"\"\n" - "shell echo \"* FPU Status\"\n" - "info float\n" - "shell echo \"\"\n" - "shell echo \"* Registers\"\n" - "info registers\n" - "shell echo \"\"\n" - "shell echo \"* Backtrace\"\n" - "thread apply all backtrace full\n" - "detach\n" - "quit\n", pid); - fclose(f); + /* Create a temp file to put gdb commands into */ + strcpy(respfile, "gdb-respfile-XXXXXX"); + if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != NULL) + { + fprintf(f, "attach %d\n" + "shell echo \"\"\n" + "shell echo \"* Loaded Libraries\"\n" + "info sharedlibrary\n" + "shell echo \"\"\n" + "shell echo \"* Threads\"\n" + "info threads\n" + "shell echo \"\"\n" + "shell echo \"* FPU Status\"\n" + "info float\n" + "shell echo \"\"\n" + "shell echo \"* Registers\"\n" + "info registers\n" + "shell echo \"\"\n" + "shell echo \"* Backtrace\"\n" + "thread apply all backtrace full\n" + "detach\n" + "quit\n", pid); + fclose(f); - /* Run gdb and print process info. */ - snprintf(cmd_buf, sizeof(cmd_buf), "gdb --quiet --batch --command=%s", respfile); - printf("Executing: %s\n", cmd_buf); - fflush(stdout); + /* Run gdb and print process info. */ + snprintf(cmd_buf, sizeof(cmd_buf), "gdb --quiet --batch --command=%s", respfile); + printf("Executing: %s\n", cmd_buf); + fflush(stdout); - system(cmd_buf); - /* Clean up */ - remove(respfile); - } - else - { - /* Error creating temp file */ - if(fd >= 0) - { - close(fd); - remove(respfile); - } - printf("!!! Could not create gdb command file\n"); - } - fflush(stdout); + system(cmd_buf); + /* Clean up */ + remove(respfile); + } + else + { + /* Error creating temp file */ + if(fd >= 0) + { + close(fd); + remove(respfile); + } + printf("!!! Could not create gdb command file\n"); + } + fflush(stdout); } static void sys_info(void) { #ifdef __unix__ - system("echo \"System: `uname -a`\""); - putchar('\n'); - fflush(stdout); + struct utsname info; + if(!uname(&info)) + printf("!!! Failed to get system information\n"); + else + printf("System: %s %s %s %s %s\n", + info.sysname, info.nodename, info.release, info.version, info.machine); + + fflush(stdout); #endif } - static size_t safe_write(int fd, const void *buf, size_t len) { - size_t ret = 0; - while(ret < len) - { - ssize_t rem; - if((rem=write(fd, (const char*)buf+ret, len-ret)) == -1) - { - if(errno == EINTR) - continue; - break; - } - ret += rem; - } - return ret; + size_t ret = 0; + while(ret < len) + { + ssize_t rem; + if((rem=write(fd, (const char*)buf+ret, len-ret)) == -1) + { + if(errno == EINTR) + continue; + break; + } + ret += rem; + } + return ret; } static void crash_catcher(int signum, siginfo_t *siginfo, void *context) { //ucontext_t *ucontext = (ucontext_t*)context; - pid_t dbg_pid; - int fd[2]; + pid_t dbg_pid; + int fd[2]; - /* Make sure the effective uid is the real uid */ - if(getuid() != geteuid()) - { - raise(signum); - return; - } + /* Make sure the effective uid is the real uid */ + if(getuid() != geteuid()) + { + raise(signum); + return; + } - safe_write(STDERR_FILENO, fatal_err, sizeof(fatal_err)-1); - if(pipe(fd) == -1) - { - safe_write(STDERR_FILENO, pipe_err, sizeof(pipe_err)-1); - raise(signum); - return; - } + safe_write(STDERR_FILENO, fatal_err, sizeof(fatal_err)-1); + if(pipe(fd) == -1) + { + safe_write(STDERR_FILENO, pipe_err, sizeof(pipe_err)-1); + raise(signum); + return; + } - crash_info.signum = signum; - crash_info.pid = getpid(); - crash_info.has_siginfo = !!siginfo; - if(siginfo) - crash_info.siginfo = *siginfo; - if(cc_user_info) - cc_user_info(crash_info.buf, crash_info.buf+sizeof(crash_info.buf)); + crash_info.signum = signum; + crash_info.pid = getpid(); + crash_info.has_siginfo = !!siginfo; + if(siginfo) + crash_info.siginfo = *siginfo; + if(cc_user_info) + cc_user_info(crash_info.buf, crash_info.buf+sizeof(crash_info.buf)); - /* Fork off to start a crash handler */ - switch((dbg_pid=fork())) - { - /* Error */ - case -1: - safe_write(STDERR_FILENO, fork_err, sizeof(fork_err)-1); - raise(signum); - return; + /* Fork off to start a crash handler */ + switch((dbg_pid=fork())) + { + /* Error */ + case -1: + safe_write(STDERR_FILENO, fork_err, sizeof(fork_err)-1); + raise(signum); + return; - case 0: - dup2(fd[0], STDIN_FILENO); - close(fd[0]); - close(fd[1]); + case 0: + dup2(fd[0], STDIN_FILENO); + close(fd[0]); + close(fd[1]); - execl(argv0, argv0, crash_switch, NULL); + execl(argv0, argv0, crash_switch, NULL); - safe_write(STDERR_FILENO, exec_err, sizeof(exec_err)-1); - _exit(1); + safe_write(STDERR_FILENO, exec_err, sizeof(exec_err)-1); + _exit(1); - default: + default: #ifdef __linux__ - prctl(PR_SET_PTRACER, dbg_pid, 0, 0, 0); + prctl(PR_SET_PTRACER, dbg_pid, 0, 0, 0); #endif - safe_write(fd[1], &crash_info, sizeof(crash_info)); - close(fd[0]); - close(fd[1]); + safe_write(fd[1], &crash_info, sizeof(crash_info)); + close(fd[0]); + close(fd[1]); - /* Wait; we'll be killed when gdb is done */ - do { - int status; - if(waitpid(dbg_pid, &status, 0) == dbg_pid && - (WIFEXITED(status) || WIFSIGNALED(status))) - { - /* The debug process died before it could kill us */ - raise(signum); - break; - } - } while(1); - } + /* Wait; we'll be killed when gdb is done */ + do { + int status; + if(waitpid(dbg_pid, &status, 0) == dbg_pid && + (WIFEXITED(status) || WIFSIGNALED(status))) + { + /* The debug process died before it could kill us */ + raise(signum); + break; + } + } while(1); + } } static void crash_handler(const char *logfile) { - const char *sigdesc = ""; + const char *sigdesc = ""; int i; - if(fread(&crash_info, sizeof(crash_info), 1, stdin) != 1) - { - fprintf(stderr, "!!! Failed to retrieve info from crashed process\n"); - exit(1); - } + if(fread(&crash_info, sizeof(crash_info), 1, stdin) != 1) + { + fprintf(stderr, "!!! Failed to retrieve info from crashed process\n"); + exit(1); + } - /* Get the signal description */ - for(i = 0;signals[i].name;++i) - { - if(signals[i].signum == crash_info.signum) - { - sigdesc = signals[i].name; - break; - } - } + /* Get the signal description */ + for(i = 0;signals[i].name;++i) + { + if(signals[i].signum == crash_info.signum) + { + sigdesc = signals[i].name; + break; + } + } - if(crash_info.has_siginfo) - { - switch(crash_info.signum) - { - case SIGSEGV: - for(i = 0;sigsegv_codes[i].name;++i) - { - if(sigsegv_codes[i].code == crash_info.siginfo.si_code) - { - sigdesc = sigsegv_codes[i].name; - break; - } - } - break; + if(crash_info.has_siginfo) + { + switch(crash_info.signum) + { + case SIGSEGV: + for(i = 0;sigsegv_codes[i].name;++i) + { + if(sigsegv_codes[i].code == crash_info.siginfo.si_code) + { + sigdesc = sigsegv_codes[i].name; + break; + } + } + break; - case SIGFPE: - for(i = 0;sigfpe_codes[i].name;++i) - { - if(sigfpe_codes[i].code == crash_info.siginfo.si_code) - { - sigdesc = sigfpe_codes[i].name; - break; - } - } - break; + case SIGFPE: + for(i = 0;sigfpe_codes[i].name;++i) + { + if(sigfpe_codes[i].code == crash_info.siginfo.si_code) + { + sigdesc = sigfpe_codes[i].name; + break; + } + } + break; - case SIGILL: - for(i = 0;sigill_codes[i].name;++i) - { - if(sigill_codes[i].code == crash_info.siginfo.si_code) - { - sigdesc = sigill_codes[i].name; - break; - } - } - break; + case SIGILL: + for(i = 0;sigill_codes[i].name;++i) + { + if(sigill_codes[i].code == crash_info.siginfo.si_code) + { + sigdesc = sigill_codes[i].name; + break; + } + } + break; - case SIGBUS: - for(i = 0;sigbus_codes[i].name;++i) - { - if(sigbus_codes[i].code == crash_info.siginfo.si_code) - { - sigdesc = sigbus_codes[i].name; - break; - } - } - break; - } - } - fprintf(stderr, "%s (signal %i)\n", sigdesc, crash_info.signum); - if(crash_info.has_siginfo) - fprintf(stderr, "Address: %p\n", crash_info.siginfo.si_addr); - fputc('\n', stderr); + case SIGBUS: + for(i = 0;sigbus_codes[i].name;++i) + { + if(sigbus_codes[i].code == crash_info.siginfo.si_code) + { + sigdesc = sigbus_codes[i].name; + break; + } + } + break; + } + } + fprintf(stderr, "%s (signal %i)\n", sigdesc, crash_info.signum); + if(crash_info.has_siginfo) + fprintf(stderr, "Address: %p\n", crash_info.siginfo.si_addr); + fputc('\n', stderr); - if(logfile) - { - /* Create crash log file and redirect shell output to it */ - if(freopen(logfile, "wa", stdout) != stdout) - { - fprintf(stderr, "!!! Could not create %s following signal\n", logfile); - exit(1); - } - fprintf(stderr, "Generating %s and killing process %d, please wait... ", logfile, crash_info.pid); + if(logfile) + { + /* Create crash log file and redirect shell output to it */ + if(freopen(logfile, "wa", stdout) != stdout) + { + fprintf(stderr, "!!! Could not create %s following signal\n", logfile); + exit(1); + } + fprintf(stderr, "Generating %s and killing process %d, please wait... ", logfile, crash_info.pid); - printf("*** Fatal Error ***\n" - "%s (signal %i)\n", sigdesc, crash_info.signum); - if(crash_info.has_siginfo) - printf("Address: %p\n", crash_info.siginfo.si_addr); - fputc('\n', stdout); - fflush(stdout); - } + printf("*** Fatal Error ***\n" + "%s (signal %i)\n", sigdesc, crash_info.signum); + if(crash_info.has_siginfo) + printf("Address: %p\n", crash_info.siginfo.si_addr); + fputc('\n', stdout); + fflush(stdout); + } - sys_info(); + sys_info(); - crash_info.buf[sizeof(crash_info.buf)-1] = '\0'; - printf("%s\n", crash_info.buf); - fflush(stdout); + crash_info.buf[sizeof(crash_info.buf)-1] = '\0'; + printf("%s\n", crash_info.buf); + fflush(stdout); - if(crash_info.pid > 0) - { - gdb_info(crash_info.pid); - kill(crash_info.pid, SIGKILL); - } + if(crash_info.pid > 0) + { + gdb_info(crash_info.pid); + kill(crash_info.pid, SIGKILL); + } - if(logfile) - { + if(logfile) + { char cwd[MAXPATHLEN]; getcwd(cwd, MAXPATHLEN); std::string message = "OpenMW has encountered a fatal error.\nCrash log saved to '" + std::string(cwd) + "/" + std::string(logfile) + "'.\n Please report this to https://bugs.openmw.org !"; SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), NULL); - } - exit(0); + } + exit(0); } int cc_install_handlers(int argc, char **argv, int num_signals, int *signals, const char *logfile, int (*user_info)(char*, char*)) { - struct sigaction sa; - stack_t altss; - int retval; + struct sigaction sa; + stack_t altss; + int retval; - if(argc == 2 && strcmp(argv[1], crash_switch) == 0) - crash_handler(logfile); + if(argc == 2 && strcmp(argv[1], crash_switch) == 0) + crash_handler(logfile); - cc_user_info = user_info; + cc_user_info = user_info; - if(argv[0][0] == '/') - snprintf(argv0, sizeof(argv0), "%s", argv[0]); - else - { - getcwd(argv0, sizeof(argv0)); - retval = strlen(argv0); - snprintf(argv0+retval, sizeof(argv0)-retval, "/%s", argv[0]); - } + if(argv[0][0] == '/') + snprintf(argv0, sizeof(argv0), "%s", argv[0]); + else + { + getcwd(argv0, sizeof(argv0)); + retval = strlen(argv0); + snprintf(argv0+retval, sizeof(argv0)-retval, "/%s", argv[0]); + } - /* Set an alternate signal stack so SIGSEGVs caused by stack overflows - * still run */ - altss.ss_sp = altstack; - altss.ss_flags = 0; - altss.ss_size = sizeof(altstack); - sigaltstack(&altss, NULL); + /* Set an alternate signal stack so SIGSEGVs caused by stack overflows + * still run */ + altss.ss_sp = altstack; + altss.ss_flags = 0; + altss.ss_size = sizeof(altstack); + sigaltstack(&altss, NULL); - memset(&sa, 0, sizeof(sa)); - sa.sa_sigaction = crash_catcher; - sa.sa_flags = SA_RESETHAND | SA_NODEFER | SA_SIGINFO | SA_ONSTACK; - sigemptyset(&sa.sa_mask); + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = crash_catcher; + sa.sa_flags = SA_RESETHAND | SA_NODEFER | SA_SIGINFO | SA_ONSTACK; + sigemptyset(&sa.sa_mask); - retval = 0; - while(num_signals--) - { + retval = 0; + while(num_signals--) + { if((*signals != SIGSEGV && *signals != SIGILL && *signals != SIGFPE && *signals != SIGABRT && - *signals != SIGBUS) || sigaction(*signals, &sa, NULL) == -1) - { - *signals = 0; - retval = -1; - } - ++signals; - } - return retval; + *signals != SIGBUS) || sigaction(*signals, &sa, NULL) == -1) + { + *signals = 0; + retval = -1; + } + ++signals; + } + return retval; } From 634a53211c43ab9136e6744baf641d3a56494a33 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 4 Jan 2014 22:56:06 +0100 Subject: [PATCH 364/889] Make sure materials are built before trying to determine their transparency --- apps/openmw/mwrender/animation.cpp | 2 ++ apps/openmw/mwrender/npcanimation.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index faf9d979a..a9b76093c 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -185,6 +185,7 @@ public: for(unsigned int i = 0;i < numsubs;++i) { Ogre::SubEntity* subEnt = entity->getSubEntity(i); + sh::Factory::getInstance()._ensureMaterial(subEnt->getMaterial()->getName(), "Default"); subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? mTransQueue : mSolidQueue); } } @@ -1188,6 +1189,7 @@ public: unsigned int numsubs = ent->getNumSubEntities(); for(unsigned int i = 0;i < numsubs;++i) { + sh::Factory::getInstance()._ensureMaterial(ent->getSubEntity(i)->getMaterial()->getName(), "Default"); if(ent->getSubEntity(i)->getMaterial()->isTransparent()) return true; } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index dbb5a0868..75744af38 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -750,6 +750,7 @@ void NpcAnimation::preRender(Ogre::Camera *camera) void NpcAnimation::applyAlpha(float alpha, Ogre::Entity *ent, NifOgre::ObjectScenePtr scene) { + sh::Factory::getInstance()._ensureMaterial(ent->getSubEntity(0)->getMaterial()->getName(), "Default"); ent->getSubEntity(0)->setRenderQueueGroup(alpha != 1.f || ent->getSubEntity(0)->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); From 14b70a3ce61482b425060e5a9523f539d8f90ae1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 5 Jan 2014 01:34:35 +0100 Subject: [PATCH 365/889] Implement AI related magic effects (calm, frenzy, rally, demoralize, turn undead) --- apps/opencs/model/world/columns.cpp | 4 +-- apps/openmw/mwclass/creature.cpp | 8 +++--- apps/openmw/mwclass/npc.cpp | 8 +++--- apps/openmw/mwdialogue/filter.cpp | 3 ++- apps/openmw/mwmechanics/actors.cpp | 26 ++++++++++++++++++- apps/openmw/mwmechanics/creaturestats.cpp | 10 +++++-- apps/openmw/mwmechanics/creaturestats.hpp | 17 +++++++----- .../mwmechanics/mechanicsmanagerimp.cpp | 16 +++++++++--- apps/openmw/mwscript/aiextensions.cpp | 17 ++++++++---- components/esm/loadcrea.hpp | 2 +- 10 files changed, 81 insertions(+), 30 deletions(-) diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index 7a1313720..9c0d5b0fd 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -263,7 +263,7 @@ namespace static const char *sCreatureTypes[] = { - "Creature", "Deadra", "Undead", "Humanoid", 0 + "Creature", "Daedra", "Undead", "Humanoid", 0 }; static const char *sWeaponTypes[] = @@ -342,4 +342,4 @@ std::vector CSMWorld::Columns::getEnums (ColumnId column) } return enums; -} \ No newline at end of file +} diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index b225441aa..4506285ef 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -84,10 +84,10 @@ namespace MWClass data->mCreatureStats.getAiSequence().fill(ref->mBase->mAiPackage); - data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello); - data->mCreatureStats.setAiSetting (1, ref->mBase->mAiData.mFight); - data->mCreatureStats.setAiSetting (2, ref->mBase->mAiData.mFlee); - data->mCreatureStats.setAiSetting (3, ref->mBase->mAiData.mAlarm); + data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Hello, ref->mBase->mAiData.mHello); + data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Fight, ref->mBase->mAiData.mFight); + data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Flee, ref->mBase->mAiData.mFlee); + data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Alarm, ref->mBase->mAiData.mAlarm); // spells for (std::vector::const_iterator iter (ref->mBase->mSpells.mList.begin()); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 08947c4c0..8a32f58b9 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -309,10 +309,10 @@ namespace MWClass data->mNpcStats.getAiSequence().fill(ref->mBase->mAiPackage); - data->mNpcStats.setAiSetting (0, ref->mBase->mAiData.mHello); - data->mNpcStats.setAiSetting (1, ref->mBase->mAiData.mFight); - data->mNpcStats.setAiSetting (2, ref->mBase->mAiData.mFlee); - data->mNpcStats.setAiSetting (3, ref->mBase->mAiData.mAlarm); + data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Hello, ref->mBase->mAiData.mHello); + data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Fight, ref->mBase->mAiData.mFight); + data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Flee, ref->mBase->mAiData.mFlee); + data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Alarm, ref->mBase->mAiData.mAlarm); // spells for (std::vector::const_iterator iter (ref->mBase->mSpells.mList.begin()); diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 92738ec0e..6f16a79ca 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -279,7 +279,8 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con case SelectWrapper::Function_AiSetting: - return MWWorld::Class::get (mActor).getCreatureStats (mActor).getAiSetting (select.getArgument()); + return MWWorld::Class::get (mActor).getCreatureStats (mActor).getAiSetting ( + (MWMechanics::CreatureStats::AiSetting)select.getArgument()).getModified(); case SelectWrapper::Function_PcAttribute: diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index f4cf1d482..0a3f21939 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -172,7 +172,7 @@ namespace MWMechanics float d = sqrt((actorpos.pos[0] - playerpos.pos[0])*(actorpos.pos[0] - playerpos.pos[0]) +(actorpos.pos[1] - playerpos.pos[1])*(actorpos.pos[1] - playerpos.pos[1]) +(actorpos.pos[2] - playerpos.pos[2])*(actorpos.pos[2] - playerpos.pos[2])); - float fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(1); + float fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified(); float disp = 100; //creatures don't have disposition, so set it to 100 by default if(ptr.getTypeName() == typeid(ESM::NPC).name()) { @@ -341,6 +341,30 @@ namespace MWMechanics creatureStats.setDynamic(i, stat); } + // AI setting modifiers + int creature = !ptr.getClass().isNpc(); + if (creature && ptr.get()->mBase->mData.mType == ESM::Creature::Humanoid) + creature = false; + // Note: the Creature variants only work on normal creatures, not on daedra or undead creatures. + if (!creature || ptr.get()->mBase->mData.mType == ESM::Creature::Creatures) + { + Stat stat = creatureStats.getAiSetting(CreatureStats::AI_Fight); + stat.setModifier(creatureStats.getMagicEffects().get(ESM::MagicEffect::FrenzyHumanoid+creature).mMagnitude + - creatureStats.getMagicEffects().get(ESM::MagicEffect::CalmHumanoid+creature).mMagnitude); + creatureStats.setAiSetting(CreatureStats::AI_Fight, stat); + + stat = creatureStats.getAiSetting(CreatureStats::AI_Flee); + stat.setModifier(creatureStats.getMagicEffects().get(ESM::MagicEffect::DemoralizeHumanoid+creature).mMagnitude + - creatureStats.getMagicEffects().get(ESM::MagicEffect::RallyHumanoid+creature).mMagnitude); + creatureStats.setAiSetting(CreatureStats::AI_Flee, stat); + } + if (creature && ptr.get()->mBase->mData.mType == ESM::Creature::Undead) + { + Stat stat = creatureStats.getAiSetting(CreatureStats::AI_Flee); + stat.setModifier(creatureStats.getMagicEffects().get(ESM::MagicEffect::TurnUndead).mMagnitude); + creatureStats.setAiSetting(CreatureStats::AI_Flee, stat); + } + // Apply disintegration (reduces item health) float disintegrateWeapon = effects.get(ESM::MagicEffect::DisintegrateWeapon).mMagnitude; if (disintegrateWeapon > 0) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index a21796ce6..85f6cfdbc 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -122,7 +122,7 @@ namespace MWMechanics return mLevel; } - int CreatureStats::getAiSetting (int index) const + Stat CreatureStats::getAiSetting (AiSetting index) const { assert (index>=0 && index<4); return mAiSettings[index]; @@ -240,12 +240,18 @@ namespace MWMechanics mAttackingOrSpell = attackingOrSpell; } - void CreatureStats::setAiSetting (int index, int value) + void CreatureStats::setAiSetting (AiSetting index, Stat value) { assert (index>=0 && index<4); mAiSettings[index] = value; } + void CreatureStats::setAiSetting (AiSetting index, int base) + { + Stat stat(base); + setAiSetting(index, stat); + } + bool CreatureStats::isDead() const { return mDead; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 8a525523d..322970e73 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -24,7 +24,7 @@ namespace MWMechanics Spells mSpells; ActiveSpells mActiveSpells; MagicEffects mMagicEffects; - int mAiSettings[4]; + Stat mAiSettings[4]; AiSequence mAiSequence; float mLevelHealthBonus; bool mDead; @@ -85,9 +85,6 @@ namespace MWMechanics int getLevel() const; - int getAiSetting (int index) const; - ///< 0: hello, 1 fight, 2 flee, 3 alarm - Spells & getSpells(); ActiveSpells & getActiveSpells(); @@ -125,8 +122,16 @@ namespace MWMechanics void setLevel(int level); - void setAiSetting (int index, int value); - ///< 0: hello, 1 fight, 2 flee, 3 alarm + enum AiSetting + { + AI_Hello, + AI_Fight, + AI_Flee, + AI_Alarm + }; + void setAiSetting (AiSetting index, Stat value); + void setAiSetting (AiSetting index, int base); + Stat getAiSetting (AiSetting index) const; const AiSequence& getAiSequence() const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 2e18fae63..97764890f 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -605,8 +605,12 @@ namespace MWMechanics { float s = int(r * fPerDieRollMult * fPerTempMult); - npcStats.setAiSetting (2, std::max(0, std::min(100, npcStats.getAiSetting (2) + int(std::max(iPerMinChange, s))))); - npcStats.setAiSetting (1, std::max(0, std::min(100, npcStats.getAiSetting (1) + int(std::min(-iPerMinChange, -s))))); + int flee = npcStats.getAiSetting(MWMechanics::CreatureStats::AI_Flee).getBase(); + int fight = npcStats.getAiSetting(MWMechanics::CreatureStats::AI_Fight).getBase(); + npcStats.setAiSetting (MWMechanics::CreatureStats::AI_Flee, + std::max(0, std::min(100, flee + int(std::max(iPerMinChange, s))))); + npcStats.setAiSetting (MWMechanics::CreatureStats::AI_Fight, + std::max(0, std::min(100, fight + int(std::min(-iPerMinChange, -s))))); } float c = -std::abs(int(r * fPerDieRollMult)); @@ -640,8 +644,12 @@ namespace MWMechanics { float s = c * fPerDieRollMult * fPerTempMult; - npcStats.setAiSetting (2, std::max(0, std::min(100, npcStats.getAiSetting (2) + std::min(-int(iPerMinChange), int(-s))))); - npcStats.setAiSetting (1, std::max(0, std::min(100, npcStats.getAiSetting (1) + std::max(int(iPerMinChange), int(s))))); + int flee = npcStats.getAiSetting (CreatureStats::AI_Flee).getBase(); + int fight = npcStats.getAiSetting (CreatureStats::AI_Fight).getBase(); + npcStats.setAiSetting (CreatureStats::AI_Flee, + std::max(0, std::min(100, flee + std::min(-int(iPerMinChange), int(-s))))); + npcStats.setAiSetting (CreatureStats::AI_Fight, + std::max(0, std::min(100, fight + std::max(int(iPerMinChange), int(s))))); } x = int(-c * fPerDieRollMult); diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 1cdbaa008..966a064c7 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -225,7 +225,8 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - runtime.push(MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSetting (mIndex)); + runtime.push(MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSetting ( + (MWMechanics::CreatureStats::AiSetting)mIndex).getModified()); } }; template @@ -241,8 +242,11 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWWorld::Class::get (ptr).getCreatureStats (ptr).setAiSetting (mIndex, - MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSetting (mIndex) + value); + MWMechanics::CreatureStats::AiSetting setting + = MWMechanics::CreatureStats::AiSetting(mIndex); + + MWWorld::Class::get (ptr).getCreatureStats (ptr).setAiSetting (setting, + MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSetting (setting).getBase() + value); } }; template @@ -258,8 +262,11 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWWorld::Class::get (ptr).getCreatureStats (ptr).setAiSetting (mIndex, - value); + MWMechanics::CreatureStats::AiSetting setting = (MWMechanics::CreatureStats::AiSetting)mIndex; + + MWMechanics::Stat stat = ptr.getClass().getCreatureStats(ptr).getAiSetting(setting); + stat.setModified(value, 0); + ptr.getClass().getCreatureStats(ptr).setAiSetting(setting, stat); } }; diff --git a/components/esm/loadcrea.hpp b/components/esm/loadcrea.hpp index 99c4f5225..9e48f7c1a 100644 --- a/components/esm/loadcrea.hpp +++ b/components/esm/loadcrea.hpp @@ -40,7 +40,7 @@ struct Creature enum Type { Creatures = 0, - Deadra = 1, + Daedra = 1, Undead = 2, Humanoid = 3 }; From 44e96fcaaa37dc0cf133e4cd17eeda39f4329af0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 5 Jan 2014 01:56:36 +0100 Subject: [PATCH 366/889] Implement Charm magic effect --- apps/openmw/mwmechanics/actors.cpp | 2 +- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0a3f21939..2cbfc020e 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -228,7 +228,7 @@ namespace MWMechanics now += creatureStats.getActiveSpells().getMagicEffects(); - MagicEffects diff = MagicEffects::diff (creatureStats.getMagicEffects(), now); + //MagicEffects diff = MagicEffects::diff (creatureStats.getMagicEffects(), now); creatureStats.setMagicEffects(now); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 97764890f..67f42fe0a 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -481,6 +481,8 @@ namespace MWMechanics if (playerStats.getDrawState() == MWMechanics::DrawState_Weapon) x += MWBase::Environment::get().getWorld()->getStore().get().find("fDispWeaponDrawn")->getFloat(); + x += ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Charm).mMagnitude; + int effective_disposition = std::max(0,std::min(int(x),100));//, normally clamped to [0..100] when used return effective_disposition; } From 220d92f865fdf094616d60e3edeb1aa993132e88 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 5 Jan 2014 11:30:03 +0100 Subject: [PATCH 367/889] changed ID check in leveled list to name. --- apps/opencs/model/tools/referenceablecheck.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index e94f0a401..0460444c5 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -1072,9 +1072,9 @@ template void CSMTools::ReferenceableCheckStage::listCheck( { for (unsigned i = 0; i < somelist.mList.size(); ++i) { - if (somelist.mList[i].mId.empty()) + if (somelist.mList[i].mName.empty()) { - messages.push_back(someid + "|" + somelist.mId + " contains item with empty Id"); + messages.push_back(someid + "|" + somelist.mId + " contains item with empty name"); } if (somelist.mList[i].mLevel < 1) From 46931d85a0fcb3f84d55ac07e6dfcd1107fb8a00 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 5 Jan 2014 11:51:31 +0100 Subject: [PATCH 368/889] changed changelog --- readme.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/readme.txt b/readme.txt index 5a74770f2..21422a8da 100644 --- a/readme.txt +++ b/readme.txt @@ -142,9 +142,7 @@ Bug #1023: Journals doesn't open Bug #1026: Game loses track of torch usage. Bug #1028: Crash on pickup of jug in Unexplored Shipwreck, Upper level Bug #1029: Quick keys menu: Select compatible replacement when tool used up -Bug #1041: Music playback issues on OS X >= 10.9 Bug #1042: TES3 header data wrong encoding -Bug #1045: OS X: deployed OpenCS won't launch Bug #1046: All damaged weaponry is worth 1 gold Bug #1048: Links in "locked" dialogue are still clickable Bug #1052: Using color codes when naming your character actually changes the name's color From 89d8ee62fac1eb01ceb3a4401eb06826b7dfda8a Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 5 Jan 2014 12:12:36 +0100 Subject: [PATCH 369/889] Removing name check from listCheck. Id can't be empty to be sure. --- apps/opencs/model/tools/referenceablecheck.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 0460444c5..187a74627 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -1072,11 +1072,6 @@ template void CSMTools::ReferenceableCheckStage::listCheck( { for (unsigned i = 0; i < somelist.mList.size(); ++i) { - if (somelist.mList[i].mName.empty()) - { - messages.push_back(someid + "|" + somelist.mId + " contains item with empty name"); - } - if (somelist.mList[i].mLevel < 1) { messages.push_back(someid + "|" + somelist.mId + " contains item with non-positive level"); From c1f4a9cb0e45ce8fb6adf55ad43a9c947884d119 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 5 Jan 2014 12:16:25 +0100 Subject: [PATCH 370/889] Added proper id check. --- apps/opencs/model/tools/referenceablecheck.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 187a74627..424f29820 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -1072,6 +1072,11 @@ template void CSMTools::ReferenceableCheckStage::listCheck( { for (unsigned i = 0; i < somelist.mList.size(); ++i) { + if (mReferencables.searchId(somelist.mList[i].mId).first == -1) + { + messages.push_back(someid + "|" + somelist.mId + " contains item without referencable"); + } + if (somelist.mList[i].mLevel < 1) { messages.push_back(someid + "|" + somelist.mId + " contains item with non-positive level"); From ce013315ba89d4150d8291632363a021567b12d8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 5 Jan 2014 15:38:12 +0100 Subject: [PATCH 371/889] Nothing to see here, move along. Fine... it's flying cliff racers. But did you really want to know? --- apps/openmw/mwclass/creature.cpp | 8 ++++++++ apps/openmw/mwclass/creature.hpp | 2 ++ apps/openmw/mwworld/class.cpp | 6 ++++++ apps/openmw/mwworld/class.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 8 ++++++-- 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 4506285ef..98919d6f4 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -413,6 +413,14 @@ namespace MWClass return MWWorld::Ptr(&cell.mCreatures.insert(*ref), &cell); } + bool Creature::isFlying(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mFlags & ESM::Creature::Flies; + } + int Creature::getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name) { if(name == "left") diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 0d8694ff8..34e19ebdc 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -99,6 +99,8 @@ namespace MWClass isActor() const { return true; } + + virtual bool isFlying (const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index ffe81a4ac..0119cdea5 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -372,4 +372,10 @@ namespace MWWorld return newPtr; } + + bool Class::isFlying(const Ptr &ptr) const + { + return false; + } + } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index d737c18a2..08d769fbe 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -306,6 +306,8 @@ namespace MWWorld return false; } + virtual bool isFlying(const MWWorld::Ptr& ptr) const; + static const Class& get (const std::string& key); ///< If there is no class for this \a key, an exception is thrown. diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 621cd75ea..ba76eee88 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1605,13 +1605,17 @@ namespace MWWorld if(!ptr.getClass().isActor()) return false; + if (ptr.getClass().getCreatureStats(ptr).isDead()) + return false; + + if (ptr.getClass().isFlying(ptr)) + return true; + const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); if(stats.getMagicEffects().get(ESM::MagicEffect::Levitate).mMagnitude > 0 && isLevitationEnabled()) return true; - // TODO: Check if flying creature - const OEngine::Physic::PhysicActor *actor = mPhysEngine->getCharacter(ptr.getRefData().getHandle()); if(!actor || !actor->getCollisionMode()) return true; From 873870c7cefee09a36693a0e2f8c18d78eda1084 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 5 Jan 2014 16:52:11 +0100 Subject: [PATCH 372/889] Chainging names to comply our policy. --- .../opencs/model/tools/referenceablecheck.cpp | 106 +++++++++--------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 424f29820..74a7dbb7f 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -25,145 +25,145 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::string >& messages) { //Checks for books, than, when stage is above mBooksSize goes to other checks, with (stage - PrevSum) as stage. - const int BookSize(mReferencables.getBooks().getSize()); + const int bookSize(mReferencables.getBooks().getSize()); - if (stage < BookSize) + if (stage < bookSize) { bookCheck(stage, mReferencables.getBooks(), messages); return; } - stage -= BookSize; + stage -= bookSize; - const int ActivatorSize(mReferencables.getActivators().getSize()); + const int activatorSize(mReferencables.getActivators().getSize()); - if (stage < ActivatorSize) + if (stage < activatorSize) { activatorCheck(stage, mReferencables.getActivators(), messages); return; } - stage -= ActivatorSize; + stage -= activatorSize; - const int PotionSize(mReferencables.getActivators().getSize()); + const int potionSize(mReferencables.getActivators().getSize()); - if (stage < PotionSize) + if (stage < potionSize) { potionCheck(stage, mReferencables.getPotions(), messages); return; } - stage -= PotionSize; + stage -= potionSize; - const int ApparatusSize(mReferencables.getApparati().getSize()); + const int apparatusSize(mReferencables.getApparati().getSize()); - if (stage < ApparatusSize) + if (stage < apparatusSize) { apparatusCheck(stage, mReferencables.getApparati(), messages); return; } - stage -= ApparatusSize; + stage -= apparatusSize; - const int ArmorSize(mReferencables.getArmors().getSize()); + const int armorSize(mReferencables.getArmors().getSize()); - if (stage < ArmorSize) + if (stage < armorSize) { armorCheck(stage, mReferencables.getArmors(), messages); return; } - stage -= ArmorSize; + stage -= armorSize; - const int ClothingSize(mReferencables.getClothing().getSize()); + const int clothingSize(mReferencables.getClothing().getSize()); - if (stage < ClothingSize) + if (stage < clothingSize) { clothingCheck(stage, mReferencables.getClothing(), messages); return; } - stage -= ClothingSize; + stage -= clothingSize; - const int ContainerSize(mReferencables.getContainers().getSize()); + const int containerSize(mReferencables.getContainers().getSize()); - if (stage < ContainerSize) + if (stage < containerSize) { containerCheck(stage, mReferencables.getContainers(), messages); return; } - stage -= ContainerSize; + stage -= containerSize; - const int DoorSize(mReferencables.getDoors().getSize()); + const int doorSize(mReferencables.getDoors().getSize()); - if (stage < DoorSize) + if (stage < doorSize) { doorCheck(stage, mReferencables.getDoors(), messages); return; } - stage -= DoorSize; + stage -= doorSize; - const int IngredientSize(mReferencables.getIngredients().getSize()); + const int ingredientSize(mReferencables.getIngredients().getSize()); - if (stage < IngredientSize) + if (stage < ingredientSize) { ingredientCheck(stage, mReferencables.getIngredients(), messages); return; } - stage -= IngredientSize; + stage -= ingredientSize; - const int CreatureLevListSize(mReferencables.getCreatureLevelledLists().getSize()); + const int creatureLevListSize(mReferencables.getCreatureLevelledLists().getSize()); - if (stage < CreatureLevListSize) + if (stage < creatureLevListSize) { creaturesLevListCheck(stage, mReferencables.getCreatureLevelledLists(), messages); return; } - stage -= CreatureLevListSize; + stage -= creatureLevListSize; - const int ItemLevelledListSize(mReferencables.getItemLevelledList().getSize()); + const int itemLevelledListSize(mReferencables.getItemLevelledList().getSize()); - if (stage < ItemLevelledListSize) + if (stage < itemLevelledListSize) { itemLevelledListCheck(stage, mReferencables.getItemLevelledList(), messages); return; } - stage -= ItemLevelledListSize; + stage -= itemLevelledListSize; - const int LightSize(mReferencables.getLights().getSize()); + const int lightSize(mReferencables.getLights().getSize()); - if (stage < LightSize) + if (stage < lightSize) { lightCheck(stage, mReferencables.getLights(), messages); return; } - stage -= LightSize; + stage -= lightSize; - const int LockpickSize(mReferencables.getLocpicks().getSize()); + const int lockpickSize(mReferencables.getLocpicks().getSize()); - if (stage < LockpickSize) + if (stage < lockpickSize) { lockpickCheck(stage, mReferencables.getLocpicks(), messages); return; } - stage -= LockpickSize; + stage -= lockpickSize; - const int MiscSize(mReferencables.getMiscellaneous().getSize()); + const int miscSize(mReferencables.getMiscellaneous().getSize()); - if (stage < MiscSize) + if (stage < miscSize) { miscCheck(stage, mReferencables.getMiscellaneous(), messages); return; } - stage -= MiscSize; + stage -= miscSize; const int NPCSize(mReferencables.getNPCs().getSize()); @@ -175,39 +175,39 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str stage -= NPCSize; - const int WeaponSize(mReferencables.getWeapons().getSize()); + const int weaponSize(mReferencables.getWeapons().getSize()); - if (stage < WeaponSize) + if (stage < weaponSize) { weaponCheck(stage, mReferencables.getWeapons(), messages); return; } - stage -= WeaponSize; + stage -= weaponSize; - const int ProbeSize(mReferencables.getProbes().getSize()); + const int probeSize(mReferencables.getProbes().getSize()); - if (stage < ProbeSize) + if (stage < probeSize) { probeCheck(stage, mReferencables.getProbes(), messages); return; } - stage -= ProbeSize; + stage -= probeSize; - const int RepairSize(mReferencables.getRepairs().getSize()); + const int repairSize(mReferencables.getRepairs().getSize()); - if (stage < RepairSize) + if (stage < repairSize) { repairCheck(stage, mReferencables.getRepairs(), messages); return; } - stage -= RepairSize; + stage -= repairSize; - const int StaticSize(mReferencables.getStatics().getSize()); + const int staticSize(mReferencables.getStatics().getSize()); - if (stage < StaticSize) + if (stage < staticSize) { staticCheck(stage, mReferencables.getStatics(), messages); return; From 693c39820472ce5b9320f82f645bb2e632a2034d Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 5 Jan 2014 17:05:57 +0100 Subject: [PATCH 373/889] forgot to save the file before comit -_-' --- .../opencs/model/tools/referenceablecheck.cpp | 474 +++++++++--------- 1 file changed, 237 insertions(+), 237 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 74a7dbb7f..7d9210593 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -224,17 +224,17 @@ void CSMTools::ReferenceableCheckStage::bookCheck( const CSMWorld::RefIdDataContainer< ESM::Book >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); - if (baserecord.isDeleted()) + if (baseRecord.isDeleted()) { return; } - const ESM::Book& Book = (dynamic_cast& >(baserecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Book, Book.mId); + const ESM::Book& book = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Book, book.mId); - inventoryItemCheck(Book, messages, id.toString(), true); + inventoryItemCheck(book, messages, id.toString(), true); } void CSMTools::ReferenceableCheckStage::activatorCheck( @@ -242,20 +242,20 @@ void CSMTools::ReferenceableCheckStage::activatorCheck( const CSMWorld::RefIdDataContainer< ESM::Activator >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); - if (baserecord.isDeleted()) + if (baseRecord.isDeleted()) { return; } - const ESM::Activator& Activator = (dynamic_cast& >(baserecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Activator, Activator.mId); + const ESM::Activator& activator = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Activator, activator.mId); //Checking for model, IIRC all activators should have a model - if (Activator.mModel.empty()) + if (activator.mModel.empty()) { - messages.push_back(id.toString() + "|" + Activator.mId + " has no model"); + messages.push_back(id.toString() + "|" + activator.mId + " has no model"); } } @@ -264,17 +264,17 @@ void CSMTools::ReferenceableCheckStage::potionCheck( const CSMWorld::RefIdDataContainer< ESM::Potion >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); - if (baserecord.isDeleted()) + if (baseRecord.isDeleted()) { return; } - const ESM::Potion& Potion = (dynamic_cast& >(baserecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, Potion.mId); + const ESM::Potion& potion = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, potion.mId); - inventoryItemCheck(Potion, messages, id.toString()); + inventoryItemCheck(potion, messages, id.toString()); //IIRC potion can have empty effects list just fine. } @@ -284,20 +284,20 @@ void CSMTools::ReferenceableCheckStage::apparatusCheck( const CSMWorld::RefIdDataContainer< ESM::Apparatus >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); - if (baserecord.isDeleted()) + if (baseRecord.isDeleted()) { return; } - const ESM::Apparatus& Apparatus = (dynamic_cast& >(baserecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Apparatus, Apparatus.mId); + const ESM::Apparatus& apparatus = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Apparatus, apparatus.mId); - inventoryItemCheck(Apparatus, messages, id.toString()); + inventoryItemCheck(apparatus, messages, id.toString()); //checking for quality, 0 → apparatus is basicly useless, any negative → apparatus is harmfull instead of helpfull - toolCheck(Apparatus, messages, id.toString()); + toolCheck(apparatus, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::armorCheck( @@ -305,28 +305,28 @@ void CSMTools::ReferenceableCheckStage::armorCheck( const CSMWorld::RefIdDataContainer< ESM::Armor >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); - if (baserecord.isDeleted()) + if (baseRecord.isDeleted()) { return; } - const ESM::Armor& Armor = (dynamic_cast& >(baserecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Armor, Armor.mId); + const ESM::Armor& armor = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Armor, armor.mId); - inventoryItemCheck(Armor, messages, id.toString(), true); + inventoryItemCheck(armor, messages, id.toString(), true); //checking for armor class, armor should have poistive armor class, but 0 is considered legal - if (Armor.mData.mArmor < 0) + if (armor.mData.mArmor < 0) { - messages.push_back(id.toString() + "|" + Armor.mId + " has negative armor class"); + messages.push_back(id.toString() + "|" + armor.mId + " has negative armor class"); } //checking for health. Only positive numbers are allowed, or 0 is illegal - if (Armor.mData.mHealth <= 0) + if (armor.mData.mHealth <= 0) { - messages.push_back(id.toString() + "|" + Armor.mId + " has non positive health"); + messages.push_back(id.toString() + "|" + armor.mId + " has non positive health"); } } @@ -335,16 +335,16 @@ void CSMTools::ReferenceableCheckStage::clothingCheck( const CSMWorld::RefIdDataContainer< ESM::Clothing >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); - if (baserecord.isDeleted()) + if (baseRecord.isDeleted()) { return; } - const ESM::Clothing& Clothing = (dynamic_cast& >(baserecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Clothing, Clothing.mId); - inventoryItemCheck(Clothing, messages, id.toString(), true); + const ESM::Clothing& clothing = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Clothing, clothing.mId); + inventoryItemCheck(clothing, messages, id.toString(), true); } void CSMTools::ReferenceableCheckStage::containerCheck( @@ -352,32 +352,32 @@ void CSMTools::ReferenceableCheckStage::containerCheck( const CSMWorld::RefIdDataContainer< ESM::Container >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); - if (baserecord.isDeleted()) + if (baseRecord.isDeleted()) { return; } - const ESM::Container& Container = (dynamic_cast& >(baserecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Container, Container.mId); + const ESM::Container& container = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Container, container.mId); //Checking for model, IIRC all containers should have a model - if (Container.mModel.empty()) + if (container.mModel.empty()) { - messages.push_back(id.toString() + "|" + Container.mId + " has no model"); + messages.push_back(id.toString() + "|" + container.mId + " has no model"); } //Checking for capacity (weight) - if (Container.mWeight < 0) //0 is allowed + if (container.mWeight < 0) //0 is allowed { - messages.push_back(id.toString() + "|" + Container.mId + " has negative weight (capacity)"); + messages.push_back(id.toString() + "|" + container.mId + " has negative weight (capacity)"); } //checking for name - if (Container.mName.empty()) + if (container.mName.empty()) { - messages.push_back(id.toString() + "|" + Container.mId + " has an empty name"); + messages.push_back(id.toString() + "|" + container.mId + " has an empty name"); } } @@ -386,95 +386,95 @@ void CSMTools::ReferenceableCheckStage::creatureCheck( const CSMWorld::RefIdDataContainer< ESM::Creature >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); - if (baserecord.isDeleted()) + if (baseRecord.isDeleted()) { return; } - const ESM::Creature& Creature = (dynamic_cast&>(baserecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Creature, Creature.mId); + const ESM::Creature& creature = (dynamic_cast&>(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Creature, creature.mId); - if (Creature.mModel.empty()) + if (creature.mModel.empty()) { - messages.push_back(id.toString() + "|" + Creature.mId + " has no model"); + messages.push_back(id.toString() + "|" + creature.mId + " has no model"); } - if (Creature.mName.empty()) + if (creature.mName.empty()) { - messages.push_back(id.toString() + "|" + Creature.mId + " has an empty name"); + messages.push_back(id.toString() + "|" + creature.mId + " has an empty name"); } //stats checks - if (Creature.mData.mLevel < 1) + if (creature.mData.mLevel < 1) { - messages.push_back(id.toString() + "|" + Creature.mId + " has non-postive level"); + messages.push_back(id.toString() + "|" + creature.mId + " has non-postive level"); } - if (Creature.mData.mStrength < 0) + if (creature.mData.mStrength < 0) { - messages.push_back(id.toString() + "|" + Creature.mId + " has negative strength"); + messages.push_back(id.toString() + "|" + creature.mId + " has negative strength"); } - if (Creature.mData.mIntelligence < 0) + if (creature.mData.mIntelligence < 0) { - messages.push_back(id.toString() + "|" + Creature.mId + " has negative intelligence"); + messages.push_back(id.toString() + "|" + creature.mId + " has negative intelligence"); } - if (Creature.mData.mWillpower < 0) + if (creature.mData.mWillpower < 0) { - messages.push_back(id.toString() + "|" + Creature.mId + " has negative willpower"); + messages.push_back(id.toString() + "|" + creature.mId + " has negative willpower"); } - if (Creature.mData.mAgility < 0) + if (creature.mData.mAgility < 0) { - messages.push_back(id.toString() + "|" + Creature.mId + " has negative agility"); + messages.push_back(id.toString() + "|" + creature.mId + " has negative agility"); } - if (Creature.mData.mSpeed < 0) + if (creature.mData.mSpeed < 0) { - messages.push_back(id.toString() + "|" + Creature.mId + " has negative speed"); + messages.push_back(id.toString() + "|" + creature.mId + " has negative speed"); } - if (Creature.mData.mEndurance < 0) + if (creature.mData.mEndurance < 0) { - messages.push_back(id.toString() + "|" + Creature.mId + " has negative endurance"); + messages.push_back(id.toString() + "|" + creature.mId + " has negative endurance"); } - if (Creature.mData.mPersonality < 0) + if (creature.mData.mPersonality < 0) { - messages.push_back(id.toString() + "|" + Creature.mId + " has negative personality"); + messages.push_back(id.toString() + "|" + creature.mId + " has negative personality"); } - if (Creature.mData.mLuck < 0) + if (creature.mData.mLuck < 0) { - messages.push_back(id.toString() + "|" + Creature.mId + " has negative luck"); + messages.push_back(id.toString() + "|" + creature.mId + " has negative luck"); } - if (Creature.mData.mHealth < 0) + if (creature.mData.mHealth < 0) { - messages.push_back(id.toString() + "|" + Creature.mId + " has negative health"); + messages.push_back(id.toString() + "|" + creature.mId + " has negative health"); } - if (Creature.mData.mSoul < 0) + if (creature.mData.mSoul < 0) { - messages.push_back(id.toString() + "|" + Creature.mId + " has negative soul value"); + messages.push_back(id.toString() + "|" + creature.mId + " has negative soul value"); } for (int i = 0; i < 6; ++i) { - if (Creature.mData.mAttack[i] < 0) + if (creature.mData.mAttack[i] < 0) { - messages.push_back(id.toString() + "|" + Creature.mId + " has negative attack strength"); + messages.push_back(id.toString() + "|" + creature.mId + " has negative attack strength"); break; } } //TODO, find meaning of other values - if (Creature.mData.mGold < 0) //It seems that this is for gold in merchant creatures + if (creature.mData.mGold < 0) //It seems that this is for gold in merchant creatures { - messages.push_back(id.toString() + "|" + Creature.mId + " has negative gold "); + messages.push_back(id.toString() + "|" + creature.mId + " has negative gold "); } } @@ -483,14 +483,14 @@ void CSMTools::ReferenceableCheckStage::doorCheck( const CSMWorld::RefIdDataContainer< ESM::Door >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); - if (baserecord.isDeleted()) + if (baseRecord.isDeleted()) { return; } - const ESM::Door& Door = (dynamic_cast&>(baserecord)).get(); + const ESM::Door& Door = (dynamic_cast&>(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Door, Door.mId); //usual, name or model @@ -512,14 +512,14 @@ void CSMTools::ReferenceableCheckStage::ingredientCheck( const CSMWorld::RefIdDataContainer< ESM::Ingredient >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); - if (baserecord.isDeleted()) + if (baseRecord.isDeleted()) { return; } - const ESM::Ingredient& Ingredient = (dynamic_cast& >(baserecord)).get(); + const ESM::Ingredient& Ingredient = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Ingredient, Ingredient.mId); inventoryItemCheck(Ingredient, messages, id.toString()); @@ -530,14 +530,14 @@ void CSMTools::ReferenceableCheckStage::creaturesLevListCheck( const CSMWorld::RefIdDataContainer< ESM::CreatureLevList >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); - if (baserecord.isDeleted()) + if (baseRecord.isDeleted()) { return; } - const ESM::CreatureLevList& CreatureLevList = (dynamic_cast& >(baserecord)).get(); + const ESM::CreatureLevList& CreatureLevList = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_CreatureLevelledList, CreatureLevList.mId); //CreatureLevList but Type_CreatureLevelledList :/ @@ -549,14 +549,14 @@ void CSMTools::ReferenceableCheckStage::itemLevelledListCheck( const CSMWorld::RefIdDataContainer< ESM::ItemLevList >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); - if (baserecord.isDeleted()) + if (baseRecord.isDeleted()) { return; } - const ESM::ItemLevList& ItemLevList = (dynamic_cast& >(baserecord)).get(); + const ESM::ItemLevList& ItemLevList = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_ItemLevelledList, ItemLevList.mId); listCheck(ItemLevList, messages, id.toString()); @@ -567,30 +567,30 @@ void CSMTools::ReferenceableCheckStage::lightCheck( const CSMWorld::RefIdDataContainer< ESM::Light >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); - if (baserecord.isDeleted()) + if (baseRecord.isDeleted()) { return; } - const ESM::Light& Light = (dynamic_cast& >(baserecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Light, Light.mId); + const ESM::Light& light = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Light, light.mId); - if (Light.mData.mRadius < 0) + if (light.mData.mRadius < 0) { - messages.push_back(id.toString() + "|" + Light.mId + " has negative light radius"); + messages.push_back(id.toString() + "|" + light.mId + " has negative light radius"); } - if (Light.mData.mFlags & ESM::Light::Carry) + if (light.mData.mFlags & ESM::Light::Carry) { - if (Light.mIcon.empty()) //Needs to be checked with carrable flag + if (light.mIcon.empty()) //Needs to be checked with carrable flag { - inventoryItemCheck(Light, messages, id.toString()); + inventoryItemCheck(light, messages, id.toString()); - if (Light.mData.mTime == 0) + if (light.mData.mTime == 0) { - messages.push_back(id.toString() + "|" + Light.mId + " has zero duration"); + messages.push_back(id.toString() + "|" + light.mId + " has zero duration"); } } } @@ -601,19 +601,19 @@ void CSMTools::ReferenceableCheckStage::lockpickCheck( const CSMWorld::RefIdDataContainer< ESM::Lockpick >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); - if (baserecord.isDeleted()) + if (baseRecord.isDeleted()) { return; } - const ESM::Lockpick& Lockpick = (dynamic_cast& >(baserecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Lockpick, Lockpick.mId); + const ESM::Lockpick& lockpick = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Lockpick, lockpick.mId); - inventoryItemCheck(Lockpick, messages, id.toString()); + inventoryItemCheck(lockpick, messages, id.toString()); - toolCheck(Lockpick, messages, id.toString(), true); + toolCheck(lockpick, messages, id.toString(), true); } void CSMTools::ReferenceableCheckStage::miscCheck( @@ -621,17 +621,17 @@ void CSMTools::ReferenceableCheckStage::miscCheck( const CSMWorld::RefIdDataContainer< ESM::Miscellaneous >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); - if (baserecord.isDeleted()) + if (baseRecord.isDeleted()) { return; } - const ESM::Miscellaneous& Miscellaneous = (dynamic_cast& >(baserecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Miscellaneous, Miscellaneous.mId); + const ESM::Miscellaneous& miscellaneous = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Miscellaneous, miscellaneous.mId); - inventoryItemCheck(Miscellaneous, messages, id.toString()); + inventoryItemCheck(miscellaneous, messages, id.toString()); } void CSMTools::ReferenceableCheckStage::npcCheck( @@ -639,22 +639,22 @@ void CSMTools::ReferenceableCheckStage::npcCheck( const CSMWorld::RefIdDataContainer< ESM::NPC >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); - if (baserecord.isDeleted()) + if (baseRecord.isDeleted()) { return; } - const ESM::NPC& NPC = (dynamic_cast& >(baserecord)).get(); + const ESM::NPC& NPC = (dynamic_cast& >(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); + char disposition(NPC.mNpdt52.mDisposition); + char reputation(NPC.mNpdt52.mReputation); + char rank(NPC.mNpdt52.mRank); //Don't know what unknown is for - int Gold(NPC.mNpdt52.mGold); + int gold(NPC.mNpdt52.mGold); if (NPC.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated { @@ -665,10 +665,10 @@ void CSMTools::ReferenceableCheckStage::npcCheck( } level = NPC.mNpdt12.mLevel; - Disposition = NPC.mNpdt12.mDisposition; - Reputation = NPC.mNpdt12.mReputation; - Rank = NPC.mNpdt12.mRank; - Gold = NPC.mNpdt12.mGold; + disposition = NPC.mNpdt12.mDisposition; + reputation = NPC.mNpdt12.mReputation; + rank = NPC.mNpdt12.mRank; + gold = NPC.mNpdt12.mGold; } else { @@ -728,7 +728,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck( messages.push_back(id.toString() + "|" + NPC.mId + " level is non positive"); } - if (Gold < 0) + if (gold < 0) { messages.push_back(id.toString() + "|" + NPC.mId + " gold has negative value"); } @@ -773,19 +773,19 @@ void CSMTools::ReferenceableCheckStage::npcCheck( } } - if (Disposition < 0) + if (disposition < 0) { messages.push_back(id.toString() + "|" + NPC.mId + " has negative disposition"); } - if (Reputation < 0) //It seems that no character in Morrowind.esm have negative reputation. I'm assuming that negative reputation is invalid + if (reputation < 0) //It seems that no character in Morrowind.esm have negative reputation. I'm assuming that negative reputation is invalid { messages.push_back(id.toString() + "|" + NPC.mId + " has negative reputation"); } if (NPC.mFaction.empty() == false) { - if (Rank < 0) + if (rank < 0) { messages.push_back(id.toString() + "|" + NPC.mId + " has negative rank"); } @@ -814,82 +814,82 @@ void CSMTools::ReferenceableCheckStage::weaponCheck( const CSMWorld::RefIdDataContainer< ESM::Weapon >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); - if (baserecord.isDeleted()) + if (baseRecord.isDeleted()) { return; } - const ESM::Weapon& Weapon = (dynamic_cast& >(baserecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Weapon, Weapon.mId); + const ESM::Weapon& weapon = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Weapon, weapon.mId); //TODO, It seems that this stuff for spellcasting is obligatory and In fact We should check if records are present if ( //THOSE ARE HARDCODED! - !(Weapon.mId == "VFX_Hands" || - Weapon.mId == "VFX_Absorb" || - Weapon.mId == "VFX_Reflect" || - Weapon.mId == "VFX_DefaultBolt" || + !(weapon.mId == "VFX_Hands" || + weapon.mId == "VFX_Absorb" || + weapon.mId == "VFX_Reflect" || + weapon.mId == "VFX_DefaultBolt" || //TODO I don't know how to get full list of effects :/ //DANGER!, ACHTUNG! FIXME! The following is the list of the magical bolts, valid for Morrowind.esm. However those are not hardcoded. - Weapon.mId == "magic_bolt" || - Weapon.mId == "shock_bolt" || - Weapon.mId == "shield_bolt" || - Weapon.mId == "VFX_DestructBolt" || - Weapon.mId == "VFX_PoisonBolt" || - Weapon.mId == "VFX_RestoreBolt" || - Weapon.mId == "VFX_AlterationBolt" || - Weapon.mId == "VFX_ConjureBolt" || - Weapon.mId == "VFX_FrostBolt" || - Weapon.mId == "VFX_MysticismBolt" || - Weapon.mId == "VFX_IllusionBolt" || - Weapon.mId == "VFX_Multiple2" || - Weapon.mId == "VFX_Multiple3" || - Weapon.mId == "VFX_Multiple4" || - Weapon.mId == "VFX_Multiple5" || - Weapon.mId == "VFX_Multiple6" || - Weapon.mId == "VFX_Multiple7" || - Weapon.mId == "VFX_Multiple8" || - Weapon.mId == "VFX_Multiple9")) + weapon.mId == "magic_bolt" || + weapon.mId == "shock_bolt" || + weapon.mId == "shield_bolt" || + weapon.mId == "VFX_DestructBolt" || + weapon.mId == "VFX_PoisonBolt" || + weapon.mId == "VFX_RestoreBolt" || + weapon.mId == "VFX_AlterationBolt" || + weapon.mId == "VFX_ConjureBolt" || + weapon.mId == "VFX_FrostBolt" || + weapon.mId == "VFX_MysticismBolt" || + weapon.mId == "VFX_IllusionBolt" || + weapon.mId == "VFX_Multiple2" || + weapon.mId == "VFX_Multiple3" || + weapon.mId == "VFX_Multiple4" || + weapon.mId == "VFX_Multiple5" || + weapon.mId == "VFX_Multiple6" || + weapon.mId == "VFX_Multiple7" || + weapon.mId == "VFX_Multiple8" || + weapon.mId == "VFX_Multiple9")) { - inventoryItemCheck(Weapon, messages, id.toString(), true); + inventoryItemCheck(weapon, messages, id.toString(), true); - if (!(Weapon.mData.mType == ESM::Weapon::MarksmanBow || - Weapon.mData.mType == ESM::Weapon::MarksmanCrossbow || - Weapon.mData.mType == ESM::Weapon::MarksmanThrown || - Weapon.mData.mType == ESM::Weapon::Arrow || - Weapon.mData.mType == ESM::Weapon::Bolt)) + if (!(weapon.mData.mType == ESM::Weapon::MarksmanBow || + weapon.mData.mType == ESM::Weapon::MarksmanCrossbow || + weapon.mData.mType == ESM::Weapon::MarksmanThrown || + weapon.mData.mType == ESM::Weapon::Arrow || + weapon.mData.mType == ESM::Weapon::Bolt)) { - if (Weapon.mData.mSlash[0] > Weapon.mData.mSlash[1]) + if (weapon.mData.mSlash[0] > weapon.mData.mSlash[1]) { - messages.push_back(id.toString() + "|" + Weapon.mId + " has minimum slash damage higher than maximum"); + messages.push_back(id.toString() + "|" + weapon.mId + " has minimum slash damage higher than maximum"); } - if (Weapon.mData.mThrust[0] > Weapon.mData.mThrust[1]) + if (weapon.mData.mThrust[0] > weapon.mData.mThrust[1]) { - messages.push_back(id.toString() + "|" + Weapon.mId + " has minimum thrust damage higher than maximum"); + messages.push_back(id.toString() + "|" + weapon.mId + " has minimum thrust damage higher than maximum"); } } - if (Weapon.mData.mChop[0] > Weapon.mData.mChop[1]) + if (weapon.mData.mChop[0] > weapon.mData.mChop[1]) { - messages.push_back(id.toString() + "|" + Weapon.mId + " has minimum chop damage higher than maximum"); + messages.push_back(id.toString() + "|" + weapon.mId + " has minimum chop damage higher than maximum"); } - if (!(Weapon.mData.mType == ESM::Weapon::Arrow || - Weapon.mData.mType == ESM::Weapon::Bolt || - Weapon.mData.mType == ESM::Weapon::MarksmanThrown)) + if (!(weapon.mData.mType == ESM::Weapon::Arrow || + weapon.mData.mType == ESM::Weapon::Bolt || + weapon.mData.mType == ESM::Weapon::MarksmanThrown)) { //checking of health - if (Weapon.mData.mHealth <= 0) + if (weapon.mData.mHealth <= 0) { - messages.push_back(id.toString() + "|" + Weapon.mId + " has non-positivie health"); + messages.push_back(id.toString() + "|" + weapon.mId + " has non-positivie health"); } - if (Weapon.mData.mReach < 0) + if (weapon.mData.mReach < 0) { - messages.push_back(id.toString() + "|" + Weapon.mId + " has negative reach"); + messages.push_back(id.toString() + "|" + weapon.mId + " has negative reach"); } } } @@ -900,18 +900,18 @@ void CSMTools::ReferenceableCheckStage::probeCheck( const CSMWorld::RefIdDataContainer< ESM::Probe >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); - if (baserecord.isDeleted()) + if (baseRecord.isDeleted()) { return; } - const ESM::Probe& Probe = (dynamic_cast& >(baserecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Probe, Probe.mId); + const ESM::Probe& probe = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Probe, probe.mId); - inventoryItemCheck(Probe, messages, id.toString()); - toolCheck(Probe, messages, id.toString(), true); + inventoryItemCheck(probe, messages, id.toString()); + toolCheck(probe, messages, id.toString(), true); } void CSMTools::ReferenceableCheckStage::repairCheck( @@ -919,18 +919,18 @@ void CSMTools::ReferenceableCheckStage::repairCheck( const CSMWorld::RefIdDataContainer< ESM::Repair >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); - if (baserecord.isDeleted()) + if (baseRecord.isDeleted()) { return; } - const ESM::Repair& Repair = (dynamic_cast& >(baserecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Repair, Repair.mId); + const ESM::Repair& repair = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Repair, repair.mId); - inventoryItemCheck(Repair, messages, id.toString()); - toolCheck(Repair, messages, id.toString(), true); + inventoryItemCheck(repair, messages, id.toString()); + toolCheck(repair, messages, id.toString(), true); } void CSMTools::ReferenceableCheckStage::staticCheck( @@ -938,19 +938,19 @@ void CSMTools::ReferenceableCheckStage::staticCheck( const CSMWorld::RefIdDataContainer< ESM::Static >& records, std::vector< std::string >& messages) { - const CSMWorld::RecordBase& baserecord = records.getRecord(stage); + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); - if (baserecord.isDeleted()) + if (baseRecord.isDeleted()) { return; } - const ESM::Static& Static = (dynamic_cast& >(baserecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Static, Static.mId); + const ESM::Static& static = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Static, static.mId); - if (Static.mModel.empty()) + if (static.mModel.empty()) { - messages.push_back(id.toString() + "|" + Static.mId + " has no model"); + messages.push_back(id.toString() + "|" + static.mId + " has no model"); } } @@ -958,128 +958,128 @@ void CSMTools::ReferenceableCheckStage::staticCheck( //Templates begins here template void CSMTools::ReferenceableCheckStage::inventoryItemCheck( - const ITEM& someitem, + const ITEM& someItem, std::vector< std::string >& messages, - const std::string& someid, bool enchantable) + const std::string& someID, bool enchantable) { - if (someitem.mName.empty()) + if (someItem.mName.empty()) { - messages.push_back(someid + "|" + someitem.mId + " has an empty name"); + messages.push_back(someID + "|" + someItem.mId + " has an empty name"); } //Checking for weight - if (someitem.mData.mWeight < 0) + if (someItem.mData.mWeight < 0) { - messages.push_back(someid + "|" + someitem.mId + " has negative weight"); + messages.push_back(someID + "|" + someItem.mId + " has negative weight"); } //Checking for value - if (someitem.mData.mValue < 0) + if (someItem.mData.mValue < 0) { - messages.push_back(someid + "|" + someitem.mId + " has negative value"); + messages.push_back(someID + "|" + someItem.mId + " has negative value"); } //checking for model - if (someitem.mModel.empty()) + if (someItem.mModel.empty()) { - messages.push_back(someid + "|" + someitem.mId + " has no model"); + messages.push_back(someID + "|" + someItem.mId + " has no model"); } //checking for icon - if (someitem.mIcon.empty()) + if (someItem.mIcon.empty()) { - messages.push_back(someid + "|" + someitem.mId + " has no icon"); + messages.push_back(someID + "|" + someItem.mId + " has no icon"); } if (enchantable) { - if (someitem.mData.mEnchant < 0) + if (someItem.mData.mEnchant < 0) { - messages.push_back(someid + "|" + someitem.mId + " has negative enchantment"); + messages.push_back(someID + "|" + someItem.mId + " has negative enchantment"); } } } template void CSMTools::ReferenceableCheckStage::inventoryItemCheck( - const ITEM& someitem, + const ITEM& someItem, std::vector< std::string >& messages, - const std::string& someid) + const std::string& someID) { - if (someitem.mName.empty()) + if (someItem.mName.empty()) { - messages.push_back(someid + "|" + someitem.mId + " has an empty name"); + messages.push_back(someID + "|" + someItem.mId + " has an empty name"); } //Checking for weight - if (someitem.mData.mWeight < 0) + if (someItem.mData.mWeight < 0) { - messages.push_back(someid + "|" + someitem.mId + " has negative weight"); + messages.push_back(someID + "|" + someItem.mId + " has negative weight"); } //Checking for value - if (someitem.mData.mValue < 0) + if (someItem.mData.mValue < 0) { - messages.push_back(someid + "|" + someitem.mId + " has negative value"); + messages.push_back(someID + "|" + someItem.mId + " has negative value"); } //checking for model - if (someitem.mModel.empty()) + if (someItem.mModel.empty()) { - messages.push_back(someid + "|" + someitem.mId + " has no model"); + messages.push_back(someID + "|" + someItem.mId + " has no model"); } //checking for icon - if (someitem.mIcon.empty()) + if (someItem.mIcon.empty()) { - messages.push_back(someid + "|" + someitem.mId + " has no icon"); + messages.push_back(someID + "|" + someItem.mId + " has no icon"); } } template void CSMTools::ReferenceableCheckStage::toolCheck( - const TOOL& sometool, + const TOOL& someTool, std::vector< std::string >& messages, - const std::string& someid, bool canbebroken) + const std::string& someID, bool canBeBroken) { - if (sometool.mData.mQuality <= 0) + if (someTool.mData.mQuality <= 0) { - messages.push_back(someid + "|" + sometool.mId + " has non-positive quality"); + messages.push_back(someID + "|" + someTool.mId + " has non-positive quality"); } - if (canbebroken) + if (canBeBroken) { - if (sometool.mData.mUses <= 0) + if (someTool.mData.mUses <= 0) { - messages.push_back(someid + "|" + sometool.mId + " has non-positive uses count"); + messages.push_back(someID + "|" + someTool.mId + " has non-positive uses count"); } } } template void CSMTools::ReferenceableCheckStage::toolCheck( - const TOOL& sometool, + const TOOL& someTool, std::vector< std::string >& messages, - const std::string& someid) + const std::string& someID) { - if (sometool.mData.mQuality <= 0) + if (someTool.mData.mQuality <= 0) { - messages.push_back(someid + "|" + sometool.mId + " has non-positive quality"); + messages.push_back(someID + "|" + someTool.mId + " has non-positive quality"); } } template void CSMTools::ReferenceableCheckStage::listCheck( - const LIST& somelist, + const LIST& someList, std::vector< std::string >& messages, - const std::string& someid) + const std::string& someID) { - for (unsigned i = 0; i < somelist.mList.size(); ++i) + for (unsigned i = 0; i < someList.mList.size(); ++i) { - if (mReferencables.searchId(somelist.mList[i].mId).first == -1) + if (mReferencables.searchId(someList.mList[i].mId).first == -1) { - messages.push_back(someid + "|" + somelist.mId + " contains item without referencable"); + messages.push_back(someid + "|" + someList.mId + " contains item without referencable"); } - if (somelist.mList[i].mLevel < 1) + if (someList.mList[i].mLevel < 1) { - messages.push_back(someid + "|" + somelist.mId + " contains item with non-positive level"); + messages.push_back(someid + "|" + someList.mId + " contains item with non-positive level"); } } } From 80d424591f2ba3b0093d02a631285878eb0655e7 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 5 Jan 2014 17:28:47 +0100 Subject: [PATCH 374/889] npc instead of NPC. --- .../opencs/model/tools/referenceablecheck.cpp | 120 +++++++++--------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 7d9210593..64260e0a2 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -165,15 +165,15 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str stage -= miscSize; - const int NPCSize(mReferencables.getNPCs().getSize()); + const int npcSize(mReferencables.getNPCs().getSize()); - if (stage < NPCSize) + if (stage < npcSize) { npcCheck(stage, mReferencables.getNPCs(), messages); return; } - stage -= NPCSize; + stage -= npcSize; const int weaponSize(mReferencables.getWeapons().getSize()); @@ -646,113 +646,113 @@ void CSMTools::ReferenceableCheckStage::npcCheck( return; } - const ESM::NPC& NPC = (dynamic_cast& >(baseRecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Npc, NPC.mId); + const ESM::NPC& npc = (dynamic_cast& >(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.mNpdt52.mLevel); + char disposition(npc.mNpdt52.mDisposition); + char reputation(npc.mNpdt52.mReputation); + char rank(npc.mNpdt52.mRank); //Don't know what unknown is for - int gold(NPC.mNpdt52.mGold); + int gold(npc.mNpdt52.mGold); - if (NPC.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated + if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated { - if ((NPC.mFlags & ESM::NPC::Autocalc) == 0) //0x0008 = autocalculated flag + if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0008 = autocalculated flag { - messages.push_back(id.toString() + "|" + NPC.mId + " mNpdtType or flags mismatch!"); //should not happend? + messages.push_back(id.toString() + "|" + npc.mId + " mNpdtType or flags mismatch!"); //should not happend? return; } - level = NPC.mNpdt12.mLevel; - disposition = NPC.mNpdt12.mDisposition; - reputation = NPC.mNpdt12.mReputation; - rank = NPC.mNpdt12.mRank; - gold = NPC.mNpdt12.mGold; + level = npc.mNpdt12.mLevel; + disposition = npc.mNpdt12.mDisposition; + reputation = npc.mNpdt12.mReputation; + rank = npc.mNpdt12.mRank; + gold = npc.mNpdt12.mGold; } else { - if (NPC.mNpdt52.mMana < 0) + if (npc.mNpdt52.mMana < 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " mana has negative value"); + messages.push_back(id.toString() + "|" + npc.mId + " mana has negative value"); } - if (NPC.mNpdt52.mFatigue < 0) + if (npc.mNpdt52.mFatigue < 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " fatigue has negative value"); + messages.push_back(id.toString() + "|" + npc.mId + " fatigue has negative value"); } - if (NPC.mNpdt52.mAgility == 0) + if (npc.mNpdt52.mAgility == 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " agility has zero value"); + messages.push_back(id.toString() + "|" + npc.mId + " agility has zero value"); } - if (NPC.mNpdt52.mEndurance == 0) + if (npc.mNpdt52.mEndurance == 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " endurance has zero value"); + messages.push_back(id.toString() + "|" + npc.mId + " endurance has zero value"); } - if (NPC.mNpdt52.mIntelligence == 0) + if (npc.mNpdt52.mIntelligence == 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " intelligence has zero value"); + messages.push_back(id.toString() + "|" + npc.mId + " intelligence has zero value"); } - if (NPC.mNpdt52.mLuck == 0) + if (npc.mNpdt52.mLuck == 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " luck has zero value"); + messages.push_back(id.toString() + "|" + npc.mId + " luck has zero value"); } - if (NPC.mNpdt52.mPersonality == 0) + if (npc.mNpdt52.mPersonality == 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " personality has zero value"); + messages.push_back(id.toString() + "|" + npc.mId + " personality has zero value"); } - if (NPC.mNpdt52.mStrength == 0) + if (npc.mNpdt52.mStrength == 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " strength has zero value"); + messages.push_back(id.toString() + "|" + npc.mId + " strength has zero value"); } - if (NPC.mNpdt52.mSpeed == 0) + if (npc.mNpdt52.mSpeed == 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " speed has zero value"); + messages.push_back(id.toString() + "|" + npc.mId + " speed has zero value"); } - if (NPC.mNpdt52.mWillpower == 0) + if (npc.mNpdt52.mWillpower == 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " willpower has zero value"); + messages.push_back(id.toString() + "|" + npc.mId + " willpower has zero value"); } } if (level < 1) { - messages.push_back(id.toString() + "|" + NPC.mId + " level is non positive"); + messages.push_back(id.toString() + "|" + npc.mId + " level is non positive"); } if (gold < 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " gold has negative value"); + messages.push_back(id.toString() + "|" + npc.mId + " gold has negative value"); } - if (NPC.mName.empty()) + if (npc.mName.empty()) { - messages.push_back(id.toString() + "|" + NPC.mId + " has any empty name"); + messages.push_back(id.toString() + "|" + npc.mId + " has any empty name"); } - if (NPC.mClass.empty()) + if (npc.mClass.empty()) { - messages.push_back(id.toString() + "|" + NPC.mId + " has any empty class"); + messages.push_back(id.toString() + "|" + npc.mId + " has any empty class"); } else //checking if there is such class { - if (mClasses.searchId(NPC.mClass) == -1) + if (mClasses.searchId(npc.mClass) == -1) { - messages.push_back(id.toString() + "|" + NPC.mId + " has invalid class"); + messages.push_back(id.toString() + "|" + npc.mId + " has invalid class"); } } - if (NPC.mRace.empty()) + if (npc.mRace.empty()) { - messages.push_back(id.toString() + "|" + NPC.mId + " has any empty race"); + messages.push_back(id.toString() + "|" + npc.mId + " has any empty race"); } else //checking if there is a such race { @@ -760,7 +760,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck( for (int i = 0; i < mRaces.getSize(); ++i) { - if (dynamic_cast(mRaces.getRecord(i).get()).mName == NPC.mRace) //mId in class, mName for race. Stupid. + if (dynamic_cast(mRaces.getRecord(i).get()).mName == npc.mRace) //mId in class, mName for race. Stupid. { nosuchrace = false; break; @@ -769,41 +769,41 @@ void CSMTools::ReferenceableCheckStage::npcCheck( if (nosuchrace) { - messages.push_back(id.toString() + "|" + NPC.mId + " has invalid race"); + messages.push_back(id.toString() + "|" + npc.mId + " has invalid race"); } } if (disposition < 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " has negative disposition"); + messages.push_back(id.toString() + "|" + npc.mId + " has negative disposition"); } if (reputation < 0) //It seems that no character in Morrowind.esm have negative reputation. I'm assuming that negative reputation is invalid { - messages.push_back(id.toString() + "|" + NPC.mId + " has negative reputation"); + messages.push_back(id.toString() + "|" + npc.mId + " has negative reputation"); } - if (NPC.mFaction.empty() == false) + if (npc.mFaction.empty() == false) { if (rank < 0) { - messages.push_back(id.toString() + "|" + NPC.mId + " has negative rank"); + messages.push_back(id.toString() + "|" + npc.mId + " has negative rank"); } - if (mFactions.searchId(NPC.mFaction) == -1) + if (mFactions.searchId(npc.mFaction) == -1) { - messages.push_back(id.toString() + "|" + NPC.mId + " has invalid faction"); + messages.push_back(id.toString() + "|" + npc.mId + " has invalid faction"); } } - if (NPC.mHead.empty()) + if (npc.mHead.empty()) { - messages.push_back(id.toString() + "|" + NPC.mId + " has no head"); + messages.push_back(id.toString() + "|" + npc.mId + " has no head"); } - if (NPC.mHair.empty()) + if (npc.mHair.empty()) { - messages.push_back(id.toString() + "|" + NPC.mId + " has no hair"); + messages.push_back(id.toString() + "|" + npc.mId + " has no hair"); } //TODO: reputation, Disposition, rank, everything else From 43387063079d446429e916260b4fc70559e7ae0d Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 5 Jan 2014 17:31:54 +0100 Subject: [PATCH 375/889] static is a keyword. renamed static to staticElement --- apps/opencs/model/tools/referenceablecheck.cpp | 12 ++++++------ apps/opencs/model/tools/referenceablecheck.hpp | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 64260e0a2..73c88a507 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -945,12 +945,12 @@ void CSMTools::ReferenceableCheckStage::staticCheck( return; } - const ESM::Static& static = (dynamic_cast& >(baseRecord)).get(); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Static, static.mId); + const ESM::Static& staticElement = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Static, staticElement.mId); - if (static.mModel.empty()) + if (staticElement.mModel.empty()) { - messages.push_back(id.toString() + "|" + static.mId + " has no model"); + messages.push_back(id.toString() + "|" + staticElement.mId + " has no model"); } } @@ -1074,12 +1074,12 @@ template void CSMTools::ReferenceableCheckStage::listCheck( { if (mReferencables.searchId(someList.mList[i].mId).first == -1) { - messages.push_back(someid + "|" + someList.mId + " contains item without referencable"); + messages.push_back(someID + "|" + someList.mId + " contains item without referencable"); } if (someList.mList[i].mLevel < 1) { - messages.push_back(someid + "|" + someList.mId + " contains item with non-positive level"); + messages.push_back(someID + "|" + someList.mId + " contains item with non-positive level"); } } } diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index feeb32e6a..217e77a05 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -43,7 +43,7 @@ namespace CSMTools template void inventoryItemCheck(const ITEM& someitem, std::vector& messages, const std::string& someid); //for non-enchantable items. template void toolCheck(const TOOL& sometool, std::vector& messages, const std::string& someid, bool canbebroken); //for tools with uses. template void toolCheck(const TOOL& sometool, std::vector& messages, const std::string& someid); //for tools without uses. - template void listCheck(const LIST& some, std::vector< std::string >& messages, const std::string& someid); + template void listCheck(const LIST& somelist, std::vector< std::string >& messages, const std::string& Som); const CSMWorld::RefIdData& mReferencables; const CSMWorld::IdCollection& mRaces; From c69814ed14f0fa386847c28f012e728d34063bfb Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 5 Jan 2014 18:00:49 +0100 Subject: [PATCH 376/889] corrected one, additional name to follow policy --- apps/opencs/model/tools/referenceablecheck.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 73c88a507..92d116244 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -756,18 +756,18 @@ void CSMTools::ReferenceableCheckStage::npcCheck( } else //checking if there is a such race { - bool nosuchrace(true); + bool noSuchRace(true); for (int i = 0; i < mRaces.getSize(); ++i) { if (dynamic_cast(mRaces.getRecord(i).get()).mName == npc.mRace) //mId in class, mName for race. Stupid. { - nosuchrace = false; + noSuchRace = false; break; } } - if (nosuchrace) + if (noSuchRace) { messages.push_back(id.toString() + "|" + npc.mId + " has invalid race"); } From b8e93437645b16995ba6e28694e5392aa9067187 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 5 Jan 2014 16:52:06 +0100 Subject: [PATCH 377/889] Extend NifOverrides to allow material overrides, useful for texture modding e.g. adding normal maps without having to edit the affected meshes --- apps/openmw/engine.cpp | 16 ++++-- components/nifogre/material.cpp | 7 ++- components/nifoverrides/nifoverrides.cpp | 72 +++++++++++++++++++----- components/nifoverrides/nifoverrides.hpp | 19 ++++++- 4 files changed, 90 insertions(+), 24 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 3c2423345..2a5ab3f07 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -309,12 +309,16 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings) // load nif overrides NifOverrides::Overrides nifOverrides; - if (boost::filesystem::exists(mCfgMgr.getLocalPath().string() + "/transparency-overrides.cfg")) - nifOverrides.loadTransparencyOverrides(mCfgMgr.getLocalPath().string() + "/transparency-overrides.cfg"); - else if (boost::filesystem::exists(mCfgMgr.getGlobalPath().string() + "/transparency-overrides.cfg")) - nifOverrides.loadTransparencyOverrides(mCfgMgr.getGlobalPath().string() + "/transparency-overrides.cfg"); - - settings.setBool("hardware cursors", "GUI", true); + std::string transparencyOverrides = "/transparency-overrides.cfg"; + std::string materialOverrides = "/material-overrides.cfg"; + if (boost::filesystem::exists(mCfgMgr.getLocalPath().string() + transparencyOverrides)) + nifOverrides.loadTransparencyOverrides(mCfgMgr.getLocalPath().string() + transparencyOverrides); + else if (boost::filesystem::exists(mCfgMgr.getGlobalPath().string() + transparencyOverrides)) + nifOverrides.loadTransparencyOverrides(mCfgMgr.getGlobalPath().string() + transparencyOverrides); + if (boost::filesystem::exists(mCfgMgr.getLocalPath().string() + materialOverrides)) + nifOverrides.loadMaterialOverrides(mCfgMgr.getLocalPath().string() + materialOverrides); + else if (boost::filesystem::exists(mCfgMgr.getGlobalPath().string() + materialOverrides)) + nifOverrides.loadMaterialOverrides(mCfgMgr.getGlobalPath().string() + materialOverrides); return settingspath; } diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index 8ae86b64a..be6ccbed6 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -157,7 +157,6 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, ctrls = ctrls->next; } } - needTangents = !texName[Nif::NiTexturingProperty::BumpTexture].empty(); // Alpha modifiers if(alphaprop) @@ -407,6 +406,12 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, instance->setProperty("depth_write", sh::makeProperty(new sh::StringValue(((depthFlags>>1)&1) ? "on" : "off"))); // depth_func??? + if (!texName[0].empty()) + NifOverrides::Overrides::getMaterialOverrides(texName[0], instance); + + // Don't use texName, as it may be overridden + needTangents = !sh::retrieveValue(instance->getProperty("normalMap"), instance).get().empty(); + return name; } diff --git a/components/nifoverrides/nifoverrides.cpp b/components/nifoverrides/nifoverrides.cpp index 191b4ac2f..972cf1b84 100644 --- a/components/nifoverrides/nifoverrides.cpp +++ b/components/nifoverrides/nifoverrides.cpp @@ -4,14 +4,51 @@ #include <../components/misc/stringops.hpp> +#include "../extern/shiny/Main/MaterialInstance.hpp" + +#include + using namespace NifOverrides; -Ogre::ConfigFile Overrides::mTransparencyOverrides = Ogre::ConfigFile(); +Overrides::TransparencyOverrideMap Overrides::mTransparencyOverrides = Overrides::TransparencyOverrideMap(); +Overrides::MaterialOverrideMap Overrides::mMaterialOverrides = Overrides::MaterialOverrideMap(); void Overrides::loadTransparencyOverrides (const std::string& file) { - mTransparencyOverrides.load(file); + Ogre::ConfigFile cf; + cf.load(file); + + Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator(); + while (seci.hasMoreElements()) + { + Ogre::String sectionName = seci.peekNextKey(); + mTransparencyOverrides[sectionName] = + Ogre::StringConverter::parseInt(cf.getSetting("alphaRejectValue", sectionName)); + seci.getNext(); + } +} + +void Overrides::loadMaterialOverrides(const std::string &file) +{ + Ogre::ConfigFile cf; + cf.load(file); + + Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator(); + while (seci.hasMoreElements()) + { + Ogre::String sectionName = seci.peekNextKey(); + + Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext(); + Ogre::ConfigFile::SettingsMultiMap::iterator i; + std::map overrides; + for (i = settings->begin(); i != settings->end(); ++i) + { + overrides[i->first] = i->second; + } + mMaterialOverrides[sectionName] = overrides; + } + } TransparencyResult Overrides::getTransparencyOverride(const std::string& texture) @@ -19,20 +56,25 @@ TransparencyResult Overrides::getTransparencyOverride(const std::string& texture TransparencyResult result; result.first = false; - std::string tex = texture; - Misc::StringUtils::toLower(tex); - - Ogre::ConfigFile::SectionIterator seci = mTransparencyOverrides.getSectionIterator(); - while (seci.hasMoreElements()) + TransparencyOverrideMap::iterator it = mTransparencyOverrides.find(Misc::StringUtils::lowerCase(texture)); + if (it != mTransparencyOverrides.end()) { - Ogre::String sectionName = seci.peekNextKey(); - if (sectionName == tex) - { - result.first = true; - result.second = Ogre::StringConverter::parseInt(mTransparencyOverrides.getSetting("alphaRejectValue", sectionName)); - break; - } - seci.getNext(); + result.first = true; + result.second = it->second; } + return result; } + +void Overrides::getMaterialOverrides(const std::string &texture, sh::MaterialInstance* material) +{ + MaterialOverrideMap::iterator it = mMaterialOverrides.find(Misc::StringUtils::lowerCase(texture)); + if (it != mMaterialOverrides.end()) + { + const std::map& overrides = it->second; + for (std::map::const_iterator it = overrides.begin(); it != overrides.end(); ++it) + { + material->setProperty(it->first, sh::makeProperty(it->second)); + } + } +} diff --git a/components/nifoverrides/nifoverrides.hpp b/components/nifoverrides/nifoverrides.hpp index ba2e4cc3c..edff876d4 100644 --- a/components/nifoverrides/nifoverrides.hpp +++ b/components/nifoverrides/nifoverrides.hpp @@ -3,19 +3,34 @@ #include +namespace sh +{ + class MaterialInstance; +} + namespace NifOverrides { typedef std::pair TransparencyResult; - /// \brief provide overrides for some model / texture properties that bethesda has chosen poorly + /// Allows to provide overrides for some material properties in NIF files. + /// NIFs are a bit limited in that they don't allow specifying a material externally, which is + /// painful for texture modding. + /// We also use this to patch up transparency settings in certain NIFs that bethesda has chosen poorly. class Overrides { public: - static Ogre::ConfigFile mTransparencyOverrides; + typedef std::map TransparencyOverrideMap; + static TransparencyOverrideMap mTransparencyOverrides; + + typedef std::map > MaterialOverrideMap; + static MaterialOverrideMap mMaterialOverrides; + void loadTransparencyOverrides (const std::string& file); + void loadMaterialOverrides (const std::string& file); static TransparencyResult getTransparencyOverride(const std::string& texture); + static void getMaterialOverrides (const std::string& texture, sh::MaterialInstance* instance); }; } From c004fb778f69abacfc77e2f056db07a65f688fde Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 5 Jan 2014 18:22:29 +0100 Subject: [PATCH 378/889] Get rid of underscore defines. They are reserved by the standard. --- apps/openmw/mwinput/inputmanagerimp.hpp | 4 ++-- apps/openmw/mwrender/activatoranimation.hpp | 4 ++-- apps/openmw/mwrender/actors.hpp | 4 ++-- apps/openmw/mwrender/animation.hpp | 4 ++-- apps/openmw/mwrender/creatureanimation.hpp | 4 ++-- apps/openmw/mwrender/debugging.hpp | 4 ++-- apps/openmw/mwrender/globalmap.hpp | 4 ++-- apps/openmw/mwrender/localmap.hpp | 4 ++-- apps/openmw/mwrender/npcanimation.hpp | 4 ++-- apps/openmw/mwrender/objects.hpp | 4 ++-- apps/openmw/mwrender/occlusionquery.hpp | 4 ++-- apps/openmw/mwrender/renderinginterface.hpp | 4 ++-- apps/openmw/mwrender/renderingmanager.hpp | 4 ++-- components/settings/settings.hpp | 4 ++-- extern/oics/ICSChannel.h | 6 +++--- extern/oics/ICSControl.h | 4 ++-- extern/oics/ICSInputControlSystem.h | 4 ++-- extern/sdl4ogre/OISCompat.h | 4 ++-- extern/sdl4ogre/cursormanager.hpp | 4 ++-- extern/sdl4ogre/sdlcursormanager.hpp | 4 ++-- extern/sdl4ogre/sdlinputwrapper.hpp | 4 ++-- libs/openengine/bullet/BtOgreExtras.h | 4 ++-- libs/openengine/bullet/BtOgreGP.h | 4 ++-- libs/openengine/bullet/BtOgrePG.h | 4 ++-- libs/openengine/bullet/BulletShapeLoader.h | 4 ++-- 25 files changed, 51 insertions(+), 51 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index d4693ff28..4eaee9b69 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -1,5 +1,5 @@ -#ifndef _MWINPUT_MWINPUTMANAGERIMP_H -#define _MWINPUT_MWINPUTMANAGERIMP_H +#ifndef MWINPUT_MWINPUTMANAGERIMP_H +#define MWINPUT_MWINPUTMANAGERIMP_H #include "../mwgui/mode.hpp" diff --git a/apps/openmw/mwrender/activatoranimation.hpp b/apps/openmw/mwrender/activatoranimation.hpp index f3ea38f44..eb3e5815e 100644 --- a/apps/openmw/mwrender/activatoranimation.hpp +++ b/apps/openmw/mwrender/activatoranimation.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDER_ACTIVATORANIMATION_H -#define _GAME_RENDER_ACTIVATORANIMATION_H +#ifndef GAME_RENDER_ACTIVATORANIMATION_H +#define GAME_RENDER_ACTIVATORANIMATION_H #include "animation.hpp" diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index d91321843..af71525fa 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDER_ACTORS_H -#define _GAME_RENDER_ACTORS_H +#ifndef GAME_RENDER_ACTORS_H +#define GAME_RENDER_ACTORS_H #include diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 72d1c100e..573f769c3 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDER_ANIMATION_H -#define _GAME_RENDER_ANIMATION_H +#ifndef GAME_RENDER_ANIMATION_H +#define GAME_RENDER_ANIMATION_H #include #include diff --git a/apps/openmw/mwrender/creatureanimation.hpp b/apps/openmw/mwrender/creatureanimation.hpp index 0c277d198..a902df5d8 100644 --- a/apps/openmw/mwrender/creatureanimation.hpp +++ b/apps/openmw/mwrender/creatureanimation.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDER_CREATUREANIMATION_H -#define _GAME_RENDER_CREATUREANIMATION_H +#ifndef GAME_RENDER_CREATUREANIMATION_H +#define GAME_RENDER_CREATUREANIMATION_H #include "animation.hpp" diff --git a/apps/openmw/mwrender/debugging.hpp b/apps/openmw/mwrender/debugging.hpp index 4a574017c..39be34cb0 100644 --- a/apps/openmw/mwrender/debugging.hpp +++ b/apps/openmw/mwrender/debugging.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDER_MWSCENE_H -#define _GAME_RENDER_MWSCENE_H +#ifndef GAME_RENDER_MWSCENE_H +#define GAME_RENDER_MWSCENE_H #include #include diff --git a/apps/openmw/mwrender/globalmap.hpp b/apps/openmw/mwrender/globalmap.hpp index dd3787b62..aad9adcc4 100644 --- a/apps/openmw/mwrender/globalmap.hpp +++ b/apps/openmw/mwrender/globalmap.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDER_GLOBALMAP_H -#define _GAME_RENDER_GLOBALMAP_H +#ifndef GAME_RENDER_GLOBALMAP_H +#define GAME_RENDER_GLOBALMAP_H #include diff --git a/apps/openmw/mwrender/localmap.hpp b/apps/openmw/mwrender/localmap.hpp index 538489640..638469d2d 100644 --- a/apps/openmw/mwrender/localmap.hpp +++ b/apps/openmw/mwrender/localmap.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDER_LOCALMAP_H -#define _GAME_RENDER_LOCALMAP_H +#ifndef GAME_RENDER_LOCALMAP_H +#define GAME_RENDER_LOCALMAP_H #include diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 8edcc04be..6f0403b9d 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDER_NPCANIMATION_H -#define _GAME_RENDER_NPCANIMATION_H +#ifndef GAME_RENDER_NPCANIMATION_H +#define GAME_RENDER_NPCANIMATION_H #include "animation.hpp" diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 8a5074503..665a1e657 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDER_OBJECTS_H -#define _GAME_RENDER_OBJECTS_H +#ifndef GAME_RENDER_OBJECTS_H +#define GAME_RENDER_OBJECTS_H #include #include diff --git a/apps/openmw/mwrender/occlusionquery.hpp b/apps/openmw/mwrender/occlusionquery.hpp index 983361c18..6974f37b9 100644 --- a/apps/openmw/mwrender/occlusionquery.hpp +++ b/apps/openmw/mwrender/occlusionquery.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_OCCLUSION_QUERY_H -#define _GAME_OCCLUSION_QUERY_H +#ifndef GAME_OCCLUSION_QUERY_H +#define GAME_OCCLUSION_QUERY_H #include #include diff --git a/apps/openmw/mwrender/renderinginterface.hpp b/apps/openmw/mwrender/renderinginterface.hpp index 8ae2c0f8f..02f3c804a 100644 --- a/apps/openmw/mwrender/renderinginterface.hpp +++ b/apps/openmw/mwrender/renderinginterface.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDERING_INTERFACE_H -#define _GAME_RENDERING_INTERFACE_H +#ifndef GAME_RENDERING_INTERFACE_H +#define GAME_RENDERING_INTERFACE_H namespace MWRender { diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index b13e546e8..37488b157 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDERING_MANAGER_H -#define _GAME_RENDERING_MANAGER_H +#ifndef GAME_RENDERING_MANAGER_H +#define GAME_RENDERING_MANAGER_H #include "sky.hpp" #include "debugging.hpp" diff --git a/components/settings/settings.hpp b/components/settings/settings.hpp index e9858eb94..b7c7d59a9 100644 --- a/components/settings/settings.hpp +++ b/components/settings/settings.hpp @@ -1,5 +1,5 @@ -#ifndef _COMPONENTS_SETTINGS_H -#define _COMPONENTS_SETTINGS_H +#ifndef COMPONENTS_SETTINGS_H +#define COMPONENTS_SETTINGS_H #include diff --git a/extern/oics/ICSChannel.h b/extern/oics/ICSChannel.h index f98f0d94d..5ec6cd575 100644 --- a/extern/oics/ICSChannel.h +++ b/extern/oics/ICSChannel.h @@ -24,8 +24,8 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------- */ -#ifndef _Channel_H_ -#define _Channel_H_ +#ifndef OICS_Channel_H_ +#define OICS_Channel_H_ #include "ICSPrerequisites.h" @@ -119,4 +119,4 @@ namespace ICS } -#endif \ No newline at end of file +#endif diff --git a/extern/oics/ICSControl.h b/extern/oics/ICSControl.h index 7939c86b9..ebf75a3fe 100644 --- a/extern/oics/ICSControl.h +++ b/extern/oics/ICSControl.h @@ -24,8 +24,8 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------- */ -#ifndef _Control_H_ -#define _Control_H_ +#ifndef OICS_Control_H_ +#define OICS_Control_H_ #include "ICSPrerequisites.h" diff --git a/extern/oics/ICSInputControlSystem.h b/extern/oics/ICSInputControlSystem.h index f42f9c0b5..907cba5fc 100644 --- a/extern/oics/ICSInputControlSystem.h +++ b/extern/oics/ICSInputControlSystem.h @@ -24,8 +24,8 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------- */ -#ifndef _InputControlSystem_H_ -#define _InputControlSystem_H_ +#ifndef OICS_InputControlSystem_H_ +#define OICS_InputControlSystem_H_ #include "ICSPrerequisites.h" diff --git a/extern/sdl4ogre/OISCompat.h b/extern/sdl4ogre/OISCompat.h index 3cffa143d..a0acc5837 100644 --- a/extern/sdl4ogre/OISCompat.h +++ b/extern/sdl4ogre/OISCompat.h @@ -1,5 +1,5 @@ -#ifndef _OIS_SDL_COMPAT_H -#define _OIS_SDL_COMPAT_H +#ifndef OIS_SDL_COMPAT_H +#define OIS_SDL_COMPAT_H #include #include diff --git a/extern/sdl4ogre/cursormanager.hpp b/extern/sdl4ogre/cursormanager.hpp index 35ec92a70..3036b236b 100644 --- a/extern/sdl4ogre/cursormanager.hpp +++ b/extern/sdl4ogre/cursormanager.hpp @@ -1,5 +1,5 @@ -#ifndef _SDL4OGRE_CURSOR_MANAGER_H -#define _SDL4OGRE_CURSOR_MANAGER_H +#ifndef SDL4OGRE_CURSOR_MANAGER_H +#define SDL4OGRE_CURSOR_MANAGER_H #include #include diff --git a/extern/sdl4ogre/sdlcursormanager.hpp b/extern/sdl4ogre/sdlcursormanager.hpp index 7ba69f013..7e3e59b4a 100644 --- a/extern/sdl4ogre/sdlcursormanager.hpp +++ b/extern/sdl4ogre/sdlcursormanager.hpp @@ -1,5 +1,5 @@ -#ifndef _SDL4OGRE_CURSORMANAGER_H -#define _SDL4OGRE_CURSORMANAGER_H +#ifndef SDL4OGRE_CURSORMANAGER_H +#define SDL4OGRE_CURSORMANAGER_H #include diff --git a/extern/sdl4ogre/sdlinputwrapper.hpp b/extern/sdl4ogre/sdlinputwrapper.hpp index a2b698f86..f08e3eff6 100644 --- a/extern/sdl4ogre/sdlinputwrapper.hpp +++ b/extern/sdl4ogre/sdlinputwrapper.hpp @@ -1,5 +1,5 @@ -#ifndef _SDL4OGRE_SDLINPUTWRAPPER_H -#define _SDL4OGRE_SDLINPUTWRAPPER_H +#ifndef SDL4OGRE_SDLINPUTWRAPPER_H +#define SDL4OGRE_SDLINPUTWRAPPER_H #include diff --git a/libs/openengine/bullet/BtOgreExtras.h b/libs/openengine/bullet/BtOgreExtras.h index b20a3ff98..9572b8a7b 100644 --- a/libs/openengine/bullet/BtOgreExtras.h +++ b/libs/openengine/bullet/BtOgreExtras.h @@ -13,8 +13,8 @@ * ===================================================================================== */ -#ifndef _BtOgreShapes_H_ -#define _BtOgreShapes_H_ +#ifndef BtOgreShapes_H_ +#define BtOgreShapes_H_ #include "btBulletDynamicsCommon.h" #include "OgreSimpleRenderable.h" diff --git a/libs/openengine/bullet/BtOgreGP.h b/libs/openengine/bullet/BtOgreGP.h index 4ce2f181e..dde606a4f 100644 --- a/libs/openengine/bullet/BtOgreGP.h +++ b/libs/openengine/bullet/BtOgreGP.h @@ -14,8 +14,8 @@ * ===================================================================================== */ -#ifndef _BtOgrePG_H_ -#define _BtOgrePG_H_ +#ifndef BtOgrePG_H_ +#define BtOgrePG_H_ #include "btBulletDynamicsCommon.h" #include "BtOgreExtras.h" diff --git a/libs/openengine/bullet/BtOgrePG.h b/libs/openengine/bullet/BtOgrePG.h index 9ff069a8f..2e42fe1f9 100644 --- a/libs/openengine/bullet/BtOgrePG.h +++ b/libs/openengine/bullet/BtOgrePG.h @@ -14,8 +14,8 @@ * ===================================================================================== */ -#ifndef _BtOgreGP_H_ -#define _BtOgreGP_H_ +#ifndef BtOgreGP_H_ +#define BtOgreGP_H_ #include "btBulletDynamicsCommon.h" #include "OgreSceneNode.h" diff --git a/libs/openengine/bullet/BulletShapeLoader.h b/libs/openengine/bullet/BulletShapeLoader.h index 98cda859d..0e5c65226 100644 --- a/libs/openengine/bullet/BulletShapeLoader.h +++ b/libs/openengine/bullet/BulletShapeLoader.h @@ -1,5 +1,5 @@ -#ifndef _BULLET_SHAPE_LOADER_H_ -#define _BULLET_SHAPE_LOADER_H_ +#ifndef OPENMW_BULLET_SHAPE_LOADER_H_ +#define OPENMW_BULLET_SHAPE_LOADER_H_ #include #include From 45847c67ad02b3e62036cc822d159b313f05df8d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 5 Jan 2014 18:38:21 +0100 Subject: [PATCH 379/889] Lock NIF cache when loading an interior cell as well. Should improve load performance. --- apps/openmw/mwworld/scene.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index dab272f7c..b3a9c9bb3 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -350,6 +350,7 @@ namespace MWWorld void Scene::changeToInteriorCell (const std::string& cellName, const ESM::Position& position) { + Nif::NIFFile::CacheLock lock; MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0.5); mRendering.enableTerrain(false); From b3764c504a010b1aaea7104cd24cb5e709482c78 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 5 Jan 2014 19:08:12 +0100 Subject: [PATCH 380/889] Implement GetPcJumping instruction --- apps/openmw/mwscript/docs/vmformat.txt | 3 ++- apps/openmw/mwscript/miscextensions.cpp | 13 +++++++++++++ components/compiler/extensions0.cpp | 1 + components/compiler/opcodes.hpp | 1 + 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index b4ac8e154..504a8638b 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -370,4 +370,5 @@ op 0x200022f: Resurrect op 0x2000230: Resurrect, explicit op 0x2000231: GetSpellReadied op 0x2000232: GetSpellReadied, explicit -opcodes 0x2000233-0x3ffffff unused +op 0x2000233: GetPcJumping +opcodes 0x2000234-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 8d435959b..54a8139d8 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -58,6 +58,18 @@ namespace MWScript } }; + class OpGetPcJumping : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWBase::World* world = MWBase::Environment::get().getWorld(); + MWWorld::Ptr player = world->getPlayer().getPlayer(); + runtime.push (!world->isOnGround(player) && !world->isFlying(player)); + } + }; + class OpWakeUpPc : public Interpreter::Opcode0 { public: @@ -771,6 +783,7 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeDontSaveObject, new OpDontSaveObject); interpreter.installSegment5 (Compiler::Misc::opcodeToggleVanityMode, new OpToggleVanityMode); interpreter.installSegment5 (Compiler::Misc::opcodeGetPcSleep, new OpGetPcSleep); + interpreter.installSegment5 (Compiler::Misc::opcodeGetPcJumping, new OpGetPcJumping); interpreter.installSegment5 (Compiler::Misc::opcodeWakeUpPc, new OpWakeUpPc); interpreter.installSegment5 (Compiler::Misc::opcodePlayBink, new OpPlayBink); interpreter.installSegment5 (Compiler::Misc::opcodeGetLocked, new OpGetLocked); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 43dcf0ba4..6194be532 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -241,6 +241,7 @@ namespace Compiler extensions.registerInstruction ("togglevanitymode", "", opcodeToggleVanityMode); extensions.registerInstruction ("tvm", "", opcodeToggleVanityMode); extensions.registerFunction ("getpcsleep", 'l', "", opcodeGetPcSleep); + extensions.registerFunction ("getpcjumping", 'l', "", opcodeGetPcJumping); extensions.registerInstruction ("wakeuppc", "", opcodeWakeUpPc); extensions.registerInstruction ("playbink", "Sl", opcodePlayBink); extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 8742ba946..46524c7cd 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -188,6 +188,7 @@ namespace Compiler const int opcodeDontSaveObject = 0x2000153; const int opcodeToggleVanityMode = 0x2000174; const int opcodeGetPcSleep = 0x200019f; + const int opcodeGetPcJumping = 0x2000233; const int opcodeWakeUpPc = 0x20001a2; const int opcodeGetLocked = 0x20001c7; const int opcodeGetLockedExplicit = 0x20001c8; From 73f8161d1ea273e514f0fcdec89378c67b88862f Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 5 Jan 2014 19:40:05 +0100 Subject: [PATCH 381/889] Fix spell deletion not resetting the selected spell correctly --- apps/openmw/mwgui/spellwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index e2817c38b..2ca783bfc 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -393,7 +393,7 @@ namespace MWGui MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); - if (spells.getSelectedSpell() == mSpellToDelete) + if (MWBase::Environment::get().getWindowManager()->getSelectedSpell() == mSpellToDelete) MWBase::Environment::get().getWindowManager()->unsetSelectedSpell(); spells.remove(mSpellToDelete); From dde2cd5d5a2e4a5dc66f45e29180f163ecd5d162 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 5 Jan 2014 20:53:45 +0100 Subject: [PATCH 382/889] Fix some code that still used setCount directly instead of using the ContainerStore interface. Also fix a related annoyance with the interface. --- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwgui/containeritemmodel.cpp | 5 +---- apps/openmw/mwgui/inventoryitemmodel.cpp | 5 +---- apps/openmw/mwgui/inventorywindow.cpp | 7 ++----- apps/openmw/mwmechanics/enchanting.cpp | 2 +- apps/openmw/mwworld/actiontake.cpp | 5 +---- apps/openmw/mwworld/containerstore.cpp | 26 +++++++++++++----------- apps/openmw/mwworld/containerstore.hpp | 6 +++--- apps/openmw/mwworld/inventorystore.cpp | 16 ++++----------- apps/openmw/mwworld/inventorystore.hpp | 2 +- 10 files changed, 29 insertions(+), 47 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 8a32f58b9..9d3002dfd 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -613,7 +613,7 @@ namespace MWClass // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying // something, alert the character controller, scripts, etc. - MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); + MWBase::Environment::get().getDialogueManager()->say(ptr, "thief"); if(object.isEmpty()) { diff --git a/apps/openmw/mwgui/containeritemmodel.cpp b/apps/openmw/mwgui/containeritemmodel.cpp index 6b0fbd890..bcb8440bf 100644 --- a/apps/openmw/mwgui/containeritemmodel.cpp +++ b/apps/openmw/mwgui/containeritemmodel.cpp @@ -76,10 +76,7 @@ void ContainerItemModel::copyItem (const ItemStack& item, size_t count) const MWWorld::Ptr& source = mItemSources[mItemSources.size()-1]; if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source)) throw std::runtime_error("Item to copy needs to be from a different container!"); - int origCount = item.mBase.getRefData().getCount(); - item.mBase.getRefData().setCount(count); - source.getClass().getContainerStore(source).add(item.mBase, source); - item.mBase.getRefData().setCount(origCount); + source.getClass().getContainerStore(source).add(item.mBase, count, source); } void ContainerItemModel::removeItem (const ItemStack& item, size_t count) diff --git a/apps/openmw/mwgui/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp index a16e67a7f..d26feba88 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.cpp +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -42,10 +42,7 @@ void InventoryItemModel::copyItem (const ItemStack& item, size_t count) { if (item.mBase.getContainerStore() == &mActor.getClass().getContainerStore(mActor)) throw std::runtime_error("Item to copy needs to be from a different container!"); - int origCount = item.mBase.getRefData().getCount(); - item.mBase.getRefData().setCount(count); - mActor.getClass().getContainerStore(mActor).add(item.mBase, mActor); - item.mBase.getRefData().setCount(origCount); + mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor); } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 70295c4c7..21da53c6d 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -364,10 +364,7 @@ namespace MWGui MWWorld::ContainerStore& invStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); MWWorld::ContainerStoreIterator it = invStore.begin(); - int origCount = ptr.getRefData().getCount(); - ptr.getRefData().setCount(mDragAndDrop->mDraggedCount); - it = invStore.add(ptr, mPtr); - ptr.getRefData().setCount(origCount); + it = invStore.add(ptr, mDragAndDrop->mDraggedCount, mPtr); mDragAndDrop->mSourceModel->removeItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount); ptr = *it; @@ -521,7 +518,7 @@ namespace MWGui // add to player inventory // can't use ActionTake here because we need an MWWorld::Ptr to the newly inserted object MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWWorld::Ptr newObject = *MWWorld::Class::get (player).getContainerStore (player).add (object, player); + MWWorld::Ptr newObject = *player.getClass().getContainerStore (player).add (object, object.getRefData().getCount(), player); // remove from world MWBase::Environment::get().getWorld()->deleteObject (object); diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 7e11acdb0..3991454db 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -90,7 +90,7 @@ namespace MWMechanics // Add the new item to player inventory and remove the old one store.remove(mOldItemPtr, 1, player); - store.add(newItemPtr, player); + store.add(newItemPtr, 1, player); if(!mSelfEnchanting) payForEnchantment(); diff --git a/apps/openmw/mwworld/actiontake.cpp b/apps/openmw/mwworld/actiontake.cpp index d3c4aa2f6..867a046cf 100644 --- a/apps/openmw/mwworld/actiontake.cpp +++ b/apps/openmw/mwworld/actiontake.cpp @@ -14,10 +14,7 @@ namespace MWWorld void ActionTake::executeImp (const Ptr& actor) { - // insert into player's inventory - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPtr ("player", true); - - MWWorld::Class::get (player).getContainerStore (player).add (getTarget(), player); + actor.getClass().getContainerStore (actor).add (getTarget(), getTarget().getRefData().getCount(), actor); MWBase::Environment::get().getWorld()->deleteObject (getTarget()); } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 686e790a3..221766647 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -81,7 +81,7 @@ void MWWorld::ContainerStore::unstack(const Ptr &ptr, const Ptr& container) { if (ptr.getRefData().getCount() <= 1) return; - addNewStack(ptr)->getRefData().setCount(ptr.getRefData().getCount()-1); + addNewStack(ptr, ptr.getRefData().getCount()-1); remove(ptr, ptr.getRefData().getCount()-1, container); } @@ -123,12 +123,12 @@ bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& 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); - return add(ref.getPtr(), actorPtr); + return add(ref.getPtr(), count, actorPtr); } -MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, const Ptr& actorPtr) +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr) { - MWWorld::ContainerStoreIterator it = addImp(itemPtr); + MWWorld::ContainerStoreIterator it = addImp(itemPtr, count); MWWorld::Ptr item = *it; // we may have copied an item from the world, so reset a few things first @@ -165,7 +165,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr return it; } -MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr) +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr, int count) { int type = getType(ptr); @@ -180,20 +180,20 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr) || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_025") || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_100")) { - int count = MWWorld::Class::get(ptr).getValue(ptr) * ptr.getRefData().getCount(); + int realCount = MWWorld::Class::get(ptr).getValue(ptr) * ptr.getRefData().getCount(); for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) { if (Misc::StringUtils::ciEqual((*iter).get()->mRef.mRefID, "gold_001")) { - iter->getRefData().setCount(iter->getRefData().getCount() + count); + iter->getRefData().setCount(iter->getRefData().getCount() + realCount); flagAsModified(); return iter; } } MWWorld::ManualRef ref(esmStore, "Gold_001", count); - return addNewStack(ref.getPtr()); + return addNewStack(ref.getPtr(), count); } // determine whether to stack or not @@ -202,17 +202,17 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr) if (stacks(*iter, ptr)) { // stack - iter->getRefData().setCount( iter->getRefData().getCount() + ptr.getRefData().getCount() ); + iter->getRefData().setCount( iter->getRefData().getCount() + count ); flagAsModified(); return iter; } } // if we got here, this means no stacking - return addNewStack(ptr); + return addNewStack(ptr, count); } -MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addNewStack (const Ptr& ptr) +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addNewStack (const Ptr& ptr, int count) { ContainerStoreIterator it = begin(); @@ -232,6 +232,8 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addNewStack (const Ptr& case Type_Weapon: weapons.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --weapons.mList.end()); break; } + it->getRefData().setCount(count); + flagAsModified(); return it; } @@ -343,7 +345,7 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: else { ref.getPtr().getCellRef().mOwner = owner; - addImp (ref.getPtr()); + addImp (ref.getPtr(), count); } } catch (std::logic_error& e) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index b34c71006..ed0fc8b8f 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -51,7 +51,7 @@ namespace MWWorld MWWorld::CellRefList weapons; mutable float mCachedWeight; mutable bool mWeightUpToDate; - ContainerStoreIterator addImp (const Ptr& ptr); + ContainerStoreIterator addImp (const Ptr& ptr, int count); void addInitialItem (const std::string& id, const std::string& owner, int count, unsigned char failChance=0, bool topLevel=true); public: @@ -64,7 +64,7 @@ namespace MWWorld ContainerStoreIterator end(); - virtual ContainerStoreIterator add (const Ptr& itemPtr, const Ptr& actorPtr); + virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr); ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed) /// /// \note The item pointed to is not required to exist beyond this function call. @@ -91,7 +91,7 @@ namespace MWWorld ///< Unstack an item in this container. The item's count will be set to 1, then a new stack will be added with (origCount-1). protected: - ContainerStoreIterator addNewStack (const Ptr& ptr); + ContainerStoreIterator addNewStack (const Ptr& ptr, int count); ///< Add the item to this container (do not try to stack it onto existing items) virtual void flagAsModified(); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 2944f00d4..2c0cf5feb 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -75,9 +75,9 @@ MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStor return *this; } -MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, const Ptr& actorPtr) +MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, int count, const Ptr& actorPtr) { - const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, actorPtr); + const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr); // Auto-equip items if an armor/clothing item is added, but not for the player nor werewolves if ((actorPtr.getRefData().getHandle() != "player") @@ -118,11 +118,7 @@ void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& ite // unstack item pointed to by iterator if required if (iterator!=end() && !slots_.second && iterator->getRefData().getCount() > 1) // if slots.second is true, item can stay stacked when equipped { - // add the item again with a count of count-1, then set the count of the original (that will be equipped) to 1 - int count = iterator->getRefData().getCount(); - iterator->getRefData().setCount(count-1); - addNewStack(*iterator); - iterator->getRefData().setCount(1); + unstack(*iterator, actor); } mSlots[slot] = iterator; @@ -274,11 +270,7 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) // unstack item pointed to by iterator if required if (iter->getRefData().getCount() > 1) { - // add the item again with a count of count-1, then set the count of the original (that will be equipped) to 1 - int count = iter->getRefData().getCount(); - iter->getRefData().setCount(count-1); - addNewStack(*iter); - iter->getRefData().setCount(1); + unstack(*iter, actor); } } diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index e764f64fb..abd5f5ab3 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -113,7 +113,7 @@ namespace MWWorld InventoryStore& operator= (const InventoryStore& store); - virtual ContainerStoreIterator add (const Ptr& itemPtr, const Ptr& actorPtr); + virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr); ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed) /// Auto-equip items if specific conditions are fulfilled (see the implementation). /// From 2f35e5a04ef828d4e99e28e0be74b175c766d13d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 5 Jan 2014 22:23:53 +0100 Subject: [PATCH 383/889] Stop merchants from autoequipping items sold to them --- apps/openmw/mwworld/containerstore.cpp | 7 +++++-- apps/openmw/mwworld/containerstore.hpp | 6 ++++-- apps/openmw/mwworld/inventorystore.cpp | 9 +++++++-- apps/openmw/mwworld/inventorystore.hpp | 4 +++- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 221766647..ba8936bf7 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -123,10 +123,10 @@ bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& 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); - return add(ref.getPtr(), count, actorPtr); + return add(ref.getPtr(), count, actorPtr, true); } -MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr) +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner) { MWWorld::ContainerStoreIterator it = addImp(itemPtr, count); MWWorld::Ptr item = *it; @@ -140,6 +140,9 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr item.getCellRef().mPos.pos[1] = 0; item.getCellRef().mPos.pos[2] = 0; + if (setOwner) + item.getCellRef().mOwner = actorPtr.getCellRef().mRefID; + std::string script = MWWorld::Class::get(item).getScript(item); if(script != "") { diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index ed0fc8b8f..83e490e37 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -64,7 +64,7 @@ namespace MWWorld ContainerStoreIterator end(); - virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr); + virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner=false); ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed) /// /// \note The item pointed to is not required to exist beyond this function call. @@ -72,10 +72,12 @@ namespace MWWorld /// \attention Do not add items to an existing stack by increasing the count instead of /// calling this function! /// + /// @param setOwner Set the owner of the added item to \a actorPtr? + /// /// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item. ContainerStoreIterator add(const std::string& id, int count, const Ptr& actorPtr); - ///< Utility to construct a ManualRef and call add(ptr, actorPtr) + ///< Utility to construct a ManualRef and call add(ptr, count, actorPtr, true) int remove(const std::string& itemId, int count, const Ptr& actor); ///< Remove \a count item(s) designated by \a itemId from this container. diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 2c0cf5feb..422265c0e 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -75,9 +75,9 @@ MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStor return *this; } -MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, int count, const Ptr& actorPtr) +MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner) { - const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr); + const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr, setOwner); // Auto-equip items if an armor/clothing item is added, but not for the player nor werewolves if ((actorPtr.getRefData().getHandle() != "player") @@ -183,6 +183,11 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) continue; } + // Only autoEquip if we are the original owner of the item. + // This stops merchants from auto equipping anything you sell to them. + if (!Misc::StringUtils::ciEqual(test.getCellRef().mOwner, actor.getCellRef().mRefID)) + continue; + int testSkill = MWWorld::Class::get (test).getEquipmentSkill (test); std::pair, bool> itemsSlots = diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index abd5f5ab3..067c8261e 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -113,7 +113,7 @@ namespace MWWorld InventoryStore& operator= (const InventoryStore& store); - virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr); + virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner=false); ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed) /// Auto-equip items if specific conditions are fulfilled (see the implementation). /// @@ -122,6 +122,8 @@ namespace MWWorld /// \attention Do not add items to an existing stack by increasing the count instead of /// calling this function! /// + /// @param setOwner Set the owner of the added item to \a actorPtr? + /// /// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item. void equip (int slot, const ContainerStoreIterator& iterator, const Ptr& actor); From 43dd3b8ef22f691a7e55b176aafaba9714660ccb Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 6 Jan 2014 13:53:20 +0100 Subject: [PATCH 384/889] removed redundant deletion flag from CellRef --- apps/esmtool/esmtool.cpp | 5 +- apps/opencs/model/world/ref.cpp | 3 +- apps/opencs/model/world/refcollection.cpp | 3 +- apps/openmw/mwworld/cellstore.cpp | 177 ++++++++-------------- apps/openmw/mwworld/cellstore.hpp | 7 +- apps/openmw/mwworld/esmstore.cpp | 2 +- apps/openmw/mwworld/store.cpp | 9 +- components/esm/cellref.hpp | 3 - components/esm/loadcell.cpp | 13 +- components/esm/loadcell.hpp | 2 +- 10 files changed, 91 insertions(+), 133 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 27980096e..a0593e60d 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -236,7 +236,9 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info) // Loop through all the references ESM::CellRef ref; if(!quiet) std::cout << " References:\n"; - while(cell.getNextRef(esm, ref)) + + bool deleted = false; + while(cell.getNextRef(esm, ref, deleted)) { if (save) { info.data.mCellRefs[&cell].push_back(ref); @@ -251,6 +253,7 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info) std::cout << " Uses/health: '" << ref.mCharge << "'\n"; std::cout << " Gold value: '" << ref.mGoldValue << "'\n"; std::cout << " Blocked: '" << static_cast(ref.mReferenceBlocked) << "'" << std::endl; + std::cout << " Deleted: " << deleted << std::endl; } } diff --git a/apps/opencs/model/world/ref.cpp b/apps/opencs/model/world/ref.cpp index 74f60419b..cf9e496ee 100644 --- a/apps/opencs/model/world/ref.cpp +++ b/apps/opencs/model/world/ref.cpp @@ -8,6 +8,5 @@ void CSMWorld::CellRef::load (ESM::ESMReader &esm, Cell& cell, const std::string mId = id; mCell = cell.mId; - if (!mDeleted) - cell.addRef (mId); + cell.addRef (mId); } \ No newline at end of file diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 696aeefaa..7c95c2d30 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -14,7 +14,8 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool CellRef ref; - while (cell2.getNextRef (reader, ref)) + bool deleted = false; + while (cell2.getNextRef (reader, ref, deleted)) { /// \todo handle deleted and moved references ref.load (reader, cell2, getNewId()); diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 0c145ab60..88f422a1d 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -35,32 +35,30 @@ namespace MWWorld { template - void CellRefList::load(ESM::CellRef &ref, const MWWorld::ESMStore &esmStore) + void CellRefList::load(ESM::CellRef &ref, bool deleted, const MWWorld::ESMStore &esmStore) { - // Get existing reference, in case we need to overwrite it. - typename std::list::iterator iter = std::find(mList.begin(), mList.end(), ref.mRefnum); - - // Skip this when reference was deleted. - // TODO: Support respawning references, in this case, we need to track it somehow. - if (ref.mDeleted) { - if (iter != mList.end()) - mList.erase(iter); - return; - } - - // for throwing exception on unhandled record type const MWWorld::Store &store = esmStore.get(); - const X *ptr = store.search(ref.mRefID); - /// \note no longer redundant - changed to Store::search(), don't throw - /// an exception on miss, try to continue (that's how MW does it, anyway) - if (ptr == NULL) { - std::cout << "Warning: could not resolve cell reference " << ref.mRefID << ", trying to continue anyway" << std::endl; - } else { - if (iter != mList.end()) - *iter = LiveRef(ref, ptr); - else - mList.push_back(LiveRef(ref, ptr)); + if (const X *ptr = store.search (ref.mRefID)) + { + typename std::list::iterator iter = + std::find(mList.begin(), mList.end(), ref.mRefnum); + + LiveRef liveCellRef (ref, ptr); + + if (deleted) + liveCellRef.mData.setCount (0); + + if (iter != mList.end()) + *iter = liveCellRef; + else + mList.push_back (liveCellRef); + } + else + { + std::cerr + << "Error: could not resolve cell reference " << ref.mRefID + << " (dropping reference)" << std::endl; } } @@ -117,16 +115,13 @@ namespace MWWorld ESM::CellRef ref; // Get each reference in turn - while (mCell->getNextRef (esm[index], ref)) + bool deleted = false; + while (mCell->getNextRef (esm[index], ref, deleted)) { - std::string lowerCase = Misc::StringUtils::lowerCase (ref.mRefID); - if (ref.mDeleted) { - // Right now, don't do anything. Where is "listRefs" actually used, anyway? - // Skipping for now... + if (deleted) continue; - } - mIds.push_back (lowerCase); + mIds.push_back (Misc::StringUtils::lowerCase (ref.mRefID)); } } @@ -135,7 +130,7 @@ namespace MWWorld void CellStore::loadRefs(const MWWorld::ESMStore &store, std::vector &esm) { - assert (mCell); + assert (mCell); if (mCell->mContextList.empty()) return; // this is a dynamically generated cell -> skipping. @@ -150,102 +145,25 @@ namespace MWWorld ESM::CellRef ref; // Get each reference in turn - while(mCell->getNextRef(esm[index], ref)) + bool deleted = false; + while(mCell->getNextRef(esm[index], ref, deleted)) { // Don't load reference if it was moved to a different cell. - std::string lowerCase = Misc::StringUtils::lowerCase(ref.mRefID); ESM::MovedCellRefTracker::const_iterator iter = std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefnum); if (iter != mCell->mMovedRefs.end()) { continue; } - int rec = store.find(ref.mRefID); - ref.mRefID = lowerCase; - - /* We can optimize this further by storing the pointer to the - record itself in store.all, so that we don't need to look it - up again here. However, never optimize. There are infinite - opportunities to do that later. - */ - switch(rec) - { - case ESM::REC_ACTI: mActivators.load(ref, store); break; - case ESM::REC_ALCH: mPotions.load(ref, store); break; - case ESM::REC_APPA: mAppas.load(ref, store); break; - case ESM::REC_ARMO: mArmors.load(ref, store); break; - case ESM::REC_BOOK: mBooks.load(ref, store); break; - case ESM::REC_CLOT: mClothes.load(ref, store); break; - case ESM::REC_CONT: mContainers.load(ref, store); break; - case ESM::REC_CREA: mCreatures.load(ref, store); break; - case ESM::REC_DOOR: mDoors.load(ref, store); break; - case ESM::REC_INGR: mIngreds.load(ref, store); break; - case ESM::REC_LEVC: mCreatureLists.load(ref, store); break; - case ESM::REC_LEVI: mItemLists.load(ref, store); break; - case ESM::REC_LIGH: mLights.load(ref, store); break; - case ESM::REC_LOCK: mLockpicks.load(ref, store); break; - case ESM::REC_MISC: mMiscItems.load(ref, store); break; - case ESM::REC_NPC_: mNpcs.load(ref, store); break; - case ESM::REC_PROB: mProbes.load(ref, store); break; - case ESM::REC_REPA: mRepairs.load(ref, store); break; - case ESM::REC_STAT: mStatics.load(ref, store); break; - case ESM::REC_WEAP: mWeapons.load(ref, store); break; - - case 0: std::cout << "Cell reference " + ref.mRefID + " not found!\n"; break; - default: - std::cout << "WARNING: Ignoring reference '" << ref.mRefID << "' of unhandled type\n"; - } + loadRef (ref, deleted, store); } } // Load moved references, from separately tracked list. for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); ++it) { - // Doesn't seem to work in one line... huh? Too sleepy to check... ESM::CellRef &ref = const_cast(*it); - //ESM::CellRef &ref = const_cast(it->second); - - std::string lowerCase; - - std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); - - int rec = store.find(ref.mRefID); - - ref.mRefID = lowerCase; - - /* We can optimize this further by storing the pointer to the - record itself in store.all, so that we don't need to look it - up again here. However, never optimize. There are infinite - opportunities to do that later. - */ - switch(rec) - { - case ESM::REC_ACTI: mActivators.load(ref, store); break; - case ESM::REC_ALCH: mPotions.load(ref, store); break; - case ESM::REC_APPA: mAppas.load(ref, store); break; - case ESM::REC_ARMO: mArmors.load(ref, store); break; - case ESM::REC_BOOK: mBooks.load(ref, store); break; - case ESM::REC_CLOT: mClothes.load(ref, store); break; - case ESM::REC_CONT: mContainers.load(ref, store); break; - case ESM::REC_CREA: mCreatures.load(ref, store); break; - case ESM::REC_DOOR: mDoors.load(ref, store); break; - case ESM::REC_INGR: mIngreds.load(ref, store); break; - case ESM::REC_LEVC: mCreatureLists.load(ref, store); break; - case ESM::REC_LEVI: mItemLists.load(ref, store); break; - case ESM::REC_LIGH: mLights.load(ref, store); break; - case ESM::REC_LOCK: mLockpicks.load(ref, store); break; - case ESM::REC_MISC: mMiscItems.load(ref, store); break; - case ESM::REC_NPC_: mNpcs.load(ref, store); break; - case ESM::REC_PROB: mProbes.load(ref, store); break; - case ESM::REC_REPA: mRepairs.load(ref, store); break; - case ESM::REC_STAT: mStatics.load(ref, store); break; - case ESM::REC_WEAP: mWeapons.load(ref, store); break; - - case 0: std::cout << "Cell reference " + ref.mRefID + " not found!\n"; break; - default: - std::cout << "WARNING: Ignoring reference '" << ref.mRefID << "' of unhandled type\n"; - } + loadRef (ref, false, store); } } @@ -274,4 +192,39 @@ namespace MWWorld return Ptr(); } + + void CellStore::loadRef (ESM::CellRef& ref, bool deleted, const ESMStore& store) + { + Misc::StringUtils::toLower (ref.mRefID); + + switch (store.find (ref.mRefID)) + { + case ESM::REC_ACTI: mActivators.load(ref, deleted, store); break; + case ESM::REC_ALCH: mPotions.load(ref, deleted, store); break; + case ESM::REC_APPA: mAppas.load(ref, deleted, store); break; + case ESM::REC_ARMO: mArmors.load(ref, deleted, store); break; + case ESM::REC_BOOK: mBooks.load(ref, deleted, store); break; + case ESM::REC_CLOT: mClothes.load(ref, deleted, store); break; + case ESM::REC_CONT: mContainers.load(ref, deleted, store); break; + case ESM::REC_CREA: mCreatures.load(ref, deleted, store); break; + case ESM::REC_DOOR: mDoors.load(ref, deleted, store); break; + case ESM::REC_INGR: mIngreds.load(ref, deleted, store); break; + case ESM::REC_LEVC: mCreatureLists.load(ref, deleted, store); break; + case ESM::REC_LEVI: mItemLists.load(ref, deleted, store); break; + case ESM::REC_LIGH: mLights.load(ref, deleted, store); break; + case ESM::REC_LOCK: mLockpicks.load(ref, deleted, store); break; + case ESM::REC_MISC: mMiscItems.load(ref, deleted, store); break; + case ESM::REC_NPC_: mNpcs.load(ref, deleted, store); break; + case ESM::REC_PROB: mProbes.load(ref, deleted, store); break; + case ESM::REC_REPA: mRepairs.load(ref, deleted, store); break; + case ESM::REC_STAT: mStatics.load(ref, deleted, store); break; + case ESM::REC_WEAP: mWeapons.load(ref, deleted, store); break; + + case 0: std::cerr << "Cell reference " + ref.mRefID + " not found!\n"; break; + + default: + std::cerr + << "WARNING: Ignoring reference '" << ref.mRefID << "' of unhandled type\n"; + } + } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 8a01caf18..b109557f9 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -25,7 +25,7 @@ namespace MWWorld // and the build will fail with an ugly three-way cyclic header dependence // so we need to pass the instantiation of the method to the lnker, when // all methods are known. - void load(ESM::CellRef &ref, const MWWorld::ESMStore &esmStore); + void load(ESM::CellRef &ref, bool deleted, const MWWorld::ESMStore &esmStore); LiveRef *find (const std::string& name) { @@ -154,6 +154,11 @@ namespace MWWorld void loadRefs(const MWWorld::ESMStore &store, std::vector &esm); + void loadRef (ESM::CellRef& ref, bool deleted, const ESMStore& store); + ///< Make case-adjustments to \a ref and insert it into the respective container. + /// + /// Invalid \a ref objects are silently dropped. + }; } diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 62b91c2e4..cab10ee51 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -108,7 +108,7 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) } // Insert the reference into the global lookup if (!id.empty() && isCacheableRecord(n.val)) { - mIds[id] = n.val; + mIds[Misc::StringUtils::lowerCase (id)] = n.val; } } listener->setProgress(esm.getFileOffset() / (float)esm.getFileSize() * 1000); diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 512883f1a..44c814e81 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -10,7 +10,7 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) // and we merge all this data into one Cell object. However, we can't simply search for the cell id, // as many exterior cells do not have a name. Instead, we need to search by (x,y) coordinates - and they // are not available until both cells have been loaded! So first, proceed as usual. - + // All cells have a name record, even nameless exterior cells. std::string idLower = Misc::StringUtils::lowerCase(id); ESM::Cell *cell = new ESM::Cell; @@ -30,11 +30,10 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) // Get regular moved reference data. Adapted from CellStore::loadRefs. Maybe we can optimize the following // implementation when the oher implementation works as well. - cell->getNextRef(esm, ref); - std::string lowerCase; + bool deleted = false; + cell->getNextRef(esm, ref, deleted); - std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); + Misc::StringUtils::toLower (ref.mRefID); // Add data required to make reference appear in the correct cell. // We should not need to test for duplicates, as this part of the code is pre-cell merge. diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 47cb0b99e..04451535b 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -71,9 +71,6 @@ namespace ESM // -1 is not blocked, otherwise it is blocked. signed char mReferenceBlocked; - // Track deleted references. 0 - not deleted, 1 - deleted, but respawns, 2 - deleted and does not respawn. - int mDeleted; - // Occurs in Tribunal.esm, eg. in the cell "Mournhold, Plaza // Brindisi Dorom", where it has the value 100. Also only for // activators. diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index c22c1b22b..0d69b0263 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -141,7 +141,7 @@ std::string Cell::getDescription() const } } -bool Cell::getNextRef(ESMReader &esm, CellRef &ref) +bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted) { // TODO: Try and document reference numbering, I don't think this has been done anywhere else. if (!esm.hasMoreSubs()) @@ -259,12 +259,13 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) //esm.getHNOT(NAM0, "NAM0"); } - if (esm.isNextSub("DELE")) { + if (esm.isNextSub("DELE")) + { esm.skipHSub(); - ref.mDeleted = 2; // Deleted, will not respawn. - // TODO: find out when references do respawn. - } else - ref.mDeleted = 0; + deleted = true; + } + else + deleted = false; return true; } diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 61d586b9d..38aaa0494 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -141,7 +141,7 @@ struct Cell All fields of the CellRef struct are overwritten. You can safely reuse one memory location without blanking it between calls. */ - static bool getNextRef(ESMReader &esm, CellRef &ref); + static bool getNextRef(ESMReader &esm, CellRef &ref, bool& deleted); /* This fetches an MVRF record, which is used to track moved references. * Since they are comparably rare, we use a separate method for this. From 06f85370878c8876cec2a1e93d87eae55a395cf6 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 6 Jan 2014 18:43:44 +0100 Subject: [PATCH 385/889] added final check for player npc. Removed useless includes. However, this code spams exceptions and I can't figure out why. --- .../opencs/model/tools/referenceablecheck.cpp | 41 +++++++++++++------ .../opencs/model/tools/referenceablecheck.hpp | 6 ++- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 92d116244..12f347afb 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -1,14 +1,6 @@ #include "referenceablecheck.hpp" - -#include -#include -#include -#include - #include "../world/record.hpp" - #include "../world/universalid.hpp" -#include CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, @@ -18,12 +10,18 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( mReferencables(referenceable), mClasses(classes), mRaces(races), - mFactions(faction) + mFactions(faction), + mPlayerPresent(false) { } void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::string >& messages) { + if (stage == mReferencables.getSize() - 1) + { + finalCheck(messages); + } + //Checks for books, than, when stage is above mBooksSize goes to other checks, with (stage - PrevSum) as stage. const int bookSize(mReferencables.getBooks().getSize()); @@ -104,7 +102,6 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str } stage -= doorSize; - const int ingredientSize(mReferencables.getIngredients().getSize()); if (stage < ingredientSize) @@ -296,7 +293,6 @@ void CSMTools::ReferenceableCheckStage::apparatusCheck( inventoryItemCheck(apparatus, messages, id.toString()); - //checking for quality, 0 → apparatus is basicly useless, any negative → apparatus is harmfull instead of helpfull toolCheck(apparatus, messages, id.toString()); } @@ -656,6 +652,12 @@ void CSMTools::ReferenceableCheckStage::npcCheck( //Don't know what unknown is for int gold(npc.mNpdt52.mGold); + //Detect if player is present + if (npc.mId == "player") + { + mPlayerPresent = true; + } + if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated { if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0008 = autocalculated flag @@ -954,6 +956,19 @@ void CSMTools::ReferenceableCheckStage::staticCheck( } } +//final check + +void CSMTools::ReferenceableCheckStage::finalCheck(std::vector< std::string >& messages) +{ + if (!mPlayerPresent) + { + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Npc); + messages.push_back(id.toString() + "| There is no player record"); + } + + mPlayerPresent = false; +} + //Templates begins here @@ -1022,7 +1037,7 @@ template void CSMTools::ReferenceableCheckStage::inventoryItemChe messages.push_back(someID + "|" + someItem.mId + " has negative value"); } -//checking for model + //checking for model if (someItem.mModel.empty()) { messages.push_back(someID + "|" + someItem.mId + " has no model"); @@ -1076,7 +1091,7 @@ template void CSMTools::ReferenceableCheckStage::listCheck( { messages.push_back(someID + "|" + someList.mId + " contains item without referencable"); } - + if (someList.mList[i].mLevel < 1) { messages.push_back(someID + "|" + someList.mId + " contains item with non-positive level"); diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index 217e77a05..bc31ad537 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -37,7 +37,10 @@ namespace CSMTools void probeCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void repairCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); void staticCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); - + + //FINAL CHECK + void finalCheck(std::vector& messages); + //TEMPLATE CHECKS template void inventoryItemCheck(const ITEM& someitem, std::vector& messages, const std::string& someid, bool enchantable); //for all enchantable items. template void inventoryItemCheck(const ITEM& someitem, std::vector& messages, const std::string& someid); //for non-enchantable items. @@ -49,6 +52,7 @@ namespace CSMTools const CSMWorld::IdCollection& mRaces; const CSMWorld::IdCollection& mClasses; const CSMWorld::IdCollection& mFactions; + bool mPlayerPresent; }; } #endif // REFERENCEABLECHECKSTAGE_H From 4ad43fdf92f935c33be9894f52231e13ec287102 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 6 Jan 2014 19:01:05 +0100 Subject: [PATCH 386/889] Closes #1088: Quick&dirty fix for NIF filters not working properly with some mods --- components/nifogre/ogrenifloader.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 63e905766..0c1fdfbee 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -1209,20 +1209,27 @@ ObjectScenePtr Loader::createObjects(Ogre::Entity *parent, const std::string &bo if(isskinned) { + // Apparently both are allowed. Sigh. + // This could also mean that filters are supposed to work on the actual node + // hierarchy, rather than just trishapes, and the 'tri ' should be omitted? std::string filter = "@shape=tri "+bonename; + std::string filter2 = "@shape="+bonename; Misc::StringUtils::toLower(filter); + Misc::StringUtils::toLower(filter2); for(size_t i = 0;i < scene->mEntities.size();i++) { Ogre::Entity *entity = scene->mEntities[i]; if(entity->hasSkeleton()) { if(entity == scene->mSkelBase || - entity->getMesh()->getName().find(filter) != std::string::npos) + entity->getMesh()->getName().find(filter) != std::string::npos + || entity->getMesh()->getName().find(filter2) != std::string::npos) parentNode->attachObject(entity); } else { - if(entity->getMesh()->getName().find(filter) == std::string::npos) + if(entity->getMesh()->getName().find(filter) == std::string::npos + || entity->getMesh()->getName().find(filter2) == std::string::npos) entity->detachFromParent(); } } From 2591ff2d5a4adde5d3e2dc5b80ae1fd94a43bab5 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Mon, 6 Jan 2014 22:00:01 +0200 Subject: [PATCH 387/889] bug repairing --- apps/openmw/mwmechanics/character.cpp | 30 +++++++++++++++++---------- apps/openmw/mwrender/animation.cpp | 15 +++++++++++++- apps/openmw/mwrender/animation.hpp | 1 + 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 299c9dc99..d93ae9519 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -249,7 +249,8 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat else { mAnimation->disable(mCurrentJump); - mCurrentJump.clear(); + //mCurrentJump.clear(); + mCurrentJump = jump; mAnimation->play(jump, Priority_Jump, jumpgroup, true, 1.0f, "loop stop", "stop", 0.0f, 0); } @@ -678,6 +679,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun mUpperBodyState = UpperCharState_StartToMinAttack; } } + animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); } else @@ -708,7 +710,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun } } stats.setAttackStrength(complete); - + mAnimation->disable(mCurrentWeapon); mAnimation->play(mCurrentWeapon, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, @@ -725,7 +727,6 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun mUpperBodyState == UpperCharState_CastingSpell) { mUpperBodyState = UpperCharState_WeapEquiped; - //don't allow to continue playing hit animation on UpperBody after actor had attacked during it if(mHitState != CharState_None) { @@ -748,6 +749,12 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun stop = mAttackType+" max attack"; mUpperBodyState = UpperCharState_MinAttackToMaxAttack; break; + /*case UpperCharState_MinAttackToMaxAttack: + if(!mAnimation->isPlaying(mCurrentWeapon)) + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + 1e-9f, mAttackType+" min attack", mAttackType+" max attack", 0.99f, ~0ul); + break;*/ case UpperCharState_MaxAttackToMinHit: if(mAttackType == "shoot") { @@ -797,12 +804,8 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun } } - if(!animPlaying) - { - mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f)); - } //if playing combat animation and lowerbody is not busy switch to whole body animation - if(weaptype != WeapType_None && animPlaying) + if((weaptype != WeapType_None || UpperCharState_UnEquipingWeap) && animPlaying) { if( mMovementState != CharState_None || mJumpState != JumpState_None || @@ -810,15 +813,14 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun MWBase::Environment::get().getWorld()->isSwimming(mPtr) || cls.getStance(mPtr, MWWorld::Class::Sneak)) { - mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f)); mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_UpperBody); } else { - mAnimation->setAccumulation(Ogre::Vector3(0.0f, 0.0f, 0.0f)); mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_All); } } + MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name() @@ -1001,7 +1003,13 @@ void CharacterController::update(float duration) else { if(!(vec.z > 0.0f)) - mJumpState = JumpState_None; + { + if(!mAnimation->isPlaying(mCurrentJump)) + { + mJumpState = JumpState_None; + mCurrentJump.clear(); + } + } vec.z = 0.0f; if(std::abs(vec.x/2.0f) > std::abs(vec.y)) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 664b0343d..3e682399e 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -562,6 +562,7 @@ void Animation::updatePosition(float oldtime, float newtime, Ogre::Vector3 &posi /* Translate the accumulation root back to compensate for the move. */ mAccumRoot->setPosition(-off); + mAccumRootPosUpd=true; } bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint) @@ -851,7 +852,7 @@ void Animation::disable(const std::string &groupname) Ogre::Vector3 Animation::runAnimation(float duration) { Ogre::Vector3 movement(0.0f); - + mAccumRootPosUpd = false; AnimStateMap::iterator stateiter = mStates.begin(); while(stateiter != mStates.end()) { @@ -945,6 +946,18 @@ Ogre::Vector3 Animation::runAnimation(float duration) updateEffects(duration); + if (!mAccumRootPosUpd) + { + for(stateiter = mStates.begin();stateiter != mStates.end(); stateiter++) + { + if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) + { + updatePosition(stateiter->second.mTime, stateiter->second.mTime, movement); + break; + } + } + } + return movement; } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index f5f79dd72..013b49400 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -128,6 +128,7 @@ protected: NifOgre::ObjectScenePtr mObjectRoot; AnimSourceList mAnimSources; Ogre::Node *mAccumRoot; + bool mAccumRootPosUpd; Ogre::Node *mNonAccumRoot; NifOgre::NodeTargetValue *mNonAccumCtrl; Ogre::Vector3 mAccumulate; From 68b87714bb41a779b46803f91facbc6b9d6d97cd Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 6 Jan 2014 22:14:11 +0100 Subject: [PATCH 388/889] Addition to 2f35e5a04ef828d: companions should still auto equip --- apps/openmw/mwworld/inventorystore.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 422265c0e..2aee74eee 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -79,13 +79,13 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, { const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr, setOwner); - // Auto-equip items if an armor/clothing item is added, but not for the player nor werewolves + // Auto-equip items if an armor/clothing or weapon item is added, but not for the player nor werewolves if ((actorPtr.getRefData().getHandle() != "player") && !(MWWorld::Class::get(actorPtr).getNpcStats(actorPtr).isWerewolf()) && !actorPtr.getClass().getCreatureStats(actorPtr).isDead()) { std::string type = itemPtr.getTypeName(); - if ((type == typeid(ESM::Armor).name()) || (type == typeid(ESM::Clothing).name())) + if ((type == typeid(ESM::Armor).name()) || (type == typeid(ESM::Clothing).name()) || (type == typeid(ESM::Weapon).name())) autoEquip(actorPtr); } @@ -185,7 +185,10 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) // Only autoEquip if we are the original owner of the item. // This stops merchants from auto equipping anything you sell to them. - if (!Misc::StringUtils::ciEqual(test.getCellRef().mOwner, actor.getCellRef().mRefID)) + // ...unless this is a companion, he should always equip items given to him. + if (!Misc::StringUtils::ciEqual(test.getCellRef().mOwner, actor.getCellRef().mRefID) && + (actor.getClass().getScript(actor).empty() || + !actor.getRefData().getLocals().getIntVar(actor.getClass().getScript(actor), "companion"))) continue; int testSkill = MWWorld::Class::get (test).getEquipmentSkill (test); From b85fe2becf57f7c377a0ea66438bfe4bb2c9693c Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 6 Jan 2014 23:37:18 +0100 Subject: [PATCH 389/889] Changes according to the comment. --- apps/opencs/model/tools/referenceablecheck.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 12f347afb..6132b3407 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -1,6 +1,7 @@ #include "referenceablecheck.hpp" #include "../world/record.hpp" #include "../world/universalid.hpp" +#include CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, @@ -17,11 +18,6 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::string >& messages) { - if (stage == mReferencables.getSize() - 1) - { - finalCheck(messages); - } - //Checks for books, than, when stage is above mBooksSize goes to other checks, with (stage - PrevSum) as stage. const int bookSize(mReferencables.getBooks().getSize()); @@ -102,6 +98,7 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str } stage -= doorSize; + const int ingredientSize(mReferencables.getIngredients().getSize()); if (stage < ingredientSize) @@ -209,11 +206,15 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str staticCheck(stage, mReferencables.getStatics(), messages); return; } +// if we come that far, we are about to perform our last, final check. + finalCheck(messages); + return; } int CSMTools::ReferenceableCheckStage::setup() { - return mReferencables.getSize(); + mPlayerPresent = false; + return mReferencables.getSize() + 1; } void CSMTools::ReferenceableCheckStage::bookCheck( @@ -965,8 +966,6 @@ void CSMTools::ReferenceableCheckStage::finalCheck(std::vector< std::string >& m CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Npc); messages.push_back(id.toString() + "| There is no player record"); } - - mPlayerPresent = false; } From 4a3d148a48f24a61e9d0849d8246b14f3b463cf1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 00:37:52 +0100 Subject: [PATCH 390/889] Fixes #1089 (skill increases) --- apps/openmw/mwmechanics/stat.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index 77b3f6364..e66cf86de 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -231,6 +231,7 @@ namespace MWMechanics { float mProgress; public: + SkillValue() : mProgress(0) {} float getProgress() const { return mProgress; } void setProgress(float progress) { mProgress = progress; } }; From 29c823b9d435ff9b5cb8c2015f0230f209ad164e Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 00:51:09 +0100 Subject: [PATCH 391/889] Implement awareness check function. Use this for combat AI and GetDetected instruction. --- apps/openmw/mwbase/mechanicsmanager.hpp | 3 + apps/openmw/mwmechanics/actors.cpp | 73 +++++++++---------- .../mwmechanics/mechanicsmanagerimp.cpp | 70 ++++++++++++++++++ .../mwmechanics/mechanicsmanagerimp.hpp | 3 + apps/openmw/mwscript/aiextensions.cpp | 16 ++-- apps/openmw/mwworld/worldimp.cpp | 7 -- 6 files changed, 121 insertions(+), 51 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 3ab234de1..90f0caee6 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -88,6 +88,9 @@ namespace MWBase virtual int countDeaths (const std::string& id) const = 0; ///< Return the number of deaths for actors with the given ID. + /// Check if \a observer is potentially aware of \a ptr. Does not do a line of sight check! + virtual bool awarenessCheck (const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer) = 0; + enum PersuasionType { PT_Admire, diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 2cbfc020e..4c9c73ca1 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -158,50 +158,49 @@ namespace MWMechanics calculateDynamicStats (ptr); calculateCreatureStatModifiers (ptr, duration); - if(!MWBase::Environment::get().getWindowManager()->isGuiMode()) + // AI + if(MWBase::Environment::get().getMechanicsManager()->isAIActive()) { - // AI - if(MWBase::Environment::get().getMechanicsManager()->isAIActive()) + CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); + //engage combat or not? + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + if(ptr != player && !creatureStats.isHostile()) { - CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); - //engage combat or not? - if(ptr != MWBase::Environment::get().getWorld()->getPlayer().getPlayer() && !creatureStats.isHostile()) + ESM::Position playerpos = player.getRefData().getPosition(); + ESM::Position actorpos = ptr.getRefData().getPosition(); + float d = sqrt((actorpos.pos[0] - playerpos.pos[0])*(actorpos.pos[0] - playerpos.pos[0]) + +(actorpos.pos[1] - playerpos.pos[1])*(actorpos.pos[1] - playerpos.pos[1]) + +(actorpos.pos[2] - playerpos.pos[2])*(actorpos.pos[2] - playerpos.pos[2])); + float fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified(); + float disp = 100; //creatures don't have disposition, so set it to 100 by default + if(ptr.getTypeName() == typeid(ESM::NPC).name()) { - ESM::Position playerpos = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getRefData().getPosition(); - ESM::Position actorpos = ptr.getRefData().getPosition(); - float d = sqrt((actorpos.pos[0] - playerpos.pos[0])*(actorpos.pos[0] - playerpos.pos[0]) - +(actorpos.pos[1] - playerpos.pos[1])*(actorpos.pos[1] - playerpos.pos[1]) - +(actorpos.pos[2] - playerpos.pos[2])*(actorpos.pos[2] - playerpos.pos[2])); - float fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified(); - float disp = 100; //creatures don't have disposition, so set it to 100 by default - if(ptr.getTypeName() == typeid(ESM::NPC).name()) - { - disp = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr); - } - bool LOS = MWBase::Environment::get().getWorld()->getLOS(ptr,MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); - if( ( (fight == 100 ) - || (fight >= 95 && d <= 3000) - || (fight >= 90 && d <= 2000) - || (fight >= 80 && d <= 1000) - || (fight >= 80 && disp <= 40) - || (fight >= 70 && disp <= 35 && d <= 1000) - || (fight >= 60 && disp <= 30 && d <= 1000) - || (fight >= 50 && disp == 0) - || (fight >= 40 && disp <= 10 && d <= 500) ) - && LOS - ) - { - creatureStats.getAiSequence().stack(AiCombat("player")); - creatureStats.setHostile(true); - } + disp = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr); + } + bool LOS = MWBase::Environment::get().getWorld()->getLOS(ptr,player) + && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr); + if( ( (fight == 100 ) + || (fight >= 95 && d <= 3000) + || (fight >= 90 && d <= 2000) + || (fight >= 80 && d <= 1000) + || (fight >= 80 && disp <= 40) + || (fight >= 70 && disp <= 35 && d <= 1000) + || (fight >= 60 && disp <= 30 && d <= 1000) + || (fight >= 50 && disp == 0) + || (fight >= 40 && disp <= 10 && d <= 500) ) + && LOS + ) + { + creatureStats.getAiSequence().stack(AiCombat("player")); + creatureStats.setHostile(true); } - - creatureStats.getAiSequence().execute (ptr,duration); } - // fatigue restoration - calculateRestoration(ptr, duration); + creatureStats.getAiSequence().execute (ptr,duration); } + + // fatigue restoration + calculateRestoration(ptr, duration); } void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 67f42fe0a..07d41505b 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -12,6 +12,8 @@ #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include + #include "spellcasting.hpp" namespace MWMechanics @@ -725,4 +727,72 @@ namespace MWMechanics { return mAI; } + + bool MechanicsManager::awarenessCheck(const MWWorld::Ptr &ptr, const MWWorld::Ptr &observer) + { + const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); + + CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); + + float invisibility = stats.getMagicEffects().get(ESM::MagicEffect::Invisibility).mMagnitude; + if (invisibility > 0) + return false; + + float sneakTerm = 0; + if (ptr.getClass().getStance(ptr, MWWorld::Class::Sneak)) + { + static float fSneakSkillMult = store.find("fSneakSkillMult")->getFloat(); + static float fSneakBootMult = store.find("fSneakBootMult")->getFloat(); + float sneak = 0; + if (ptr.getClass().isNpc()) + sneak = ptr.getClass().getNpcStats(ptr).getSkill(ESM::Skill::Sneak).getModified(); + int agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); + int luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + float bootWeight = 0; + if (ptr.getClass().isNpc()) + { + MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr); + MWWorld::ContainerStoreIterator it = inv.getSlot(MWWorld::InventoryStore::Slot_Boots); + if (it != inv.end()) + bootWeight = it->getClass().getWeight(*it); + } + sneakTerm = fSneakSkillMult * sneak + 0.2 * agility + 0.1 * luck + bootWeight * fSneakBootMult; + } + + static float fSneakDistBase = store.find("fSneakDistanceBase")->getFloat(); + static float fSneakDistMult = store.find("fSneakDistanceMultiplier")->getFloat(); + + Ogre::Vector3 pos1 (ptr.getRefData().getPosition().pos); + Ogre::Vector3 pos2 (observer.getRefData().getPosition().pos); + float distTerm = fSneakDistBase + fSneakDistMult * pos1.distance(pos2); + + float chameleon = stats.getMagicEffects().get(ESM::MagicEffect::Chameleon).mMagnitude; + float x = sneakTerm * distTerm * stats.getFatigueTerm() + chameleon + invisibility; + + CreatureStats& observerStats = observer.getClass().getCreatureStats(observer); + int obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified(); + int obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified(); + float obsBlind = observerStats.getMagicEffects().get(ESM::MagicEffect::Blind).mMagnitude; + int obsSneak = 0; + if (observer.getClass().isNpc()) + obsSneak = observer.getClass().getNpcStats(observer).getSkill(ESM::Skill::Sneak).getModified(); + + float obsTerm = obsSneak + 0.2 * obsAgility + 0.1 * obsLuck - obsBlind; + + // is ptr behind the observer? + static float fSneakNoViewMult = store.find("fSneakNoViewMult")->getFloat(); + static float fSneakViewMult = store.find("fSneakViewMult")->getFloat(); + float y = 0; + Ogre::Vector3 vec = pos1 - pos2; + Ogre::Radian angle = observer.getRefData().getBaseNode()->getOrientation().yAxis().angleBetween(vec); + if (angle < Ogre::Degree(90)) + y = obsTerm * observerStats.getFatigueTerm() * fSneakNoViewMult; + else + y = obsTerm * observerStats.getFatigueTerm() * fSneakViewMult; + + float target = x - y; + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + + return (roll >= target); + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index ec03b457b..63111f1cc 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -98,6 +98,9 @@ namespace MWMechanics void toLower(std::string npcFaction); ///< Perform a persuasion action on NPC + /// Check if \a observer is potentially aware of \a ptr. Does not do a line of sight check! + virtual bool awarenessCheck (const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer); + virtual void forceStateUpdate(const MWWorld::Ptr &ptr); virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 966a064c7..213f13882 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -358,19 +358,21 @@ namespace MWScript }; template - class OpGetDetected : public Interpreter::Opcode1 + class OpGetDetected : public Interpreter::Opcode0 { public: - virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) + virtual void execute (Interpreter::Runtime& runtime) { - + MWWorld::Ptr observer = R()(runtime); std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - Interpreter::Type_Integer value = false; // TODO replace with implementation + MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true); - std::cout << "AiGetDetected: " << actorID << ", " << value << std::endl; + Interpreter::Type_Integer value = + MWBase::Environment::get().getWorld()->getLOS(observer, actor) && + MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, observer); runtime.push (value); } @@ -432,8 +434,8 @@ namespace MWScript new OpGetAiPackageDone); interpreter.installSegment5 (Compiler::Ai::opcodeGetCurrentAiPackage, new OpGetCurrentAIPackage); interpreter.installSegment5 (Compiler::Ai::opcodeGetCurrentAiPackageExplicit, new OpGetCurrentAIPackage); - interpreter.installSegment3 (Compiler::Ai::opcodeGetDetected, new OpGetDetected); - interpreter.installSegment3 (Compiler::Ai::opcodeGetDetectedExplicit, new OpGetDetected); + interpreter.installSegment5 (Compiler::Ai::opcodeGetDetected, new OpGetDetected); + interpreter.installSegment5 (Compiler::Ai::opcodeGetDetectedExplicit, new OpGetDetected); interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSight, new OpGetLineOfSight); interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSightExplicit, new OpGetLineOfSight); interpreter.installSegment5 (Compiler::Ai::opcodeToggleAI, new OpToggleAI); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ba76eee88..2d3d3d07f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1840,13 +1840,6 @@ namespace MWWorld bool World::getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc) { - // This is a placeholder! Needs to go into an NPC awareness check function (see - // https://wiki.openmw.org/index.php?title=Research:NPC_AI_Behaviour#NPC_Awareness_Check ) - if (targetNpc.getClass().getCreatureStats(targetNpc).getMagicEffects().get(ESM::MagicEffect::Invisibility).mMagnitude) - return false; - if (targetNpc.getClass().getCreatureStats(targetNpc).getMagicEffects().get(ESM::MagicEffect::Chameleon).mMagnitude > 100) - return false; - Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(npc.getRefData().getHandle())->getHalfExtents(); float* pos1 = npc.getRefData().getPosition().pos; Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetNpc.getRefData().getHandle())->getHalfExtents(); From 887db76ed2084fb7b7d1b3e1a0832f9c20494381 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 01:20:13 +0100 Subject: [PATCH 392/889] Don't consider swimming or in-air characters as sneaking --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 07d41505b..884294df3 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -739,7 +739,9 @@ namespace MWMechanics return false; float sneakTerm = 0; - if (ptr.getClass().getStance(ptr, MWWorld::Class::Sneak)) + if (ptr.getClass().getStance(ptr, MWWorld::Class::Sneak) + && !MWBase::Environment::get().getWorld()->isSwimming(ptr) + && MWBase::Environment::get().getWorld()->isOnGround(ptr)) { static float fSneakSkillMult = store.find("fSneakSkillMult")->getFloat(); static float fSneakBootMult = store.find("fSneakBootMult")->getFloat(); From 5c7e39a92f0a59c108d87141b1715c741f9f98e3 Mon Sep 17 00:00:00 2001 From: Sergey Shambir Date: Tue, 7 Jan 2014 04:12:37 +0400 Subject: [PATCH 393/889] Implemented script commands StartCombat, StopCombat, GetTarget. Also renamed one field of AIWander class because it's not longer unknown. --- apps/esmtool/record.cpp | 4 +- apps/openmw/mwmechanics/aiactivate.cpp | 38 ++++++++--------- apps/openmw/mwmechanics/aicombat.cpp | 7 ++- apps/openmw/mwmechanics/aicombat.hpp | 4 +- apps/openmw/mwmechanics/aiescort.cpp | 2 +- apps/openmw/mwmechanics/aifollow.cpp | 34 +++++++-------- apps/openmw/mwmechanics/aipackage.hpp | 13 +++++- apps/openmw/mwmechanics/aisequence.cpp | 17 +++++++- apps/openmw/mwmechanics/aisequence.hpp | 9 +++- apps/openmw/mwmechanics/aitravel.cpp | 2 +- apps/openmw/mwmechanics/aiwander.cpp | 2 +- apps/openmw/mwscript/aiextensions.cpp | 59 ++++++++++++++++++++++++++ components/compiler/extensions0.cpp | 3 ++ components/compiler/opcodes.hpp | 6 +++ components/esm/aipackage.hpp | 2 +- 15 files changed, 154 insertions(+), 48 deletions(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index cc09452c9..a041bb2de 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -13,8 +13,8 @@ void printAIPackage(ESM::AIPackage p) std::cout << " Distance: " << p.mWander.mDistance << std::endl; std::cout << " Duration: " << p.mWander.mDuration << std::endl; std::cout << " Time of Day: " << (int)p.mWander.mTimeOfDay << std::endl; - if (p.mWander.mUnk != 1) - std::cout << " Unknown: " << (int)p.mWander.mUnk << std::endl; + if (p.mWander.mShouldRepeat != 1) + std::cout << " Unknown: " << (int)p.mWander.mShouldRepeat << std::endl; std::cout << " Idle: "; for (int i = 0; i != 8; i++) diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index ee0dcf96e..531ba5568 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -1,21 +1,21 @@ #include "aiactivate.hpp" -#include +#include -MWMechanics::AiActivate::AiActivate(const std::string &objectId) -: mObjectId(objectId) -{ -} -MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const -{ - return new AiActivate(*this); -} -bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) -{ - std::cout << "AiActivate completed.\n"; - return true; -} - -int MWMechanics::AiActivate::getTypeId() const -{ - return 4; -} +MWMechanics::AiActivate::AiActivate(const std::string &objectId) +: mObjectId(objectId) +{ +} +MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const +{ + return new AiActivate(*this); +} +bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) +{ + std::cout << "AiActivate completed.\n"; + return true; +} + +int MWMechanics::AiActivate::getTypeId() const +{ + return TypeIdActivate; +} diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 6f643aa68..3ce366580 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -135,7 +135,7 @@ namespace MWMechanics int AiCombat::getTypeId() const { - return 5; + return TypeIdCombat; } unsigned int AiCombat::getPriority() const @@ -143,6 +143,11 @@ namespace MWMechanics return 1; } + const std::string &AiCombat::getTargetId() const + { + return mTargetId; + } + AiCombat *MWMechanics::AiCombat::clone() const { return new AiCombat(*this); diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index fa71e261f..82efc043b 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -23,6 +23,8 @@ namespace MWMechanics virtual unsigned int getPriority() const; + const std::string &getTargetId() const; + private: std::string mTargetId; @@ -33,4 +35,4 @@ namespace MWMechanics }; } -#endif \ No newline at end of file +#endif diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 5099625c0..d91e02be2 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -179,7 +179,7 @@ namespace MWMechanics int AiEscort::getTypeId() const { - return 2; + return TypeIdEscort; } } diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 73bf9259a..c5b1f1bf3 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -1,7 +1,7 @@ #include "aifollow.hpp" -#include +#include -MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) +MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) : mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId) { } @@ -10,18 +10,18 @@ MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &ce { } -MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const -{ - return new AiFollow(*this); -} - - bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) -{ - std::cout << "AiFollow completed.\n"; - return true; -} - - int MWMechanics::AiFollow::getTypeId() const -{ - return 3; -} +MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const +{ + return new AiFollow(*this); +} + + bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) +{ + std::cout << "AiFollow completed.\n"; + return true; +} + + int MWMechanics::AiFollow::getTypeId() const +{ + return TypeIdFollow; +} diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 5832198da..74c77bf97 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -12,7 +12,16 @@ namespace MWMechanics class AiPackage { public: - + enum TypeId { + TypeIdNone = -1, + TypeIdWander = 0, + TypeIdTravel = 1, + TypeIdEscort = 2, + TypeIdFollow = 3, + TypeIdActivate = 4, + TypeIdCombat = 5 + }; + virtual ~AiPackage(); virtual AiPackage *clone() const = 0; @@ -21,7 +30,7 @@ namespace MWMechanics ///< \return Package completed? virtual int getTypeId() const = 0; - ///< 0: Wanter, 1 Travel, 2 Escort, 3 Follow, 4 Activate + ///< @see enum TypeId virtual unsigned int getPriority() const {return 0;} ///< higher number is higher priority (0 beeing the lowest) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 6d461e5f6..5d0686084 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -56,6 +56,21 @@ int MWMechanics::AiSequence::getTypeId() const return mPackages.front()->getTypeId(); } +bool MWMechanics::AiSequence::getCombatTarget(std::string &targetActorId) const +{ + if (getTypeId() != AiPackage::TypeIdCombat) + return false; + const AiCombat *combat = static_cast(mPackages.front()); + targetActorId = combat->getTargetId(); + return true; +} + +void MWMechanics::AiSequence::stopCombat() +{ + while (getTypeId() == AiPackage::TypeIdCombat) + mPackages.erase (mPackages.begin()); +} + bool MWMechanics::AiSequence::isPackageDone() const { return mDone; @@ -114,7 +129,7 @@ void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list) std::vector idles; for (int i=0; i<8; ++i) idles.push_back(data.mIdle[i]); - package = new MWMechanics::AiWander(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mUnk); + package = new MWMechanics::AiWander(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mShouldRepeat); } else if (it->mType == ESM::AI_Escort) { diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 0976ef099..d65c31616 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -34,7 +34,14 @@ namespace MWMechanics virtual ~AiSequence(); int getTypeId() const; - ///< -1: None, 0: Wanter, 1 Travel, 2 Escort, 3 Follow, 4 Activate, 5 Combat + ///< @see enum AiPackage::TypeId + + bool getCombatTarget (std::string &targetActorId) const; + ///< Return true and assign target if combat package is currently + /// active, return false otherwise + + void stopCombat(); + ///< Removes all combat packages until first non-combat or stack empty. bool isPackageDone() const; ///< Has a package been completed during the last update? diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index f56c75996..db577a32f 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -106,7 +106,7 @@ namespace MWMechanics int AiTravel::getTypeId() const { - return 1; + return TypeIdTravel; } } diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 93c94a3f4..e1f65a509 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -254,7 +254,7 @@ namespace MWMechanics int AiWander::getTypeId() const { - return 0; + return TypeIdWander; } void AiWander::stopWalking(const MWWorld::Ptr& actor) diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 966a064c7..3c95e9ca1 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -16,6 +16,7 @@ #include "../mwmechanics/aifollow.hpp" #include "../mwmechanics/aitravel.hpp" #include "../mwmechanics/aiwander.hpp" +#include "../mwmechanics/aicombat.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -399,6 +400,58 @@ namespace MWScript } }; + template + class OpGetTarget : public Interpreter::Opcode0 + { + public: + virtual void execute (Interpreter::Runtime &runtime) + { + MWWorld::Ptr actor = R()(runtime); + std::string testedTargetId = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + const MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); + std::string currentTargetId; + + bool targetsAreEqual = false; + if (creatureStats.getAiSequence().getCombatTarget (currentTargetId)) + { + if (currentTargetId == testedTargetId) + targetsAreEqual = true; + } + runtime.push(int(targetsAreEqual)); + } + }; + + template + class OpStartCombat : public Interpreter::Opcode0 + { + public: + virtual void execute (Interpreter::Runtime &runtime) + { + MWWorld::Ptr actor = R()(runtime); + std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); + creatureStats.getAiSequence().stack(MWMechanics::AiCombat(actorID)); + if (actorID == "player") + creatureStats.setHostile(true); + } + }; + + template + class OpStopCombat : public Interpreter::Opcode0 + { + public: + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr actor = R()(runtime); + MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); + creatureStats.getAiSequence().stopCombat(); + } + }; + template class OpToggleAI : public Interpreter::Opcode0 { @@ -436,6 +489,12 @@ namespace MWScript interpreter.installSegment3 (Compiler::Ai::opcodeGetDetectedExplicit, new OpGetDetected); interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSight, new OpGetLineOfSight); interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSightExplicit, new OpGetLineOfSight); + interpreter.installSegment5 (Compiler::Ai::opcodeGetTarget, new OpGetTarget); + interpreter.installSegment5 (Compiler::Ai::opcodeGetTargetExplicit, new OpGetTarget); + interpreter.installSegment5 (Compiler::Ai::opcodeStartCombat, new OpStartCombat); + interpreter.installSegment5 (Compiler::Ai::opcodeStartCombatExplicit, new OpStartCombat); + interpreter.installSegment5 (Compiler::Ai::opcodeStopCombat, new OpStopCombat); + interpreter.installSegment5 (Compiler::Ai::opcodeStopCombatExplicit, new OpStopCombat); interpreter.installSegment5 (Compiler::Ai::opcodeToggleAI, new OpToggleAI); interpreter.installSegment5 (Compiler::Ai::opcodeToggleAIExplicit, new OpToggleAI); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 6194be532..4ad9dfc5e 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -61,12 +61,15 @@ namespace Compiler extensions.registerInstruction ("modalarm", "l", opcodeModAlarm, opcodeModAlarmExplicit); extensions.registerInstruction ("toggleai", "", opcodeToggleAI, opcodeToggleAI); extensions.registerInstruction ("tai", "", opcodeToggleAI, opcodeToggleAI); + extensions.registerInstruction("startcombat", "c", opcodeStartCombat, opcodeStartCombatExplicit); + extensions.registerInstruction("stopcombat", "", opcodeStopCombat, opcodeStopCombatExplicit); extensions.registerFunction ("gethello", 'l', "", opcodeGetHello, opcodeGetHelloExplicit); extensions.registerFunction ("getfight", 'l', "", opcodeGetFight, opcodeGetFightExplicit); extensions.registerFunction ("getflee", 'l', "", opcodeGetFlee, opcodeGetFleeExplicit); extensions.registerFunction ("getalarm", 'l', "", opcodeGetAlarm, opcodeGetAlarmExplicit); extensions.registerFunction ("getlineofsight", 'l', "c", opcodeGetLineOfSight, opcodeGetLineOfSightExplicit); extensions.registerFunction ("getlos", 'l', "c", opcodeGetLineOfSight, opcodeGetLineOfSightExplicit); + extensions.registerFunction("gettarget", 'l', "c", opcodeGetTarget, opcodeGetTargetExplicit); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 46524c7cd..f84614c37 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -53,6 +53,12 @@ namespace Compiler const int opcodeGetLineOfSightExplicit = 0x2000223; const int opcodeToggleAI = 0x2000224; const int opcodeToggleAIExplicit = 0x2000225; + const int opcodeGetTarget = 0x2000b23; + const int opcodeGetTargetExplicit = 0x2000b24; + const int opcodeStartCombat = 0x2000b25; + const int opcodeStartCombatExplicit = 0x2000b26; + const int opcodeStopCombat = 0x2000b27; + const int opcodeStopCombatExplicit = 0x2000b28; } namespace Animation diff --git a/components/esm/aipackage.hpp b/components/esm/aipackage.hpp index b06cb529a..8a31aadf5 100644 --- a/components/esm/aipackage.hpp +++ b/components/esm/aipackage.hpp @@ -30,7 +30,7 @@ namespace ESM short mDuration; unsigned char mTimeOfDay; unsigned char mIdle[8]; - unsigned char mUnk; + unsigned char mShouldRepeat; }; struct AITravel From d536ff3cdcca8d203bdc0575962f0dd582805a77 Mon Sep 17 00:00:00 2001 From: Sergey Shambir Date: Tue, 7 Jan 2014 04:58:59 +0400 Subject: [PATCH 394/889] printAIPackage: changed field name from Unknown to ShouldRepeat too. --- apps/esmtool/record.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index a041bb2de..a5664c1c8 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -14,7 +14,7 @@ void printAIPackage(ESM::AIPackage p) std::cout << " Duration: " << p.mWander.mDuration << std::endl; std::cout << " Time of Day: " << (int)p.mWander.mTimeOfDay << std::endl; if (p.mWander.mShouldRepeat != 1) - std::cout << " Unknown: " << (int)p.mWander.mShouldRepeat << std::endl; + std::cout << " Should repeat: " << (bool)p.mWander.mShouldRepeat << std::endl; std::cout << " Idle: "; for (int i = 0; i != 8; i++) From d5a0ff17fde7204c6ac1ec3021a79ccb02237b1a Mon Sep 17 00:00:00 2001 From: Sergey Shambir Date: Tue, 7 Jan 2014 05:06:20 +0400 Subject: [PATCH 395/889] MWScript: updated vmformat.txt, changed opcodes to fix sequence. Opcodes for StartCombat, StopCombat, GetTarget now follow the last previous opcode. --- apps/openmw/mwscript/docs/vmformat.txt | 8 +++++++- components/compiler/opcodes.hpp | 12 ++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 504a8638b..3dc5492bf 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -371,4 +371,10 @@ op 0x2000230: Resurrect, explicit op 0x2000231: GetSpellReadied op 0x2000232: GetSpellReadied, explicit op 0x2000233: GetPcJumping -opcodes 0x2000234-0x3ffffff unused +op 0x2000234: GetTarget +op 0x2000235: GetTargetExplicit +op 0x2000236: StartCombat +op 0x2000237: StartCombatExplicit +op 0x2000238: StopCombat +op 0x2000239: StopCombatExplicit +opcodes 0x2000239-0x3ffffff unused diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index f84614c37..6f8f26e97 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -53,12 +53,12 @@ namespace Compiler const int opcodeGetLineOfSightExplicit = 0x2000223; const int opcodeToggleAI = 0x2000224; const int opcodeToggleAIExplicit = 0x2000225; - const int opcodeGetTarget = 0x2000b23; - const int opcodeGetTargetExplicit = 0x2000b24; - const int opcodeStartCombat = 0x2000b25; - const int opcodeStartCombatExplicit = 0x2000b26; - const int opcodeStopCombat = 0x2000b27; - const int opcodeStopCombatExplicit = 0x2000b28; + const int opcodeGetTarget = 0x2000234; + const int opcodeGetTargetExplicit = 0x2000235; + const int opcodeStartCombat = 0x2000236; + const int opcodeStartCombatExplicit = 0x2000237; + const int opcodeStopCombat = 0x2000238; + const int opcodeStopCombatExplicit = 0x2000239; } namespace Animation From ea3ee4407fa3d6600e967f1d440984fce7d4bb73 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 02:48:44 +0100 Subject: [PATCH 396/889] oops, didn't mean to commit this --- apps/openmw/mwclass/npc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 9d3002dfd..8a32f58b9 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -613,7 +613,7 @@ namespace MWClass // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying // something, alert the character controller, scripts, etc. - MWBase::Environment::get().getDialogueManager()->say(ptr, "thief"); + MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); if(object.isEmpty()) { From 780bf5a2cdcc0bdb642901685165f6882c4bbec8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 03:01:33 +0100 Subject: [PATCH 397/889] Implement pickpocket detection. Play a voiced dialogue entry when detected. --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwgui/container.cpp | 57 +++++++++++++++++- apps/openmw/mwgui/container.hpp | 6 ++ apps/openmw/mwmechanics/pickpocket.cpp | 67 ++++++++++++++++++++++ apps/openmw/mwmechanics/pickpocket.hpp | 30 ++++++++++ files/mygui/openmw_container_window.layout | 1 + 6 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 apps/openmw/mwmechanics/pickpocket.cpp create mode 100644 apps/openmw/mwmechanics/pickpocket.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 8c8a1b324..77d442eef 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -74,7 +74,7 @@ add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting - disease + disease pickpocket ) add_openmw_dir (mwbase diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index b7c6e3367..31cfd8bc9 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -6,10 +6,13 @@ #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/dialoguemanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include "../mwmechanics/pickpocket.hpp" + #include "countdialog.hpp" #include "tradewindow.hpp" #include "inventorywindow.hpp" @@ -123,6 +126,7 @@ namespace MWGui , mSelectedItem(-1) , mModel(NULL) , mSortModel(NULL) + , mPickpocketDetected(false) { getWidget(mDisposeCorpseButton, "DisposeCorpseButton"); getWidget(mTakeButton, "TakeButton"); @@ -171,6 +175,9 @@ namespace MWGui void ContainerWindow::dragItem(MyGUI::Widget* sender, int count) { + if (!onTakeItem(mModel->getItem(mSelectedItem), count)) + return; + mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count); } @@ -208,6 +215,7 @@ namespace MWGui void ContainerWindow::open(const MWWorld::Ptr& container, bool loot) { + mPickpocketDetected = false; mPtr = container; if (mPtr.getTypeName() == typeid(ESM::NPC).name() && !loot) @@ -230,6 +238,28 @@ namespace MWGui setTitle(MWWorld::Class::get(container).getName(container)); } + void ContainerWindow::close() + { + WindowBase::close(); + + // Make sure we were actually closed, rather than just temporarily hidden (e.g. console or main menu opened) + if (!MWBase::Environment::get().getWindowManager()->containsMode(GM_Container) + && !mPickpocketDetected // If it was already detected while taking an item, no need to check now + ) + { + MWMechanics::Pickpocket pickpocket(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), + mPtr); + if (pickpocket.finish()) + { + // TODO: crime + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); + MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); + mPickpocketDetected = true; + return; + } + } + } + void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender) { if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) @@ -255,8 +285,13 @@ namespace MWGui MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); } - playerModel->copyItem(mModel->getItem(i), mModel->getItem(i).mCount); - mModel->removeItem(mModel->getItem(i), mModel->getItem(i).mCount); + const ItemStack& item = mModel->getItem(i); + + if (!onTakeItem(item, item.mCount)) + break; + + playerModel->copyItem(item, item.mCount); + mModel->removeItem(item, item.mCount); } MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); @@ -283,4 +318,22 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } + bool ContainerWindow::onTakeItem(const ItemStack &item, int count) + { + if (dynamic_cast(mModel)) + { + MWMechanics::Pickpocket pickpocket(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), + mPtr); + if (pickpocket.pick(item.mBase, count)) + { + // TODO: crime + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); + MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); + mPickpocketDetected = true; + return false; + } + } + return true; + } + } diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 243f77aa5..f934d8828 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -52,10 +52,13 @@ namespace MWGui ContainerWindow(DragAndDrop* dragAndDrop); void open(const MWWorld::Ptr& container, bool loot=false); + virtual void close(); private: DragAndDrop* mDragAndDrop; + bool mPickpocketDetected; + MWGui::ItemView* mItemView; SortFilterItemModel* mSortModel; ItemModel* mModel; @@ -73,6 +76,9 @@ namespace MWGui void onTakeAllButtonClicked(MyGUI::Widget* _sender); void onDisposeCorpseButtonClicked(MyGUI::Widget* sender); + /// @return is taking the item allowed? + bool onTakeItem(const ItemStack& item, int count); + virtual void onReferenceUnavailable(); }; } diff --git a/apps/openmw/mwmechanics/pickpocket.cpp b/apps/openmw/mwmechanics/pickpocket.cpp new file mode 100644 index 000000000..8e8a70d88 --- /dev/null +++ b/apps/openmw/mwmechanics/pickpocket.cpp @@ -0,0 +1,67 @@ +#include "pickpocket.hpp" + +#include "../mwworld/class.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "npcstats.hpp" + +namespace MWMechanics +{ + + Pickpocket::Pickpocket(const MWWorld::Ptr &thief, const MWWorld::Ptr &victim) + : mThief(thief) + , mVictim(victim) + { + } + + float Pickpocket::getChanceModifier(const MWWorld::Ptr &ptr, float add) + { + NpcStats& stats = ptr.getClass().getNpcStats(ptr); + float agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); + float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + float sneak = stats.getSkill(ESM::Skill::Sneak).getModified(); + return (add + 0.2 * agility + 0.1 * luck + sneak) * stats.getFatigueTerm(); + } + + bool Pickpocket::getDetected(float valueTerm) + { + float x = getChanceModifier(mThief); + float y = getChanceModifier(mVictim, valueTerm); + + float t = 2*x - y; + + NpcStats& pcStats = mThief.getClass().getNpcStats(mThief); + float pcSneak = pcStats.getSkill(ESM::Skill::Sneak).getModified(); + int iPickMinChance = MWBase::Environment::get().getWorld()->getStore().get() + .find("iPickMinChance")->getInt(); + int iPickMaxChance = MWBase::Environment::get().getWorld()->getStore().get() + .find("iPickMaxChance")->getInt(); + + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (t < pcSneak / iPickMinChance) + { + return (roll > int(pcSneak / iPickMinChance)); + } + else + { + t = std::min(float(iPickMaxChance), t); + return (roll > int(t)); + } + } + + bool Pickpocket::pick(MWWorld::Ptr item, int count) + { + float stackValue = item.getClass().getValue(item) * count; + float fPickPocketMod = MWBase::Environment::get().getWorld()->getStore().get() + .find("fPickPocketMod")->getFloat(); + float valueTerm = 10 * fPickPocketMod * stackValue; + + return getDetected(valueTerm); + } + + bool Pickpocket::finish() + { + return getDetected(0.f); + } + +} diff --git a/apps/openmw/mwmechanics/pickpocket.hpp b/apps/openmw/mwmechanics/pickpocket.hpp new file mode 100644 index 000000000..4de1e37f8 --- /dev/null +++ b/apps/openmw/mwmechanics/pickpocket.hpp @@ -0,0 +1,30 @@ +#ifndef OPENMW_MECHANICS_PICKPOCKET_H +#define OPENMW_MECHANICS_PICKPOCKET_H + +#include "../mwworld/ptr.hpp" + +namespace MWMechanics +{ + + class Pickpocket + { + public: + Pickpocket (const MWWorld::Ptr& thief, const MWWorld::Ptr& victim); + + /// Steal some items + /// @return Was the thief detected? + bool pick (MWWorld::Ptr item, int count); + /// End the pickpocketing process + /// @return Was the thief detected? + bool finish (); + + private: + bool getDetected(float valueTerm); + float getChanceModifier(const MWWorld::Ptr& ptr, float add=0); + MWWorld::Ptr mThief; + MWWorld::Ptr mVictim; + }; + +} + +#endif diff --git a/files/mygui/openmw_container_window.layout b/files/mygui/openmw_container_window.layout index 06cc04ebe..87651b0f2 100644 --- a/files/mygui/openmw_container_window.layout +++ b/files/mygui/openmw_container_window.layout @@ -3,6 +3,7 @@ + From 9afa8e952e1d54f77c8f5506646ec1de95e51d27 Mon Sep 17 00:00:00 2001 From: Dmitriy 'Endorph' Shkurskiy Date: Tue, 7 Jan 2014 14:32:14 +0200 Subject: [PATCH 398/889] tabs -> spaces --- apps/openmw/mwworld/containerstore.cpp | 54 +++++++++++++------------- apps/openmw/mwworld/containerstore.hpp | 8 ++-- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 39fda7ab9..f86ccfb54 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -557,7 +557,7 @@ MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *contain MWWorld::ContainerStoreIterator::ContainerStoreIterator( const ContainerStoreIterator& src ) { - copy(src); + copy(src); } void MWWorld::ContainerStoreIterator::incType() @@ -814,37 +814,37 @@ const MWWorld::ContainerStore *MWWorld::ContainerStoreIterator::getContainerStor void MWWorld::ContainerStoreIterator::copy(const ContainerStoreIterator& src) { - mType = src.mType; - mMask = src.mMask; - mContainer = src.mContainer; - mPtr = src.mPtr; + mType = src.mType; + mMask = src.mMask; + mContainer = src.mContainer; + mPtr = src.mPtr; - switch (mType) - { - case MWWorld::ContainerStore::Type_Potion: mPotion = src.mPotion; break; - case MWWorld::ContainerStore::Type_Apparatus: mApparatus = src.mApparatus; break; - case MWWorld::ContainerStore::Type_Armor: mArmor = src.mArmor; break; - case MWWorld::ContainerStore::Type_Book: mBook = src.mBook; break; - case MWWorld::ContainerStore::Type_Clothing: mClothing = src.mClothing; break; - case MWWorld::ContainerStore::Type_Ingredient: mIngredient = src.mIngredient; break; - case MWWorld::ContainerStore::Type_Light: mLight = src.mLight; break; - case MWWorld::ContainerStore::Type_Lockpick: mLockpick = src.mLockpick; break; - case MWWorld::ContainerStore::Type_Miscellaneous: mMiscellaneous = src.mMiscellaneous; break; - case MWWorld::ContainerStore::Type_Probe: mProbe = src.mProbe; break; - case MWWorld::ContainerStore::Type_Repair: mRepair = src.mRepair; break; - case MWWorld::ContainerStore::Type_Weapon: mWeapon = src.mWeapon; break; - case -1: break; - default: assert(0); - } + switch (mType) + { + case MWWorld::ContainerStore::Type_Potion: mPotion = src.mPotion; break; + case MWWorld::ContainerStore::Type_Apparatus: mApparatus = src.mApparatus; break; + case MWWorld::ContainerStore::Type_Armor: mArmor = src.mArmor; break; + case MWWorld::ContainerStore::Type_Book: mBook = src.mBook; break; + case MWWorld::ContainerStore::Type_Clothing: mClothing = src.mClothing; break; + case MWWorld::ContainerStore::Type_Ingredient: mIngredient = src.mIngredient; break; + case MWWorld::ContainerStore::Type_Light: mLight = src.mLight; break; + case MWWorld::ContainerStore::Type_Lockpick: mLockpick = src.mLockpick; break; + case MWWorld::ContainerStore::Type_Miscellaneous: mMiscellaneous = src.mMiscellaneous; break; + case MWWorld::ContainerStore::Type_Probe: mProbe = src.mProbe; break; + case MWWorld::ContainerStore::Type_Repair: mRepair = src.mRepair; break; + case MWWorld::ContainerStore::Type_Weapon: mWeapon = src.mWeapon; break; + case -1: break; + default: assert(0); + } } MWWorld::ContainerStoreIterator& MWWorld::ContainerStoreIterator::operator=( const ContainerStoreIterator& rhs ) { - if (this!=&rhs) - { - copy(rhs); - } - return *this; + if (this!=&rhs) + { + copy(rhs); + } + return *this; } bool MWWorld::operator== (const ContainerStoreIterator& left, const ContainerStoreIterator& right) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 780d46199..a4c30edd4 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -143,10 +143,6 @@ namespace MWWorld MWWorld::CellRefList::List::iterator mRepair; MWWorld::CellRefList::List::iterator mWeapon; - public: - - ContainerStoreIterator(const ContainerStoreIterator& src); - private: ContainerStoreIterator (ContainerStore *container); @@ -187,6 +183,8 @@ namespace MWWorld public: + ContainerStoreIterator(const ContainerStoreIterator& src); + Ptr *operator->() const; Ptr operator*() const; @@ -195,7 +193,7 @@ namespace MWWorld ContainerStoreIterator operator++ (int); - ContainerStoreIterator& operator= (const ContainerStoreIterator& rhs); + ContainerStoreIterator& operator= (const ContainerStoreIterator& rhs); bool isEqual (const ContainerStoreIterator& iter) const; From 90b55c8d4b344c6bd26f0cd7a24b91b97a2bfce7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 16:37:51 +0100 Subject: [PATCH 399/889] Use Ogre's asin/acos functions which will protect against NaNs --- apps/openmw/mwmechanics/aicombat.cpp | 8 ++++---- apps/openmw/mwmechanics/pathfinding.cpp | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 6f643aa68..df45b7133 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -12,13 +12,13 @@ #include "creaturestats.hpp" #include "npcstats.hpp" -#include "OgreMath.h" +#include namespace { - static float sgn(float a) + static float sgn(Ogre::Radian a) { - if(a > 0) + if(a.valueDegrees() > 0) return 1.0; return -1.0; } @@ -106,7 +106,7 @@ namespace MWMechanics float directionY = dest.mY - start.mY; float directionResult = sqrt(directionX * directionX + directionY * directionY); - zAngle = Ogre::Radian( acos(directionY / directionResult) * sgn(asin(directionX / directionResult)) ).valueDegrees(); + zAngle = Ogre::Radian( Ogre::Math::ACos(directionY / directionResult) * sgn(Ogre::Math::ASin(directionX / directionResult)) ).valueDegrees(); // TODO: use movement settings instead of rotating directly MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index ff266f9ae..18e66d228 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -53,9 +53,9 @@ namespace return sqrt(x * x + y * y + z * z); } - static float sgn(float a) + static float sgn(Ogre::Radian a) { - if(a > 0) + if(a.valueRadians() > 0) return 1.0; return -1.0; } @@ -196,7 +196,7 @@ namespace MWMechanics float directionY = nextPoint.mY - y; float directionResult = sqrt(directionX * directionX + directionY * directionY); - return Ogre::Radian(acos(directionY / directionResult) * sgn(asin(directionX / directionResult))).valueDegrees(); + return Ogre::Radian(Ogre::Math::ACos(directionY / directionResult) * sgn(Ogre::Math::ASin(directionX / directionResult))).valueDegrees(); } bool PathFinder::checkWaypoint(float x, float y, float z) From d97615d5d87604caeffc1ad303e0ef6ea510680d Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 18:05:44 +0100 Subject: [PATCH 400/889] Support separate specular maps --- files/materials/objects.mat | 7 +++++++ files/materials/objects.shader | 25 +++++++++++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/files/materials/objects.mat b/files/materials/objects.mat index 751b51243..2281226b0 100644 --- a/files/materials/objects.mat +++ b/files/materials/objects.mat @@ -8,6 +8,7 @@ material openmw_objects_base diffuseMap black.png normalMap emissiveMap + specMap darkMap use_emissive_map false use_detail_map false @@ -44,6 +45,7 @@ material openmw_objects_base emissiveMap $emissiveMap detailMap $detailMap diffuseMap $diffuseMap + specMap $specMap darkMap $darkMap env_map $env_map env_map_color $env_map_color @@ -107,6 +109,11 @@ material openmw_objects_base anim_texture2 textures\magicitem\caust.dds 32 2 colour_op add } + + texture_unit specMap + { + direct_texture $specMap + } texture_unit shadowMap0 { diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 93368f1f6..ed75babdd 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -14,11 +14,14 @@ #define NEED_DEPTH #endif +#define SPECULAR 1 + #define NORMAL_MAP @shPropertyHasValue(normalMap) #define EMISSIVE_MAP @shPropertyHasValue(emissiveMap) #define DETAIL_MAP @shPropertyHasValue(detailMap) #define DIFFUSE_MAP @shPropertyHasValue(diffuseMap) #define DARK_MAP @shPropertyHasValue(darkMap) +#define SPEC_MAP @shPropertyHasValue(specMap) && SPECULAR #define PARALLAX @shPropertyBool(use_parallax) #define PARALLAX_SCALE 0.04 @@ -38,8 +41,6 @@ #define ENV_MAP @shPropertyBool(env_map) -#define SPECULAR 1 - #define NEED_NORMAL (!VERTEX_LIGHTING || ENV_MAP) || SPECULAR #ifdef SH_VERTEX_SHADER @@ -273,6 +274,10 @@ shUniform(float3, env_map_color) @shUniformProperty3f(env_map_color, env_map_color) #endif +#if SPEC_MAP + shSampler2D(specMap) +#endif + #if ENV_MAP || SPECULAR || PARALLAX shUniform(float3, cameraPosObjSpace) @shAutoConstant(cameraPosObjSpace, camera_position_object_space) #endif @@ -511,8 +516,20 @@ float NdotL = max(dot(normal, light0Dir), 0); float3 halfVec = normalize (light0Dir + eyeDir); - float3 specular = pow(max(dot(normal, halfVec), 0), matShininess) * lightSpec0 * matSpec; - shOutputColour(0).xyz += specular * shadow * diffuse.a; + float shininess = matShininess; +#if SPEC_MAP + float4 specTex = shSample(specMap, UV.xy); + shininess *= (specTex.a); +#endif + + float3 specular = pow(max(dot(normal, halfVec), 0), shininess) * lightSpec0 * matSpec; +#if SPEC_MAP + specular *= specTex.xyz; +#else + specular *= diffuse.a; +#endif + + shOutputColour(0).xyz += specular * shadow; #endif #if FOG From c4ab2f417aec0504910c8df334c26f76dd5329e3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 18:11:19 +0100 Subject: [PATCH 401/889] Fix exception closing container window --- apps/openmw/mwgui/container.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 31cfd8bc9..b4de0aa62 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -242,9 +242,11 @@ namespace MWGui { WindowBase::close(); - // Make sure we were actually closed, rather than just temporarily hidden (e.g. console or main menu opened) - if (!MWBase::Environment::get().getWindowManager()->containsMode(GM_Container) - && !mPickpocketDetected // If it was already detected while taking an item, no need to check now + if (dynamic_cast(mModel) + // Make sure we were actually closed, rather than just temporarily hidden (e.g. console or main menu opened) + && !MWBase::Environment::get().getWindowManager()->containsMode(GM_Container) + // If it was already detected while taking an item, no need to check now + && !mPickpocketDetected ) { MWMechanics::Pickpocket pickpocket(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), From d2d76f4f4787a2029860e38906e138036114ca76 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 19:21:19 +0100 Subject: [PATCH 402/889] Fix disintegration bug --- apps/openmw/mwmechanics/actors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 4c9c73ca1..74b459e6a 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -80,7 +80,7 @@ bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate) return true; } } - return true; + return false; } From 3c0080d2c17bcfd8a02e372ecb300ab942c53139 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 19:49:16 +0100 Subject: [PATCH 403/889] Implement theft detection --- apps/openmw/mwbase/mechanicsmanager.hpp | 21 ++++++ apps/openmw/mwgui/container.cpp | 21 ++++-- apps/openmw/mwgui/inventorywindow.cpp | 3 + apps/openmw/mwmechanics/actors.hpp | 12 +++- .../mwmechanics/mechanicsmanagerimp.cpp | 66 +++++++++++++++++++ .../mwmechanics/mechanicsmanagerimp.hpp | 12 ++++ apps/openmw/mwworld/actiontake.cpp | 4 +- 7 files changed, 129 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 90f0caee6..a0abb8e48 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -91,6 +91,27 @@ namespace MWBase /// Check if \a observer is potentially aware of \a ptr. Does not do a line of sight check! virtual bool awarenessCheck (const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer) = 0; + enum OffenseType + { + OT_Theft, // Taking items owned by an NPC or a faction you are not a member of + OT_Assault, // Attacking a peaceful NPC + OT_Murder, // Murdering a peaceful NPC + OT_Trespassing, // Staying in a cell you are not allowed in (where is this defined?) + OT_SleepingInOwnedBed, // Sleeping in a bed owned by an NPC or a faction you are not a member of + OT_Pickpocket // Entering pickpocket mode, leaving it, and being detected. Any items stolen are a separate crime (Theft) + }; + /** + * @brief Commit a crime. If any actors witness the crime and report it, + * reportCrime will be called automatically. + * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. + */ + virtual void commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, + OffenseType type, int arg=0) = 0; + virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, + OffenseType type, int arg=0) = 0; + /// Utility to check if taking this item is illegal and calling commitCrime if so + virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count) = 0; + enum PersuasionType { PT_Admire, diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index b4de0aa62..aa80388f3 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -7,9 +7,11 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/containerstore.hpp" #include "../mwmechanics/pickpocket.hpp" @@ -249,11 +251,12 @@ namespace MWGui && !mPickpocketDetected ) { - MWMechanics::Pickpocket pickpocket(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), - mPtr); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::Pickpocket pickpocket(player, mPtr); if (pickpocket.finish()) { - // TODO: crime + MWBase::Environment::get().getMechanicsManager()->reportCrime( + player, MWWorld::Ptr(), MWBase::MechanicsManager::OT_Pickpocket); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); mPickpocketDetected = true; @@ -322,19 +325,25 @@ namespace MWGui bool ContainerWindow::onTakeItem(const ItemStack &item, int count) { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); if (dynamic_cast(mModel)) { - MWMechanics::Pickpocket pickpocket(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), - mPtr); + MWMechanics::Pickpocket pickpocket(player, mPtr); if (pickpocket.pick(item.mBase, count)) { - // TODO: crime + int value = item.mBase.getClass().getValue(item.mBase) * count; + MWBase::Environment::get().getMechanicsManager()->reportCrime( + player, MWWorld::Ptr(), MWBase::MechanicsManager::OT_Theft, value); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); mPickpocketDetected = true; return false; } } + else + { + MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item.mBase, count); + } return true; } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 21da53c6d..33814455a 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -8,6 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" @@ -533,6 +534,8 @@ namespace MWGui if (i == mTradeModel->getItemCount()) throw std::runtime_error("Added item not found"); mDragAndDrop->startDrag(i, mSortModel, mTradeModel, mItemView, count); + + MWBase::Environment::get().getMechanicsManager()->itemTaken(player, newObject, count); } MyGUI::IntCoord InventoryWindow::getAvatarScreenCoord () diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 83aff63e3..7046543e6 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -25,9 +25,6 @@ namespace MWMechanics { class Actors { - typedef std::map PtrControllerMap; - PtrControllerMap mActors; - std::map mDeathCount; void updateNpc(const MWWorld::Ptr &ptr, float duration, bool paused); @@ -50,6 +47,11 @@ namespace MWMechanics Actors(); ~Actors(); + typedef std::map PtrControllerMap; + + PtrControllerMap::const_iterator begin() { return mActors.begin(); } + PtrControllerMap::const_iterator end() { return mActors.end(); } + /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently /// paused we may want to do it manually (after equipping permanent enchantment) void updateMagicEffects (const MWWorld::Ptr& ptr) { adjustMagicEffects(ptr); } @@ -88,6 +90,10 @@ namespace MWMechanics void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); void skipAnimation(const MWWorld::Ptr& ptr); bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName); + + private: + PtrControllerMap mActors; + }; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 884294df3..94ea66067 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -728,6 +728,72 @@ namespace MWMechanics return mAI; } + void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, int count) + { + const std::string& owner = item.getCellRef().mOwner; + if (owner.empty()) + return; + const std::string& faction = item.getCellRef().mFaction; + if (faction.empty()) + return; + const std::map& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks(); + if (factions.find(Misc::StringUtils::lowerCase(faction)) != factions.end()) + return; + + MWWorld::Ptr victim = MWBase::Environment::get().getWorld()->getPtr(owner, true); + + commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); + } + + void MechanicsManager::commitCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) + { + bool reported=false; + for (Actors::PtrControllerMap::const_iterator it = mActors.begin(); it != mActors.end(); ++it) + { + if (it->first != ptr && awarenessCheck(ptr, it->first)) + { + // NPCs will always curse you when they notice you steal their items, even if they don't report the crime + if (it->first == victim && type == OT_Theft) + { + MWBase::Environment::get().getDialogueManager()->say(victim, "Thief"); + } + + // Actor has witnessed a crime. Will he report it? + // (not sure, is > 0 correct?) + if (it->first.getClass().getCreatureStats(it->first).getAiSetting(CreatureStats::AI_Alarm).getModified() > 0 + // This is a bit inconsistent, but AFAIK assaulted NPCs can not report if they are alone + && (type != OT_Assault || it->first != victim) + ) + { + reported=true; + break; + } + } + } + + if (reported) + reportCrime(ptr, victim, type, arg); + } + + void MechanicsManager::reportCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) + { + // Bounty for each type of crime + if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) + arg = 5; + else if (type == OT_Pickpocket) + arg = 25; + else if (type == OT_Assault) + arg = 40; + else if (type == OT_Murder) + arg = 1000; + + MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}"); + ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty() + + arg); + + // TODO: make any guards in the area try to arrest the player + } + bool MechanicsManager::awarenessCheck(const MWWorld::Ptr &ptr, const MWWorld::Ptr &observer) { const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 63111f1cc..198a62d84 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -101,6 +101,18 @@ namespace MWMechanics /// Check if \a observer is potentially aware of \a ptr. Does not do a line of sight check! virtual bool awarenessCheck (const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer); + /** + * @brief Commit a crime. If any actors witness the crime and report it, + * reportCrime will be called automatically. + * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. + */ + virtual void commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, + OffenseType type, int arg=0); + virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, + OffenseType type, int arg=0); + /// Utility to check if taking this item is illegal and calling commitCrime if so + virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count); + virtual void forceStateUpdate(const MWWorld::Ptr &ptr); virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); diff --git a/apps/openmw/mwworld/actiontake.cpp b/apps/openmw/mwworld/actiontake.cpp index 867a046cf..548a94981 100644 --- a/apps/openmw/mwworld/actiontake.cpp +++ b/apps/openmw/mwworld/actiontake.cpp @@ -4,6 +4,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "class.hpp" #include "containerstore.hpp" @@ -14,8 +15,9 @@ namespace MWWorld void ActionTake::executeImp (const Ptr& actor) { + MWBase::Environment::get().getMechanicsManager()->itemTaken( + actor, getTarget(), getTarget().getRefData().getCount()); actor.getClass().getContainerStore (actor).add (getTarget(), getTarget().getRefData().getCount(), actor); - MWBase::Environment::get().getWorld()->deleteObject (getTarget()); } } From 0285d18fc22bd2b16b99c2dd6af580cbeeb1a9aa Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 20:24:01 +0100 Subject: [PATCH 404/889] Respect items belonging to a faction --- apps/openmw/mwclass/activator.cpp | 4 ++++ apps/openmw/mwclass/apparatus.cpp | 1 + apps/openmw/mwclass/armor.cpp | 1 + apps/openmw/mwclass/book.cpp | 1 + apps/openmw/mwclass/clothing.cpp | 1 + apps/openmw/mwclass/container.cpp | 3 ++- apps/openmw/mwclass/creature.cpp | 4 +++- apps/openmw/mwclass/ingredient.cpp | 1 + apps/openmw/mwclass/light.cpp | 1 + apps/openmw/mwclass/lockpick.cpp | 1 + apps/openmw/mwclass/misc.cpp | 1 + apps/openmw/mwclass/npc.cpp | 4 +++- apps/openmw/mwclass/potion.cpp | 1 + apps/openmw/mwclass/probe.cpp | 1 + apps/openmw/mwclass/repair.cpp | 1 + apps/openmw/mwclass/weapon.cpp | 1 + .../mwmechanics/mechanicsmanagerimp.cpp | 21 ++++++++++++------- apps/openmw/mwworld/containerstore.cpp | 12 ++++++----- apps/openmw/mwworld/containerstore.hpp | 4 ++-- components/esm/cellref.hpp | 4 ++-- 20 files changed, 49 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 583cb08d3..8bc104d25 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -96,7 +96,11 @@ namespace MWClass std::string text; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) + { + text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); + } info.text = text; return info; diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 53c62273d..b3a1af288 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -128,6 +128,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 0fae1b05c..13d19d86d 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -252,6 +252,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 1da920970..429d91259 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -140,6 +140,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index ffa96260d..b3278d07a 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -195,6 +195,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 55b15a7d2..d129e617d 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -53,7 +53,7 @@ namespace MWClass ptr.get(); data->mContainerStore.fill( - ref->mBase->mInventory, ptr.getCellRef().mOwner, MWBase::Environment::get().getWorld()->getStore()); + ref->mBase->mInventory, ptr.getCellRef().mOwner, ptr.getCellRef().mFaction, MWBase::Environment::get().getWorld()->getStore()); // store ptr.getRefData().setCustomData (data.release()); @@ -216,6 +216,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 98919d6f4..864a756c5 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -95,9 +95,11 @@ namespace MWClass data->mCreatureStats.getSpells().add (*iter); // inventory - data->mContainerStore.fill(ref->mBase->mInventory, getId(ptr), + data->mContainerStore.fill(ref->mBase->mInventory, getId(ptr), "", MWBase::Environment::get().getWorld()->getStore()); + // TODO: this is not quite correct, in vanilla the merchant's gold pool is not available in his inventory. + // (except for gold you gave him) data->mContainerStore.add("gold_001", ref->mBase->mData.mGold, ptr); // store diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 4296d4e1b..c6ec3deb9 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -149,6 +149,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 6a6133cb9..cc56ec4c8 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -187,6 +187,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index e1dc5b2e1..795b66052 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -145,6 +145,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index d21189103..e5120462e 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -184,6 +184,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 8a32f58b9..24caed3f5 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -320,12 +320,14 @@ namespace MWClass data->mNpcStats.getSpells().add (*iter); // inventory - data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr), + data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr), "", MWBase::Environment::get().getWorld()->getStore()); // store ptr.getRefData().setCustomData (data.release()); + // TODO: this is not quite correct, in vanilla the merchant's gold pool is not available in his inventory. + // (except for gold you gave him) getContainerStore(ptr).add("gold_001", gold, ptr); getInventoryStore(ptr).autoEquip(ptr); diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index e276c58aa..457a1c696 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -153,6 +153,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index b54464acd..4209c1431 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -144,6 +144,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index ce2b4ff10..5f2065c3c 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -148,6 +148,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 5e93e0d81..82935673c 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -355,6 +355,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 94ea66067..6f15becff 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -731,16 +731,23 @@ namespace MWMechanics void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, int count) { const std::string& owner = item.getCellRef().mOwner; - if (owner.empty()) - return; + bool isOwned = !owner.empty(); + const std::string& faction = item.getCellRef().mFaction; - if (faction.empty()) - return; - const std::map& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks(); - if (factions.find(Misc::StringUtils::lowerCase(faction)) != factions.end()) + bool isFactionOwned = false; + if (!faction.empty()) + { + const std::map& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks(); + if (factions.find(Misc::StringUtils::lowerCase(faction)) == factions.end()) + isFactionOwned = true; + } + + if (!isOwned && !isFactionOwned) return; - MWWorld::Ptr victim = MWBase::Environment::get().getWorld()->getPtr(owner, true); + MWWorld::Ptr victim; + if (!owner.empty()) + victim = MWBase::Environment::get().getWorld()->getPtr(owner, true); commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 3d5af7bf1..ee4080755 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -279,19 +279,20 @@ int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor return count - toRemove; } -void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::string& owner, const MWWorld::ESMStore& store) +void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::string& owner, const std::string& faction, const MWWorld::ESMStore& store) { for (std::vector::const_iterator iter (items.mList.begin()); iter!=items.mList.end(); ++iter) { std::string id = iter->mItem.toString(); - addInitialItem(id, owner, iter->mCount); + addInitialItem(id, owner, faction, iter->mCount); } flagAsModified(); } -void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, int count, unsigned char failChance, bool topLevel) +void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, + int count, unsigned char failChance, bool topLevel) { count = std::abs(count); /// \todo implement item restocking (indicated by negative count) @@ -312,7 +313,7 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: if (topLevel && count > 1 && levItem->mFlags & ESM::ItemLevList::Each) { for (int i=0; i Date: Tue, 7 Jan 2014 20:43:08 +0100 Subject: [PATCH 405/889] Get rid of a hack --- apps/openmw/mwrender/renderingmanager.cpp | 8 +------- apps/openmw/mwworld/cells.cpp | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index a40535030..a68c6af28 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -19,7 +19,6 @@ #include -#include #include #include @@ -407,12 +406,7 @@ void RenderingManager::postRenderTargetUpdate(const RenderTargetEvent &evt) void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store) { - const MWWorld::Store &lands = - MWBase::Environment::get().getWorld()->getStore().get(); - - if(store->mCell->mData.mFlags & ESM::Cell::HasWater - || ((store->mCell->isExterior()) - && !lands.search(store->mCell->getGridX(),store->mCell->getGridY()) )) // always use water, if the cell does not have land. + if(store->mCell->mData.mFlags & ESM::Cell::HasWater) { mWater->changeCell(store->mCell); mWater->setActive(true); diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 865c0d01f..621ff3b31 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -79,7 +79,7 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y) // Cell isn't predefined. Make one on the fly. ESM::Cell record; - record.mData.mFlags = 0; + record.mData.mFlags = ESM::Cell::HasWater; record.mData.mX = x; record.mData.mY = y; record.mWater = 0; From 728365b48dadeec68279fe445a6bb45f70491299 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 21:07:02 +0100 Subject: [PATCH 406/889] Remove an unused hook --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/world.hpp | 3 --- apps/openmw/mwrender/characterpreview.hpp | 2 -- apps/openmw/mwrender/externalrendering.hpp | 23 ---------------------- apps/openmw/mwrender/renderingmanager.cpp | 6 ------ apps/openmw/mwrender/renderingmanager.hpp | 3 --- apps/openmw/mwworld/worldimp.cpp | 5 ----- apps/openmw/mwworld/worldimp.hpp | 2 -- 8 files changed, 1 insertion(+), 45 deletions(-) delete mode 100644 apps/openmw/mwrender/externalrendering.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 77d442eef..eb5b71ec3 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -19,7 +19,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation actors objects renderinginterface localmap occlusionquery water shadows - characterpreview externalrendering globalmap videoplayer ripplesimulation refraction + characterpreview globalmap videoplayer ripplesimulation refraction terrainstorage renderconst ) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 3a8897114..39919012e 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -40,7 +40,6 @@ namespace ESM namespace MWRender { - class ExternalRendering; class Animation; } @@ -366,8 +365,6 @@ namespace MWBase virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable) = 0; - virtual void setupExternalRendering (MWRender::ExternalRendering& rendering) = 0; - virtual int canRest() = 0; ///< check if the player is allowed to rest \n /// 0 - yes \n diff --git a/apps/openmw/mwrender/characterpreview.hpp b/apps/openmw/mwrender/characterpreview.hpp index b2dfc9679..cd30cdf46 100644 --- a/apps/openmw/mwrender/characterpreview.hpp +++ b/apps/openmw/mwrender/characterpreview.hpp @@ -6,8 +6,6 @@ #include -#include "externalrendering.hpp" - #include "../mwworld/ptr.hpp" namespace OEngine diff --git a/apps/openmw/mwrender/externalrendering.hpp b/apps/openmw/mwrender/externalrendering.hpp deleted file mode 100644 index 33c9afd87..000000000 --- a/apps/openmw/mwrender/externalrendering.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef GAME_RENDERING_EXTERNALRENDERING_H -#define GAME_RENDERING_EXTERNALRENDERING_H - -namespace Ogre -{ - class SceneManager; -} - -namespace MWRender -{ - /// \brief Base class for out of world rendering - class ExternalRendering - { - public: - - virtual void setup (Ogre::SceneManager *sceneManager) = 0; - - virtual ~ExternalRendering() {} - }; -} - -#endif - diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index a68c6af28..d950b8414 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -39,7 +39,6 @@ #include "localmap.hpp" #include "water.hpp" #include "npcanimation.hpp" -#include "externalrendering.hpp" #include "globalmap.hpp" #include "videoplayer.hpp" #include "terrainstorage.hpp" @@ -932,11 +931,6 @@ bool RenderingManager::isPositionExplored (float nX, float nY, int x, int y, boo return mLocalMap->isPositionExplored(nX, nY, x, y, interior); } -void RenderingManager::setupExternalRendering (MWRender::ExternalRendering& rendering) -{ - rendering.setup (mRendering.getScene()); -} - Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr) { Animation *anim = mActors->getAnimation(ptr); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 37488b157..9f77f0a3c 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -48,7 +48,6 @@ namespace MWRender class Shadows; class LocalMap; class Water; - class ExternalRendering; class GlobalMap; class VideoPlayer; class Animation; @@ -204,8 +203,6 @@ public: bool isPositionExplored (float nX, float nY, int x, int y, bool interior); ///< see MWRender::LocalMap::isPositionExplored - void setupExternalRendering (MWRender::ExternalRendering& rendering); - Animation* getAnimation(const MWWorld::Ptr &ptr); void playVideo(const std::string& name, bool allowSkipping); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2d3d3d07f..b72433e5a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1705,11 +1705,6 @@ namespace MWWorld mPhysics->addActor(mPlayer->getPlayer()); } - void World::setupExternalRendering (MWRender::ExternalRendering& rendering) - { - mRendering->setupExternalRendering (rendering); - } - int World::canRest () { Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1aecb6fb6..92a993157 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -456,8 +456,6 @@ namespace MWWorld virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable); - virtual void setupExternalRendering (MWRender::ExternalRendering& rendering); - virtual int canRest(); ///< check if the player is allowed to rest \n /// 0 - yes \n From c85c2cff4ee13558ec484e1c086182cd1f5150b3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 01:24:06 +0100 Subject: [PATCH 407/889] Fix disposition changes from trades not applying properly --- apps/openmw/mwbase/dialoguemanager.hpp | 2 +- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 14 +++++++++++++- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 2 +- apps/openmw/mwgui/tradewindow.cpp | 4 ++-- apps/openmw/mwmechanics/creaturestats.cpp | 3 ++- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 4 ++++ 6 files changed, 23 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index 58731d1c7..971bc3b4e 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -51,7 +51,7 @@ namespace MWBase virtual void persuade (int type) = 0; virtual int getTemporaryDispositionChange () const = 0; - virtual void applyTemporaryDispositionChange (int delta) = 0; + virtual void applyDispositionChange (int delta) = 0; }; } diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index b97986562..0eb20c50a 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -126,6 +126,8 @@ namespace MWDialogue void DialogueManager::startDialogue (const MWWorld::Ptr& actor) { mLastTopic = ""; + mPermanentDispositionChange = 0; + mTemporaryDispositionChange = 0; mChoice = -1; mIsInChoice = false; @@ -514,9 +516,19 @@ namespace MWDialogue return mTemporaryDispositionChange; } - void DialogueManager::applyTemporaryDispositionChange(int delta) + void DialogueManager::applyDispositionChange(int delta) { + int oldTemp = mTemporaryDispositionChange; mTemporaryDispositionChange += delta; + // don't allow increasing beyond 100 or decreasing below 0 + int curDisp = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor); + if (curDisp + mTemporaryDispositionChange < 0) + mTemporaryDispositionChange = -curDisp; + else if (curDisp + mTemporaryDispositionChange > 100) + mTemporaryDispositionChange = 100 - curDisp; + + int diff = mTemporaryDispositionChange - oldTemp; + mPermanentDispositionChange += diff; } bool DialogueManager::checkServiceRefused() diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index c9aad1022..5baf20a0e 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -76,7 +76,7 @@ namespace MWDialogue virtual void persuade (int type); virtual int getTemporaryDispositionChange () const; - virtual void applyTemporaryDispositionChange (int delta); + virtual void applyDispositionChange (int delta); }; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 6000de858..f67ea1a3f 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -319,7 +319,7 @@ namespace MWGui messageBox("#{sNotifyMessage9}"); int iBarterFailDisposition = gmst.find("iBarterFailDisposition")->getInt(); - MWBase::Environment::get().getDialogueManager()->applyTemporaryDispositionChange(iBarterFailDisposition); + MWBase::Environment::get().getDialogueManager()->applyDispositionChange(iBarterFailDisposition); return; } @@ -328,7 +328,7 @@ namespace MWGui } int iBarterSuccessDisposition = gmst.find("iBarterSuccessDisposition")->getInt(); - MWBase::Environment::get().getDialogueManager()->applyTemporaryDispositionChange(iBarterSuccessDisposition); + MWBase::Environment::get().getDialogueManager()->applyDispositionChange(iBarterSuccessDisposition); // make the item transfer mTradeModel->transferItems(); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 85f6cfdbc..b5b9b7156 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -248,7 +248,8 @@ namespace MWMechanics void CreatureStats::setAiSetting (AiSetting index, int base) { - Stat stat(base); + Stat stat = getAiSetting(index); + stat.setBase(base); setAiSetting(index, stat); } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 6f15becff..89b3fef83 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -749,11 +749,15 @@ namespace MWMechanics if (!owner.empty()) victim = MWBase::Environment::get().getWorld()->getPtr(owner, true); + // TODO: expell from faction + commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); } void MechanicsManager::commitCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) { + // TODO: expell from faction + bool reported=false; for (Actors::PtrControllerMap::const_iterator it = mActors.begin(); it != mActors.end(); ++it) { From 2bb21f2f765f2513836be1473caaeb182218862d Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 02:35:36 +0100 Subject: [PATCH 408/889] Don't copy NpcStats/CreatureStats unnecessarily. Fixes a bug: taunt actions would not correctly increase the target's fight rating. --- apps/openmw/mwgui/charactercreation.cpp | 2 +- apps/openmw/mwgui/waitdialog.cpp | 2 +- apps/openmw/mwmechanics/creaturestats.hpp | 9 ++++----- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 9 +++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 04507cfc6..fb593650d 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -221,7 +221,7 @@ namespace MWGui { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWMechanics::CreatureStats stats = MWWorld::Class::get(player).getCreatureStats(player); + const MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); mReviewDialog->setHealth ( stats.getHealth() ); mReviewDialog->setMagicka( stats.getMagicka() ); diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 0a05cd22b..5d1c4b4b6 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -149,7 +149,7 @@ namespace MWGui // I'm making the assumption here that the # of hours rested is calculated when rest is started // TODO: the rougher logic here (calculating the hourly deltas) should really go into helper funcs elsewhere MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWMechanics::CreatureStats stats = MWWorld::Class::get(player).getCreatureStats(player); + const MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); float hourlyHealthDelta = stats.getAttribute(ESM::Attribute::Endurance).getModified() * 0.1; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 322970e73..6e0804638 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -46,7 +46,6 @@ namespace MWMechanics bool mRecalcDynamicStats; std::map mUsedPowers; - protected: bool mIsWerewolf; AttributeValue mWerewolfAttributes[8]; @@ -124,10 +123,10 @@ namespace MWMechanics enum AiSetting { - AI_Hello, - AI_Fight, - AI_Flee, - AI_Alarm + AI_Hello = 0, + AI_Fight = 1, + AI_Flee = 2, + AI_Alarm = 3 }; void setAiSetting (AiSetting index, Stat value); void setAiSetting (AiSetting index, int base); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 89b3fef83..0058fac6f 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -424,7 +424,7 @@ namespace MWMechanics int MechanicsManager::getDerivedDisposition(const MWWorld::Ptr& ptr) { - MWMechanics::NpcStats npcSkill = MWWorld::Class::get(ptr).getNpcStats(ptr); + const MWMechanics::NpcStats& npcSkill = MWWorld::Class::get(ptr).getNpcStats(ptr); float x = npcSkill.getBaseDisposition(); MWWorld::LiveCellRef* npc = ptr.get(); @@ -539,7 +539,7 @@ namespace MWMechanics const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - MWMechanics::NpcStats npcStats = MWWorld::Class::get(npc).getNpcStats(npc); + MWMechanics::NpcStats& npcStats = MWWorld::Class::get(npc).getNpcStats(npc); MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); const MWMechanics::NpcStats &playerStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); @@ -613,6 +613,8 @@ namespace MWMechanics int fight = npcStats.getAiSetting(MWMechanics::CreatureStats::AI_Fight).getBase(); npcStats.setAiSetting (MWMechanics::CreatureStats::AI_Flee, std::max(0, std::min(100, flee + int(std::max(iPerMinChange, s))))); + // TODO: initiate combat and quit dialogue if fight rating is too high + // or should setAiSetting handle this? npcStats.setAiSetting (MWMechanics::CreatureStats::AI_Fight, std::max(0, std::min(100, fight + int(std::min(-iPerMinChange, -s))))); } @@ -644,10 +646,9 @@ namespace MWMechanics float c = std::abs(int(target1 - roll)); - if (roll <= target1) + if (success) { float s = c * fPerDieRollMult * fPerTempMult; - int flee = npcStats.getAiSetting (CreatureStats::AI_Flee).getBase(); int fight = npcStats.getAiSetting (CreatureStats::AI_Fight).getBase(); npcStats.setAiSetting (CreatureStats::AI_Flee, From 46519062d3614f2d5259bb34bc781b22a4d4f263 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Wed, 8 Jan 2014 16:05:14 +0200 Subject: [PATCH 409/889] hit recoils/knockdowns feature --- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwmechanics/character.cpp | 57 ++++++++------------------- apps/openmw/mwmechanics/character.hpp | 1 + apps/openmw/mwrender/animation.cpp | 14 ------- apps/openmw/mwrender/animation.hpp | 1 - 5 files changed, 19 insertions(+), 56 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index b23cb0814..4fb00a032 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -614,7 +614,7 @@ namespace MWClass // something, alert the character controller, scripts, etc. MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); - getCreatureStats(ptr).setAttacked(true); + getCreatureStats(ptr).setAttacked(true);//used in CharacterController if(object.isEmpty()) { diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index d93ae9519..a4b4f2471 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -158,6 +158,7 @@ public: void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force) { + //hit recoils/knockdown animations handling if(MWWorld::Class::get(mPtr).isActor()) { if(MWWorld::Class::get(mPtr).getCreatureStats(mPtr).getAttacked()) @@ -166,19 +167,20 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat if(mHitState == CharState_None) { - mHitState = CharState_Hit; if(mJumpState != JumpState_None && !MWBase::Environment::get().getWorld()->isFlying(mPtr) && !MWBase::Environment::get().getWorld()->isSwimming(mPtr) ) { - mCurrentHit = sHitList[sHitListSize-1]; //knockdown animation mHitState = CharState_KnockDown; + mCurrentHit = sHitList[sHitListSize-1]; + mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } else { + mHitState = CharState_Hit; int iHit = rand() % (sHitListSize-1); mCurrentHit = sHitList[iHit]; + mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } - mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } } else if(mHitState != CharState_None && !mAnimation->isPlaying(mCurrentHit)) @@ -249,8 +251,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat else { mAnimation->disable(mCurrentJump); - //mCurrentJump.clear(); - mCurrentJump = jump; + mCurrentJump.clear(); mAnimation->play(jump, Priority_Jump, jumpgroup, true, 1.0f, "loop stop", "stop", 0.0f, 0); } @@ -685,7 +686,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun else { animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); - if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack) + if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && mHitState != CharState_KnockDown) { if(mAttackType != "shoot") { @@ -710,7 +711,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun } } stats.setAttackStrength(complete); - + mAnimation->disable(mCurrentWeapon); mAnimation->play(mCurrentWeapon, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, @@ -718,6 +719,11 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun 1.0f-complete, 0); mUpperBodyState = UpperCharState_MaxAttackToMinHit; } + else if (mHitState == CharState_KnockDown) + { + mUpperBodyState = UpperCharState_WeapEquiped; + mAnimation->disable(mCurrentWeapon); + } } if(!animPlaying) @@ -728,7 +734,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun { mUpperBodyState = UpperCharState_WeapEquiped; //don't allow to continue playing hit animation on UpperBody after actor had attacked during it - if(mHitState != CharState_None) + if(mHitState == CharState_Hit) { mAnimation->changeGroups(mCurrentHit, MWRender::Animation::Group_LowerBody); //commenting out following 2 lines will give a bit different combat dynamics(slower) @@ -749,12 +755,6 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun stop = mAttackType+" max attack"; mUpperBodyState = UpperCharState_MinAttackToMaxAttack; break; - /*case UpperCharState_MinAttackToMaxAttack: - if(!mAnimation->isPlaying(mCurrentWeapon)) - mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - 1e-9f, mAttackType+" min attack", mAttackType+" max attack", 0.99f, ~0ul); - break;*/ case UpperCharState_MaxAttackToMinHit: if(mAttackType == "shoot") { @@ -803,23 +803,6 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun weapSpeed, start, stop, 0.0f, 0); } } - - //if playing combat animation and lowerbody is not busy switch to whole body animation - if((weaptype != WeapType_None || UpperCharState_UnEquipingWeap) && animPlaying) - { - if( mMovementState != CharState_None || - mJumpState != JumpState_None || - mHitState != CharState_None || - MWBase::Environment::get().getWorld()->isSwimming(mPtr) || - cls.getStance(mPtr, MWWorld::Class::Sneak)) - { - mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_UpperBody); - } - else - { - mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_All); - } - } MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); @@ -1002,14 +985,8 @@ void CharacterController::update(float duration) } else { - if(!(vec.z > 0.0f)) - { - if(!mAnimation->isPlaying(mCurrentJump)) - { - mJumpState = JumpState_None; - mCurrentJump.clear(); - } - } + if(!(vec.z > 0.0f)) + mJumpState = JumpState_None; vec.z = 0.0f; if(std::abs(vec.x/2.0f) > std::abs(vec.y)) @@ -1076,7 +1053,7 @@ void CharacterController::update(float duration) rot *= duration * Ogre::Math::RadiansToDegrees(1.0f); world->rotateObject(mPtr, rot.x, rot.y, rot.z, true); } - else + else //avoid z-rotating for knockdown world->rotateObject(mPtr, rot.x, rot.y, 0.0f, true); world->queueMovement(mPtr, vec); diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index b7c29e9ef..438f542f0 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -30,6 +30,7 @@ enum Priority { Priority_Movement, Priority_Hit, Priority_Weapon, + Priority_Knockdown, Priority_Torch, Priority_Death, diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 3e682399e..8ce751286 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -562,7 +562,6 @@ void Animation::updatePosition(float oldtime, float newtime, Ogre::Vector3 &posi /* Translate the accumulation root back to compensate for the move. */ mAccumRoot->setPosition(-off); - mAccumRootPosUpd=true; } bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint) @@ -852,7 +851,6 @@ void Animation::disable(const std::string &groupname) Ogre::Vector3 Animation::runAnimation(float duration) { Ogre::Vector3 movement(0.0f); - mAccumRootPosUpd = false; AnimStateMap::iterator stateiter = mStates.begin(); while(stateiter != mStates.end()) { @@ -946,18 +944,6 @@ Ogre::Vector3 Animation::runAnimation(float duration) updateEffects(duration); - if (!mAccumRootPosUpd) - { - for(stateiter = mStates.begin();stateiter != mStates.end(); stateiter++) - { - if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) - { - updatePosition(stateiter->second.mTime, stateiter->second.mTime, movement); - break; - } - } - } - return movement; } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 013b49400..f5f79dd72 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -128,7 +128,6 @@ protected: NifOgre::ObjectScenePtr mObjectRoot; AnimSourceList mAnimSources; Ogre::Node *mAccumRoot; - bool mAccumRootPosUpd; Ogre::Node *mNonAccumRoot; NifOgre::NodeTargetValue *mNonAccumCtrl; Ogre::Vector3 mAccumulate; From bc90443aeb0b98e18787918e3f289af84b9e39d0 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Wed, 8 Jan 2014 18:07:58 +0400 Subject: [PATCH 410/889] Revert "Music playback on OS X >= 10.9 works again. Fixes bug #1041." This reverts commit fc21dd1f3afc44ba13a3c868690964b6b636daf9. --- apps/openmw/mwsound/openal_output.cpp | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 868b4bdbd..4ee754b35 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -316,23 +316,8 @@ void OpenAL_SoundStream::play() throwALerror(); mSamplesQueued = 0; - int srate; - ChannelConfig chans; - SampleType sampleType; - - mDecoder->getInfo(&srate, &chans, &sampleType); - - // Use exactly one sample of silence. - // This is required for OpenAL implementations that don't accept empty buffer data. - // (like one in OS X 10.9) - ALuint sampleSize = framesToBytes(1, chans, sampleType); - std::vector silenceSample(sampleSize); - - if (sampleType == SampleType_UInt8) - std::fill(silenceSample.begin(), silenceSample.end(), 0x80); - - for(ALuint i = 0;i < sNumBuffers;i++) - alBufferData(mBuffers[i], mFormat, &silenceSample[0], sampleSize, mSampleRate); + for(ALuint i = 0;i < sNumBuffers;i++) + alBufferData(mBuffers[i], mFormat, this, 0, mSampleRate); throwALerror(); alSourceQueueBuffers(mSource, sNumBuffers, mBuffers); From b99ca92fee0fe6bd76042a4f8e3824a1db3e6ffe Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 17:18:13 +0100 Subject: [PATCH 411/889] Add missing line of sight check --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 0058fac6f..c5b5e6401 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -762,7 +762,9 @@ namespace MWMechanics bool reported=false; for (Actors::PtrControllerMap::const_iterator it = mActors.begin(); it != mActors.end(); ++it) { - if (it->first != ptr && awarenessCheck(ptr, it->first)) + if (it->first != ptr && + MWBase::Environment::get().getWorld()->getLOS(ptr, it->first) && + awarenessCheck(ptr, it->first)) { // NPCs will always curse you when they notice you steal their items, even if they don't report the crime if (it->first == victim && type == OT_Theft) From 7b33f6f2acb02df45575dbfd5215c29e672798cd Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 17:19:43 +0100 Subject: [PATCH 412/889] Detect crime of sleeping in other NPC's beds. ShowRestMenu needs to support an explicit/implicit reference for this. --- apps/openmw/mwbase/mechanicsmanager.hpp | 6 +- .../mwmechanics/mechanicsmanagerimp.cpp | 62 +++++++++++++------ .../mwmechanics/mechanicsmanagerimp.hpp | 6 +- apps/openmw/mwscript/docs/vmformat.txt | 4 +- apps/openmw/mwscript/guiextensions.cpp | 28 ++++++++- components/compiler/extensions0.cpp | 2 +- components/compiler/opcodes.hpp | 1 + 7 files changed, 85 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index a0abb8e48..24e955cdf 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -104,13 +104,17 @@ namespace MWBase * @brief Commit a crime. If any actors witness the crime and report it, * reportCrime will be called automatically. * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. + * @return was the crime reported? */ - virtual void commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, + virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type, int arg=0) = 0; virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type, int arg=0) = 0; /// Utility to check if taking this item is illegal and calling commitCrime if so virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count) = 0; + /// Attempt sleeping in a bed. If this is illegal, call commitCrime. + /// @return was it illegal, and someone saw you doing it? + virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed) = 0; enum PersuasionType { diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index c5b5e6401..b125b2899 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -16,6 +16,27 @@ #include "spellcasting.hpp" +namespace +{ + /// @return is \a ptr allowed to take/use \a item or is it a crime? + bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item) + { + const std::string& owner = item.getCellRef().mOwner; + bool isOwned = !owner.empty(); + + const std::string& faction = item.getCellRef().mFaction; + bool isFactionOwned = false; + if (!faction.empty()) + { + const std::map& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks(); + if (factions.find(Misc::StringUtils::lowerCase(faction)) == factions.end()) + isFactionOwned = true; + } + + return (!isOwned && !isFactionOwned); + } +} + namespace MWMechanics { void MechanicsManager::buildPlayer() @@ -729,33 +750,35 @@ namespace MWMechanics return mAI; } + bool MechanicsManager::sleepInBed(const MWWorld::Ptr &ptr, const MWWorld::Ptr &bed) + { + if (isAllowedToUse(ptr, bed)) + return false; + MWWorld::Ptr victim; + if (!bed.getCellRef().mOwner.empty()) + victim = MWBase::Environment::get().getWorld()->getPtr(bed.getCellRef().mOwner, true); + + if(commitCrime(ptr, victim, OT_SleepingInOwnedBed)) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage64}"); + return true; + } + else + return false; + } + void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, int count) { - const std::string& owner = item.getCellRef().mOwner; - bool isOwned = !owner.empty(); - - const std::string& faction = item.getCellRef().mFaction; - bool isFactionOwned = false; - if (!faction.empty()) - { - const std::map& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks(); - if (factions.find(Misc::StringUtils::lowerCase(faction)) == factions.end()) - isFactionOwned = true; - } - - if (!isOwned && !isFactionOwned) + if (isAllowedToUse(ptr, item)) return; - MWWorld::Ptr victim; - if (!owner.empty()) - victim = MWBase::Environment::get().getWorld()->getPtr(owner, true); - - // TODO: expell from faction + if (!item.getCellRef().mOwner.empty()) + victim = MWBase::Environment::get().getWorld()->getPtr(item.getCellRef().mOwner, true); commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); } - void MechanicsManager::commitCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) + bool MechanicsManager::commitCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) { // TODO: expell from faction @@ -787,6 +810,7 @@ namespace MWMechanics if (reported) reportCrime(ptr, victim, type, arg); + return reported; } void MechanicsManager::reportCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 198a62d84..cec08fa92 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -105,13 +105,17 @@ namespace MWMechanics * @brief Commit a crime. If any actors witness the crime and report it, * reportCrime will be called automatically. * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. + * @return was the crime reported? */ - virtual void commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, + virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type, int arg=0); virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type, int arg=0); /// Utility to check if taking this item is illegal and calling commitCrime if so virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count); + /// Attempt sleeping in a bed. If this is illegal, call commitCrime. + /// @return was it illegal, and someone saw you doing it? + virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed); virtual void forceStateUpdate(const MWWorld::Ptr &ptr); diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 504a8638b..a6349c4da 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -371,4 +371,6 @@ op 0x2000230: Resurrect, explicit op 0x2000231: GetSpellReadied op 0x2000232: GetSpellReadied, explicit op 0x2000233: GetPcJumping -opcodes 0x2000234-0x3ffffff unused +op 0x2000234: ShowRestMenu, explicit +opcodes 0x2000235-0x3ffffff unused + diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index 6c89a0d1c..ebba2a492 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -11,11 +11,15 @@ #include #include "../mwworld/esmstore.hpp" +#include "../mwworld/player.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" + #include "interpretercontext.hpp" +#include "ref.hpp" namespace MWScript { @@ -45,6 +49,27 @@ namespace MWScript } }; + template + class OpShowRestMenu : public Interpreter::Opcode0 + { + public: + virtual void execute (Interpreter::Runtime& runtime) + { + // FIXME: No way to tell if we have a reference before trying to get it, and it will + // cause an exception is there isn't one :( + MWWorld::Ptr bed; + try { + bed = R()(runtime); + } + catch(std::runtime_error&) { + } + + if (bed.isEmpty() || !MWBase::Environment::get().getMechanicsManager()->sleepInBed(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), + bed)) + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_RestBed); + } + }; + class OpShowDialogue : public Interpreter::Opcode0 { MWGui::GuiMode mDialogue; @@ -172,7 +197,8 @@ namespace MWScript new OpEnableRest ()); interpreter.installSegment5 (Compiler::Gui::opcodeShowRestMenu, - new OpShowDialogue (MWGui::GM_RestBed)); + new OpShowRestMenu); + interpreter.installSegment5 (Compiler::Gui::opcodeShowRestMenuExplicit, new OpShowRestMenu); interpreter.installSegment5 (Compiler::Gui::opcodeGetButtonPressed, new OpGetButtonPressed); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 6194be532..e35a32ffa 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -198,7 +198,7 @@ namespace Compiler extensions.registerInstruction ("enablerest", "", opcodeEnableRest); extensions.registerInstruction ("enablelevelupmenu", "", opcodeEnableRest); - extensions.registerInstruction ("showrestmenu", "", opcodeShowRestMenu); + extensions.registerInstruction ("showrestmenu", "", opcodeShowRestMenu, opcodeShowRestMenuExplicit); extensions.registerFunction ("getbuttonpressed", 'l', "", opcodeGetButtonPressed); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 46524c7cd..eb2240964 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -161,6 +161,7 @@ namespace Compiler const int opcodeEnableStatsMenu = 0x2000016; const int opcodeEnableRest = 0x2000017; const int opcodeShowRestMenu = 0x2000018; + const int opcodeShowRestMenuExplicit = 0x2000234; const int opcodeGetButtonPressed = 0x2000137; const int opcodeToggleFogOfWar = 0x2000145; const int opcodeToggleFullHelp = 0x2000151; From 098f9712f19787881ad0121b7d7debb8ce839b80 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 18:39:44 +0100 Subject: [PATCH 413/889] Add getPlayerPtr() utility method. Reduces dependencies a lot. --- apps/openmw/engine.cpp | 4 +-- apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwclass/armor.cpp | 1 - apps/openmw/mwclass/clothing.cpp | 1 - apps/openmw/mwclass/container.cpp | 3 +- apps/openmw/mwclass/door.cpp | 5 ++- apps/openmw/mwclass/ingredient.cpp | 3 +- apps/openmw/mwclass/potion.cpp | 5 ++- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 3 +- apps/openmw/mwdialogue/filter.cpp | 11 +++---- apps/openmw/mwgui/alchemywindow.cpp | 7 ++-- apps/openmw/mwgui/bookwindow.cpp | 3 +- apps/openmw/mwgui/charactercreation.cpp | 5 ++- apps/openmw/mwgui/container.cpp | 7 ++-- apps/openmw/mwgui/dialogue.cpp | 3 +- apps/openmw/mwgui/enchantingdialog.cpp | 7 ++-- apps/openmw/mwgui/hud.cpp | 9 +++-- apps/openmw/mwgui/inventorywindow.cpp | 13 ++++---- apps/openmw/mwgui/levelupdialog.cpp | 7 ++-- apps/openmw/mwgui/mapwindow.cpp | 6 ++-- apps/openmw/mwgui/merchantrepair.cpp | 5 ++- apps/openmw/mwgui/quickkeysmenu.cpp | 11 +++---- apps/openmw/mwgui/recharge.cpp | 5 ++- apps/openmw/mwgui/referenceinterface.cpp | 4 +-- apps/openmw/mwgui/repair.cpp | 3 +- apps/openmw/mwgui/scrollwindow.cpp | 3 +- apps/openmw/mwgui/spellbuyingwindow.cpp | 5 ++- apps/openmw/mwgui/spellcreationdialog.cpp | 7 ++-- apps/openmw/mwgui/spellicons.cpp | 3 +- apps/openmw/mwgui/spellwindow.cpp | 11 +++---- apps/openmw/mwgui/statswindow.cpp | 8 ++--- apps/openmw/mwgui/tradewindow.cpp | 4 +-- apps/openmw/mwgui/trainingwindow.cpp | 5 ++- apps/openmw/mwgui/travelwindow.cpp | 5 ++- apps/openmw/mwgui/waitdialog.cpp | 7 ++-- apps/openmw/mwinput/inputmanagerimp.cpp | 2 +- apps/openmw/mwmechanics/actors.cpp | 5 ++- apps/openmw/mwmechanics/aicombat.cpp | 1 - apps/openmw/mwmechanics/aiescort.cpp | 3 +- apps/openmw/mwmechanics/aisequence.cpp | 3 +- apps/openmw/mwmechanics/aitravel.cpp | 3 +- apps/openmw/mwmechanics/aiwander.cpp | 1 - apps/openmw/mwmechanics/character.cpp | 1 - apps/openmw/mwmechanics/enchanting.cpp | 7 ++-- .../mwmechanics/mechanicsmanagerimp.cpp | 22 ++++++------- apps/openmw/mwmechanics/npcstats.cpp | 1 - apps/openmw/mwmechanics/repair.cpp | 5 ++- apps/openmw/mwmechanics/security.cpp | 1 - apps/openmw/mwmechanics/spellcasting.cpp | 2 +- apps/openmw/mwrender/characterpreview.cpp | 3 +- apps/openmw/mwrender/renderingmanager.cpp | 7 ++-- apps/openmw/mwrender/ripplesimulation.cpp | 6 ++-- apps/openmw/mwscript/cellextensions.cpp | 11 +++---- apps/openmw/mwscript/containerextensions.cpp | 5 ++- apps/openmw/mwscript/controlextensions.cpp | 5 ++- apps/openmw/mwscript/dialogueextensions.cpp | 3 +- apps/openmw/mwscript/guiextensions.cpp | 3 +- apps/openmw/mwscript/interpretercontext.cpp | 15 ++++----- apps/openmw/mwscript/miscextensions.cpp | 3 +- apps/openmw/mwscript/statsextensions.cpp | 33 +++++++++---------- .../mwscript/transformationextensions.cpp | 4 +-- apps/openmw/mwsound/soundmanagerimp.cpp | 5 ++- apps/openmw/mwworld/actionequip.cpp | 4 +-- apps/openmw/mwworld/actionread.cpp | 2 +- apps/openmw/mwworld/containerstore.cpp | 6 ++-- apps/openmw/mwworld/scene.cpp | 10 +++--- apps/openmw/mwworld/weather.cpp | 4 +-- apps/openmw/mwworld/worldimp.cpp | 21 +++++++----- apps/openmw/mwworld/worldimp.hpp | 1 + 69 files changed, 174 insertions(+), 224 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 2a5ab3f07..b59b6a2fd 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -511,13 +511,13 @@ void OMW::Engine::activate() MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr); boost::shared_ptr action = - MWWorld::Class::get (ptr).activate (ptr, MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + MWWorld::Class::get (ptr).activate (ptr, MWBase::Environment::get().getWorld()->getPlayerPtr()); interpreterContext.activate (ptr, action); std::string script = MWWorld::Class::get (ptr).getScript (ptr); - MWBase::Environment::get().getWorld()->breakInvisibility(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + MWBase::Environment::get().getWorld()->breakInvisibility(MWBase::Environment::get().getWorld()->getPlayerPtr()); if (!script.empty()) { diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 39919012e..611bd913b 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -112,6 +112,7 @@ namespace MWBase virtual const MWWorld::Fallback *getFallback () const = 0; virtual MWWorld::Player& getPlayer() = 0; + virtual MWWorld::Ptr getPlayerPtr() = 0; virtual const MWWorld::ESMStore& getStore() const = 0; diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 13d19d86d..5e37426c9 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -17,7 +17,6 @@ #include "../mwworld/physicssystem.hpp" #include "../mwworld/nullaction.hpp" #include "../mwworld/containerstore.hpp" -#include "../mwworld/player.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index b3278d07a..62fc26a71 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -14,7 +14,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/nullaction.hpp" -#include "../mwworld/player.hpp" #include "../mwgui/tooltips.hpp" diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index d129e617d..f89a6bce0 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -16,7 +16,6 @@ #include "../mwworld/actionopen.hpp" #include "../mwworld/actiontrap.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwgui/tooltips.hpp" @@ -108,7 +107,7 @@ namespace MWClass const std::string lockedSound = "LockedChest"; const std::string trapActivationSound = "Disarm Trap Fail"; - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWWorld::InventoryStore& invStore = MWWorld::Class::get(player).getInventoryStore(player); bool needKey = ptr.getCellRef().mLockLevel>0; diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 3adb4f6fc..f99cffe04 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -8,7 +8,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/nullaction.hpp" #include "../mwworld/failedaction.hpp" @@ -97,7 +96,7 @@ namespace MWClass if (needKey && hasKey) { - if(actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer()) + if(actor == MWBase::Environment::get().getWorld()->getPlayerPtr()) MWBase::Environment::get().getWindowManager()->messageBox(keyName + " #{sKeyUsed}"); ptr.getCellRef().mLockLevel = 0; // using a key disarms the trap @@ -118,7 +117,7 @@ namespace MWClass { // teleport door /// \todo remove this if clause once ActionTeleport can also support other actors - if (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()==actor) + if (MWBase::Environment::get().getWorld()->getPlayerPtr()==actor) { boost::shared_ptr action(new MWWorld::ActionTeleport (ref->mRef.mDestCell, ref->mRef.mDoorDest)); diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index c6ec3deb9..faf29bc83 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -12,7 +12,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/actioneat.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/nullaction.hpp" #include "../mwmechanics/npcstats.hpp" @@ -153,7 +152,7 @@ namespace MWClass text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats (player); int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase(); diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 457a1c696..d68db4e45 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -13,7 +13,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/nullaction.hpp" #include "../mwgui/tooltips.hpp" @@ -133,7 +132,7 @@ namespace MWClass info.effects = MWGui::Widgets::MWEffectList::effectListFromESM(&ref->mBase->mEffects); // hide effects the player doesnt know about - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats (player); int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase(); int i=0; @@ -167,7 +166,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPlayerPtr(); // remove used potion (assume it is present in inventory) ptr.getContainerStore()->remove(ptr, 1, actor); diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 0eb20c50a..2fce7e327 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -30,7 +30,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/esmstore.hpp" -#include "../mwworld/player.hpp" #include "../mwgui/dialogue.hpp" @@ -493,7 +492,7 @@ namespace MWDialogue else if (curDisp + mTemporaryDispositionChange > 100) mTemporaryDispositionChange = 100 - curDisp; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Class::get(player).skillUsageSucceeded(player, ESM::Skill::Speechcraft, success ? 0 : 1); std::string text; diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 6f16a79ca..af676d643 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -8,7 +8,6 @@ #include "../mwbase/dialoguemanager.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/inventorystore.hpp" @@ -93,7 +92,7 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const { - const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); // check player faction if (!info.mPcFaction.empty()) @@ -212,7 +211,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c case SelectWrapper::Function_PcHealthPercent: { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); float ratio = MWWorld::Class::get (player).getCreatureStats (player).getHealth().getCurrent() / MWWorld::Class::get (player).getCreatureStats (player).getHealth().getModified(); @@ -222,7 +221,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c case SelectWrapper::Function_PcDynamicStat: { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); float value = MWWorld::Class::get (player).getCreatureStats (player). getDynamic (select.getArgument()).getCurrent(); @@ -246,7 +245,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) const { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); switch (select.getFunction()) { @@ -420,7 +419,7 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) const { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); switch (select.getFunction()) { diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 09f692e4f..ddbd3f120 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -8,7 +8,6 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "inventoryitemmodel.hpp" @@ -143,9 +142,9 @@ namespace MWGui void AlchemyWindow::open() { - mAlchemy.setAlchemist (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + mAlchemy.setAlchemist (MWBase::Environment::get().getWorld()->getPlayerPtr()); - InventoryItemModel* model = new InventoryItemModel(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + InventoryItemModel* model = new InventoryItemModel(MWBase::Environment::get().getWorld()->getPlayerPtr()); mSortModel = new SortFilterItemModel(model); mSortModel->setFilter(SortFilterItemModel::Filter_OnlyIngredients); mItemView->setModel (mSortModel); @@ -154,7 +153,7 @@ namespace MWGui int index = 0; - mAlchemy.setAlchemist (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + mAlchemy.setAlchemist (MWBase::Environment::get().getWorld()->getPlayerPtr()); for (MWMechanics::Alchemy::TToolsIterator iter (mAlchemy.beginTools()); iter!=mAlchemy.endTools() && index (mApparatus.size()); ++iter, ++index) diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index c28ef96ef..98d963b22 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -8,7 +8,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwworld/actiontake.hpp" -#include "../mwworld/player.hpp" #include "formatting.hpp" @@ -138,7 +137,7 @@ namespace MWGui MWBase::Environment::get().getSoundManager()->playSound("Item Book Up", 1.0, 1.0); MWWorld::ActionTake take(mBook); - take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + take.execute (MWBase::Environment::get().getWorld()->getPlayerPtr()); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book); } diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index fb593650d..1a3226074 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -13,7 +13,6 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwworld/class.hpp" #include "../mwworld/fallback.hpp" -#include "../mwworld/player.hpp" namespace { @@ -47,7 +46,7 @@ namespace void updatePlayerHealth() { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats(player); creatureStats.updateHealth(); @@ -220,7 +219,7 @@ namespace MWGui mReviewDialog->setBirthSign(mPlayerBirthSignId); { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); mReviewDialog->setHealth ( stats.getHealth() ); diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index aa80388f3..28f6dd489 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -10,7 +10,6 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwmechanics/pickpocket.hpp" @@ -223,7 +222,7 @@ namespace MWGui if (mPtr.getTypeName() == typeid(ESM::NPC).name() && !loot) { // we are stealing stuff - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); mModel = new PickpocketItemModel(player, new InventoryItemModel(container)); } else @@ -251,7 +250,7 @@ namespace MWGui && !mPickpocketDetected ) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::Pickpocket pickpocket(player, mPtr); if (pickpocket.finish()) { @@ -325,7 +324,7 @@ namespace MWGui bool ContainerWindow::onTakeItem(const ItemStack &item, int count) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if (dynamic_cast(mModel)) { MWMechanics::Pickpocket pickpocket(player, mPtr); diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 914302d84..0460900ed 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -12,7 +12,6 @@ #include "../mwmechanics/npcstats.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwdialogue/dialoguemanagerimp.hpp" @@ -69,7 +68,7 @@ namespace MWGui void PersuasionDialog::onPersuade(MyGUI::Widget *sender) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWBase::MechanicsManager::PersuasionType type; if (sender == mAdmireButton) type = MWBase::MechanicsManager::PT_Admire; else if (sender == mIntimidateButton) type = MWBase::MechanicsManager::PT_Intimidate; diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index d2e914d17..cb57b51b1 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -5,7 +5,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/class.hpp" @@ -106,7 +105,7 @@ namespace MWGui void EnchantingDialog::startSelfEnchanting(MWWorld::Ptr soulgem) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); mEnchanting.setSelfEnchanting(true); mEnchanting.setEnchanter(player); @@ -149,7 +148,7 @@ namespace MWGui mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &EnchantingDialog::onItemSelected); mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &EnchantingDialog::onItemCancel); mItemSelectionDialog->setVisible(true); - mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayerPtr()); mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyEnchantable); } @@ -236,7 +235,7 @@ namespace MWGui mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &EnchantingDialog::onSoulSelected); mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &EnchantingDialog::onSoulCancel); mItemSelectionDialog->setVisible(true); - mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayerPtr()); mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyChargedSoulstones); //MWBase::Environment::get().getWindowManager()->messageBox("#{sInventorySelectNoSoul}"); diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 8ef5e59d0..7cd9e2256 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -6,7 +6,6 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwmechanics/creaturestats.hpp" @@ -242,7 +241,7 @@ namespace MWGui if (world->canPlaceObject(mouseX, mouseY)) world->placeObject(object, mouseX, mouseY, mDragAndDrop->mDraggedCount); else - world->dropObjectOnGround(world->getPlayer().getPlayer(), object, mDragAndDrop->mDraggedCount); + world->dropObjectOnGround(world->getPlayerPtr(), object, mDragAndDrop->mDraggedCount); MWBase::Environment::get().getWindowManager()->changePointer("arrow"); @@ -320,7 +319,7 @@ namespace MWGui void HUD::onWeaponClicked(MyGUI::Widget* _sender) { - const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if (MWWorld::Class::get(player).getNpcStats(player).isWerewolf()) { MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}"); @@ -332,7 +331,7 @@ namespace MWGui void HUD::onMagicClicked(MyGUI::Widget* _sender) { - const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if (MWWorld::Class::get(player).getNpcStats(player).isWerewolf()) { MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}"); @@ -517,7 +516,7 @@ namespace MWGui mWeapStatus->setProgressPosition(0); MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); if (MWWorld::Class::get(player).getNpcStats(player).isWerewolf()) mWeapImage->setImageTexture("icons\\k\\tx_werewolf_hand.dds"); else diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 33814455a..27be9339b 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -10,7 +10,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/action.hpp" @@ -34,7 +33,7 @@ namespace MWGui , mTrading(false) , mLastXSize(0) , mLastYSize(0) - , mPreview(MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()) + , mPreview(MWBase::Environment::get().getWorld ()->getPlayerPtr()) , mPreviewDirty(true) , mDragAndDrop(dragAndDrop) , mSelectedItem(-1) @@ -85,7 +84,7 @@ namespace MWGui void InventoryWindow::updatePlayer() { - mPtr = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer (); + mPtr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); mTradeModel = new TradeItemModel(new InventoryItemModel(mPtr), MWWorld::Ptr()); mSortModel = new SortFilterItemModel(mTradeModel); mItemView->setModel(mSortModel); @@ -277,7 +276,7 @@ namespace MWGui void InventoryWindow::open() { - mPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + mPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); updateEncumbranceBar(); @@ -373,7 +372,7 @@ namespace MWGui boost::shared_ptr action = MWWorld::Class::get(ptr).use(ptr); - action->execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + action->execute (MWBase::Environment::get().getWorld()->getPlayerPtr()); // this is necessary for books/scrolls: if they are already in the player's inventory, // the "Take" button should not be visible. @@ -433,7 +432,7 @@ namespace MWGui void InventoryWindow::updateEncumbranceBar() { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); float capacity = MWWorld::Class::get(player).getCapacity(player); float encumbrance = MWWorld::Class::get(player).getEncumbrance(player); @@ -518,7 +517,7 @@ namespace MWGui // add to player inventory // can't use ActionTake here because we need an MWWorld::Ptr to the newly inserted object - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr newObject = *player.getClass().getContainerStore (player).add (object, object.getRefData().getCount(), player); // remove from world MWBase::Environment::get().getWorld()->deleteObject (object); diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index e3d8f5dd7..e55d9d8ca 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -6,7 +6,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/fallback.hpp" @@ -59,7 +58,7 @@ namespace MWGui void LevelupDialog::setAttributeValues() { - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats (player); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); @@ -115,7 +114,7 @@ namespace MWGui void LevelupDialog::open() { MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats (player); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); @@ -155,7 +154,7 @@ namespace MWGui void LevelupDialog::onOkButtonClicked (MyGUI::Widget* sender) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats (player); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 2f34df236..ba6114262 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -294,7 +294,7 @@ namespace MWGui std::vector markers; MWBase::World* world = MWBase::Environment::get().getWorld(); world->listDetectedReferences( - world->getPlayer().getPlayer(), + world->getPlayerPtr(), markers, MWBase::World::DetectionType(type)); if (markers.empty()) return; @@ -515,8 +515,8 @@ namespace MWGui // For interiors, position is set by WindowManager via setGlobalMapPlayerPosition if (MWBase::Environment::get().getWorld ()->isCellExterior ()) { - Ogre::Vector3 pos = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedPosition (); - Ogre::Quaternion orient = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedOrientation (); + Ogre::Vector3 pos = MWBase::Environment::get().getWorld ()->getPlayerPtr().getRefData ().getBaseNode ()->_getDerivedPosition (); + Ogre::Quaternion orient = MWBase::Environment::get().getWorld ()->getPlayerPtr().getRefData ().getBaseNode ()->_getDerivedOrientation (); Ogre::Vector2 dir (orient.yAxis ().x, orient.yAxis().y); float worldX, worldY; diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 4da166820..20eb3a615 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -8,7 +8,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" @@ -36,7 +35,7 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) int currentY = 0; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); int categories = MWWorld::ContainerStore::Type_Weapon | MWWorld::ContainerStore::Type_Armor; for (MWWorld::ContainerStoreIterator iter (store.begin(categories)); @@ -119,7 +118,7 @@ void MerchantRepair::onRepairButtonClick(MyGUI::Widget *sender) int price = boost::lexical_cast(sender->getUserString("Price")); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); player.getClass().getContainerStore(player).remove("gold_001", price, player); startRepair(mActor); diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index b8f52cd1f..77127f59b 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -2,7 +2,6 @@ #include -#include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/actionequip.hpp" #include "../mwmechanics/spellcasting.hpp" @@ -126,7 +125,7 @@ namespace MWGui mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItemCancel); } mItemSelectionDialog->setVisible(true); - mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayerPtr()); mAssignDialog->setVisible (false); } @@ -267,7 +266,7 @@ namespace MWGui QuickKeyType type = *button->getUserData(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); if (type == Type_Item || type == Type_MagicItem) @@ -311,7 +310,7 @@ namespace MWGui boost::shared_ptr action = MWWorld::Class::get(item).use(item); - action->execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + action->execute (MWBase::Environment::get().getWorld()->getPlayerPtr()); // this is necessary for books/scrolls: if they are already in the player's inventory, // the "Take" button should not be visible. @@ -344,7 +343,7 @@ namespace MWGui // Note: can't use Class::use here because enchanted scrolls for example would then open the scroll window instead of equipping MWWorld::ActionEquip action(item); - action.execute (MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()); + action.execute (MWBase::Environment::get().getWorld ()->getPlayerPtr()); } store.setSelectedEnchantItem(it); @@ -430,7 +429,7 @@ namespace MWGui const int spellHeight = 18; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index b700360ba..683406d9e 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -7,7 +7,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" @@ -85,7 +84,7 @@ void Recharge::updateView() int currentY = 0; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) @@ -141,7 +140,7 @@ void Recharge::onItemClicked(MyGUI::Widget *sender) MWWorld::Ptr item = *sender->getUserData(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); MWMechanics::NpcStats& npcStats = player.getClass().getNpcStats(player); diff --git a/apps/openmw/mwgui/referenceinterface.cpp b/apps/openmw/mwgui/referenceinterface.cpp index 86a85be18..756cd5736 100644 --- a/apps/openmw/mwgui/referenceinterface.cpp +++ b/apps/openmw/mwgui/referenceinterface.cpp @@ -3,8 +3,6 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" -#include "../mwworld/player.hpp" - namespace MWGui { ReferenceInterface::ReferenceInterface() @@ -18,7 +16,7 @@ namespace MWGui void ReferenceInterface::checkReferenceAvailable() { - MWWorld::Ptr::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); + MWWorld::Ptr::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(); // check if player has changed cell, or count of the reference has become 0 if ((playerCell != mCurrentPlayerCell && mCurrentPlayerCell != NULL) diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index 0bd4b0995..d729ee7fa 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -6,7 +6,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" @@ -88,7 +87,7 @@ void Repair::updateRepairView() int currentY = 0; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); int categories = MWWorld::ContainerStore::Type_Weapon | MWWorld::ContainerStore::Type_Armor; for (MWWorld::ContainerStoreIterator iter (store.begin(categories)); diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 48931b18e..e1970004c 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -6,7 +6,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwworld/actiontake.hpp" -#include "../mwworld/player.hpp" #include "formatting.hpp" @@ -90,7 +89,7 @@ namespace MWGui MWBase::Environment::get().getSoundManager()->playSound("Item Book Up", 1.0, 1.0); MWWorld::ActionTake take(mScroll); - take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + take.execute (MWBase::Environment::get().getWorld()->getPlayerPtr()); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll); } diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index bbd28b2de..f285b01ca 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -8,7 +8,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" @@ -103,7 +102,7 @@ namespace MWGui bool SpellBuyingWindow::playerHasSpell(const std::string &id) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::Spells& playerSpells = MWWorld::Class::get (player).getCreatureStats (player).getSpells(); for (MWMechanics::Spells::TIterator it = playerSpells.begin(); it != playerSpells.end(); ++it) { @@ -119,7 +118,7 @@ namespace MWGui if (MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()>=price) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); spells.add (mSpellsWidgetMap.find(_sender)->second); diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index b9324fea1..3a17d50aa 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -7,7 +7,6 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwmechanics/spellcasting.hpp" @@ -342,7 +341,7 @@ namespace MWGui mSpell.mName = mNameEdit->getCaption(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); player.getClass().getContainerStore(player).remove("gold_001", boost::lexical_cast(mPriceLabel->getCaption()), player); @@ -414,7 +413,7 @@ namespace MWGui mPriceLabel->setCaption(boost::lexical_cast(int(price))); - float chance = MWMechanics::getSpellSuccessChance(&mSpell, MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + float chance = MWMechanics::getSpellSuccessChance(&mSpell, MWBase::Environment::get().getWorld()->getPlayerPtr()); mSuccessChance->setCaption(boost::lexical_cast(int(chance))); } @@ -441,7 +440,7 @@ namespace MWGui { // get the list of magic effects that are known to the player - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index 891206532..0cd665a87 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -9,7 +9,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" @@ -40,7 +39,7 @@ namespace MWGui { // TODO: Tracking add/remove/expire would be better than force updating every frame - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 2ca783bfc..21257ef9c 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -5,7 +5,6 @@ #include "../mwbase/windowmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/actionequip.hpp" @@ -81,7 +80,7 @@ namespace MWGui // retrieve all player spells, divide them into Powers and Spells and sort them std::vector spellList; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); @@ -298,7 +297,7 @@ namespace MWGui void SpellWindow::onEnchantedItemSelected(MyGUI::Widget* _sender) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); MWWorld::Ptr item = *_sender->getUserData(); @@ -320,7 +319,7 @@ namespace MWGui // Note: can't use Class::use here because enchanted scrolls for example would then open the scroll window instead of equipping MWWorld::ActionEquip action(item); - action.execute (MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()); + action.execute (MWBase::Environment::get().getWorld ()->getPlayerPtr()); // since we changed equipping status, update the inventory window MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); @@ -335,7 +334,7 @@ namespace MWGui void SpellWindow::onSpellSelected(MyGUI::Widget* _sender) { std::string spellId = _sender->getUserString("Spell"); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); if (MyGUI::InputManager::getInstance().isShiftPressed()) @@ -389,7 +388,7 @@ namespace MWGui void SpellWindow::onDeleteSpellAccept() { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 549cf65ab..17bb24e83 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -6,8 +6,8 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" #include "../mwmechanics/npcstats.hpp" @@ -224,7 +224,7 @@ namespace MWGui if (!mMainWidget->getVisible()) return; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWMechanics::NpcStats &PCstats = MWWorld::Class::get(player).getNpcStats(player); // level progress @@ -424,7 +424,7 @@ namespace MWGui MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::ESMStore &store = world->getStore(); const ESM::NPC *player = - world->getPlayer().getPlayer().get()->mBase; + world->getPlayerPtr().get()->mBase; // race tooltip const ESM::Race* playerRace = store.get().find(player->mRace); @@ -452,7 +452,7 @@ namespace MWGui if (!mSkillWidgets.empty()) addSeparator(coord1, coord2); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWMechanics::NpcStats &PCstats = MWWorld::Class::get(player).getNpcStats(player); const std::set &expelled = PCstats.getExpelled(); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index f67ea1a3f..0111623be 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -16,8 +16,6 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" -#include "../mwworld/player.hpp" - #include "inventorywindow.hpp" #include "itemview.hpp" #include "sortfilteritemmodel.hpp" @@ -271,7 +269,7 @@ namespace MWGui return; } - MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(mCurrentBalance > mCurrentMerchantOffer) { diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 04eddcb17..fc96d31bb 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -9,7 +9,6 @@ #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" @@ -72,7 +71,7 @@ namespace MWGui MyGUI::EnumeratorWidgetPtr widgets = mTrainingOptions->getEnumerator (); MyGUI::Gui::getInstance ().destroyWidgets (widgets); - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer (); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); const MWWorld::Store &gmst = @@ -115,7 +114,7 @@ namespace MWGui { int skillId = *sender->getUserData(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer (); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); const MWWorld::ESMStore &store = diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index dd5da4522..7f223c505 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -9,7 +9,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" @@ -57,7 +56,7 @@ namespace MWGui } else { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); ESM::Position PlayerPos = player.getRefData().getPosition(); float d = sqrt( pow(pos.pos[0] - PlayerPos.pos[0],2) + pow(pos.pos[1] - PlayerPos.pos[1],2) + pow(pos.pos[2] - PlayerPos.pos[2],2) ); price = d/gmst.find("fTravelMult")->getFloat(); @@ -121,7 +120,7 @@ namespace MWGui int price; iss >> price; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if (MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); @@ -218,7 +217,7 @@ namespace MWGui void WaitDialog::setCanRest (bool canRest) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); bool full = (stats.getFatigue().getCurrent() >= stats.getFatigue().getModified()) && (stats.getHealth().getCurrent() >= stats.getHealth().getModified()) @@ -272,7 +271,7 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_RestBed); mWaiting = false; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWMechanics::NpcStats &pcstats = MWWorld::Class::get(player).getNpcStats(player); // trigger levelup if possible diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 8f19fb02e..77b72fc0a 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -355,7 +355,7 @@ namespace MWInput // if player tried to start moving, but can't (due to being overencumbered), display a notification. if (triedToMove) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer (); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); mOverencumberedMessageDelay -= dt; if (MWWorld::Class::get(player).getEncumbrance(player) >= MWWorld::Class::get(player).getCapacity(player)) { diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 74b459e6a..0a4adb6e2 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -11,7 +11,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/actionequip.hpp" @@ -163,7 +162,7 @@ namespace MWMechanics { CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); //engage combat or not? - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(ptr != player && !creatureStats.isHostile()) { ESM::Position playerpos = player.getRefData().getPosition(); @@ -585,7 +584,7 @@ namespace MWMechanics ptr.getClass().setActorHealth(ptr, stats.getHealth().getCurrent() - 3.0f*duration); // Play a drowning sound as necessary for the player - if(ptr == world->getPlayer().getPlayer()) + if(ptr == world->getPlayerPtr()) { MWBase::SoundManager *sndmgr = MWBase::Environment::get().getSoundManager(); if(!sndmgr->getSoundPlaying(MWWorld::Ptr(), "drown")) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index df45b7133..32b0063b6 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -3,7 +3,6 @@ #include "movement.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/timestamp.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 5099625c0..35f9f1993 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -3,7 +3,6 @@ #include "movement.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/timestamp.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -83,7 +82,7 @@ namespace MWMechanics return true; } - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); ESM::Position pos = actor.getRefData().getPosition(); bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY; const ESM::Pathgrid *pathgrid = diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 6d461e5f6..139f54489 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -15,7 +15,6 @@ #include "npcstats.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwworld/player.hpp" void MWMechanics::AiSequence::copy (const AiSequence& sequence) { @@ -63,7 +62,7 @@ bool MWMechanics::AiSequence::isPackageDone() const void MWMechanics::AiSequence::execute (const MWWorld::Ptr& actor,float duration) { - if(actor != MWBase::Environment::get().getWorld()->getPlayer().getPlayer()) + if(actor != MWBase::Environment::get().getWorld()->getPlayerPtr()) { if (!mPackages.empty()) { diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index f56c75996..73b38dd13 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -5,7 +5,6 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" namespace { @@ -38,7 +37,7 @@ namespace MWMechanics Movement &movement = actor.getClass().getMovementSettings(actor); const ESM::Cell *cell = actor.getCell()->mCell; - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); if(cell->mData.mX != player.getCell()->mCell->mData.mX) { int sideX = sgn(cell->mData.mX - player.getCell()->mCell->mData.mX); diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 93c94a3f4..7df88c076 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -3,7 +3,6 @@ #include "movement.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 8a73c98af..50dad9cc0 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -33,7 +33,6 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 3991454db..f428bd4b0 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -1,5 +1,4 @@ #include "enchanting.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" @@ -53,7 +52,7 @@ namespace MWMechanics bool Enchanting::create() { - const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); ESM::Enchantment enchantment; enchantment.mData.mCharge = getGemCharge(); @@ -213,7 +212,7 @@ namespace MWMechanics return 0; const float enchantCost = getEnchantPoints(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::NpcStats &stats = MWWorld::Class::get(player).getNpcStats(player); int eSkill = stats.getSkill(ESM::Skill::Enchant).getModified(); @@ -297,7 +296,7 @@ namespace MWMechanics void Enchanting::payForEnchantment() const { - const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); store.remove("gold_001", getEnchantPrice(), player); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index b125b2899..47dd7858c 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -41,7 +41,7 @@ namespace MWMechanics { void MechanicsManager::buildPlayer() { - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); MWMechanics::NpcStats& npcStats = MWWorld::Class::get (ptr).getNpcStats (ptr); @@ -251,7 +251,7 @@ namespace MWMechanics { // Uses ingame time, but scaled to real time duration /= MWBase::Environment::get().getWorld()->getTimeScaleFactor(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); player.getClass().getInventoryStore(player).rechargeItems(duration); } @@ -332,7 +332,7 @@ namespace MWMechanics MWBase::Environment::get().getWindowManager(); const ESM::NPC *player = - world->getPlayer().getPlayer().get()->mBase; + world->getPlayerPtr().get()->mBase; const ESM::Race *race = world->getStore().get().find(player->mRace); @@ -358,7 +358,7 @@ namespace MWMechanics // HACK? The player has been changed, so a new Animation object may // have been made for them. Make sure they're properly updated. - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr(); mActors.removeActor(ptr); mActors.addActor(ptr); } @@ -377,7 +377,7 @@ namespace MWMechanics MWBase::World *world = MWBase::Environment::get().getWorld(); ESM::NPC player = - *world->getPlayer().getPlayer().get()->mBase; + *world->getPlayerPtr().get()->mBase; player.mName = name; world->createRecord(player); @@ -390,7 +390,7 @@ namespace MWMechanics MWBase::World *world = MWBase::Environment::get().getWorld(); ESM::NPC player = - *world->getPlayer().getPlayer().get()->mBase; + *world->getPlayerPtr().get()->mBase; player.mRace = race; player.mHead = head; @@ -416,7 +416,7 @@ namespace MWMechanics MWBase::World *world = MWBase::Environment::get().getWorld(); ESM::NPC player = - *world->getPlayer().getPlayer().get()->mBase; + *world->getPlayerPtr().get()->mBase; player.mClass = id; world->createRecord(player); @@ -433,7 +433,7 @@ namespace MWMechanics const ESM::Class *ptr = world->createRecord(cls); ESM::NPC player = - *world->getPlayer().getPlayer().get()->mBase; + *world->getPlayerPtr().get()->mBase; player.mClass = ptr->mId; world->createRecord(player); @@ -449,7 +449,7 @@ namespace MWMechanics float x = npcSkill.getBaseDisposition(); MWWorld::LiveCellRef* npc = ptr.get(); - MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::LiveCellRef* player = playerPtr.get(); const MWMechanics::NpcStats &playerStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); @@ -517,7 +517,7 @@ namespace MWMechanics const MWMechanics::NpcStats &sellerStats = MWWorld::Class::get(ptr).getNpcStats(ptr); - MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWMechanics::NpcStats &playerStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); // I suppose the temporary disposition change _has_ to be considered here, @@ -562,7 +562,7 @@ namespace MWMechanics MWMechanics::NpcStats& npcStats = MWWorld::Class::get(npc).getNpcStats(npc); - MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWMechanics::NpcStats &playerStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); float persTerm = playerStats.getAttribute(ESM::Attribute::Personality).getModified() diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 94dd97186..9b70e3347 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -15,7 +15,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" -#include "../mwworld/player.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwmechanics/repair.cpp b/apps/openmw/mwmechanics/repair.cpp index 5e8a46fd4..1b17f8305 100644 --- a/apps/openmw/mwmechanics/repair.cpp +++ b/apps/openmw/mwmechanics/repair.cpp @@ -8,7 +8,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" @@ -20,7 +19,7 @@ namespace MWMechanics void Repair::repair(const MWWorld::Ptr &itemToRepair) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::LiveCellRef *ref = mTool.get(); @@ -77,7 +76,7 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair) // tool used up? if (mTool.getCellRef().mCharge == 0) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); store.remove(mTool, 1, player); diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index c373e83f5..0769e13df 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -1,7 +1,6 @@ #include "security.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 52fb0805a..21b6d0d77 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -7,8 +7,8 @@ #include "../mwworld/containerstore.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/actionteleport.hpp" +#include "../mwworld/player.hpp" #include "../mwrender/animation.hpp" diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 643225515..f7333db35 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -10,7 +10,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" @@ -219,7 +218,7 @@ namespace MWRender // -------------------------------------------------------------------------------------------------- RaceSelectionPreview::RaceSelectionPreview() - : CharacterPreview(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), + : CharacterPreview(MWBase::Environment::get().getWorld()->getPlayerPtr(), 512, 512, "CharacterHeadPreview", Ogre::Vector3(0, 6, -35), Ogre::Vector3(0,125,0)) , mRef(&mBase) { diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index d950b8414..11d06704e 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -33,7 +33,6 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwworld/ptr.hpp" -#include "../mwworld/player.hpp" #include "shadows.hpp" #include "localmap.hpp" @@ -324,7 +323,7 @@ void RenderingManager::update (float duration, bool paused) { MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); int blind = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(ESM::MagicEffect::Blind).mMagnitude; mRendering.getFader()->setFactor(std::max(0.f, 1.f-(blind / 100.f))); @@ -585,7 +584,7 @@ void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour) { mAmbientColor = colour; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); int nightEye = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(ESM::MagicEffect::NightEye).mMagnitude; Ogre::ColourValue final = colour; final += Ogre::ColourValue(0.7,0.7,0.7,0) * std::min(1.f, (nightEye/100.f)); @@ -737,7 +736,7 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec else if (it->second == "max viewing distance" && it->first == "Viewing distance") { if (!MWBase::Environment::get().getWorld()->isCellExterior() && !MWBase::Environment::get().getWorld()->isCellQuasiExterior()) - configureFog(*MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()); + configureFog(*MWBase::Environment::get().getWorld()->getPlayerPtr().getCell()); } else if (it->first == "Video" && ( it->second == "resolution x" diff --git a/apps/openmw/mwrender/ripplesimulation.cpp b/apps/openmw/mwrender/ripplesimulation.cpp index 47fbc8ddf..e5db8346f 100644 --- a/apps/openmw/mwrender/ripplesimulation.cpp +++ b/apps/openmw/mwrender/ripplesimulation.cpp @@ -10,8 +10,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwworld/player.hpp" - namespace MWRender { @@ -154,11 +152,11 @@ void RippleSimulation::addImpulses() /// \todo it should be more efficient to render all emitters at once for (std::vector::iterator it=mEmitters.begin(); it !=mEmitters.end(); ++it) { - if (it->mPtr == MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()) + if (it->mPtr == MWBase::Environment::get().getWorld ()->getPlayerPtr()) { // fetch a new ptr (to handle cell change etc) // for non-player actors this is done in updateObjectCell - it->mPtr = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer (); + it->mPtr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); } float* _currentPos = it->mPtr.getRefData().getPosition().pos; Ogre::Vector3 currentPos (_currentPos[0], _currentPos[1], _currentPos[2]); diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index f26602f7a..e0d41cd19 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -12,7 +12,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" - #include "../mwworld/player.hpp" #include "interpretercontext.hpp" @@ -89,7 +88,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { bool interior = - !MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->isExterior(); + !MWBase::Environment::get().getWorld()->getPlayerPtr().getCell()->mCell->isExterior(); runtime.push (interior ? 1 : 0); } @@ -104,7 +103,7 @@ namespace MWScript std::string name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - const ESM::Cell *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell; + const ESM::Cell *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell()->mCell; std::string current = cell->mName; @@ -129,7 +128,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); + MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(); runtime.push (cell->mWaterLevel); } }; @@ -142,7 +141,7 @@ namespace MWScript { Interpreter::Type_Float level = runtime[0].mFloat; - MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); + MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(); if (cell->mCell->isExterior()) throw std::runtime_error("Can't set water level in exterior cell"); @@ -160,7 +159,7 @@ namespace MWScript { Interpreter::Type_Float level = runtime[0].mFloat; - MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); + MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(); if (cell->mCell->isExterior()) throw std::runtime_error("Can't set water level in exterior cell"); diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 53f4c23c9..5234eaea3 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -21,7 +21,6 @@ #include "../mwworld/containerstore.hpp" #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwworld/player.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -55,7 +54,7 @@ namespace MWScript MWWorld::Ptr itemPtr = *ptr.getClass().getContainerStore (ptr).add (item, count, ptr); // Spawn a messagebox (only for items added to player's inventory and if player is talking to someone) - if (ptr == MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer() ) + if (ptr == MWBase::Environment::get().getWorld ()->getPlayerPtr() ) { // The two GMST entries below expand to strings informing the player of what, and how many of it has been added to their inventory std::string msgBox; @@ -133,7 +132,7 @@ namespace MWScript // Spawn a messagebox (only for items removed from player's inventory) if ((numRemoved > 0) - && (ptr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer())) + && (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr())) { // The two GMST entries below expand to strings informing the player of what, and how many of it has been removed from their inventory std::string msgBox; diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp index e46302b18..7697ab619 100644 --- a/apps/openmw/mwscript/controlextensions.cpp +++ b/apps/openmw/mwscript/controlextensions.cpp @@ -11,7 +11,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/inputmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/ptr.hpp" @@ -144,7 +143,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer(); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); runtime.push (MWWorld::Class::get(ptr).getStance (ptr, MWWorld::Class::Run)); } }; @@ -155,7 +154,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer(); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); runtime.push (MWWorld::Class::get(ptr).getStance (ptr, MWWorld::Class::Sneak)); } }; diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index 5e797ee58..a882ae05e 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -13,7 +13,6 @@ #include "../mwbase/journal.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwmechanics/npcstats.hpp" #include "interpretercontext.hpp" @@ -183,7 +182,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); runtime.push (MWWorld::Class::get(ptr).getNpcStats (ptr).isSameFaction (MWWorld::Class::get(player).getNpcStats (player))); } diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index ebba2a492..e51bdcf69 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -11,7 +11,6 @@ #include #include "../mwworld/esmstore.hpp" -#include "../mwworld/player.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -64,7 +63,7 @@ namespace MWScript catch(std::runtime_error&) { } - if (bed.isEmpty() || !MWBase::Environment::get().getMechanicsManager()->sleepInBed(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), + if (bed.isEmpty() || !MWBase::Environment::get().getMechanicsManager()->sleepInBed(MWBase::Environment::get().getWorld()->getPlayerPtr(), bed)) MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_RestBed); } diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index b8fc9ed47..aa9e32008 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -14,7 +14,6 @@ #include "../mwbase/inputmanager.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwmechanics/npcstats.hpp" @@ -248,28 +247,28 @@ namespace MWScript std::string InterpreterContext::getPCName() const { MWBase::World *world = MWBase::Environment::get().getWorld(); - ESM::NPC player = *world->getPlayer().getPlayer().get()->mBase; + ESM::NPC player = *world->getPlayerPtr().get()->mBase; return player.mName; } std::string InterpreterContext::getPCRace() const { MWBase::World *world = MWBase::Environment::get().getWorld(); - std::string race = world->getPlayer().getPlayer().get()->mBase->mRace; + std::string race = world->getPlayerPtr().get()->mBase->mRace; return world->getStore().get().find(race)->mName; } std::string InterpreterContext::getPCClass() const { MWBase::World *world = MWBase::Environment::get().getWorld(); - std::string class_ = world->getPlayer().getPlayer().get()->mBase->mClass; + std::string class_ = world->getPlayerPtr().get()->mBase->mClass; return world->getStore().get().find(class_)->mName; } std::string InterpreterContext::getPCRank() const { MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); std::string factionId = MWWorld::Class::get (mReference).getNpcStats (mReference).getFactionRanks().begin()->first; @@ -288,7 +287,7 @@ namespace MWScript std::string InterpreterContext::getPCNextRank() const { MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); std::string factionId = MWWorld::Class::get (mReference).getNpcStats (mReference).getFactionRanks().begin()->first; @@ -316,7 +315,7 @@ namespace MWScript int InterpreterContext::getPCBounty() const { MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); return MWWorld::Class::get (player).getNpcStats (player).getBounty(); } @@ -387,7 +386,7 @@ namespace MWScript if (!mAction.get()) throw std::runtime_error ("activation failed, because no action to perform"); - mAction->execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + mAction->execute (MWBase::Environment::get().getWorld()->getPlayerPtr()); mActivationHandled = true; } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 54a8139d8..61d286ae3 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -18,7 +18,6 @@ #include "../mwbase/scriptmanager.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwmechanics/npcstats.hpp" @@ -65,7 +64,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWBase::World* world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); runtime.push (!world->isOnGround(player) && !world->isFlying(player)); } }; diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index f053e9a97..2ade3200e 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -21,7 +21,6 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" @@ -391,7 +390,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); runtime.push (static_cast (MWWorld::Class::get (player).getNpcStats (player).getBounty())); } }; @@ -403,7 +402,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); MWWorld::Class::get (player).getNpcStats (player).setBounty(runtime[0].mFloat); runtime.pop(); @@ -417,7 +416,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); MWWorld::Class::get (player).getNpcStats (player).setBounty(runtime[0].mFloat + MWWorld::Class::get (player).getNpcStats (player).getBounty()); runtime.pop(); @@ -539,7 +538,7 @@ namespace MWScript Misc::StringUtils::toLower(factionID); if(factionID != "") { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().find(factionID) == MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().end()) { MWWorld::Class::get(player).getNpcStats(player).getFactionRanks()[factionID] = 0; @@ -568,7 +567,7 @@ namespace MWScript Misc::StringUtils::toLower(factionID); if(factionID != "") { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().find(factionID) == MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().end()) { MWWorld::Class::get(player).getNpcStats(player).getFactionRanks()[factionID] = 0; @@ -601,7 +600,7 @@ namespace MWScript Misc::StringUtils::toLower(factionID); if(factionID != "") { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().find(factionID) != MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().end()) { MWWorld::Class::get(player).getNpcStats(player).getFactionRanks()[factionID] = MWWorld::Class::get(player).getNpcStats(player).getFactionRanks()[factionID] -1; @@ -637,7 +636,7 @@ namespace MWScript } } Misc::StringUtils::toLower(factionID); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(factionID!="") { if(MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().find(factionID) != MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().end()) @@ -740,7 +739,7 @@ namespace MWScript Misc::StringUtils::toLower (factionId); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); runtime.push ( MWWorld::Class::get (player).getNpcStats (player).getFactionReputation (factionId)); } @@ -776,7 +775,7 @@ namespace MWScript Misc::StringUtils::toLower (factionId); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Class::get (player).getNpcStats (player).setFactionReputation (factionId, value); } }; @@ -811,7 +810,7 @@ namespace MWScript Misc::StringUtils::toLower (factionId); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Class::get (player).getNpcStats (player).setFactionReputation (factionId, MWWorld::Class::get (player).getNpcStats (player).getFactionReputation (factionId)+ value); @@ -870,7 +869,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer (); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); runtime.push (MWWorld::Class::get(ptr).getNpcStats (ptr).getWerewolfKills ()); } @@ -903,7 +902,7 @@ namespace MWScript } } Misc::StringUtils::toLower(factionID); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(factionID!="") { std::set& expelled = MWWorld::Class::get(player).getNpcStats(player).getExpelled (); @@ -949,7 +948,7 @@ namespace MWScript factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first; } } - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(factionID!="") { std::set& expelled = MWWorld::Class::get(player).getNpcStats(player).getExpelled (); @@ -985,7 +984,7 @@ namespace MWScript factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first; } } - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(factionID!="") { std::set& expelled = MWWorld::Class::get(player).getNpcStats(player).getExpelled (); @@ -1011,7 +1010,7 @@ namespace MWScript { factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first; } - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); // no-op when executed on the player if (ptr == player) @@ -1038,7 +1037,7 @@ namespace MWScript { factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first; } - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); // no-op when executed on the player if (ptr == player) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index e44180977..81aff9958 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -16,8 +16,8 @@ #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/manualref.hpp" +#include "../mwworld/player.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -482,7 +482,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr actor = pc - ? MWBase::Environment::get().getWorld()->getPlayer().getPlayer() + ? MWBase::Environment::get().getWorld()->getPlayerPtr() : R()(runtime); std::string itemID = runtime.getStringLiteral (runtime[0].mInteger); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 2e52739ac..28a3aae37 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -8,7 +8,6 @@ #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" -#include "../mwworld/player.hpp" #include "sound_output.hpp" #include "sound_decoder.hpp" @@ -479,7 +478,7 @@ namespace MWSound static std::string regionName = ""; static float sTimePassed = 0.0; MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::Ptr player = world->getPlayer().getPlayer(); + const MWWorld::Ptr player = world->getPlayerPtr(); const ESM::Cell *cell = player.getCell()->mCell; sTimePassed += duration; @@ -547,7 +546,7 @@ namespace MWSound startRandomTitle(); MWWorld::Ptr player = - MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWBase::Environment::get().getWorld()->getPlayerPtr(); const ESM::Cell *cell = player.getCell()->mCell; Environment env = Env_Normal; diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index 0d091e742..6a0b4eec7 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -24,7 +24,7 @@ namespace MWWorld std::pair result = MWWorld::Class::get (object).canBeEquipped (object, actor); // display error message if the player tried to equip something - if (!result.second.empty() && actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer()) + if (!result.second.empty() && actor == MWBase::Environment::get().getWorld()->getPlayerPtr()) MWBase::Environment::get().getWindowManager()->messageBox(result.second); switch(result.first) @@ -84,7 +84,7 @@ namespace MWWorld std::string script = MWWorld::Class::get(object).getScript(object); /* Set OnPCEquip Variable on item's script, if the player is equipping it, and it has a script with that variable declared */ - if(equipped && actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() && script != "") + if(equipped && actor == MWBase::Environment::get().getWorld()->getPlayerPtr() && script != "") object.getRefData().getLocals().setVarByInt(script, "onpcequip", 1); } } diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index 6d5d9d8fd..67755259e 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -34,7 +34,7 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->getBookWindow()->open(getTarget()); } - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats (player); // Skill gain from books diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index ee4080755..08d29b86b 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -148,7 +148,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr { CellStore *cell; - Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); if(&(MWWorld::Class::get (player).getContainerStore (player)) == this) { @@ -305,8 +305,8 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: const ESM::ItemLevList* levItem = ref.getPtr().get()->mBase; const std::vector& items = levItem->mList; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - int playerLevel = MWWorld::Class::get(player).getCreatureStats(player).getLevel(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerLevel = player.getClass().getCreatureStats(player).getLevel(); failChance += levItem->mChanceNone; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index b3a9c9bb3..3607b8276 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -166,10 +166,10 @@ namespace MWWorld void Scene::playerCellChange(MWWorld::CellStore *cell, const ESM::Position& pos, bool adjustPlayerPos) { MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr old = world->getPlayer().getPlayer(); + MWWorld::Ptr old = world->getPlayerPtr(); world->getPlayer().setCell(cell); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); mRendering.updatePlayerPtr(player); if (adjustPlayerPos) { @@ -369,14 +369,14 @@ namespace MWWorld if(!loadcell) { MWBase::World *world = MWBase::Environment::get().getWorld(); - world->moveObject(world->getPlayer().getPlayer(), position.pos[0], position.pos[1], position.pos[2]); + world->moveObject(world->getPlayerPtr(), position.pos[0], position.pos[1], position.pos[2]); float x = Ogre::Radian(position.rot[0]).valueDegrees(); float y = Ogre::Radian(position.rot[1]).valueDegrees(); float z = Ogre::Radian(position.rot[2]).valueDegrees(); - world->rotateObject(world->getPlayer().getPlayer(), x, y, z); + world->rotateObject(world->getPlayerPtr(), x, y, z); - MWWorld::Class::get(world->getPlayer().getPlayer()).adjustPosition(world->getPlayer().getPlayer()); + MWWorld::Class::get(world->getPlayerPtr()).adjustPosition(world->getPlayerPtr()); world->getFader()->fadeIn(0.5f); return; } diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 513dcf6c7..c34beb3f2 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -665,7 +665,7 @@ void WeatherManager::changeWeather(const std::string& region, const unsigned int mRegionOverrides[Misc::StringUtils::lowerCase(region)] = weather; - std::string playerRegion = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion; + std::string playerRegion = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell()->mCell->mRegion; if (Misc::StringUtils::ciEqual(region, playerRegion)) setWeather(weather); } @@ -696,7 +696,7 @@ void WeatherManager::switchToNextWeather(bool instantly) MWBase::World* world = MWBase::Environment::get().getWorld(); if (world->isCellExterior() || world->isCellQuasiExterior()) { - std::string regionstr = Misc::StringUtils::lowerCase(world->getPlayer().getPlayer().getCell()->mCell->mRegion); + std::string regionstr = Misc::StringUtils::lowerCase(world->getPlayerPtr().getCell()->mCell->mRegion); if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) { diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b72433e5a..8bb2e7956 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -753,16 +753,16 @@ namespace MWWorld void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position) { - removeContainerScripts(getPlayer().getPlayer()); + removeContainerScripts(getPlayerPtr()); mWorldScene->changeToInteriorCell(cellName, position); - addContainerScripts(getPlayer().getPlayer(), getPlayer().getPlayer().getCell()); + addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell()); } void World::changeToExteriorCell (const ESM::Position& position) { - removeContainerScripts(getPlayer().getPlayer()); + removeContainerScripts(getPlayerPtr()); mWorldScene->changeToExteriorCell(position); - addContainerScripts(getPlayer().getPlayer(), getPlayer().getPlayer().getCell()); + addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell()); } void World::markCellAsUnchanged() @@ -869,7 +869,7 @@ namespace MWWorld int cellY = newCell.mCell->getGridY(); mWorldScene->changeCell(cellX, cellY, pos, false); } - addContainerScripts (getPlayer().getPlayer(), &newCell); + addContainerScripts (getPlayerPtr(), &newCell); } else { @@ -1493,9 +1493,9 @@ namespace MWWorld cell = mCells.getExterior(cellX, cellY); } else - cell = getPlayer().getPlayer().getCell(); + cell = getPlayerPtr().getCell(); - ESM::Position pos = getPlayer().getPlayer().getRefData().getPosition(); + ESM::Position pos = getPlayerPtr().getRefData().getPosition(); pos.pos[0] = result.second[0]; pos.pos[1] = result.second[1]; pos.pos[2] = result.second[2]; @@ -2035,7 +2035,7 @@ namespace MWWorld std::string message; bool fail = false; - bool isPlayer = (actor == getPlayer().getPlayer()); + bool isPlayer = (actor == getPlayerPtr()); std::string selectedSpell = stats.getSpells().getSelectedSpell(); @@ -2415,4 +2415,9 @@ namespace MWWorld // with the Telekinesis effect. return feet * 22; } + + MWWorld::Ptr World::getPlayerPtr() + { + return mPlayer->getPlayer(); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 92a993157..51ad3a045 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -187,6 +187,7 @@ namespace MWWorld virtual const Fallback *getFallback() const; virtual Player& getPlayer(); + virtual MWWorld::Ptr getPlayerPtr(); virtual const MWWorld::ESMStore& getStore() const; From 19d63f392ff184ffa6835ccd452143606209c5a7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 18:59:00 +0100 Subject: [PATCH 414/889] Clean up the NpcStats expelled interface. Show message box when expelled. --- apps/openmw/mwdialogue/filter.cpp | 4 +--- .../mwmechanics/mechanicsmanagerimp.cpp | 2 +- apps/openmw/mwmechanics/npcstats.cpp | 20 +++++++++++++++---- apps/openmw/mwmechanics/npcstats.hpp | 6 ++++-- apps/openmw/mwscript/statsextensions.cpp | 20 +++---------------- 5 files changed, 25 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index af676d643..4f478afce 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -507,9 +507,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co std::string faction = MWWorld::Class::get (mActor).getNpcStats (mActor).getFactionRanks().begin()->first; - std::set& expelled = MWWorld::Class::get (player).getNpcStats (player).getExpelled(); - - return expelled.find (faction)!=expelled.end(); + return player.getClass().getNpcStats(player).getExpelled(faction); } case SelectWrapper::Function_PcVampire: diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 47dd7858c..53422fbb1 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -470,7 +470,7 @@ namespace MWMechanics it != MWBase::Environment::get().getWorld()->getStore().get().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.end(); ++it) { if(Misc::StringUtils::lowerCase(it->mFaction) == Misc::StringUtils::lowerCase(npcFaction) - && playerStats.getExpelled().find(Misc::StringUtils::lowerCase(it->mFaction)) == playerStats.getExpelled().end()) + && !playerStats.getExpelled(it->mFaction)) reaction = it->mReaction; } rank = playerStats.getFactionRanks().find(Misc::StringUtils::lowerCase(npcFaction))->second; diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 9b70e3347..289fcc70f 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -108,14 +108,26 @@ std::map& MWMechanics::NpcStats::getFactionRanks() return mFactionRank; } -const std::set& MWMechanics::NpcStats::getExpelled() const +bool MWMechanics::NpcStats::getExpelled(const std::string& factionID) const { - return mExpelled; + return mExpelled.find(Misc::StringUtils::lowerCase(factionID)) != mExpelled.end(); } -std::set& MWMechanics::NpcStats::getExpelled() +void MWMechanics::NpcStats::expell(const std::string& factionID) { - return mExpelled; + std::string lower = Misc::StringUtils::lowerCase(factionID); + if (mExpelled.find(lower) == mExpelled.end()) + { + std::string message = "#{sExpelledMessage}"; + message += MWBase::Environment::get().getWorld()->getStore().get().find(factionID)->mName; + MWBase::Environment::get().getWindowManager()->messageBox(message); + mExpelled.insert(lower); + } +} + +void MWMechanics::NpcStats::clearExpelled(const std::string& factionID) +{ + mExpelled.erase(Misc::StringUtils::lowerCase(factionID)); } bool MWMechanics::NpcStats::isSameFaction (const NpcStats& npcStats) const diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 586e06832..5fd358c85 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -99,8 +99,10 @@ namespace MWMechanics const std::map& getFactionRanks() const; std::map& getFactionRanks(); - const std::set& getExpelled() const; - std::set& getExpelled(); + const std::set& getExpelled() const { return mExpelled; } + bool getExpelled(const std::string& factionID) const; + void expell(const std::string& factionID); + void clearExpelled(const std::string& factionID); bool isSameFaction (const NpcStats& npcStats) const; ///< Do *this and \a npcStats share a faction? diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 2ade3200e..56b309576 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -905,15 +905,7 @@ namespace MWScript MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(factionID!="") { - std::set& expelled = MWWorld::Class::get(player).getNpcStats(player).getExpelled (); - if (expelled.find (factionID) != expelled.end()) - { - runtime.push(1); - } - else - { - runtime.push(0); - } + runtime.push(player.getClass().getNpcStats(player).getExpelled(factionID)); } else { @@ -951,9 +943,7 @@ namespace MWScript MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(factionID!="") { - std::set& expelled = MWWorld::Class::get(player).getNpcStats(player).getExpelled (); - Misc::StringUtils::toLower(factionID); - expelled.insert(factionID); + player.getClass().getNpcStats(player).expell(factionID); } } }; @@ -986,11 +976,7 @@ namespace MWScript } MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(factionID!="") - { - std::set& expelled = MWWorld::Class::get(player).getNpcStats(player).getExpelled (); - Misc::StringUtils::toLower(factionID); - expelled.erase (factionID); - } + player.getClass().getNpcStats(player).clearExpelled(factionID); } }; From 9baa1bef7854e249dab21c4b5575d376df440736 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 19:26:03 +0100 Subject: [PATCH 415/889] Expell player from faction when committing a crime against a faction member --- apps/openmw/mwgui/container.cpp | 2 +- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 28f6dd489..bccc7120f 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -255,7 +255,7 @@ namespace MWGui if (pickpocket.finish()) { MWBase::Environment::get().getMechanicsManager()->reportCrime( - player, MWWorld::Ptr(), MWBase::MechanicsManager::OT_Pickpocket); + player, mPtr, MWBase::MechanicsManager::OT_Pickpocket); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); mPickpocketDetected = true; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 53422fbb1..429683b97 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -829,6 +829,18 @@ namespace MWMechanics ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty() + arg); + // If committing a crime against a faction member, expell from the faction + if (!victim.isEmpty() && victim.getClass().isNpc()) + { + std::string factionID; + if(!victim.getClass().getNpcStats(victim).getFactionRanks().empty()) + factionID = victim.getClass().getNpcStats(victim).getFactionRanks().begin()->first; + if (ptr.getClass().getNpcStats(ptr).isSameFaction(victim.getClass().getNpcStats(victim))) + { + ptr.getClass().getNpcStats(ptr).expell(factionID); + } + } + // TODO: make any guards in the area try to arrest the player } From 154fae9f251de3a589c75ee1291c4cc21bce7aac Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 19:52:57 +0100 Subject: [PATCH 416/889] Don't suppress exceptions thrown while running scripts --- apps/openmw/mwscript/scriptmanagerimp.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index 14fe5b7fd..8b6b7ed2a 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -114,10 +114,8 @@ namespace MWScript } catch (const std::exception& e) { - std::cerr << "execution of script " << name << " failed." << std::endl; - - if (mVerbose) - std::cerr << "(" << e.what() << ")" << std::endl; + std::cerr << "Execution of script " << name << " failed:" << std::endl; + std::cerr << e.what() << std::endl; iter->second.first.clear(); // don't execute again. } From bf02b77c1dc008c022b9e5ef294b77b5f53f5fa7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 19:57:13 +0100 Subject: [PATCH 417/889] Closes #1090: Don't throw an exception if a cell has no region in GetPCCell --- apps/openmw/mwscript/cellextensions.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index e0d41cd19..0e3b379d8 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -107,7 +107,8 @@ namespace MWScript std::string current = cell->mName; - if (!(cell->mData.mFlags & ESM::Cell::Interior) && current.empty()) + if (!(cell->mData.mFlags & ESM::Cell::Interior) && current.empty() + && !cell->mRegion.empty()) { const ESM::Region *region = MWBase::Environment::get().getWorld()->getStore().get().find (cell->mRegion); From 3127b8518e321ec2ad5dce758c0960bd5acec03f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 8 Jan 2014 20:15:54 +0100 Subject: [PATCH 418/889] updated credits file --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index bd0c6ca74..561931cde 100644 --- a/credits.txt +++ b/credits.txt @@ -24,6 +24,7 @@ Chris Robinson (KittyCat) Cory F. Cohen (cfcohen) Cris Mihalache (Mirceam) darkf +Dmitry Shkurskiy (endorph) Douglas Diniz (Dgdiniz) Douglas Mencken (dougmencken) Edmondo Tommasina (edmondo) From c55f9bd0aaa931afde650849856eb0371778440a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 20:19:47 +0100 Subject: [PATCH 419/889] GetWaterLevel fix (similar to MCP): if there is no water in the cell, return -FLT_MAX to prevent mods from incorrectly thinking the player is underwater. --- apps/openmw/mwscript/cellextensions.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index 0e3b379d8..5de95d1d4 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -130,7 +130,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(); - runtime.push (cell->mWaterLevel); + if (cell->mCell->hasWater()) + runtime.push (cell->mWaterLevel); + else + runtime.push (-std::numeric_limits().max()); } }; From 92521bd23d1d973ba31b62b24cd2db3a0f7b966c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 8 Jan 2014 20:27:33 +0100 Subject: [PATCH 420/889] updated changelog --- readme.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.txt b/readme.txt index 21422a8da..242dcb5c0 100644 --- a/readme.txt +++ b/readme.txt @@ -143,6 +143,7 @@ Bug #1026: Game loses track of torch usage. Bug #1028: Crash on pickup of jug in Unexplored Shipwreck, Upper level Bug #1029: Quick keys menu: Select compatible replacement when tool used up Bug #1042: TES3 header data wrong encoding +Bug #1045: OS X: deployed OpenCS won't launch Bug #1046: All damaged weaponry is worth 1 gold Bug #1048: Links in "locked" dialogue are still clickable Bug #1052: Using color codes when naming your character actually changes the name's color From 372cd437d199529c9d4d2409d0692689eeaafd03 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 22:58:36 +0100 Subject: [PATCH 421/889] Add a utility function for counting items in a container --- apps/openmw/mwscript/containerextensions.cpp | 10 ++-------- apps/openmw/mwworld/containerstore.cpp | 9 +++++++++ apps/openmw/mwworld/containerstore.hpp | 3 +++ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 5234eaea3..202ec6464 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -87,15 +87,9 @@ namespace MWScript std::string item = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); + MWWorld::ContainerStore& store = ptr.getClass().getContainerStore (ptr); - Interpreter::Type_Integer sum = 0; - - for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) - if (Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, item)) - sum += iter->getRefData().getCount(); - - runtime.push (sum); + runtime.push (store.count(item)); } }; diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 08d29b86b..1583f26e1 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -77,6 +77,15 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::end() return ContainerStoreIterator (this); } +int MWWorld::ContainerStore::count(const std::string &id) +{ + int total=0; + for (MWWorld::ContainerStoreIterator iter (begin()); iter!=end(); ++iter) + if (Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, id)) + total += iter->getRefData().getCount(); + return total; +} + void MWWorld::ContainerStore::unstack(const Ptr &ptr, const Ptr& container) { if (ptr.getRefData().getCount() <= 1) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index e4e10b327..9a97f9c1c 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -92,6 +92,9 @@ namespace MWWorld void unstack (const Ptr& ptr, const Ptr& container); ///< Unstack an item in this container. The item's count will be set to 1, then a new stack will be added with (origCount-1). + /// @return How many items with refID \a id are in this container? + int count (const std::string& id); + protected: ContainerStoreIterator addNewStack (const Ptr& ptr, int count); ///< Add the item to this container (do not try to stack it onto existing items) From 0f5dc5917695d61953bce045f82919b4063f253e Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 23:37:46 +0100 Subject: [PATCH 422/889] Remove useless dependencies on InventoryWindow for getting player gold. Don't use string literals for gold_001 id, just to be sure. --- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwgui/dialogue.cpp | 10 +++---- apps/openmw/mwgui/enchantingdialog.cpp | 7 ++--- apps/openmw/mwgui/inventorywindow.cpp | 13 ---------- apps/openmw/mwgui/inventorywindow.hpp | 2 -- apps/openmw/mwgui/merchantrepair.cpp | 12 ++++----- apps/openmw/mwgui/spellbuyingwindow.cpp | 19 +++++++++----- apps/openmw/mwgui/spellcreationdialog.cpp | 10 +++---- apps/openmw/mwgui/tradeitemmodel.cpp | 2 +- apps/openmw/mwgui/tradewindow.cpp | 26 +++++++++++-------- apps/openmw/mwgui/tradewindow.hpp | 4 +-- apps/openmw/mwgui/trainingwindow.cpp | 14 +++++----- apps/openmw/mwgui/travelwindow.cpp | 20 +++++++++----- apps/openmw/mwmechanics/enchanting.cpp | 2 +- .../mwmechanics/mechanicsmanagerimp.cpp | 1 + apps/openmw/mwworld/containerstore.cpp | 6 +++-- apps/openmw/mwworld/containerstore.hpp | 2 ++ 18 files changed, 82 insertions(+), 72 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 864a756c5..480c335c2 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -100,7 +100,7 @@ namespace MWClass // TODO: this is not quite correct, in vanilla the merchant's gold pool is not available in his inventory. // (except for gold you gave him) - data->mContainerStore.add("gold_001", ref->mBase->mData.mGold, ptr); + data->mContainerStore.add(MWWorld::ContainerStore::sGoldId, ref->mBase->mData.mGold, ptr); // store ptr.getRefData().setCustomData (data.release()); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 24caed3f5..f3b396a70 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -328,7 +328,7 @@ namespace MWClass // TODO: this is not quite correct, in vanilla the merchant's gold pool is not available in his inventory. // (except for gold you gave him) - getContainerStore(ptr).add("gold_001", gold, ptr); + getContainerStore(ptr).add(MWWorld::ContainerStore::sGoldId, gold, ptr); getInventoryStore(ptr).autoEquip(ptr); } diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 0460900ed..8269e8364 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -20,7 +20,6 @@ #include "list.hpp" #include "tradewindow.hpp" #include "spellbuyingwindow.hpp" -#include "inventorywindow.hpp" #include "travelwindow.hpp" #include "bookpage.hpp" @@ -75,17 +74,17 @@ namespace MWGui else if (sender == mTauntButton) type = MWBase::MechanicsManager::PT_Taunt; else if (sender == mBribe10Button) { - player.getClass().getContainerStore(player).remove("gold_001", 10, player); + player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, 10, player); type = MWBase::MechanicsManager::PT_Bribe10; } else if (sender == mBribe100Button) { - player.getClass().getContainerStore(player).remove("gold_001", 100, player); + player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, 100, player); type = MWBase::MechanicsManager::PT_Bribe100; } else /*if (sender == mBribe1000Button)*/ { - player.getClass().getContainerStore(player).remove("gold_001", 1000, player); + player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, 1000, player); type = MWBase::MechanicsManager::PT_Bribe1000; } @@ -99,7 +98,8 @@ namespace MWGui WindowModal::open(); center(); - int playerGold = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); mBribe10Button->setEnabled (playerGold >= 10); mBribe100Button->setEnabled (playerGold >= 100); diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index cb57b51b1..4d79875a2 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -5,12 +5,11 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" -#include "../mwworld/manualref.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/containerstore.hpp" #include "itemselection.hpp" #include "container.hpp" -#include "inventorywindow.hpp" #include "sortfilteritemmodel.hpp" @@ -289,7 +288,9 @@ namespace MWGui mEnchanting.setNewItemName(mName->getCaption()); mEnchanting.setEffect(mEffectList); - if (mEnchanting.getEnchantPrice() > MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + if (mEnchanting.getEnchantPrice() > playerGold) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage18}"); return; diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 27be9339b..0ad156cdf 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -447,19 +447,6 @@ namespace MWGui updateEncumbranceBar(); } - int InventoryWindow::getPlayerGold() - { - MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); - - for (MWWorld::ContainerStoreIterator it = invStore.begin(); - it != invStore.end(); ++it) - { - if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, "gold_001")) - return it->getRefData().getCount(); - } - return 0; - } - void InventoryWindow::setTrading(bool trading) { mTrading = trading; diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 94ecfd4c8..112e737fa 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -31,8 +31,6 @@ namespace MWGui void pickUpObject (MWWorld::Ptr object); - int getPlayerGold(); - MyGUI::IntCoord getAvatarScreenCoord(); MWWorld::Ptr getAvatarSelectedItem(int x, int y); diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 20eb3a615..8b811ff50 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -11,8 +11,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" -#include "inventorywindow.hpp" - namespace MWGui { @@ -36,6 +34,8 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) int currentY = 0; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); int categories = MWWorld::ContainerStore::Type_Weapon | MWWorld::ContainerStore::Type_Armor; for (MWWorld::ContainerStoreIterator iter (store.begin(categories)); @@ -69,7 +69,7 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) MyGUI::Button* button = mList->createWidget( - (price>MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", + (price>playerGold) ? "SandTextGreyedOut" : "SandTextButton", 0, currentY, 0, @@ -79,7 +79,7 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) currentY += 18; - button->setEnabled(price<=MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()); + button->setEnabled(price<=playerGold); button->setUserString("Price", boost::lexical_cast(price)); button->setUserData(*iter); button->setCaptionWithReplacing(name); @@ -92,7 +92,7 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) mList->setCanvasSize (MyGUI::IntSize(mList->getWidth(), std::max(mList->getHeight(), currentY))); mGoldLabel->setCaptionWithReplacing("#{sGold}: " - + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); + + boost::lexical_cast(playerGold)); } void MerchantRepair::onMouseWheel(MyGUI::Widget* _sender, int _rel) @@ -119,7 +119,7 @@ void MerchantRepair::onRepairButtonClick(MyGUI::Widget *sender) int price = boost::lexical_cast(sender->getUserString("Price")); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - player.getClass().getContainerStore(player).remove("gold_001", price, player); + player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); startRepair(mActor); } diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index f285b01ca..4be7919a9 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -13,8 +13,6 @@ #include "../mwmechanics/creaturestats.hpp" -#include "inventorywindow.hpp" - namespace MWGui { const int SpellBuyingWindow::sLineHeight = 18; @@ -42,9 +40,12 @@ namespace MWGui int price = spell->mData.mCost*store.get().find("fSpellValueMult")->getFloat(); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + MyGUI::Button* toAdd = mSpellsView->createWidget( - (price>MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", + (price>playerGold) ? "SandTextGreyedOut" : "SandTextButton", 0, mCurrentY, 200, @@ -116,13 +117,16 @@ namespace MWGui { int price = *_sender->getUserData(); - if (MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()>=price) + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + + if (playerGold>=price) { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); spells.add (mSpellsWidgetMap.find(_sender)->second); - player.getClass().getContainerStore(player).remove("gold_001", price, player); + player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); startSpellBuying(mPtr); MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); @@ -136,7 +140,10 @@ namespace MWGui void SpellBuyingWindow::updateLabels() { - mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + + mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(playerGold)); mPlayerGold->setCoord(8, mPlayerGold->getTop(), mPlayerGold->getTextSize().width, diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 3a17d50aa..857dd76d5 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -13,7 +13,6 @@ #include "tooltips.hpp" #include "class.hpp" -#include "inventorywindow.hpp" namespace { @@ -333,7 +332,10 @@ namespace MWGui return; } - if (boost::lexical_cast(mPriceLabel->getCaption()) > MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + + if (boost::lexical_cast(mPriceLabel->getCaption()) > playerGold) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage18}"); return; @@ -341,9 +343,7 @@ namespace MWGui mSpell.mName = mNameEdit->getCaption(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - - player.getClass().getContainerStore(player).remove("gold_001", boost::lexical_cast(mPriceLabel->getCaption()), player); + player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, boost::lexical_cast(mPriceLabel->getCaption()), player); MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); diff --git a/apps/openmw/mwgui/tradeitemmodel.cpp b/apps/openmw/mwgui/tradeitemmodel.cpp index 5c12843da..28216ad14 100644 --- a/apps/openmw/mwgui/tradeitemmodel.cpp +++ b/apps/openmw/mwgui/tradeitemmodel.cpp @@ -149,7 +149,7 @@ namespace MWGui if(!mMerchant.isEmpty()) { MWWorld::Ptr base = item.mBase; - if(Misc::StringUtils::ciEqual(base.getCellRef().mRefID, "gold_001")) + if(Misc::StringUtils::ciEqual(base.getCellRef().mRefID, MWWorld::ContainerStore::sGoldId)) continue; if(!MWWorld::Class::get(base).canSell(base, services)) continue; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 0111623be..1cd4c1c7c 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -210,11 +210,11 @@ namespace MWGui if (amount > 0) { - store.add("gold_001", amount, actor); + store.add(MWWorld::ContainerStore::sGoldId, amount, actor); } else { - store.remove("gold_001", - amount, actor); + store.remove(MWWorld::ContainerStore::sGoldId, - amount, actor); } } @@ -251,8 +251,11 @@ namespace MWGui return; } + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + // check if the player can afford this - if (mCurrentBalance < 0 && MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold() < std::abs(mCurrentBalance)) + if (mCurrentBalance < 0 && playerGold < std::abs(mCurrentBalance)) { // user notification MWBase::Environment::get().getWindowManager()-> @@ -269,8 +272,6 @@ namespace MWGui return; } - MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); - if(mCurrentBalance > mCurrentMerchantOffer) { //if npc is a creature: reject (no haggle) @@ -292,8 +293,8 @@ namespace MWGui float clampedDisposition = std::max(0,std::min(int(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr) + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange()),100)); - const MWMechanics::NpcStats &sellerStats = MWWorld::Class::get(mPtr).getNpcStats(mPtr); - const MWMechanics::NpcStats &playerStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); + const MWMechanics::NpcStats &sellerStats = mPtr.getClass().getNpcStats(mPtr); + const MWMechanics::NpcStats &playerStats = player.getClass().getNpcStats(player); float a1 = std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100); float b1 = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); @@ -322,7 +323,7 @@ namespace MWGui } //skill use! - MWWorld::Class::get(playerPtr).skillUsageSucceeded(playerPtr, ESM::Skill::Mercantile, 0); + player.getClass().skillUsageSucceeded(player, ESM::Skill::Mercantile, 0); } int iBarterSuccessDisposition = gmst.find("iBarterSuccessDisposition")->getInt(); @@ -335,7 +336,7 @@ namespace MWGui // transfer the gold if (mCurrentBalance != 0) { - addOrRemoveGold(mCurrentBalance, playerPtr); + addOrRemoveGold(mCurrentBalance, player); addOrRemoveGold(-mCurrentBalance, mPtr); } @@ -396,7 +397,10 @@ namespace MWGui void TradeWindow::updateLabels() { - mPlayerGold->setCaptionWithReplacing("#{sYourGold} " + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + + mPlayerGold->setCaptionWithReplacing("#{sYourGold} " + boost::lexical_cast(playerGold)); if (mCurrentBalance > 0) { @@ -445,7 +449,7 @@ namespace MWGui MWWorld::ContainerStore store = mPtr.getClass().getContainerStore(mPtr); for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { - if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, "gold_001")) + if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, MWWorld::ContainerStore::sGoldId)) merchantGold += it->getRefData().getCount(); } return merchantGold; diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index 7c11bd539..1a8999e6e 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -28,8 +28,6 @@ namespace MWGui void startTrade(const MWWorld::Ptr& actor); - void addOrRemoveGold(int gold, const MWWorld::Ptr& actor); - void onFrame(float frameDuration); void borrowItem (int index, size_t count); @@ -95,6 +93,8 @@ namespace MWGui void onIncreaseButtonTriggered(); void onDecreaseButtonTriggered(); + void addOrRemoveGold(int gold, const MWWorld::Ptr& actor); + void updateLabels(); virtual void onReferenceUnavailable(); diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index fc96d31bb..e196e93f2 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -14,7 +14,6 @@ #include "../mwmechanics/npcstats.hpp" -#include "inventorywindow.hpp" #include "tooltips.hpp" namespace MWGui @@ -40,7 +39,10 @@ namespace MWGui { mPtr = actor; - mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + + mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(playerGold)); MWMechanics::NpcStats& npcStats = MWWorld::Class::get(actor).getNpcStats (actor); @@ -71,7 +73,6 @@ namespace MWGui MyGUI::EnumeratorWidgetPtr widgets = mTrainingOptions->getEnumerator (); MyGUI::Gui::getInstance ().destroyWidgets (widgets); - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); const MWWorld::Store &gmst = @@ -82,7 +83,7 @@ namespace MWGui int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer (mPtr,pcStats.getSkill (bestSkills[i].first).getBase() * gmst.find("iTrainingMod")->getInt (),true); - std::string skin = (price > MWBase::Environment::get().getWindowManager()->getInventoryWindow ()->getPlayerGold ()) ? "SandTextGreyedOut" : "SandTextButton"; + std::string skin = (price > playerGold) ? "SandTextGreyedOut" : "SandTextButton"; MyGUI::Button* button = mTrainingOptions->createWidget(skin, MyGUI::IntCoord(5, 5+i*18, mTrainingOptions->getWidth()-10, 18), MyGUI::Align::Default); @@ -115,6 +116,7 @@ namespace MWGui int skillId = *sender->getUserData(); MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); const MWWorld::ESMStore &store = @@ -123,7 +125,7 @@ namespace MWGui int price = pcStats.getSkill (skillId).getBase() * store.get().find("iTrainingMod")->getInt (); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); - if (MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()removeGuiMode (GM_Training); diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 7f223c505..2f844c3fd 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -2,6 +2,8 @@ #include +#include + #include #include "../mwbase/environment.hpp" @@ -12,8 +14,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" -#include "inventorywindow.hpp" - namespace MWGui { const int TravelWindow::sLineHeight = 18; @@ -50,13 +50,15 @@ namespace MWGui const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + if(interior) { price = gmst.find("fMagesGuildTravel")->getFloat(); } else { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); ESM::Position PlayerPos = player.getRefData().getPosition(); float d = sqrt( pow(pos.pos[0] - PlayerPos.pos[0],2) + pow(pos.pos[1] - PlayerPos.pos[1],2) + pow(pos.pos[2] - PlayerPos.pos[2],2) ); price = d/gmst.find("fTravelMult")->getFloat(); @@ -64,7 +66,7 @@ namespace MWGui price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); - MyGUI::Button* toAdd = mDestinationsView->createWidget((price>MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); + MyGUI::Button* toAdd = mDestinationsView->createWidget((price>playerGold) ? "SandTextGreyedOut" : "SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); mCurrentY += sLineHeight; if(interior) toAdd->setUserString("interior","y"); @@ -121,12 +123,13 @@ namespace MWGui iss >> price; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); - if (MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()getFader ()->fadeOut(1); ESM::Position pos = *_sender->getUserData(); @@ -165,7 +168,10 @@ namespace MWGui void TravelWindow::updateLabels() { - mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + + mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(playerGold)); mPlayerGold->setCoord(8, mPlayerGold->getTop(), mPlayerGold->getTextSize().width, diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index f428bd4b0..9ccc69f90 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -299,6 +299,6 @@ namespace MWMechanics const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); - store.remove("gold_001", getEnchantPrice(), player); + store.remove(MWWorld::ContainerStore::sGoldId, getEnchantPrice(), player); } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 429683b97..2a613b7ff 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -802,6 +802,7 @@ namespace MWMechanics && (type != OT_Assault || it->first != victim) ) { + // TODO: stats.setAlarmed(true) on NPCs within earshot reported=true; break; } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 1583f26e1..70a6fe856 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -63,6 +63,8 @@ namespace } } +const std::string MWWorld::ContainerStore::sGoldId = "gold_001"; + MWWorld::ContainerStore::ContainerStore() : mCachedWeight (0), mWeightUpToDate (false) {} MWWorld::ContainerStore::~ContainerStore() {} @@ -196,7 +198,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr, for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) { - if (Misc::StringUtils::ciEqual((*iter).get()->mRef.mRefID, "gold_001")) + if (Misc::StringUtils::ciEqual((*iter).get()->mRef.mRefID, MWWorld::ContainerStore::sGoldId)) { iter->getRefData().setCount(iter->getRefData().getCount() + realCount); flagAsModified(); @@ -204,7 +206,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr, } } - MWWorld::ManualRef ref(esmStore, "Gold_001", count); + MWWorld::ManualRef ref(esmStore, MWWorld::ContainerStore::sGoldId, count); return addNewStack(ref.getPtr(), count); } diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 9a97f9c1c..936468f8d 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -35,6 +35,8 @@ namespace MWWorld static const int Type_All = 0xffff; + static const std::string sGoldId; + private: MWWorld::CellRefList potions; From d7f69205f652bad7a2f043ca357fbf61cd6d9022 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 23:56:40 +0100 Subject: [PATCH 423/889] Don't play "Menu Click" sound for disabled (= cannot receive input) buttons, plus some redundancy fixes --- apps/openmw/mwgui/merchantrepair.cpp | 3 +-- apps/openmw/mwgui/spellbuyingwindow.cpp | 21 ++++++++------------- apps/openmw/mwgui/trainingwindow.cpp | 9 ++------- apps/openmw/mwgui/travelwindow.cpp | 3 ++- apps/openmw/mwinput/inputmanagerimp.cpp | 2 +- files/mygui/openmw_text.skin.xml | 8 -------- 6 files changed, 14 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 8b811ff50..3c3335d8b 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -68,8 +68,7 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) MyGUI::Button* button = - mList->createWidget( - (price>playerGold) ? "SandTextGreyedOut" : "SandTextButton", + mList->createWidget("SandTextButton", 0, currentY, 0, diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 4be7919a9..68aecf28d 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -45,13 +45,14 @@ namespace MWGui MyGUI::Button* toAdd = mSpellsView->createWidget( - (price>playerGold) ? "SandTextGreyedOut" : "SandTextButton", + "SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default ); + toAdd->setEnabled(price<=playerGold); mCurrentY += sLineHeight; @@ -118,19 +119,13 @@ namespace MWGui int price = *_sender->getUserData(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); + MWMechanics::Spells& spells = stats.getSpells(); + spells.add (mSpellsWidgetMap.find(_sender)->second); + player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); + startSpellBuying(mPtr); - if (playerGold>=price) - { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); - MWMechanics::Spells& spells = stats.getSpells(); - spells.add (mSpellsWidgetMap.find(_sender)->second); - player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); - startSpellBuying(mPtr); - - MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); - } + MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); } void SpellBuyingWindow::onCancelButtonClicked(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index e196e93f2..709bc0fb9 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -83,11 +83,10 @@ namespace MWGui int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer (mPtr,pcStats.getSkill (bestSkills[i].first).getBase() * gmst.find("iTrainingMod")->getInt (),true); - std::string skin = (price > playerGold) ? "SandTextGreyedOut" : "SandTextButton"; - - MyGUI::Button* button = mTrainingOptions->createWidget(skin, + MyGUI::Button* button = mTrainingOptions->createWidget("SandTextButton", MyGUI::IntCoord(5, 5+i*18, mTrainingOptions->getWidth()-10, 18), MyGUI::Align::Default); + button->setEnabled(price <= playerGold); button->setUserData(bestSkills[i].first); button->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onTrainingSelected); @@ -116,7 +115,6 @@ namespace MWGui int skillId = *sender->getUserData(); MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); - int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); const MWWorld::ESMStore &store = @@ -125,9 +123,6 @@ namespace MWGui int price = pcStats.getSkill (skillId).getBase() * store.get().find("iTrainingMod")->getInt (); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); - if (playerGoldgetBarterOffer(mPtr,price,true); - MyGUI::Button* toAdd = mDestinationsView->createWidget((price>playerGold) ? "SandTextGreyedOut" : "SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); + MyGUI::Button* toAdd = mDestinationsView->createWidget("SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); + toAdd->setEnabled(price<=playerGold); mCurrentY += sLineHeight; if(interior) toAdd->setUserString("interior","y"); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 77b72fc0a..c2efa0c33 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -555,7 +555,7 @@ namespace MWInput if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0) { MyGUI::Button* b = MyGUI::InputManager::getInstance ().getMouseFocusWidget ()->castType(false); - if (b) + if (b && b->getEnabled()) { MWBase::Environment::get().getSoundManager ()->playSound ("Menu Click", 1.f, 1.f); } diff --git a/files/mygui/openmw_text.skin.xml b/files/mygui/openmw_text.skin.xml index 6a1dea60b..15287bc74 100644 --- a/files/mygui/openmw_text.skin.xml +++ b/files/mygui/openmw_text.skin.xml @@ -17,14 +17,6 @@ - - - - - - - - From 9bf7bf529c948b02a63596a17e9ca30dbeff2af3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Jan 2014 00:40:25 +0100 Subject: [PATCH 424/889] Implement crime-related dialogue globals as they are described in MSFD --- apps/openmw/mwbase/world.hpp | 4 ++++ apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 6 ++++++ apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 21 +++++++++++++++++++ apps/openmw/mwworld/worldimp.hpp | 4 ++++ 5 files changed, 36 insertions(+) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 611bd913b..b719ce4d6 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -448,6 +448,10 @@ namespace MWBase /// @note This also works for references in containers. virtual void listDetectedReferences (const MWWorld::Ptr& ptr, std::vector& out, DetectionType type) = 0; + + /// Update the value of some globals according to the world state, which may be used by dialogue entries. + /// This should be called when initiating a dialogue. + virtual void updateDialogueGlobals() = 0; }; } diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 2fce7e327..b2107c329 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -143,6 +143,7 @@ namespace MWDialogue //setup the list of topics known by the actor. Topics who are also on the knownTopics list will be added to the GUI updateTopics(); + updateGlobals(); //greeting const MWWorld::Store &dialogs = @@ -298,6 +299,11 @@ namespace MWDialogue } } + void DialogueManager::updateGlobals() + { + MWBase::Environment::get().getWorld()->updateDialogueGlobals(); + } + void DialogueManager::updateTopics() { std::list keywordList; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 5baf20a0e..c32a5dbd8 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -40,6 +40,7 @@ namespace MWDialogue void parseText (const std::string& text); void updateTopics(); + void updateGlobals(); bool compile (const std::string& cmd,std::vector& code); void executeScript (const std::string& script); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8bb2e7956..46511f5bc 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2420,4 +2420,25 @@ namespace MWWorld { return mPlayer->getPlayer(); } + + void World::updateDialogueGlobals() + { + MWWorld::Ptr player = getPlayerPtr(); + int bounty = player.getClass().getNpcStats(player).getBounty(); + int playerGold = player.getClass().getContainerStore(player).count(ContainerStore::sGoldId); + + float fCrimeGoldDiscountMult = getStore().get().find("fCrimeGoldDiscountMult")->getFloat(); + float fCrimeGoldTurnInMult = getStore().get().find("fCrimeGoldTurnInMult")->getFloat(); + + int discount = bounty*fCrimeGoldDiscountMult; + int turnIn = bounty * fCrimeGoldTurnInMult; + + mGlobalVariables->setInt("pchascrimegold", (bounty <= playerGold) ? 1 : 0); + + mGlobalVariables->setInt("pchasgolddiscount", (discount <= playerGold) ? 1 : 0); + mGlobalVariables->setInt("crimegolddiscount", discount); + + mGlobalVariables->setInt("crimegoldturnin", turnIn); + mGlobalVariables->setInt("pchasturnin", (turnIn <= playerGold) ? 1 : 0); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 51ad3a045..ba7dc66b6 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -533,6 +533,10 @@ namespace MWWorld /// @note This also works for references in containers. virtual void listDetectedReferences (const MWWorld::Ptr& ptr, std::vector& out, DetectionType type); + + /// Update the value of some globals according to the world state, which may be used by dialogue entries. + /// This should be called when initiating a dialogue. + virtual void updateDialogueGlobals(); }; } From 925d28e4a7b812ffc2cafe020b61fe93230ffd98 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Thu, 9 Jan 2014 00:46:25 +0100 Subject: [PATCH 425/889] crashcatcher uname error checking --- apps/openmw/crashcatcher.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/crashcatcher.cpp b/apps/openmw/crashcatcher.cpp index d7ebc0d47..65a036919 100644 --- a/apps/openmw/crashcatcher.cpp +++ b/apps/openmw/crashcatcher.cpp @@ -177,7 +177,7 @@ static void sys_info(void) { #ifdef __unix__ struct utsname info; - if(!uname(&info)) + if(uname(&info)) printf("!!! Failed to get system information\n"); else printf("System: %s %s %s %s %s\n", From baf55df7a11a7e199121f0a653eca4414bcd4c0e Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Jan 2014 01:34:10 +0100 Subject: [PATCH 426/889] Gold fixes (when did this break?) --- apps/openmw/mwclass/misc.cpp | 4 +++- apps/openmw/mwgui/inventorywindow.cpp | 2 -- apps/openmw/mwworld/containerstore.cpp | 10 ++++++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index e5120462e..a8a6c55ec 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -93,7 +93,9 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - int value = (ptr.getCellRef().mGoldValue == 1) ? ref->mBase->mData.mValue : ptr.getCellRef().mGoldValue; + int value = ref->mBase->mData.mValue; + if (ptr.getCellRef().mGoldValue > 1 && ptr.getRefData().getCount() == 1) + value = ptr.getCellRef().mGoldValue; if (ptr.getCellRef().mSoul != "") { diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 0ad156cdf..7781c8526 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -499,8 +499,6 @@ namespace MWGui return; int count = object.getRefData().getCount(); - if (object.getCellRef().mGoldValue > 1) - count = object.getCellRef().mGoldValue; // add to player inventory // can't use ActionTake here because we need an MWWorld::Ptr to the newly inserted object diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 70a6fe856..744971985 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -134,7 +134,11 @@ bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& 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); - return add(ref.getPtr(), count, actorPtr, true); + // a bit pointless to set owner for the player + if (actorPtr.getRefData().getHandle() != "player") + return add(ref.getPtr(), count, actorPtr, true); + else + return add(ref.getPtr(), count, actorPtr, false); } MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner) @@ -194,7 +198,9 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr, || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_025") || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_100")) { - int realCount = MWWorld::Class::get(ptr).getValue(ptr) * ptr.getRefData().getCount(); + int realCount = ptr.getRefData().getCount(); + if (ptr.getCellRef().mGoldValue > 1 && realCount == 1) + realCount = ptr.getCellRef().mGoldValue; for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) { From 6f9113fe882c183d572bcffe846af1a63becd071 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Jan 2014 01:49:58 +0100 Subject: [PATCH 427/889] Add preliminary implementation of PayFine, PayFineThief and GoToJail instructions --- apps/openmw/mwbase/world.hpp | 5 ++- apps/openmw/mwgui/messagebox.cpp | 1 + apps/openmw/mwmechanics/spellcasting.cpp | 8 ++--- apps/openmw/mwrender/camera.cpp | 1 + apps/openmw/mwscript/docs/vmformat.txt | 5 ++- apps/openmw/mwscript/miscextensions.cpp | 40 ++++++++++++++++++++++++ apps/openmw/mwworld/worldimp.cpp | 8 ++++- apps/openmw/mwworld/worldimp.hpp | 5 ++- components/compiler/extensions0.cpp | 3 ++ components/compiler/opcodes.hpp | 3 ++ 10 files changed, 65 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index b719ce4d6..43e526ecb 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -431,11 +431,10 @@ namespace MWBase virtual bool findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, Ogre::Vector3& result) = 0; - /// Teleports \a ptr to the reference of \a id (e.g. DivineMarker, PrisonMarker, TempleMarker) - /// closest to \a worldPos. + /// Teleports \a ptr to the closest reference of \a id (e.g. DivineMarker, PrisonMarker, TempleMarker) /// @note id must be lower case virtual void teleportToClosestMarker (const MWWorld::Ptr& ptr, - const std::string& id, Ogre::Vector3 worldPos) = 0; + const std::string& id) = 0; enum DetectionType { diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 378e76e46..8e518668b 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -10,6 +10,7 @@ namespace MWGui MessageBoxManager::MessageBoxManager () { + // TODO: fMessageTimePerChar mMessageBoxSpeed = 0.1; mInterMessageBoxe = NULL; mStaticMessageBox = NULL; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 21b6d0d77..a1cdb8b61 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -287,17 +287,13 @@ namespace MWMechanics if (!MWBase::Environment::get().getWorld()->isTeleportingEnabled()) return; - Ogre::Vector3 worldPos; - if (!MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(target.getCell(), worldPos)) - worldPos = MWBase::Environment::get().getWorld()->getPlayer().getLastKnownExteriorPosition(); - if (effectId == ESM::MagicEffect::DivineIntervention) { - MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker", worldPos); + MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker"); } else if (effectId == ESM::MagicEffect::AlmsiviIntervention) { - MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "templemarker", worldPos); + MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "templemarker"); } else if (effectId == ESM::MagicEffect::Mark) diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 9a35725ee..dda9797ef 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -228,6 +228,7 @@ namespace MWRender void Camera::setSneakOffset() { + // TODO: iFirstPersonSneakDelta if(mAnimation) mAnimation->addFirstPersonOffset(Ogre::Vector3(0.f, 0.f, -9.8f)); } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index a6349c4da..64a9d427a 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -372,5 +372,8 @@ op 0x2000231: GetSpellReadied op 0x2000232: GetSpellReadied, explicit op 0x2000233: GetPcJumping op 0x2000234: ShowRestMenu, explicit -opcodes 0x2000235-0x3ffffff unused +op 0x2000235: GoToJail +op 0x2000236: PayFine +op 0x2000237: PayFineThief +opcodes 0x2000238-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 61d286ae3..03dc5c14c 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -762,6 +762,43 @@ namespace MWScript } }; + class OpGoToJail : public Interpreter::Opcode0 + { + public: + virtual void execute (Interpreter::Runtime& runtime) + { + MWBase::World* world = MWBase::Environment::get().getWorld(); + MWWorld::Ptr player = world->getPlayerPtr(); + world->teleportToClosestMarker(player, "prisonmarker"); + player.getClass().getNpcStats(player).setBounty(0); + // TODO: pass time, change skills, show messagebox + // TODO: move stolen items to closest evidence chest + // iDaysinPrisonMod + } + }; + + class OpPayFine : public Interpreter::Opcode0 + { + public: + virtual void execute(Interpreter::Runtime &runtime) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + player.getClass().getNpcStats(player).setBounty(0); + + // TODO: move stolen items to closest evidence chest + } + }; + + class OpPayFineThief : public Interpreter::Opcode0 + { + public: + virtual void execute(Interpreter::Runtime &runtime) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + player.getClass().getNpcStats(player).setBounty(0); + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox); @@ -785,6 +822,9 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeGetPcJumping, new OpGetPcJumping); interpreter.installSegment5 (Compiler::Misc::opcodeWakeUpPc, new OpWakeUpPc); interpreter.installSegment5 (Compiler::Misc::opcodePlayBink, new OpPlayBink); + interpreter.installSegment5 (Compiler::Misc::opcodePayFine, new OpPayFine); + interpreter.installSegment5 (Compiler::Misc::opcodePayFineThief, new OpPayFineThief); + interpreter.installSegment5 (Compiler::Misc::opcodeGoToJail, new OpGoToJail); interpreter.installSegment5 (Compiler::Misc::opcodeGetLocked, new OpGetLocked); interpreter.installSegment5 (Compiler::Misc::opcodeGetLockedExplicit, new OpGetLocked); interpreter.installSegment5 (Compiler::Misc::opcodeGetEffect, new OpGetEffect); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 46511f5bc..f61938f92 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2273,6 +2273,8 @@ namespace MWWorld bool World::findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, Ogre::Vector3& result) { + if (cell->isExterior()) + return false; MWWorld::CellRefList& doors = cell->mDoors; CellRefList::List& refList = doors.mList; @@ -2293,8 +2295,12 @@ namespace MWWorld } void World::teleportToClosestMarker (const MWWorld::Ptr& ptr, - const std::string& id, Ogre::Vector3 worldPos) + const std::string& id) { + Ogre::Vector3 worldPos; + if (!findInteriorPositionInWorldSpace(ptr.getCell(), worldPos)) + worldPos = mPlayer->getLastKnownExteriorPosition(); + MWWorld::Ptr closestMarker; float closestDistance = FLT_MAX; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index ba7dc66b6..cc087a89f 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -522,11 +522,10 @@ namespace MWWorld virtual bool findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, Ogre::Vector3& result); - /// Teleports \a ptr to the reference of \a id (e.g. DivineMarker, PrisonMarker, TempleMarker) - /// closest to \a worldPos. + /// Teleports \a ptr to the closest reference of \a id (e.g. DivineMarker, PrisonMarker, TempleMarker) /// @note id must be lower case virtual void teleportToClosestMarker (const MWWorld::Ptr& ptr, - const std::string& id, Ogre::Vector3 worldPos); + const std::string& id); /// List all references (filtered by \a type) detected by \a ptr. The range /// is determined by the current magnitude of the "Detect X" magic effect belonging to \a type. diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index e35a32ffa..03510d833 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -244,6 +244,9 @@ namespace Compiler extensions.registerFunction ("getpcjumping", 'l', "", opcodeGetPcJumping); extensions.registerInstruction ("wakeuppc", "", opcodeWakeUpPc); extensions.registerInstruction ("playbink", "Sl", opcodePlayBink); + extensions.registerInstruction ("payfine", "", opcodePayFine); + extensions.registerInstruction ("payfinethief", "", opcodePayFineThief); + extensions.registerInstruction ("gotojail", "", opcodeGoToJail); extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); extensions.registerFunction ("geteffect", 'l', "S", opcodeGetEffect, opcodeGetEffectExplicit); extensions.registerInstruction ("addsoulgem", "cc", opcodeAddSoulGem, opcodeAddSoulGemExplicit); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index eb2240964..3a4c8135d 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -223,6 +223,9 @@ namespace Compiler const int opcodeGetStandingActorExplicit = 0x200020f; const int opcodeGetWindSpeed = 0x2000212; const int opcodePlayBink = 0x20001f7; + const int opcodeGoToJail = 0x2000235; + const int opcodePayFine = 0x2000236; + const int opcodePayFineThief = 0x2000237; const int opcodeHitOnMe = 0x2000213; const int opcodeHitOnMeExplicit = 0x2000214; const int opcodeDisableTeleporting = 0x2000215; From 223c1d5a389c03f4f483d6d6ed90798cc37d2041 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Jan 2014 01:55:49 +0100 Subject: [PATCH 428/889] Use GMSTs for crime bounty --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 2a613b7ff..511fe6544 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -803,6 +803,7 @@ namespace MWMechanics ) { // TODO: stats.setAlarmed(true) on NPCs within earshot + // fAlarmRadius ? reported=true; break; } @@ -816,15 +817,18 @@ namespace MWMechanics void MechanicsManager::reportCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) { + const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); // Bounty for each type of crime if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) - arg = 5; + arg = store.find("iCrimeTresspass")->getInt(); else if (type == OT_Pickpocket) - arg = 25; + arg = store.find("iCrimePickPocket")->getInt(); else if (type == OT_Assault) - arg = 40; + arg = store.find("iCrimeAttack")->getInt(); else if (type == OT_Murder) - arg = 1000; + arg = store.find("iCrimeKilling")->getInt(); + else if (type == OT_Theft) + arg *= store.find("fCrimeStealing")->getFloat(); MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}"); ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty() From 768d9f72378da0863a252614081cf9326c8fd6e7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Jan 2014 02:14:08 +0100 Subject: [PATCH 429/889] Scripting: Add an optional 'required' parameter to getReference (default: true). If required=false, it will not throw an exception if there's no reference. Fixes PcExpell not working without a reference like it's supposed to, and makes the code nicer for some others (use required=false instead of catching the exception) --- apps/openmw/mwscript/guiextensions.cpp | 9 +-------- apps/openmw/mwscript/interpretercontext.cpp | 12 ++++++------ apps/openmw/mwscript/interpretercontext.hpp | 6 +++--- apps/openmw/mwscript/miscextensions.cpp | 10 ++++------ apps/openmw/mwscript/ref.hpp | 6 +++--- apps/openmw/mwscript/statsextensions.cpp | 6 ++---- 6 files changed, 19 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index e51bdcf69..b5e2bd293 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -54,14 +54,7 @@ namespace MWScript public: virtual void execute (Interpreter::Runtime& runtime) { - // FIXME: No way to tell if we have a reference before trying to get it, and it will - // cause an exception is there isn't one :( - MWWorld::Ptr bed; - try { - bed = R()(runtime); - } - catch(std::runtime_error&) { - } + MWWorld::Ptr bed = R()(runtime, false); if (bed.isEmpty() || !MWBase::Environment::get().getMechanicsManager()->sleepInBed(MWBase::Environment::get().getWorld()->getPlayerPtr(), bed)) diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index aa9e32008..18baf0bda 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -23,7 +23,7 @@ namespace MWScript { MWWorld::Ptr InterpreterContext::getReference ( - const std::string& id, bool activeOnly) + const std::string& id, bool activeOnly, bool doThrow) { if (!id.empty()) { @@ -31,7 +31,7 @@ namespace MWScript } else { - if (mReference.isEmpty()) + if (mReference.isEmpty() && doThrow) throw std::runtime_error ("no implicit reference"); return mReference; @@ -39,7 +39,7 @@ namespace MWScript } const MWWorld::Ptr InterpreterContext::getReference ( - const std::string& id, bool activeOnly) const + const std::string& id, bool activeOnly, bool doThrow) const { if (!id.empty()) { @@ -47,7 +47,7 @@ namespace MWScript } else { - if (mReference.isEmpty()) + if (mReference.isEmpty() && doThrow) throw std::runtime_error ("no implicit reference"); return mReference; @@ -498,8 +498,8 @@ namespace MWScript ptr.getRefData().getLocals().mFloats[index] = value; } - MWWorld::Ptr InterpreterContext::getReference() + MWWorld::Ptr InterpreterContext::getReference(bool required) { - return getReference ("", true); + return getReference ("", true, required); } } diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index 9c7b443ae..04546faed 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -33,9 +33,9 @@ namespace MWScript bool mActivationHandled; boost::shared_ptr mAction; - MWWorld::Ptr getReference (const std::string& id, bool activeOnly); + MWWorld::Ptr getReference (const std::string& id, bool activeOnly, bool doThrow=true); - const MWWorld::Ptr getReference (const std::string& id, bool activeOnly) const; + const MWWorld::Ptr getReference (const std::string& id, bool activeOnly, bool doThrow=true) const; public: @@ -150,7 +150,7 @@ namespace MWScript virtual void setMemberFloat (const std::string& id, const std::string& name, float value); - MWWorld::Ptr getReference(); + MWWorld::Ptr getReference(bool required=true); ///< Reference, that the script is running from (can be empty) }; } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 03dc5c14c..a403d76ed 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -699,13 +699,11 @@ namespace MWScript public: virtual void execute(Interpreter::Runtime& runtime) { - // No way to tell if we have a reference before trying to get it, and it will - // cause an exception is there isn't one :( - try { - MWWorld::Ptr ptr = R()(runtime); + MWWorld::Ptr ptr = R()(runtime, false); + if (!ptr.isEmpty()) printLocalVars(runtime, ptr); - } - catch(std::runtime_error&) { + else + { // No reference, no problem. printGlobalVars(runtime); } diff --git a/apps/openmw/mwscript/ref.hpp b/apps/openmw/mwscript/ref.hpp index 81b1d5ef9..795839139 100644 --- a/apps/openmw/mwscript/ref.hpp +++ b/apps/openmw/mwscript/ref.hpp @@ -16,7 +16,7 @@ namespace MWScript { struct ExplicitRef { - MWWorld::Ptr operator() (Interpreter::Runtime& runtime) const + MWWorld::Ptr operator() (Interpreter::Runtime& runtime, bool required=true) const { std::string id = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); @@ -27,12 +27,12 @@ namespace MWScript struct ImplicitRef { - MWWorld::Ptr operator() (Interpreter::Runtime& runtime) const + MWWorld::Ptr operator() (Interpreter::Runtime& runtime, bool required=true) const { MWScript::InterpreterContext& context = static_cast (runtime.getContext()); - return context.getReference(); + return context.getReference(required); } }; } diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 56b309576..d6c2cb894 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -921,8 +921,6 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { - MWWorld::Ptr ptr = R()(runtime); - std::string factionID = ""; if(arg0 >0 ) { @@ -931,6 +929,7 @@ namespace MWScript } else { + MWWorld::Ptr ptr = R()(runtime); if(MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().empty()) { factionID = ""; @@ -955,8 +954,6 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { - MWWorld::Ptr ptr = R()(runtime); - std::string factionID = ""; if(arg0 >0 ) { @@ -965,6 +962,7 @@ namespace MWScript } else { + MWWorld::Ptr ptr = R()(runtime); if(MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().empty()) { factionID = ""; From aa855e9524115d27c0dda59c69a7d0166f95e396 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Jan 2014 20:56:24 +0100 Subject: [PATCH 430/889] Include some required Ogre headers explicitely --- apps/opencs/view/render/scenewidget.cpp | 1 + apps/openmw/mwrender/characterpreview.cpp | 1 + apps/openmw/mwrender/occlusionquery.cpp | 1 + apps/openmw/mwrender/shadows.cpp | 1 + apps/openmw/mwrender/water.cpp | 1 + components/nifogre/ogrenifloader.cpp | 4 +++- libs/openengine/ogre/renderer.cpp | 1 + 7 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index c8b37e9bb..620586bd2 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace CSVRender { diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index f7333db35..2dbc72e26 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include diff --git a/apps/openmw/mwrender/occlusionquery.cpp b/apps/openmw/mwrender/occlusionquery.cpp index a69511acd..246103471 100644 --- a/apps/openmw/mwrender/occlusionquery.cpp +++ b/apps/openmw/mwrender/occlusionquery.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "renderconst.hpp" diff --git a/apps/openmw/mwrender/shadows.cpp b/apps/openmw/mwrender/shadows.cpp index 21bbe51b6..9ebb0ab08 100644 --- a/apps/openmw/mwrender/shadows.cpp +++ b/apps/openmw/mwrender/shadows.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 0a4db30e9..9e3105168 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "sky.hpp" #include "renderingmanager.hpp" diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 0c1fdfbee..5a76b702e 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -36,6 +35,9 @@ #include #include #include +#include +#include +#include #include diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 9e5ec5414..c86697497 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include From 546b0cee76477aff7409d90a6059d1591cad46e7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Jan 2014 22:17:51 +0100 Subject: [PATCH 431/889] Closes #1077: Replace tags before trying to get the message length --- apps/openmw/mwgui/messagebox.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 8e518668b..72a5bd0b0 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -63,7 +63,8 @@ namespace MWGui { MessageBox *box = new MessageBox(*this, message); box->mCurrentTime = 0; - box->mMaxTime = message.length()*mMessageBoxSpeed; + std::string realMessage = MyGUI::LanguageManager::getInstance().replaceTags(message); + box->mMaxTime = realMessage.length()*mMessageBoxSpeed; if(stat) mStaticMessageBox = box; From d41f27451b9bab6172c7a86ee5d9be025336a58d Mon Sep 17 00:00:00 2001 From: mrcheko Date: Thu, 9 Jan 2014 23:36:40 +0200 Subject: [PATCH 432/889] appropriate camera vanity<>preview mode switch + hit recoils fix --- apps/openmw/mwmechanics/character.cpp | 6 ++++++ apps/openmw/mwrender/animation.cpp | 11 ++++++----- apps/openmw/mwrender/animation.hpp | 2 +- apps/openmw/mwrender/camera.cpp | 10 +++++----- apps/openmw/mwrender/npcanimation.cpp | 3 +++ 5 files changed, 21 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index a4b4f2471..75fdc2550 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -179,6 +179,12 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat mHitState = CharState_Hit; int iHit = rand() % (sHitListSize-1); mCurrentHit = sHitList[iHit]; + if(mPtr.getRefData().getHandle()=="player" && !mAnimation->hasAnimation(mCurrentHit)) + { + //only 4 different hit animations if player is in 1st person + int iHit = rand() % (sHitListSize-3); + mCurrentHit = sHitList[iHit]; + } mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 8ce751286..1d4868361 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -387,7 +387,6 @@ Ogre::Node *Animation::getNode(const std::string &name) return NULL; } - NifOgre::TextKeyMap::const_iterator Animation::findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname) { NifOgre::TextKeyMap::const_iterator iter(keys.begin()); @@ -1008,14 +1007,16 @@ void Animation::detachObjectFromBone(Ogre::MovableObject *obj) mSkelBase->detachObjectFromBone(obj); } -bool Animation::isPlaying(Group group) const +bool Animation::allowSwitchViewMode() const { for (AnimStateMap::const_iterator stateiter = mStates.begin(); stateiter != mStates.end(); ++stateiter) { - if(stateiter->second.mGroups == group) - return true; + if(stateiter->second.mGroups == Group_UpperBody + || (stateiter->first.size()==4 && stateiter->first.find("hit") != std::string::npos) + || (stateiter->first.find("knock") != std::string::npos) ) + return false; } - return false; + return true; } void Animation::addEffect(const std::string &model, int effectId, bool loop, const std::string &bonename, std::string texture) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index f5f79dd72..a682f3960 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -258,7 +258,7 @@ public: /** Returns true if the named animation group is playing. */ bool isPlaying(const std::string &groupname) const; - bool isPlaying(Group group) const; + bool allowSwitchViewMode() const; /** Gets info about the given animation group. * \param groupname Animation group to check. diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 9a35725ee..9babb0b08 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -107,7 +107,7 @@ namespace MWRender void Camera::update(float duration, bool paused) { - if (!mAnimation->isPlaying(MWRender::Animation::Group_UpperBody)) + if (mAnimation->allowSwitchViewMode()) { // Now process the view changes we queued earlier if (mVanityToggleQueued) @@ -144,7 +144,7 @@ namespace MWRender { // Changing the view will stop all playing animations, so if we are playing // anything important, queue the view change for later - if (mAnimation->isPlaying(MWRender::Animation::Group_UpperBody)) + if (!mAnimation->allowSwitchViewMode()) { mViewModeToggleQueued = true; return; @@ -171,7 +171,7 @@ namespace MWRender { // Changing the view will stop all playing animations, so if we are playing // anything important, queue the view change for later - if (mAnimation->isPlaying(MWRender::Animation::Group_UpperBody)) + if (!mPreviewMode) { mVanityToggleQueued = true; return false; @@ -205,7 +205,7 @@ namespace MWRender void Camera::togglePreviewMode(bool enable) { - if (mAnimation->isPlaying(MWRender::Animation::Group_UpperBody)) + if (mFirstPersonView && !mAnimation->allowSwitchViewMode()) return; if(mPreviewMode == enable) @@ -358,7 +358,7 @@ namespace MWRender Ogre::TagPoint *tag = mAnimation->attachObjectToBone("Head", mCamera); tag->setInheritOrientation(false); } - else + else { mAnimation->setViewMode(NpcAnimation::VM_Normal); mCameraNode->attachObject(mCamera); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index b1455f0dc..edb6688ff 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -140,6 +140,9 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) { assert(viewMode != VM_HeadOnly); + if(mViewMode == viewMode) + return; + mViewMode = viewMode; rebuild(); } From bfdca3b73816831d45e86c2e806462c04fab5be2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Jan 2014 23:13:31 +0100 Subject: [PATCH 433/889] Fix needTangents not being set for cached/shared materials --- components/nifogre/material.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index be6ccbed6..e2cc3712b 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -277,6 +277,8 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, if (itr != sMaterialMap.end()) { // a suitable material exists already - use it + sh::MaterialInstance* instance = sh::Factory::getInstance().getMaterialInstance(itr->second); + needTangents = !sh::retrieveValue(instance->getProperty("normalMap"), instance).get().empty(); return itr->second; } // not found, create a new one From b98cdabe899e15b731ad4cad022d93cb7e2def1a Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 10 Jan 2014 09:42:36 +0100 Subject: [PATCH 434/889] Shameful bug fixed. --- apps/opencs/model/tools/referenceablecheck.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 6132b3407..940a251e3 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -206,6 +206,7 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str staticCheck(stage, mReferencables.getStatics(), messages); return; } + // if we come that far, we are about to perform our last, final check. finalCheck(messages); return; @@ -581,14 +582,11 @@ void CSMTools::ReferenceableCheckStage::lightCheck( if (light.mData.mFlags & ESM::Light::Carry) { - if (light.mIcon.empty()) //Needs to be checked with carrable flag - { - inventoryItemCheck(light, messages, id.toString()); + inventoryItemCheck(light, messages, id.toString()); - if (light.mData.mTime == 0) - { - messages.push_back(id.toString() + "|" + light.mId + " has zero duration"); - } + if (light.mData.mTime == 0) + { + messages.push_back(id.toString() + "|" + light.mId + " has zero duration"); } } } From e4d637fd6404c8b6c9a9282f3812df2ebda58289 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 10 Jan 2014 09:49:05 +0100 Subject: [PATCH 435/889] splited long lines in the header. --- .../opencs/model/tools/referenceablecheck.cpp | 1 - .../opencs/model/tools/referenceablecheck.hpp | 26 +++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 940a251e3..44bb7d302 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -538,7 +538,6 @@ void CSMTools::ReferenceableCheckStage::creaturesLevListCheck( const ESM::CreatureLevList& CreatureLevList = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_CreatureLevelledList, CreatureLevList.mId); //CreatureLevList but Type_CreatureLevelledList :/ - listCheck(CreatureLevList, messages, id.toString()); } diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index bc31ad537..56a85c8fd 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -42,11 +42,27 @@ namespace CSMTools void finalCheck(std::vector& messages); //TEMPLATE CHECKS - template void inventoryItemCheck(const ITEM& someitem, std::vector& messages, const std::string& someid, bool enchantable); //for all enchantable items. - template void inventoryItemCheck(const ITEM& someitem, std::vector& messages, const std::string& someid); //for non-enchantable items. - template void toolCheck(const TOOL& sometool, std::vector& messages, const std::string& someid, bool canbebroken); //for tools with uses. - template void toolCheck(const TOOL& sometool, std::vector& messages, const std::string& someid); //for tools without uses. - template void listCheck(const LIST& somelist, std::vector< std::string >& messages, const std::string& Som); + template void inventoryItemCheck(const ITEM& someitem, + std::vector& messages, + const std::string& someID, + bool enchantable); //for all enchantable items. + + template void inventoryItemCheck(const ITEM& someitem, + std::vector& messages, + const std::string& someID); //for non-enchantable items. + + template void toolCheck(const TOOL& sometool, + std::vector& messages, + const std::string& someID, + bool canbebroken); //for tools with uses. + + template void toolCheck(const TOOL& sometool, + std::vector& messages, + const std::string& someID); //for tools without uses. + + template void listCheck(const LIST& somelist, + std::vector< std::string >& messages, + const std::string& someID); const CSMWorld::RefIdData& mReferencables; const CSMWorld::IdCollection& mRaces; From 468b9d734030fb63415559c8eb4ccd556a953bee Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 10 Jan 2014 09:50:56 +0100 Subject: [PATCH 436/889] camel case in the header --- apps/opencs/model/tools/referenceablecheck.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index 56a85c8fd..dbfcd7574 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -42,25 +42,25 @@ namespace CSMTools void finalCheck(std::vector& messages); //TEMPLATE CHECKS - template void inventoryItemCheck(const ITEM& someitem, + template void inventoryItemCheck(const ITEM& someItem, std::vector& messages, const std::string& someID, bool enchantable); //for all enchantable items. - template void inventoryItemCheck(const ITEM& someitem, + template void inventoryItemCheck(const ITEM& someItem, std::vector& messages, const std::string& someID); //for non-enchantable items. - template void toolCheck(const TOOL& sometool, + template void toolCheck(const TOOL& someTool, std::vector& messages, const std::string& someID, bool canbebroken); //for tools with uses. - template void toolCheck(const TOOL& sometool, + template void toolCheck(const TOOL& someTool, std::vector& messages, const std::string& someID); //for tools without uses. - template void listCheck(const LIST& somelist, + template void listCheck(const LIST& someList, std::vector< std::string >& messages, const std::string& someID); From 31c59db71cbc779a0f92ced726150c210b3e7b8f Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 10 Jan 2014 10:22:18 +0100 Subject: [PATCH 437/889] Splited another long line in the header. --- apps/opencs/model/tools/referenceablecheck.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index dbfcd7574..338983cc7 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -11,7 +11,11 @@ namespace CSMTools class ReferenceableCheckStage : public CSMDoc::Stage { public: - ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& factions); + ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable, + const CSMWorld::IdCollection& races, + const CSMWorld::IdCollection& classes, + const CSMWorld::IdCollection& factions); + virtual void perform(int stage, std::vector< std::string >& messages); virtual int setup(); From 688488de6243f0765766fc08b991b5d49b1d3214 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 10 Jan 2014 10:43:03 +0100 Subject: [PATCH 438/889] replaced == operator for string comparsion in npc check with boost algorithm to get case insensitive check. --- apps/opencs/model/tools/referenceablecheck.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 44bb7d302..fccc5218b 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -1,7 +1,7 @@ #include "referenceablecheck.hpp" #include "../world/record.hpp" #include "../world/universalid.hpp" -#include +#include CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, @@ -651,7 +651,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck( int gold(npc.mNpdt52.mGold); //Detect if player is present - if (npc.mId == "player") + if ( boost::algorithm::iequals(npc.mId, "player") ) { mPlayerPresent = true; } From cb0d3794f7c0247291f3b5d1db4b537cc43cd211 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 10 Jan 2014 10:57:54 +0100 Subject: [PATCH 439/889] Final check is not performed with just +1! Something is not right. --- apps/opencs/model/tools/referenceablecheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index fccc5218b..36878d44b 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -215,7 +215,7 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str int CSMTools::ReferenceableCheckStage::setup() { mPlayerPresent = false; - return mReferencables.getSize() + 1; + return mReferencables.getSize() + 2; //DANGER, final check is not performed if it is just +1 } void CSMTools::ReferenceableCheckStage::bookCheck( From ffcb7fb280f737e1f678dbb8401b9316d643881b Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 10 Jan 2014 19:04:07 +0200 Subject: [PATCH 440/889] fix for bug scrawl mentioned --- apps/openmw/mwmechanics/character.cpp | 4 ++-- apps/openmw/mwrender/animation.hpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 75fdc2550..019054af2 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -181,7 +181,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat mCurrentHit = sHitList[iHit]; if(mPtr.getRefData().getHandle()=="player" && !mAnimation->hasAnimation(mCurrentHit)) { - //only 4 different hit animations if player is in 1st person + //only 3 different hit animations if player is in 1st person int iHit = rand() % (sHitListSize-3); mCurrentHit = sHitList[iHit]; } @@ -489,7 +489,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun const bool isWerewolf = stats.isWerewolf(); bool forcestateupdate = false; - if(weaptype != mWeaponType) + if(weaptype != mWeaponType && mHitState != CharState_KnockDown) { forcestateupdate = true; diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index a682f3960..4ba68dd85 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -258,6 +258,7 @@ public: /** Returns true if the named animation group is playing. */ bool isPlaying(const std::string &groupname) const; + //Checks if playing any animation which shouldn't be stopped when switching camera view modes bool allowSwitchViewMode() const; /** Gets info about the given animation group. From 627dcddb333a2d8e881184bf1f6a1dd72d8d15b6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Jan 2014 20:53:53 +0100 Subject: [PATCH 441/889] Do not allow training skills above their governing attribute --- apps/openmw/mwgui/trainingwindow.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 709bc0fb9..24be5363d 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -130,6 +130,14 @@ namespace MWGui return; } + // You can not train a skill above its governing attribute + const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().get().find(skillId); + if (pcStats.getSkill(skillId).getBase() >= pcStats.getAttribute(skill->mData.mAttribute).getBase()) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage17}"); + return; + } + // increase skill MWWorld::LiveCellRef *playerRef = player.get(); From f5a70dccf0f65269fa992503f9d809d8ba56f155 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Jan 2014 21:13:03 +0100 Subject: [PATCH 442/889] Fix title of buttons in generate class result dialog --- apps/openmw/mwgui/class.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 6c46f2176..7965669f1 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -29,11 +29,12 @@ namespace MWGui MyGUI::Button* backButton; getWidget(backButton, "BackButton"); + backButton->setCaptionWithReplacing("#{sMessageQuestionAnswer3}"); backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onBackClicked); MyGUI::Button* okButton; getWidget(okButton, "OKButton"); - okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + okButton->setCaptionWithReplacing("#{sMessageQuestionAnswer2}"); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked); } From 3bf36515d52dcd7cd632a37f29d603eedf662259 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Jan 2014 21:26:24 +0100 Subject: [PATCH 443/889] Implement Trespassing crime --- apps/openmw/mwbase/mechanicsmanager.hpp | 2 ++ .../mwmechanics/mechanicsmanagerimp.cpp | 26 ++++++++++++------- .../mwmechanics/mechanicsmanagerimp.hpp | 2 ++ apps/openmw/mwmechanics/security.cpp | 3 +++ apps/openmw/mwmechanics/spellcasting.cpp | 12 +++++---- apps/openmw/mwmechanics/spellcasting.hpp | 2 +- apps/openmw/mwworld/inventorystore.cpp | 2 +- 7 files changed, 32 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 24e955cdf..85fcfc75b 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -112,6 +112,8 @@ namespace MWBase OffenseType type, int arg=0) = 0; /// Utility to check if taking this item is illegal and calling commitCrime if so virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count) = 0; + /// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so + virtual void objectOpened (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item) = 0; /// Attempt sleeping in a bed. If this is illegal, call commitCrime. /// @return was it illegal, and someone saw you doing it? virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed) = 0; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 511fe6544..002cd561d 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -19,7 +19,7 @@ namespace { /// @return is \a ptr allowed to take/use \a item or is it a crime? - bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item) + bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, MWWorld::Ptr& victim) { const std::string& owner = item.getCellRef().mOwner; bool isOwned = !owner.empty(); @@ -33,6 +33,9 @@ namespace isFactionOwned = true; } + if (!item.getCellRef().mOwner.empty()) + victim = MWBase::Environment::get().getWorld()->getPtr(item.getCellRef().mOwner, true); + return (!isOwned && !isFactionOwned); } } @@ -752,11 +755,9 @@ namespace MWMechanics bool MechanicsManager::sleepInBed(const MWWorld::Ptr &ptr, const MWWorld::Ptr &bed) { - if (isAllowedToUse(ptr, bed)) - return false; MWWorld::Ptr victim; - if (!bed.getCellRef().mOwner.empty()) - victim = MWBase::Environment::get().getWorld()->getPtr(bed.getCellRef().mOwner, true); + if (isAllowedToUse(ptr, bed, victim)) + return false; if(commitCrime(ptr, victim, OT_SleepingInOwnedBed)) { @@ -767,14 +768,19 @@ namespace MWMechanics return false; } + void MechanicsManager::objectOpened(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item) + { + MWWorld::Ptr victim; + if (isAllowedToUse(ptr, item, victim)) + return; + commitCrime(ptr, victim, OT_Trespassing); + } + void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, int count) { - if (isAllowedToUse(ptr, item)) - return; MWWorld::Ptr victim; - if (!item.getCellRef().mOwner.empty()) - victim = MWBase::Environment::get().getWorld()->getPtr(item.getCellRef().mOwner, true); - + if (isAllowedToUse(ptr, item, victim)) + return; commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index cec08fa92..569cd2fca 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -113,6 +113,8 @@ namespace MWMechanics OffenseType type, int arg=0); /// Utility to check if taking this item is illegal and calling commitCrime if so virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count); + /// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so + virtual void objectOpened (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item); /// Attempt sleeping in a bed. If this is illegal, call commitCrime. /// @return was it illegal, and someone saw you doing it? virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed); diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index 0769e13df..2e5eaecfd 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -6,6 +6,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "npcstats.hpp" #include "creaturestats.hpp" @@ -45,6 +46,7 @@ namespace MWMechanics resultMessage = "#{sLockImpossible}"; else { + MWBase::Environment::get().getMechanicsManager()->objectOpened(mActor, lock); int roll = static_cast (std::rand()) / RAND_MAX * 100; if (roll <= x) { @@ -86,6 +88,7 @@ namespace MWMechanics resultMessage = "#{sTrapImpossible}"; else { + MWBase::Environment::get().getMechanicsManager()->objectOpened(mActor, trap); int roll = static_cast (std::rand()) / RAND_MAX * 100; if (roll <= x) { diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index a1cdb8b61..8c4c4d292 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -4,7 +4,7 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" - +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/actionteleport.hpp" @@ -167,7 +167,7 @@ namespace MWMechanics } } else - applyInstantEffect(target, EffectKey(*effectIt), magnitude); + applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude); // HACK: Damage attribute/skill actually has a duration, even though the actual effect is instant and permanent. // This was probably just done to have the effect visible in the magic menu for a while @@ -177,7 +177,7 @@ namespace MWMechanics || effectIt->mEffectID == ESM::MagicEffect::RestoreAttribute || effectIt->mEffectID == ESM::MagicEffect::RestoreSkill ) - applyInstantEffect(target, EffectKey(*effectIt), magnitude); + applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude); if (target.getClass().isActor() || magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration) { @@ -220,7 +220,7 @@ namespace MWMechanics mSourceName, caster.getRefData().getHandle()); } - void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, MWMechanics::EffectKey effect, float magnitude) + void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, MWMechanics::EffectKey effect, float magnitude) { short effectId = effect.mId; if (!target.getClass().isActor()) @@ -232,11 +232,13 @@ namespace MWMechanics } else if (effectId == ESM::MagicEffect::Open) { - // TODO: This is a crime if (target.getCellRef().mLockLevel <= magnitude) { if (target.getCellRef().mLockLevel > 0) + { MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock", 1.f, 1.f); + MWBase::Environment::get().getMechanicsManager()->objectOpened(caster, target); + } target.getCellRef().mLockLevel = 0; } else diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index a55c45fd1..438b65575 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -203,7 +203,7 @@ namespace MWMechanics void inflict (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false); - void applyInstantEffect (const MWWorld::Ptr& target, MWMechanics::EffectKey effect, float magnitude); + void applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, MWMechanics::EffectKey effect, float magnitude); }; } diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 2aee74eee..b11c22c72 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -392,7 +392,7 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor) // Apply instant effects MWMechanics::CastSpell cast(actor, actor); if (magnitude) - cast.applyInstantEffect(actor, effectIt->mEffectID, magnitude); + cast.applyInstantEffect(actor, actor, effectIt->mEffectID, magnitude); } if (magnitude) From 2744cde40fea2ff24f009740fd64512900b752b9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Jan 2014 22:27:31 +0100 Subject: [PATCH 444/889] Use a few additional GMSTs --- apps/openmw/mwgui/enchantingdialog.cpp | 2 +- apps/openmw/mwgui/messagebox.cpp | 5 ++--- apps/openmw/mwgui/messagebox.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 3 ++- apps/openmw/mwmechanics/actors.cpp | 8 ++++++-- files/mygui/openmw_spell_buying_window.layout | 2 +- files/mygui/openmw_travel_window.layout | 4 ++-- 7 files changed, 15 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 4d79875a2..d9d2a2ea8 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -257,7 +257,7 @@ namespace MWGui { if (mEffects.size() <= 0) { - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage30}"); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sEnchantmentMenu11}"); return; } diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 72a5bd0b0..b0abaef14 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -8,13 +8,12 @@ namespace MWGui { - MessageBoxManager::MessageBoxManager () + MessageBoxManager::MessageBoxManager (float timePerChar) { - // TODO: fMessageTimePerChar - mMessageBoxSpeed = 0.1; mInterMessageBoxe = NULL; mStaticMessageBox = NULL; mLastButtonPressed = -1; + mMessageBoxSpeed = timePerChar; } MessageBoxManager::~MessageBoxManager () diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index 0288f366c..e82a51642 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -22,7 +22,7 @@ namespace MWGui class MessageBoxManager { public: - MessageBoxManager (); + MessageBoxManager (float timePerChar); ~MessageBoxManager (); void onFrame (float frameDuration); void createMessageBox (const std::string& message, bool stat = false); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 3accd925f..e4297f8fc 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -207,7 +207,8 @@ namespace MWGui mConsole = new Console(w,h, mConsoleOnlyScripts); trackWindow(mConsole, "console"); mJournal = JournalWindow::create(JournalViewModel::create ()); - mMessageBoxManager = new MessageBoxManager(); + mMessageBoxManager = new MessageBoxManager( + MWBase::Environment::get().getWorld()->getStore().get().find("fMessageTimePerChar")->getFloat()); mInventoryWindow = new InventoryWindow(mDragAndDrop); mTradeWindow = new TradeWindow(); trackWindow(mTradeWindow, "barter"); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0a4adb6e2..afc94255b 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -581,7 +581,8 @@ namespace MWMechanics if(timeLeft == 0.0f) { // If drowning, apply 3 points of damage per second - ptr.getClass().setActorHealth(ptr, stats.getHealth().getCurrent() - 3.0f*duration); + static const float fSuffocationDamage = world->getStore().get().find("fSuffocationDamage")->getFloat(); + ptr.getClass().setActorHealth(ptr, stats.getHealth().getCurrent() - fSuffocationDamage*duration); // Play a drowning sound as necessary for the player if(ptr == world->getPlayerPtr()) @@ -593,7 +594,10 @@ namespace MWMechanics } } else - stats.setTimeToStartDrowning(20); + { + static const float fHoldBreathTime = world->getStore().get().find("fHoldBreathTime")->getFloat(); + stats.setTimeToStartDrowning(fHoldBreathTime); + } } void Actors::updateEquippedLight (const MWWorld::Ptr& ptr, float duration) diff --git a/files/mygui/openmw_spell_buying_window.layout b/files/mygui/openmw_spell_buying_window.layout index 1510372dd..b24a476c4 100644 --- a/files/mygui/openmw_spell_buying_window.layout +++ b/files/mygui/openmw_spell_buying_window.layout @@ -11,7 +11,7 @@ - + diff --git a/files/mygui/openmw_travel_window.layout b/files/mygui/openmw_travel_window.layout index db3fa24a0..683d47fe7 100644 --- a/files/mygui/openmw_travel_window.layout +++ b/files/mygui/openmw_travel_window.layout @@ -11,7 +11,7 @@ - + @@ -32,4 +32,4 @@ -
\ No newline at end of file + From 15e48107f7b5ee62e47be7d6d2ab17759395065e Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Jan 2014 22:39:01 +0100 Subject: [PATCH 445/889] Use i1stPersonSneakDelta + some cleanup --- apps/openmw/mwmechanics/spellcasting.hpp | 12 ++++++------ apps/openmw/mwrender/camera.cpp | 5 ++--- apps/openmw/mwrender/camera.hpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 4 +++- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 438b65575..a1ae254f6 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -19,12 +19,12 @@ namespace MWMechanics inline int spellSchoolToSkill(int school) { std::map schoolSkillMap; // maps spell school to skill id - schoolSkillMap[0] = 11; // alteration - schoolSkillMap[1] = 13; // conjuration - schoolSkillMap[3] = 12; // illusion - schoolSkillMap[2] = 10; // destruction - schoolSkillMap[4] = 14; // mysticism - schoolSkillMap[5] = 15; // restoration + schoolSkillMap[0] = ESM::Skill::Alteration; + schoolSkillMap[1] = ESM::Skill::Conjuration; + schoolSkillMap[3] = ESM::Skill::Illusion; + schoolSkillMap[2] = ESM::Skill::Destruction; + schoolSkillMap[4] = ESM::Skill::Mysticism; + schoolSkillMap[5] = ESM::Skill::Restoration; assert(schoolSkillMap.find(school) != schoolSkillMap.end()); return schoolSkillMap[school]; } diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index dda9797ef..d579c9793 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -226,11 +226,10 @@ namespace MWRender mCamera->setPosition(0.f, 0.f, offset); } - void Camera::setSneakOffset() + void Camera::setSneakOffset(float offset) { - // TODO: iFirstPersonSneakDelta if(mAnimation) - mAnimation->addFirstPersonOffset(Ogre::Vector3(0.f, 0.f, -9.8f)); + mAnimation->addFirstPersonOffset(Ogre::Vector3(0.f, 0.f, -offset)); } float Camera::getYaw() diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index d31d9e56c..808f817cf 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -87,7 +87,7 @@ namespace MWRender /// As animation is tied to the camera, this needs /// to be set each frame after the animation is /// applied. - void setSneakOffset(); + void setSneakOffset(float offset); bool isFirstPerson() const { return !(mVanity.enabled || mPreviewMode || !mFirstPersonView); } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 11d06704e..0c5e053c9 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -351,8 +351,10 @@ void RenderingManager::update (float duration, bool paused) bool isInAir = !world->isOnGround(player); bool isSwimming = world->isSwimming(player); + static const int i1stPersonSneakDelta = MWBase::Environment::get().getWorld()->getStore().get() + .find("i1stPersonSneakDelta")->getInt(); if(isSneaking && !(isSwimming || isInAir)) - mCamera->setSneakOffset(); + mCamera->setSneakOffset(i1stPersonSneakDelta); mOcclusionQuery->update(duration); From d9d6f376193700f51688251f354887cf07641222 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Jan 2014 22:44:40 +0100 Subject: [PATCH 446/889] Use iVoiceHitOdds --- apps/openmw/mwclass/npc.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 01fe61472..1c5c302d8 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -615,7 +615,13 @@ namespace MWClass // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying // something, alert the character controller, scripts, etc. - MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + int chance = store.get().find("iVoiceHitOdds")->getInt(); + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (roll < chance) + { + MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); + } getCreatureStats(ptr).setAttacked(true);//used in CharacterController if(object.isEmpty()) From 621e52f09d47758cb41b3e081e0d2b4069e3b53b Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Jan 2014 22:48:42 +0100 Subject: [PATCH 447/889] Play "attack" voiced dialogue entries randomly based on iVoiceAttackOdds. --- apps/openmw/mwmechanics/aicombat.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 32b0063b6..34b721755 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -7,6 +7,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/dialoguemanager.hpp" #include "creaturestats.hpp" #include "npcstats.hpp" @@ -118,6 +119,17 @@ namespace MWMechanics } if( mTimer > 1) { + if (actor.getClass().isNpc()) + { + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + int chance = store.get().find("iVoiceAttackOdds")->getInt(); + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (roll < chance) + { + MWBase::Environment::get().getDialogueManager()->say(actor, "attack"); + } + } + MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true); mTimer = 0; } From e9f63270d95b85dc2aca449e64f6ba71a0eb9628 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Jan 2014 22:51:42 +0100 Subject: [PATCH 448/889] Speed fix for "on target" spells --- apps/openmw/mwworld/worldimp.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f61938f92..738c71a7d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2174,14 +2174,13 @@ namespace MWWorld Ogre::Vector3 rot(ptr.getRefData().getPosition().rot); - // TODO: Why -rot.z, but not -rot.x? + // TODO: Why -rot.z, but not -rot.x? (note: same issue in MovementSolver::move) Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Z); orient = orient * Ogre::Quaternion(Ogre::Radian(rot.x), Ogre::Vector3::UNIT_X); - // This is just a guess, probably wrong - static float fProjectileMinSpeed = getStore().get().find("fProjectileMinSpeed")->getFloat(); - static float fProjectileMaxSpeed = getStore().get().find("fProjectileMaxSpeed")->getFloat(); - float speed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * it->second.mSpeed; + + static float fTargetSpellMaxSpeed = getStore().get().find("fTargetSpellMaxSpeed")->getFloat(); + float speed = fTargetSpellMaxSpeed * it->second.mSpeed; Ogre::Vector3 direction = orient.yAxis(); direction.normalise(); From d01f89b153921ecc8729ccf9777fbb8c3e785fd7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 00:24:21 +0100 Subject: [PATCH 449/889] Rewrite some awful code --- apps/openmw/mwbase/windowmanager.hpp | 3 --- apps/openmw/mwgui/container.cpp | 11 +++++++++ apps/openmw/mwgui/container.hpp | 1 + apps/openmw/mwgui/messagebox.cpp | 20 +++++++--------- apps/openmw/mwgui/messagebox.hpp | 3 +-- apps/openmw/mwgui/windowmanagerimp.cpp | 11 --------- apps/openmw/mwgui/windowmanagerimp.hpp | 2 -- apps/openmw/mwinput/inputmanagerimp.cpp | 31 +------------------------ apps/openmw/mwinput/inputmanagerimp.hpp | 1 - 9 files changed, 22 insertions(+), 61 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 6c85be5fd..c39de4400 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -227,9 +227,6 @@ namespace MWBase virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector(), bool showInDialogueModeOnly = false) = 0; virtual void staticMessageBox(const std::string& message) = 0; virtual void removeStaticMessageBox() = 0; - - virtual void enterPressed () = 0; - virtual void activateKeyPressed () = 0; virtual int readPressedButton() = 0; ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index bccc7120f..d22842b57 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -139,6 +139,7 @@ namespace MWGui mDisposeCorpseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onDisposeCorpseButtonClicked); mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onCloseButtonClicked); + mCloseButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ContainerWindow::onKeyPressed); mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked); setCoord(200,0,600,300); @@ -234,11 +235,21 @@ namespace MWGui mItemView->setModel (mSortModel); + MyGUI::InputManager::getInstance().setKeyFocusWidget(mCloseButton); + // Careful here. setTitle may cause size updates, causing itemview redraw, so make sure to do it last // or we end up using a possibly invalid model. setTitle(MWWorld::Class::get(container).getName(container)); } + void ContainerWindow::onKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char) + { + if (_key == MyGUI::KeyCode::Space) + onCloseButtonClicked(mCloseButton); + if (_key == MyGUI::KeyCode::Return || _key == MyGUI::KeyCode::NumpadEnter) + onTakeAllButtonClicked(mTakeButton); + } + void ContainerWindow::close() { WindowBase::close(); diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index f934d8828..ce4707af6 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -75,6 +75,7 @@ namespace MWGui void onCloseButtonClicked(MyGUI::Widget* _sender); void onTakeAllButtonClicked(MyGUI::Widget* _sender); void onDisposeCorpseButtonClicked(MyGUI::Widget* sender); + void onKeyPressed(MyGUI::Widget* _sender, MyGUI::KeyCode _key, MyGUI::Char _char); /// @return is taking the item allowed? bool onTakeItem(const ItemStack& item, int count); diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index b0abaef14..ae01f4293 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -127,12 +127,6 @@ namespace MWGui mMessageBoxSpeed = speed; } - void MessageBoxManager::okayPressed () - { - if(mInterMessageBoxe != NULL) - mInterMessageBoxe->okayPressed(); - } - int MessageBoxManager::readPressedButton () { int pressed = mLastButtonPressed; @@ -333,23 +327,25 @@ namespace MWGui } } - } - - void InteractiveMessageBox::okayPressed() - { + // Set key focus to "Ok" button std::string ok = Misc::StringUtils::lowerCase(MyGUI::LanguageManager::getInstance().replaceTags("#{sOK}")); std::vector::const_iterator button; for(button = mButtons.begin(); button != mButtons.end(); ++button) { if(Misc::StringUtils::lowerCase((*button)->getCaption()) == ok) { - buttonActivated(*button); - MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); + MyGUI::InputManager::getInstance().setKeyFocusWidget(*button); + (*button)->eventKeyButtonPressed += MyGUI::newDelegate(this, &InteractiveMessageBox::onKeyPressed); break; } } + } + void InteractiveMessageBox::onKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char) + { + if (_key == MyGUI::KeyCode::Return || _key == MyGUI::KeyCode::NumpadEnter || _key == MyGUI::KeyCode::Space) + buttonActivated(_sender); } void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed) diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index e82a51642..caa37008c 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -33,7 +33,6 @@ namespace MWGui bool removeMessageBox (MessageBox *msgbox); void setMessageBoxSpeed (int speed); - void okayPressed(); int readPressedButton (); typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Int; @@ -74,7 +73,6 @@ namespace MWGui { public: InteractiveMessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons); - void okayPressed (); void mousePressed (MyGUI::Widget* _widget); int readPressedButton (); @@ -82,6 +80,7 @@ namespace MWGui private: void buttonActivated (MyGUI::Widget* _widget); + void onKeyPressed(MyGUI::Widget* _sender, MyGUI::KeyCode _key, MyGUI::Char _char); MessageBoxManager& mMessageBoxManager; MyGUI::EditBox* mMessageWidget; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index e4297f8fc..cda146e8c 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -677,17 +677,6 @@ namespace MWGui mMessageBoxManager->removeStaticMessageBox(); } - void WindowManager::enterPressed () - { - mMessageBoxManager->okayPressed(); - } - - void WindowManager::activateKeyPressed () - { - mMessageBoxManager->okayPressed(); - mCountDialog->cancel(); - } - int WindowManager::readPressedButton () { return mMessageBoxManager->readPressedButton(); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index a3b135ad4..9838a667f 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -222,8 +222,6 @@ namespace MWGui virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector(), bool showInDialogueModeOnly = false); virtual void staticMessageBox(const std::string& message); virtual void removeStaticMessageBox(); - virtual void enterPressed (); - virtual void activateKeyPressed (); virtual int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) virtual void onFrame (float frameDuration); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index c2efa0c33..f0feba89f 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -195,14 +195,7 @@ namespace MWInput case A_Activate: resetIdleTime(); - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) - { - if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Container) - toggleContainer (); - else - MWBase::Environment::get().getWindowManager()->activateKeyPressed(); - } - else + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) activate(); break; case A_Journal: @@ -511,13 +504,6 @@ namespace MWInput mInputBinder->keyPressed (arg); - if((arg.keysym.sym == SDLK_RETURN || arg.keysym.sym == SDLK_KP_ENTER) - && MWBase::Environment::get().getWindowManager()->isGuiMode()) - { - // Pressing enter when a messagebox is prompting for "ok" will activate the ok button - MWBase::Environment::get().getWindowManager()->enterPressed(); - } - OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym); if (kc != OIS::KC_UNASSIGNED) @@ -730,21 +716,6 @@ namespace MWInput // .. but don't touch any other mode, except container. } - void InputManager::toggleContainer() - { - if (MyGUI::InputManager::getInstance ().isModalAny()) - return; - - if(MWBase::Environment::get().getWindowManager()->isGuiMode()) - { - if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Container) - MWBase::Environment::get().getWindowManager()->popGuiMode(); - else - MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Container); - } - - } - void InputManager::toggleConsole() { if (MyGUI::InputManager::getInstance ().isModalAny()) diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 4eaee9b69..d41b4c3f3 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -173,7 +173,6 @@ namespace MWInput void toggleSpell(); void toggleWeapon(); void toggleInventory(); - void toggleContainer(); void toggleConsole(); void screenshot(); void toggleJournal(); From c64dc2c8312d1177fdeb38b11adea7c776e40b9e Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 00:51:03 +0100 Subject: [PATCH 450/889] Terrain specular mapping: use a "_diffusespec" postfix to indicate specular information is present in the alpha channel. Use alpha directly instead of 1-alpha. --- components/terrain/material.cpp | 3 +++ components/terrain/storage.cpp | 9 +++++++++ components/terrain/storage.hpp | 1 + files/materials/terrain.shader | 26 +++++++++++++------------- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index 5dcdb7fed..be468866b 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -281,6 +281,7 @@ namespace Terrain // normal map (optional) bool useNormalMap = mNormalMapping && !mLayerList[layerOffset+i].mNormalMap.empty() && !renderCompositeMap; bool useParallax = useNormalMap && mParallaxMapping && layer.mParallax; + bool useSpecular = layer.mSpecular; if (useNormalMap) { anyNormalMaps = true; @@ -292,6 +293,8 @@ namespace Terrain sh::makeProperty (new sh::BooleanValue(useNormalMap))); p->mShaderProperties.setProperty ("use_parallax_" + Ogre::StringConverter::toString(i), sh::makeProperty (new sh::BooleanValue(useParallax))); + p->mShaderProperties.setProperty ("use_specular_" + Ogre::StringConverter::toString(i), + sh::makeProperty (new sh::BooleanValue(useSpecular))); boost::hash_combine(normalMaps, useNormalMap); boost::hash_combine(normalMaps, useNormalMap && layer.mParallax); diff --git a/components/terrain/storage.cpp b/components/terrain/storage.cpp index 9d6b44de8..398ebac01 100644 --- a/components/terrain/storage.cpp +++ b/components/terrain/storage.cpp @@ -475,6 +475,7 @@ namespace Terrain LayerInfo info; info.mParallax = false; + info.mSpecular = false; info.mDiffuseMap = "textures\\" + texture; std::string texture_ = texture; boost::replace_last(texture_, ".", "_nh."); @@ -491,6 +492,14 @@ namespace Terrain info.mNormalMap = "textures\\" + texture_; } + texture_ = texture; + boost::replace_last(texture_, ".", "_diffusespec."); + if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_)) + { + info.mDiffuseMap = "textures\\" + texture_; + info.mSpecular = true; + } + mLayerInfoMap[texture] = info; return info; diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index 68fae01af..18d05b100 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -16,6 +16,7 @@ namespace Terrain std::string mDiffuseMap; std::string mNormalMap; bool mParallax; // Height info in normal map alpha channel? + bool mSpecular; // Specular info in diffuse map alpha channel? }; /// We keep storage of terrain data abstract here since we need different implementations for game and editor diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 86eef36ff..1436de0c3 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -337,6 +337,7 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5; float2 layerUV = float2(UV.x, 1.f-UV.y) * 16; // Reverse Y, required to get proper tangents float2 thisLayerUV; float4 normalTex; + float4 diffuseTex; float3 eyeDir = normalize(cameraPos.xyz - worldPos); #if PARALLAX @@ -358,19 +359,18 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5; thisLayerUV += TSeyeDir.xy * ( normalTex.a * PARALLAX_SCALE + PARALLAX_BIAS ); #endif -#if IS_FIRST_PASS - #if @shIterator == 0 - // first layer of first pass is the base layer and doesn't need a blend map - albedo = shSample(diffuseMap0, layerUV); - #else - albedo = shLerp(albedo, shSample(diffuseMap@shIterator, thisLayerUV), blendValues@shPropertyString(blendmap_component_@shIterator)); - #endif + diffuseTex = shSample(diffuseMap@shIterator, layerUV); +#if !@shPropertyBool(use_specular_@shIterator) + diffuseTex.a = 0; +#endif + +#if @shIterator == 0 +albedo = diffuseTex; #else - #if @shIterator == 0 - albedo = shSample(diffuseMap@shIterator, layerUV); - #else - albedo = shLerp(albedo, shSample(diffuseMap@shIterator, thisLayerUV), blendValues@shPropertyString(blendmap_component_@shIterator)); - #endif +albedo = shLerp(albedo, diffuseTex, blendValues@shPropertyString(blendmap_component_@shIterator)); +#endif + +#if !IS_FIRST_PASS previousAlpha *= 1.f-blendValues@shPropertyString(blendmap_component_@shIterator); #endif @@ -448,7 +448,7 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5; float3 halfVec = normalize (light0Dir + eyeDir); float3 specular = pow(max(dot(normal, halfVec), 0), 32) * lightSpec0; - shOutputColour(0).xyz += specular * (1.f-albedo.a) * shadow; + shOutputColour(0).xyz += specular * (albedo.a) * shadow; #endif #if FOG From 727aa30347a345e7a61bebffa3e05d94586622ea Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 02:06:54 +0100 Subject: [PATCH 451/889] Dead actors are unaware of everything --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 002cd561d..2ce196fdd 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -857,6 +857,9 @@ namespace MWMechanics bool MechanicsManager::awarenessCheck(const MWWorld::Ptr &ptr, const MWWorld::Ptr &observer) { + if (observer.getClass().getCreatureStats(observer).isDead()) + return false; + const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); From aba72ffefe9aec66b86ccf537054ce8366e8c0b9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 02:12:41 +0100 Subject: [PATCH 452/889] Fix up the merge a bit --- apps/openmw/mwmechanics/actors.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index c488d4d2b..53adc694c 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -797,22 +797,22 @@ namespace MWMechanics } } - // Apply soultrap - if (iter->first.getTypeName() == typeid(ESM::Creature).name()) - { - SoulTrap soulTrap (iter->first); - stats.getActiveSpells().visitEffectSources(soulTrap); - } - - // Reset magic effects and recalculate derived effects - // One case where we need this is to make sure bound items are removed upon death - stats.setMagicEffects(MWMechanics::MagicEffects()); - calculateCreatureStatModifiers(iter->first, 0); - if (iter->second->kill()) { ++mDeathCount[cls.getId(iter->first)]; + // Apply soultrap + if (iter->first.getTypeName() == typeid(ESM::Creature).name()) + { + SoulTrap soulTrap (iter->first); + stats.getActiveSpells().visitEffectSources(soulTrap); + } + + // Reset magic effects and recalculate derived effects + // One case where we need this is to make sure bound items are removed upon death + stats.setMagicEffects(MWMechanics::MagicEffects()); + calculateCreatureStatModifiers(iter->first, 0); + if(cls.isEssential(iter->first)) MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); } From d4a98ffc270e6e19504c4da1615bf26d4b352ef6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 02:27:44 +0100 Subject: [PATCH 453/889] Increase fight rating when being the victim of a crime --- .../mwmechanics/mechanicsmanagerimp.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 2ce196fdd..57b876723 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -840,6 +840,25 @@ namespace MWMechanics ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty() + arg); + if (!victim.isEmpty()) + { + int fight = 0; + // Increase in fight rating for each type of crime + if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) + fight = store.find("iFightTrespass")->getFloat(); + else if (type == OT_Pickpocket) + fight = store.find("iFightPickpocket")->getInt(); + else if (type == OT_Assault) + fight = store.find("iFightAttack")->getInt(); + else if (type == OT_Murder) + fight = store.find("iFightKilling")->getInt(); + else if (type == OT_Theft) + fight = store.find("fFightStealing")->getFloat(); + // Not sure if this should be permanent? + fight = victim.getClass().getCreatureStats(victim).getAiSetting(CreatureStats::AI_Fight).getBase() + fight; + victim.getClass().getCreatureStats(victim).setAiSetting(CreatureStats::AI_Fight, fight); + } + // If committing a crime against a faction member, expell from the faction if (!victim.isEmpty() && victim.getClass().isNpc()) { From 12944f2459a13296b71ee4c9ea2bafac2d549256 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 03:07:35 +0100 Subject: [PATCH 454/889] Fix an auto equipping bug that allowed twohanded weapon and shield at the same time --- apps/openmw/mwworld/inventorystore.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index b11c22c72..3e81da212 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -165,7 +165,6 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot) void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) { const MWMechanics::NpcStats& stats = MWWorld::Class::get(actor).getNpcStats(actor); - MWWorld::InventoryStore& invStore = MWWorld::Class::get(actor).getInventoryStore(actor); TSlots slots_; initSlots (slots_); @@ -266,10 +265,10 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) case 0: continue; case 2: - invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedLeft, actor); + slots_[MWWorld::InventoryStore::Slot_CarriedLeft] = end(); break; case 3: - invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, actor); + // Prefer keeping twohanded weapon break; } From 909494ff3524a93a450d63656fe450dd863cdda5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 03:08:16 +0100 Subject: [PATCH 455/889] Implement Assault crimes. In other words, NPCs now fight back! --- apps/openmw/mwclass/npc.cpp | 4 ++++ apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 11 ++++++----- apps/openmw/mwmechanics/spellcasting.cpp | 7 +++++++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 1c5c302d8..68b7d41f5 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -587,6 +587,10 @@ namespace MWClass // NOTE: 'object' and/or 'attacker' may be empty. + // Attacking peaceful NPCs is a crime + if (ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) + MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); + if(!successful) { // TODO: Handle HitAttemptOnMe script function diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 57b876723..901b6e414 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -786,7 +786,8 @@ namespace MWMechanics bool MechanicsManager::commitCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) { - // TODO: expell from faction + if (ptr.getRefData().getHandle() != "player") + return false; bool reported=false; for (Actors::PtrControllerMap::const_iterator it = mActors.begin(); it != mActors.end(); ++it) @@ -803,10 +804,7 @@ namespace MWMechanics // Actor has witnessed a crime. Will he report it? // (not sure, is > 0 correct?) - if (it->first.getClass().getCreatureStats(it->first).getAiSetting(CreatureStats::AI_Alarm).getModified() > 0 - // This is a bit inconsistent, but AFAIK assaulted NPCs can not report if they are alone - && (type != OT_Assault || it->first != victim) - ) + if (it->first.getClass().getCreatureStats(it->first).getAiSetting(CreatureStats::AI_Alarm).getModified() > 0) { // TODO: stats.setAlarmed(true) on NPCs within earshot // fAlarmRadius ? @@ -836,6 +834,9 @@ namespace MWMechanics else if (type == OT_Theft) arg *= store.find("fCrimeStealing")->getFloat(); + // TODO: In some cases (type == Assault), if no NPCs are within earshot, the report will have no effect. + // however other crime types seem to be always produce a bounty. + MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}"); ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty() + arg); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 8c4c4d292..b8c36d213 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -57,6 +57,7 @@ namespace MWMechanics ESM::EffectList reflectedEffects; std::vector appliedLastingEffects; bool firstAppliedEffect = true; + bool anyHarmfulEffect = false; for (std::vector::const_iterator effectIt (effects.mList.begin()); effectIt!=effects.mList.end(); ++effectIt) @@ -77,6 +78,8 @@ namespace MWMechanics float magnitudeMult = 1; if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful && target.getClass().isActor()) { + anyHarmfulEffect = true; + // If player is attempting to cast a harmful spell, show the target's HP bar if (caster.getRefData().getHandle() == "player" && target != caster) MWBase::Environment::get().getWindowManager()->setEnemy(target); @@ -218,6 +221,10 @@ namespace MWMechanics if (appliedLastingEffects.size()) target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects, mSourceName, caster.getRefData().getHandle()); + + if (anyHarmfulEffect && target.getClass().isActor() + && target.getClass().getCreatureStats(target).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) + MWBase::Environment::get().getMechanicsManager()->commitCrime(caster, target, MWBase::MechanicsManager::OT_Assault); } void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, MWMechanics::EffectKey effect, float magnitude) From bf6d302fbadbfdf3399e12a2fbfaca6d06406bec Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 03:29:41 +0100 Subject: [PATCH 456/889] Confiscate stolen items when caught --- apps/openmw/mwbase/world.hpp | 3 ++ apps/openmw/mwscript/miscextensions.cpp | 5 ++-- apps/openmw/mwworld/cells.cpp | 12 ++++++++ apps/openmw/mwworld/cells.hpp | 6 ++++ apps/openmw/mwworld/worldimp.cpp | 40 +++++++++++++++++++++++++ apps/openmw/mwworld/worldimp.hpp | 3 ++ 6 files changed, 66 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 43e526ecb..10e25b376 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -451,6 +451,9 @@ namespace MWBase /// Update the value of some globals according to the world state, which may be used by dialogue entries. /// This should be called when initiating a dialogue. virtual void updateDialogueGlobals() = 0; + + /// Moves all stolen items from \a ptr to the closest evidence chest. + virtual void confiscateStolenItems(const MWWorld::Ptr& ptr) = 0; }; } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index a403d76ed..f69f2e7cb 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -769,8 +769,8 @@ namespace MWScript MWWorld::Ptr player = world->getPlayerPtr(); world->teleportToClosestMarker(player, "prisonmarker"); player.getClass().getNpcStats(player).setBounty(0); + world->confiscateStolenItems(player); // TODO: pass time, change skills, show messagebox - // TODO: move stolen items to closest evidence chest // iDaysinPrisonMod } }; @@ -782,8 +782,7 @@ namespace MWScript { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); player.getClass().getNpcStats(player).setBounty(0); - - // TODO: move stolen items to closest evidence chest + MWBase::Environment::get().getWorld()->confiscateStolenItems(player); } }; diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 621ff3b31..575e10f04 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -271,3 +271,15 @@ void MWWorld::Cells::getExteriorPtrs(const std::string &name, std::vector &out) +{ + for (std::map::iterator iter = mInteriors.begin(); + iter!=mInteriors.end(); ++iter) + { + Ptr ptr = getPtrAndCache (name, iter->second); + if (!ptr.isEmpty()) + out.push_back(ptr); + } + +} diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 31de2f60e..afa7c03f2 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -56,6 +56,12 @@ namespace MWWorld /// @note Due to the current implementation of getPtr this only supports one Ptr per cell. /// @note name must be lower case void getExteriorPtrs (const std::string& name, std::vector& out); + + /// Get all Ptrs referencing \a name in interior cells + /// @note Due to the current implementation of getPtr this only supports one Ptr per cell. + /// @note name must be lower case + void getInteriorPtrs (const std::string& name, std::vector& out); + }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 738c71a7d..c5315afe6 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2446,4 +2446,44 @@ namespace MWWorld mGlobalVariables->setInt("crimegoldturnin", turnIn); mGlobalVariables->setInt("pchasturnin", (turnIn <= playerGold) ? 1 : 0); } + + void World::confiscateStolenItems(const Ptr &ptr) + { + Ogre::Vector3 playerPos; + if (!findInteriorPositionInWorldSpace(ptr.getCell(), playerPos)) + playerPos = mPlayer->getLastKnownExteriorPosition(); + + MWWorld::Ptr closestChest; + float closestDistance = FLT_MAX; + + std::vector chests; + mCells.getInteriorPtrs("stolen_goods", chests); + + Ogre::Vector3 chestPos; + for (std::vector::iterator it = chests.begin(); it != chests.end(); ++it) + { + if (!findInteriorPositionInWorldSpace(it->getCell(), chestPos)) + continue; + + float distance = playerPos.squaredDistance(chestPos); + if (distance < closestDistance) + { + closestDistance = distance; + closestChest = *it; + } + } + + if (!closestChest.isEmpty()) + { + ContainerStore& store = ptr.getClass().getContainerStore(ptr); + for (ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + if (!it->getCellRef().mOwner.empty() && it->getCellRef().mOwner != "player") + { + closestChest.getClass().getContainerStore(closestChest).add(*it, it->getRefData().getCount(), closestChest); + store.remove(*it, it->getRefData().getCount(), ptr); + } + } + } + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index cc087a89f..8c091de50 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -536,6 +536,9 @@ namespace MWWorld /// Update the value of some globals according to the world state, which may be used by dialogue entries. /// This should be called when initiating a dialogue. virtual void updateDialogueGlobals(); + + /// Moves all stolen items from \a ptr to the closest evidence chest. + virtual void confiscateStolenItems(const MWWorld::Ptr& ptr); }; } From 9127839cc1b9e2c71aede268b1e2c0a1220a019a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 03:33:17 +0100 Subject: [PATCH 457/889] Add a searchPtr method as required for getting an owner, which may already be dead and disposed of. --- apps/openmw/mwbase/world.hpp | 4 ++++ apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 15 ++++++++++----- apps/openmw/mwworld/worldimp.hpp | 4 ++++ 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 10e25b376..2fb5f02eb 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -157,6 +157,10 @@ namespace MWBase ///< Return a pointer to a liveCellRef with the given name. /// \param activeOnly do non search inactive cells. + virtual MWWorld::Ptr searchPtr (const std::string& name, bool activeOnly) = 0; + ///< Return a pointer to a liveCellRef with the given name. + /// \param activeOnly do non search inactive cells. + virtual MWWorld::Ptr getPtrViaHandle (const std::string& handle) = 0; ///< Return a pointer to a liveCellRef with the given Ogre handle. diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 901b6e414..76ba2ff16 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -34,7 +34,7 @@ namespace } if (!item.getCellRef().mOwner.empty()) - victim = MWBase::Environment::get().getWorld()->getPtr(item.getCellRef().mOwner, true); + victim = MWBase::Environment::get().getWorld()->searchPtr(item.getCellRef().mOwner, true); return (!isOwned && !isFactionOwned); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c5315afe6..ca85d7a7f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -485,8 +485,9 @@ namespace MWWorld mLocalScripts.remove (ref); } - Ptr World::getPtr (const std::string& name, bool activeOnly) + Ptr World::searchPtr (const std::string& name, bool activeOnly) { + Ptr ret; // the player is always in an active cell. if (name=="player") { @@ -514,12 +515,16 @@ namespace MWWorld if (!activeOnly) { - Ptr ptr = mCells.getPtr (lowerCaseName); - - if (!ptr.isEmpty()) - return ptr; + ret = mCells.getPtr (lowerCaseName); } + return ret; + } + Ptr World::getPtr (const std::string& name, bool activeOnly) + { + Ptr ret = searchPtr(name, activeOnly); + if (!ret.isEmpty()) + return ret; throw std::runtime_error ("unknown ID: " + name); } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 8c091de50..d5ccd7625 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -232,6 +232,10 @@ namespace MWWorld ///< Return a pointer to a liveCellRef with the given name. /// \param activeOnly do non search inactive cells. + virtual Ptr searchPtr (const std::string& name, bool activeOnly); + ///< Return a pointer to a liveCellRef with the given name. + /// \param activeOnly do non search inactive cells. + virtual Ptr getPtrViaHandle (const std::string& handle); ///< Return a pointer to a liveCellRef with the given Ogre handle. From 9ddee8fd8c6b00e7ff5f4904118056b02bdf30d2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 04:03:13 +0100 Subject: [PATCH 458/889] Autocalculate NPC reputation as according to research wiki --- apps/openmw/mwclass/npc.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 68b7d41f5..12ed2dd09 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -307,6 +307,17 @@ namespace MWClass autoCalculateSkills(ref->mBase, data->mNpcStats); } + if (data->mNpcStats.getFactionRanks().size()) + { + static const int iAutoRepFacMod = MWBase::Environment::get().getWorld()->getStore().get() + .find("iAutoRepFacMod")->getInt(); + static const int iAutoRepLevMod = MWBase::Environment::get().getWorld()->getStore().get() + .find("iAutoRepLevMod")->getInt(); + int rank = data->mNpcStats.getFactionRanks().begin()->second; + + data->mNpcStats.setReputation(iAutoRepFacMod * (rank+1) + iAutoRepLevMod * (data->mNpcStats.getLevel()-1)); + } + data->mNpcStats.getAiSequence().fill(ref->mBase->mAiPackage); data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Hello, ref->mBase->mAiData.mHello); From ce6aab89cf9d7dde54f994aba21c7fda228ff269 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 04:58:30 +0100 Subject: [PATCH 459/889] Fix a possible permutation issue --- components/terrain/material.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index be468866b..8e78d2216 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -297,6 +297,7 @@ namespace Terrain sh::makeProperty (new sh::BooleanValue(useSpecular))); boost::hash_combine(normalMaps, useNormalMap); boost::hash_combine(normalMaps, useNormalMap && layer.mParallax); + boost::hash_combine(normalMaps, useSpecular); if (i+layerOffset > 0) { From 3896c88403c71cfef013fa9cc713fe5a8a910910 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 05:58:05 +0100 Subject: [PATCH 460/889] Use VFX_DefaultCast / VFX_DefaultHit if the magic effect does not specify any --- apps/openmw/mwmechanics/actors.cpp | 2 +- apps/openmw/mwmechanics/character.cpp | 7 ++++++- apps/openmw/mwmechanics/spellcasting.cpp | 18 ++++++++++-------- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index afc94255b..468a21892 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -525,7 +525,7 @@ namespace MWMechanics ref.getPtr().getCellRef().mPos = ipos; // TODO: Add AI to follow player and fight for him - + // TODO: VFX_SummonStart, VFX_SummonEnd creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos).getRefData().getHandle())); } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 99067a6b7..fed3f485f 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -592,7 +592,12 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun const ESM::MagicEffect *effect; effect = store.get().find(effectentry.mEffectID); - const ESM::Static* castStatic = store.get().find (effect->mCasting); + const ESM::Static* castStatic; + if (!effect->mCasting.empty()) + castStatic = store.get().find (effect->mCasting); + else + castStatic = store.get().find ("VFX_DefaultCast"); + mAnimation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex); castStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Hands"); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index b8c36d213..850c4dcf5 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -200,15 +200,17 @@ namespace MWMechanics } // Add VFX + const ESM::Static* castStatic; if (!magicEffect->mHit.empty()) - { - const ESM::Static* castStatic = MWBase::Environment::get().getWorld()->getStore().get().find (magicEffect->mHit); - bool loop = magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx; - // Note: in case of non actor, a free effect should be fine as well - MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target); - if (anim) - anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, ""); - } + castStatic = MWBase::Environment::get().getWorld()->getStore().get().find (magicEffect->mHit); + else + castStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_DefaultHit"); + + bool loop = magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx; + // Note: in case of non actor, a free effect should be fine as well + MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target); + if (anim) + anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, ""); } // TODO: For Area effects, launch a growing particle effect that applies the effect to more actors as it hits them. Best managed in World. From a2ba0dde31036bb45646488a6d4bc6348c65a443 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 06:47:58 +0100 Subject: [PATCH 461/889] Implemented GoToJail --- apps/openmw/mwbase/world.hpp | 2 + apps/openmw/mwscript/miscextensions.cpp | 7 +-- apps/openmw/mwworld/worldimp.cpp | 79 ++++++++++++++++++++++++- apps/openmw/mwworld/worldimp.hpp | 3 + 4 files changed, 84 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 2fb5f02eb..fe40fab24 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -458,6 +458,8 @@ namespace MWBase /// Moves all stolen items from \a ptr to the closest evidence chest. virtual void confiscateStolenItems(const MWWorld::Ptr& ptr) = 0; + + virtual void goToJail () = 0; }; } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index f69f2e7cb..bb3600a27 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -766,12 +766,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWBase::World* world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayerPtr(); - world->teleportToClosestMarker(player, "prisonmarker"); - player.getClass().getNpcStats(player).setBounty(0); - world->confiscateStolenItems(player); - // TODO: pass time, change skills, show messagebox - // iDaysinPrisonMod + world->goToJail(); } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ca85d7a7f..620058c65 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -216,7 +216,7 @@ namespace MWWorld mSky (true), mCells (mStore, mEsm), mActivationDistanceOverride (mActivationDistanceOverride), mFallback(fallbackMap), mPlayIntro(0), mTeleportEnabled(true), mLevitationEnabled(false), - mFacedDistance(FLT_MAX), mGodMode(false) + mFacedDistance(FLT_MAX), mGodMode(false), mGoToJail(false) { mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); @@ -259,6 +259,9 @@ namespace MWWorld void World::startNewGame() { + mGoToJail = false; + mLevitationEnabled = true; + mTeleportEnabled = true; mWorldScene->changeToVoid(); mStore.clearDynamic(); @@ -1272,6 +1275,9 @@ namespace MWWorld mRendering->playVideo(mFallback.getFallbackString("Movies_New_Game"), true); } + if (mGoToJail && !paused) + goToJail(); + updateWeather(duration); mWorldScene->update (duration, paused); @@ -2491,4 +2497,75 @@ namespace MWWorld } } } + + void World::goToJail() + { + if (!mGoToJail) + { + // Save for next update, since the player should be able to read the dialog text first + mGoToJail = true; + return; + } + else + { + mGoToJail = false; + + MWWorld::Ptr player = getPlayerPtr(); + teleportToClosestMarker(player, "prisonmarker"); + int bounty = player.getClass().getNpcStats(player).getBounty(); + player.getClass().getNpcStats(player).setBounty(0); + confiscateStolenItems(player); + + int iDaysinPrisonMod = getStore().get().find("iDaysinPrisonMod")->getInt(); + int days = std::max(1, bounty / iDaysinPrisonMod); + + advanceTime(days * 24); + + std::set skills; + for (int day=0; day (RAND_MAX) + 1) * ESM::Skill::Length; + skills.insert(skill); + + MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill); + if (skill == ESM::Skill::Security || skill == ESM::Skill::Sneak) + value.setBase(std::min(100, value.getBase()+1)); + else + value.setBase(value.getBase()-1); + } + + const Store& gmst = getStore().get(); + + std::string message; + if (days == 1) + message = gmst.find("sNotifyMessage42")->getString(); + else + message = gmst.find("sNotifyMessage43")->getString(); + + std::stringstream dayStr; + dayStr << days; + if (message.find("%d") != std::string::npos) + message.replace(message.find("%d"), 2, dayStr.str()); + + for (std::set::iterator it = skills.begin(); it != skills.end(); ++it) + { + std::string skillName = gmst.find(ESM::Skill::sSkillNameIds[*it])->getString(); + std::stringstream skillValue; + skillValue << player.getClass().getNpcStats(player).getSkill(*it).getBase(); + std::string skillMsg = gmst.find("sNotifyMessage44")->getString(); + if (*it == ESM::Skill::Sneak || *it == ESM::Skill::Security) + skillMsg = gmst.find("sNotifyMessage39")->getString(); + + if (skillMsg.find("%s") != std::string::npos) + skillMsg.replace(skillMsg.find("%s"), 2, skillName); + if (skillMsg.find("%d") != std::string::npos) + skillMsg.replace(skillMsg.find("%d"), 2, skillValue.str()); + message += "\n" + skillMsg; + } + + std::vector buttons; + buttons.push_back("#{sOk}"); + MWBase::Environment::get().getWindowManager()->messageBox(message, buttons); + } + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index d5ccd7625..634cc8d6b 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -151,6 +151,7 @@ namespace MWWorld bool mTeleportEnabled; bool mLevitationEnabled; + bool mGoToJail; /// Called when \a object is moved to an inactive cell void objectLeftActiveCell (MWWorld::Ptr object, MWWorld::Ptr movedPtr); @@ -543,6 +544,8 @@ namespace MWWorld /// Moves all stolen items from \a ptr to the closest evidence chest. virtual void confiscateStolenItems(const MWWorld::Ptr& ptr); + + virtual void goToJail (); }; } From 828e5841d86ce3b4afed7b2637913981ff662883 Mon Sep 17 00:00:00 2001 From: gus Date: Sat, 11 Jan 2014 11:00:34 +0100 Subject: [PATCH 462/889] Windows SDL fix --- apps/launcher/main.cpp | 1 + apps/openmw/engine.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index 156bbf65b..fabf77d90 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -16,6 +16,7 @@ int main(int argc, char *argv[]) { SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); + SDL_SetMainReady(); if (SDL_Init(SDL_INIT_VIDEO) != 0) { qDebug() << "SDL_Init failed: " << QString::fromStdString(SDL_GetError()); diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index b59b6a2fd..5d04b985f 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -157,6 +157,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) //kindly ask SDL not to trash our OGL context //might this be related to http://bugzilla.libsdl.org/show_bug.cgi?id=748 ? SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); + SDL_SetMainReady(); if(SDL_Init(flags) != 0) { throw std::runtime_error("Could not initialize SDL! " + std::string(SDL_GetError())); From 0609b71df9daf2a1248889c15617d99b12c71bdd Mon Sep 17 00:00:00 2001 From: gus Date: Sat, 11 Jan 2014 11:57:07 +0100 Subject: [PATCH 463/889] fix pathfinding --- apps/openmw/mwmechanics/pathfinding.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 18e66d228..c8bc9b49c 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -150,6 +150,7 @@ namespace MWMechanics void PathFinder::buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, const ESM::Pathgrid *pathGrid, float xCell, float yCell, bool allowShortcuts) { + mPath.clear(); if(allowShortcuts) { if(MWBase::Environment::get().getWorld()->castRay(startPoint.mX, startPoint.mY, startPoint.mZ, From f41f08c352962a2e00ce809027f6715748597c5f Mon Sep 17 00:00:00 2001 From: gus Date: Thu, 5 Dec 2013 14:00:50 +0100 Subject: [PATCH 464/889] Wrong logic... should improve performances --- apps/openmw/mwmechanics/aicombat.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 32b0063b6..3ca522dc7 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -77,13 +77,14 @@ namespace MWMechanics mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, true); else { - mPathFinder2.buildPath(start, dest, pathgrid, xCell, yCell, true); - ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); - if((mTimer2 > 0.25)&&(mPathFinder2.getPathSize() < mPathFinder.getPathSize() || - (dest.mX - lastPt.mX)*(dest.mX - lastPt.mX)+(dest.mY - lastPt.mY)*(dest.mY - lastPt.mY)+(dest.mZ - lastPt.mZ)*(dest.mZ - lastPt.mZ) > 200*200)) + if(mTimer2 > 0.25) { mTimer2 = 0; - mPathFinder = mPathFinder2; + mPathFinder2.buildPath(start, dest, pathgrid, xCell, yCell, true); + ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); + if(mPathFinder2.getPathSize() < mPathFinder.getPathSize() || + (dest.mX - lastPt.mX)*(dest.mX - lastPt.mX)+(dest.mY - lastPt.mY)*(dest.mY - lastPt.mY)+(dest.mZ - lastPt.mZ)*(dest.mZ - lastPt.mZ) > 200*200) + mPathFinder = mPathFinder2; } } From 2d66b2c4fa7ca03a096a3be54fc6e19d6cdc8f83 Mon Sep 17 00:00:00 2001 From: gus Date: Sat, 11 Jan 2014 12:06:36 +0100 Subject: [PATCH 465/889] AiFollow. Npc get stuck often (no stuck dtection yet) --- apps/openmw/mwmechanics/aicombat.cpp | 3 +- apps/openmw/mwmechanics/aifollow.cpp | 44 ++++++++++++++++++++++++++-- apps/openmw/mwmechanics/aifollow.hpp | 7 +++++ 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 3ca522dc7..6a1ab68d8 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -3,7 +3,7 @@ #include "movement.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/timestamp.hpp" + #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -87,6 +87,7 @@ namespace MWMechanics mPathFinder = mPathFinder2; } } + ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 73bf9259a..2f9a7a843 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -1,12 +1,16 @@ #include "aifollow.hpp" #include +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwworld/class.hpp" +#include "movement.hpp" MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) : mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId) { } MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) -: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId) +: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), mTimer(0), mStuckTimer(0) { } @@ -17,8 +21,44 @@ MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) { + const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mActorId, false); + + mTimer = mTimer + duration; + mStuckTimer = mStuckTimer + duration; + mTotalTime = mTotalTime + duration; + + if(mTotalTime > mDuration) return true; + + ESM::Pathgrid::Point dest; + dest.mX = target.getRefData().getPosition().pos[0]; + dest.mY = target.getRefData().getPosition().pos[1]; + dest.mZ = target.getRefData().getPosition().pos[2]; + + if(mTimer > 0.25) + { + ESM::Pathgrid::Point lastPos = mPathFinder.getPath().back(); + + if((dest.mX - lastPos.mX)*(dest.mX - lastPos.mX) + +(dest.mY - lastPos.mY)*(dest.mY - lastPos.mY) + +(dest.mZ - lastPos.mZ)*(dest.mZ - lastPos.mZ) + > 100*100) + mPathFinder.getPath().push_back(dest); + + mTimer = 0; + } + + ESM::Position pos = actor.getRefData().getPosition(); + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + + if((dest.mX - pos.pos[0])*(dest.mX - pos.pos[0])+(dest.mY - pos.pos[1])*(dest.mY - pos.pos[1])+(dest.mZ - pos.pos[2])*(dest.mZ - pos.pos[2]) + < 100*100) + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + else + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + std::cout << "AiFollow completed.\n"; - return true; + return false; } int MWMechanics::AiFollow::getTypeId() const diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 39df024e4..b149c55f5 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -3,6 +3,7 @@ #include "aipackage.hpp" #include +#include "pathfinding.hpp" namespace MWMechanics { @@ -24,6 +25,12 @@ namespace MWMechanics float mZ; std::string mActorId; std::string mCellId; + + float mTimer; + float mStuckTimer; + float mTotalTime; + + PathFinder mPathFinder; }; } #endif From ccf07f940662daf2ebbf5498293202d6f178a13d Mon Sep 17 00:00:00 2001 From: gus Date: Tue, 7 Jan 2014 21:10:57 +0100 Subject: [PATCH 466/889] Bugfix --- apps/openmw/mwmechanics/aifollow.cpp | 63 +++++++++++++++++++++---- apps/openmw/mwmechanics/aifollow.hpp | 3 ++ apps/openmw/mwmechanics/pathfinding.hpp | 5 ++ 3 files changed, 61 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 2f9a7a843..b2c322331 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -6,7 +6,7 @@ #include "movement.hpp" MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) -: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId) +: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mTimer(0), mStuckTimer(0) { } MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) @@ -34,22 +34,66 @@ MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const dest.mY = target.getRefData().getPosition().pos[1]; dest.mZ = target.getRefData().getPosition().pos[2]; + //std::cout << dest.mX; + + ESM::Position pos = actor.getRefData().getPosition(); + if(mTimer > 0.25) { - ESM::Pathgrid::Point lastPos = mPathFinder.getPath().back(); + if(!mPathFinder.getPath().empty()) + { + ESM::Pathgrid::Point lastPos = mPathFinder.getPath().back(); - if((dest.mX - lastPos.mX)*(dest.mX - lastPos.mX) - +(dest.mY - lastPos.mY)*(dest.mY - lastPos.mY) - +(dest.mZ - lastPos.mZ)*(dest.mZ - lastPos.mZ) + if((dest.mX - lastPos.mX)*(dest.mX - lastPos.mX) + +(dest.mY - lastPos.mY)*(dest.mY - lastPos.mY) + +(dest.mZ - lastPos.mZ)*(dest.mZ - lastPos.mZ) > 100*100) - mPathFinder.getPath().push_back(dest); + mPathFinder.addPointToPath(dest); + } + else + mPathFinder.addPointToPath(dest); mTimer = 0; } - ESM::Position pos = actor.getRefData().getPosition(); - float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + if(mStuckTimer>0.5) + { + if((mStuckPos.pos[0] - pos.pos[0])*(mStuckPos.pos[0] - pos.pos[0]) + +(mStuckPos.pos[1] - pos.pos[1])*(mStuckPos.pos[1] - pos.pos[1]) + +(mStuckPos.pos[2] - pos.pos[2])*(mStuckPos.pos[2] - pos.pos[2]) + < 100) //NPC is stuck + { + std::cout << "stck\n"; + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; + start.mZ = pos.pos[2]; + + const ESM::Pathgrid *pathgrid = + MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); + + float xCell = 0; + float yCell = 0; + + if (actor.getCell()->mCell->isExterior()) + { + xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; + yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; + } + mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, true); + } + mStuckTimer = 0; + mStuckPos = pos; + } + + if(mPathFinder.getPath().empty()) + mPathFinder.addPointToPath(dest); + + if(!mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2])) + { + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + } if((dest.mX - pos.pos[0])*(dest.mX - pos.pos[0])+(dest.mY - pos.pos[1])*(dest.mY - pos.pos[1])+(dest.mZ - pos.pos[2])*(dest.mZ - pos.pos[2]) < 100*100) @@ -57,7 +101,6 @@ MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const else MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; - std::cout << "AiFollow completed.\n"; return false; } diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index b149c55f5..135ba9aac 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -4,6 +4,7 @@ #include "aipackage.hpp" #include #include "pathfinding.hpp" +#include "../../../components/esm/defs.hpp" namespace MWMechanics { @@ -30,6 +31,8 @@ namespace MWMechanics float mStuckTimer; float mTotalTime; + ESM::Position mStuckPos; + PathFinder mPathFinder; }; } diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index 916df850b..f9c2c5cc7 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -37,6 +37,11 @@ namespace MWMechanics return mPath; } + void addPointToPath(ESM::Pathgrid::Point &point) + { + mPath.push_back(point); + } + private: std::list mPath; bool mIsPathConstructed; From 5ea25dc26958267e39d04461ce571284566a404c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 11 Jan 2014 15:34:32 +0100 Subject: [PATCH 467/889] player state cleanup --- apps/openmw/mwworld/player.cpp | 10 ++++++++++ apps/openmw/mwworld/player.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 1 + 3 files changed, 13 insertions(+) diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index c59445402..a2777d489 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -171,4 +171,14 @@ namespace MWWorld if (mMarkedCell) markedPosition = mMarkedPosition; } + + void Player::clear() + { + mCellStore = 0; + mSign.clear(); + mMarkedCell = 0; + mAutoMove = false; + mForwardBackward = 0; + mTeleported = false; + } } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 1df848111..fef577cec 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -86,6 +86,8 @@ namespace MWWorld bool wasTeleported() const; void setTeleported(bool teleported); + + void clear(); }; } #endif diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 83885e5d5..5224ffdce 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -267,6 +267,7 @@ namespace MWWorld void World::clear() { mLocalScripts.clear(); + mPlayer->clear(); // enable collision if (!mPhysics->toggleCollisionMode()) From 051d7141be83e13d7f53a0f9987de5bf350a6713 Mon Sep 17 00:00:00 2001 From: gus Date: Sat, 11 Jan 2014 20:32:38 +0100 Subject: [PATCH 468/889] check position to stop AIFollow --- apps/openmw/mwmechanics/aifollow.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index b2c322331..e9495f859 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -27,17 +27,19 @@ MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const mStuckTimer = mStuckTimer + duration; mTotalTime = mTotalTime + duration; - if(mTotalTime > mDuration) return true; + ESM::Position pos = actor.getRefData().getPosition(); + + if(mTotalTime > mDuration || + (pos.pos[0]-mX)*(pos.pos[0]-mX) + + (pos.pos[1]-mY)*(pos.pos[1]-mY) + + (pos.pos[2]-mZ)*(pos.pos[2]-mZ) < 100*100) + return true; ESM::Pathgrid::Point dest; dest.mX = target.getRefData().getPosition().pos[0]; dest.mY = target.getRefData().getPosition().pos[1]; dest.mZ = target.getRefData().getPosition().pos[2]; - //std::cout << dest.mX; - - ESM::Position pos = actor.getRefData().getPosition(); - if(mTimer > 0.25) { if(!mPathFinder.getPath().empty()) @@ -63,7 +65,6 @@ MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const +(mStuckPos.pos[2] - pos.pos[2])*(mStuckPos.pos[2] - pos.pos[2]) < 100) //NPC is stuck { - std::cout << "stck\n"; ESM::Pathgrid::Point start; start.mX = pos.pos[0]; start.mY = pos.pos[1]; From 3db299f1b2c8074d9febbe257066f1c610bf617d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 21:04:31 +0100 Subject: [PATCH 469/889] Fix fall damage crash --- apps/openmw/mwclass/npc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 12ed2dd09..41c6de40c 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -599,7 +599,7 @@ namespace MWClass // NOTE: 'object' and/or 'attacker' may be empty. // Attacking peaceful NPCs is a crime - if (ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) + if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); if(!successful) From 224f288359bfc6a523fdcfc4d338ecf47574096c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 21:30:09 +0100 Subject: [PATCH 470/889] Don't attempt to change disposition for creatures --- apps/openmw/mwgui/tradewindow.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 1cd4c1c7c..14024dec6 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -318,7 +318,8 @@ namespace MWGui messageBox("#{sNotifyMessage9}"); int iBarterFailDisposition = gmst.find("iBarterFailDisposition")->getInt(); - MWBase::Environment::get().getDialogueManager()->applyDispositionChange(iBarterFailDisposition); + if (mPtr.getClass().isNpc()) + MWBase::Environment::get().getDialogueManager()->applyDispositionChange(iBarterFailDisposition); return; } @@ -327,7 +328,8 @@ namespace MWGui } int iBarterSuccessDisposition = gmst.find("iBarterSuccessDisposition")->getInt(); - MWBase::Environment::get().getDialogueManager()->applyDispositionChange(iBarterSuccessDisposition); + if (mPtr.getClass().isNpc()) + MWBase::Environment::get().getDialogueManager()->applyDispositionChange(iBarterSuccessDisposition); // make the item transfer mTradeModel->transferItems(); From dddc0979a294aa516aeb21ab4c01ac29f04315e4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 22:04:46 +0100 Subject: [PATCH 471/889] Fix another fatigue cap issue --- apps/openmw/mwmechanics/spellcasting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 850c4dcf5..de078f9ef 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -437,7 +437,7 @@ namespace MWMechanics DynamicStat fatigue = stats.getFatigue(); const float normalizedEncumbrance = mCaster.getClass().getEncumbrance(mCaster) / mCaster.getClass().getCapacity(mCaster); float fatigueLoss = spell->mData.mCost * (fFatigueSpellBase + normalizedEncumbrance * fFatigueSpellMult); - fatigue.setCurrent(std::max(0.f, fatigue.getCurrent() - fatigueLoss)); + fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); stats.setFatigue(fatigue); bool fail = false; From 44b2380874007c7dfb29b1b4c9d1a85fab6bfed5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 22:26:26 +0100 Subject: [PATCH 472/889] Closes #947: Decrease fatigue when running, swimming and attacking --- apps/openmw/mwclass/npc.cpp | 14 +++++++++ apps/openmw/mwmechanics/character.cpp | 37 ++++++++++++++++++++++-- apps/openmw/mwmechanics/spellcasting.cpp | 3 +- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 41c6de40c..dc219f373 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -436,6 +436,20 @@ namespace MWClass if(!weapon.isEmpty() && weapon.getTypeName() != typeid(ESM::Weapon).name()) weapon = MWWorld::Ptr(); + // Reduce fatigue + // somewhat of a guess, but using the weapon weight makes sense + const float fFatigueAttackBase = gmst.find("fFatigueAttackBase")->getFloat(); + const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat(); + const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat(); + MWMechanics::DynamicStat fatigue = getCreatureStats(ptr).getFatigue(); + const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr); + float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult; + if (!weapon.isEmpty()) + fatigueLoss += weapon.getClass().getWeight(weapon) * getNpcStats(ptr).getAttackStrength() * fWeaponFatigueMult; + fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); + getCreatureStats(ptr).setFatigue(fatigue); + + float dist = 100.0f * (!weapon.isEmpty() ? weapon.get()->mBase->mData.mReach : gmst.find("fHandToHandReach")->getFloat()); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index b29ae2914..0856ada0c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -900,6 +900,41 @@ void CharacterController::update(float duration) } } + // reduce fatigue + const MWWorld::Store &gmst = world->getStore().get(); + float fatigueLoss = 0; + static const float fFatigueRunBase = gmst.find("fFatigueRunBase")->getFloat(); + static const float fFatigueRunMult = gmst.find("fFatigueRunMult")->getFloat(); + static const float fFatigueSwimWalkBase = gmst.find("fFatigueSwimWalkBase")->getFloat(); + static const float fFatigueSwimRunBase = gmst.find("fFatigueSwimRunBase")->getFloat(); + static const float fFatigueSwimWalkMult = gmst.find("fFatigueSwimWalkMult")->getFloat(); + static const float fFatigueSwimRunMult = gmst.find("fFatigueSwimRunMult")->getFloat(); + static const float fFatigueSneakBase = gmst.find("fFatigueSneakBase")->getFloat(); + static const float fFatigueSneakMult = gmst.find("fFatigueSneakMult")->getFloat(); + + const float encumbrance = cls.getEncumbrance(mPtr) / cls.getCapacity(mPtr); + if (encumbrance < 1) + { + if (sneak) + fatigueLoss = fFatigueSneakBase + encumbrance * fFatigueSneakMult; + else + { + if (inwater) + { + if (!isrunning) + fatigueLoss = fFatigueSwimWalkBase + encumbrance * fFatigueSwimWalkMult; + else + fatigueLoss = fFatigueSwimRunBase + encumbrance * fFatigueSwimRunMult; + } + if (isrunning) + fatigueLoss = fFatigueRunBase + encumbrance * fFatigueRunMult; + } + } + fatigueLoss *= duration; + DynamicStat fatigue = cls.getCreatureStats(mPtr).getFatigue(); + fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss, fatigue.getCurrent() < 0); + cls.getCreatureStats(mPtr).setFatigue(fatigue); + if(sneak || inwater || flying) vec.z = 0.0f; @@ -916,8 +951,6 @@ void CharacterController::update(float duration) cls.getCreatureStats(mPtr).land(); } - const MWWorld::Store &gmst = world->getStore().get(); - forcestateupdate = (mJumpState != JumpState_Falling); mJumpState = JumpState_Falling; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index de078f9ef..be67b6592 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -437,8 +437,7 @@ namespace MWMechanics DynamicStat fatigue = stats.getFatigue(); const float normalizedEncumbrance = mCaster.getClass().getEncumbrance(mCaster) / mCaster.getClass().getCapacity(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; From 10ddea45e9f66a03b957608e2aef9f90ec2c1ecf Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 23:11:23 +0100 Subject: [PATCH 473/889] Move crime from onHit to hit, since failed hits are apparently also a crime --- apps/openmw/mwclass/npc.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index dc219f373..86f0fbc3b 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -468,6 +468,10 @@ namespace MWClass if(ptr.getRefData().getHandle() == "player") MWBase::Environment::get().getWindowManager()->setEnemy(victim); + // Attacking peaceful NPCs is a crime + if (victim.getClass().isNpc() && victim.getClass().getCreatureStats(victim).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) + MWBase::Environment::get().getMechanicsManager()->commitCrime(ptr, victim, MWBase::MechanicsManager::OT_Assault); + int weapskill = ESM::Skill::HandToHand; if(!weapon.isEmpty()) weapskill = get(weapon).getEquipmentSkill(weapon); @@ -612,10 +616,6 @@ namespace MWClass // NOTE: 'object' and/or 'attacker' may be empty. - // Attacking peaceful NPCs is a crime - if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) - MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); - if(!successful) { // TODO: Handle HitAttemptOnMe script function From 921ef6cd9c7d36a15bf38dcd5ff2bac005c81dae Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Jan 2014 00:42:27 +0100 Subject: [PATCH 474/889] Closes #1093: Show weapon when initializing the character controller with a weapon equipped --- apps/openmw/mwmechanics/character.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 0856ada0c..e5236f02b 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -441,6 +441,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim { getWeaponGroup(mWeaponType, mCurrentWeapon); mUpperBodyState = UpperCharState_WeapEquiped; + mAnimation->showWeapons(true); } } From 1d19d36bd6a14e55a7ea90c155f386ebf235d4a4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Jan 2014 01:20:37 +0100 Subject: [PATCH 475/889] Remove unused magic effect flags and update esmtool output --- apps/esmtool/labels.cpp | 39 +++++++++-------------------- apps/openmw/mwmechanics/alchemy.cpp | 2 +- components/esm/loadmgef.hpp | 6 +---- 3 files changed, 14 insertions(+), 33 deletions(-) diff --git a/apps/esmtool/labels.cpp b/apps/esmtool/labels.cpp index 7b1fc7fb2..56c9a2fad 100644 --- a/apps/esmtool/labels.cpp +++ b/apps/esmtool/labels.cpp @@ -764,34 +764,19 @@ std::string magicEffectFlags(int flags) { std::string properties = ""; if (flags == 0) properties += "[None] "; - // Enchanting & SpellMaking occur on the same list of effects. - // "EXTRA SPELL" appears in the construction set under both the - // spell making and enchanting tabs as an allowed effect. Since - // most of the effects without this flags are defective in various - // ways, it's still very unclear what these flag bits are. - if (flags & ESM::MagicEffect::SpellMaking) properties += "SpellMaking "; - if (flags & ESM::MagicEffect::Enchanting) properties += "Enchanting "; - if (flags & 0x00000040) properties += "RangeNoSelf "; - if (flags & 0x00000080) properties += "RangeTouch "; - if (flags & 0x00000100) properties += "RangeTarget "; - if (flags & 0x00001000) properties += "Unknown2 "; - if (flags & 0x00000001) properties += "AffectSkill "; - if (flags & 0x00000002) properties += "AffectAttribute "; + if (flags & ESM::MagicEffect::TargetAttribute) properties += "TargetAttribute "; + if (flags & ESM::MagicEffect::TargetSkill) properties += "TargetSkill "; if (flags & ESM::MagicEffect::NoDuration) properties += "NoDuration "; - if (flags & 0x00000008) properties += "NoMagnitude "; - if (flags & 0x00000010) properties += "Negative "; - if (flags & 0x00000020) properties += "Unknown1 "; - // ESM componet says 0x800 is negative, but none of the magic - // effects have this flags set. - if (flags & ESM::MagicEffect::Negative) properties += "Unused "; - // Since only Chameleon has this flag it could be anything - // that uniquely distinguishes Chameleon. - if (flags & 0x00002000) properties += "Chameleon "; - if (flags & 0x00004000) properties += "Bound "; - if (flags & 0x00008000) properties += "Summon "; - // Calm, Demoralize, Frenzy, Lock, Open, Rally, Soultrap, Turn Unded - if (flags & 0x00010000) properties += "Unknown3 "; - if (flags & 0x00020000) properties += "Absorb "; + if (flags & ESM::MagicEffect::NoMagnitude) properties += "NoMagnitude "; + if (flags & ESM::MagicEffect::Harmful) properties += "Harmful "; + if (flags & ESM::MagicEffect::ContinuousVfx) properties += "ContinuousVFX "; + if (flags & ESM::MagicEffect::CastSelf) properties += "CastSelf "; + if (flags & ESM::MagicEffect::CastTouch) properties += "CastTouch "; + if (flags & ESM::MagicEffect::CastTarget) properties += "CastTarget "; + if (flags & ESM::MagicEffect::UncappedDamage) properties += "UncappedDamage "; + if (flags & ESM::MagicEffect::NonRecastable) properties += "NonRecastable "; + if (flags & ESM::MagicEffect::Unreflectable) properties += "Unreflectable "; + if (flags & ESM::MagicEffect::CasterLinked) properties += "CasterLinked "; if (flags & 0xFFFC0000) properties += "Invalid "; properties += str(boost::format("(0x%08X)") % flags); return properties; diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index f994c28b8..af58e9ee0 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -62,7 +62,7 @@ void MWMechanics::Alchemy::applyTools (int flags, float& value) const { bool magnitude = !(flags & ESM::MagicEffect::NoMagnitude); bool duration = !(flags & ESM::MagicEffect::NoDuration); - bool negative = flags & (ESM::MagicEffect::Negative | ESM::MagicEffect::Harmful); + bool negative = flags & (ESM::MagicEffect::Harmful); int tool = negative ? ESM::Apparatus::Retort : ESM::Apparatus::Albemic; diff --git a/components/esm/loadmgef.hpp b/components/esm/loadmgef.hpp index 77056b9ec..8281f4969 100644 --- a/components/esm/loadmgef.hpp +++ b/components/esm/loadmgef.hpp @@ -28,11 +28,7 @@ struct MagicEffect UncappedDamage = 0x1000, // Negates multiple cap behaviours. Allows an effect to reduce an attribute below zero; removes the normal minimum effect duration of 1 second. NonRecastable = 0x4000, // Does not land if parent spell is already affecting target. Shows "you cannot re-cast" message for self target. Unreflectable = 0x10000, // Cannot be reflected, the effect always lands normally. - CasterLinked = 0x20000, // Must quench if caster is dead, or not an NPC/creature. Not allowed in containter/door trap spells. - SpellMaking = 0x0200, - Enchanting = 0x0400, - Negative = 0x0800 // A harmful effect. Will determine whether - // eg. NPCs regard this spell as an attack. (same as 0x10?) + CasterLinked = 0x20000 // Must quench if caster is dead, or not an NPC/creature. Not allowed in containter/door trap spells. }; enum MagnitudeDisplayType { From fb778f8ecd76a703e0da6c2aa260d142f63d4490 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Jan 2014 04:09:51 +0100 Subject: [PATCH 476/889] Use fEncumbranceStrMult --- apps/openmw/mwclass/npc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 86f0fbc3b..4aa27e38a 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1055,7 +1055,8 @@ namespace MWClass float Npc::getCapacity (const MWWorld::Ptr& ptr) const { const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); - return stats.getAttribute(0).getModified()*5; + static const float fEncumbranceStrMult = MWBase::Environment::get().getWorld()->getStore().get().find("fEncumbranceStrMult")->getFloat(); + return stats.getAttribute(0).getModified()*fEncumbranceStrMult; } float Npc::getEncumbrance (const MWWorld::Ptr& ptr) const From 767c72e619b1217842bd87e4dc72793310f4b751 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Jan 2014 10:04:06 +0100 Subject: [PATCH 477/889] Fix diagonal movement being faster than forward movement --- apps/openmw/mwmechanics/character.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index e5236f02b..86785ec22 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -863,6 +863,7 @@ void CharacterController::update(float duration) bool sneak = cls.getStance(mPtr, MWWorld::Class::Sneak); bool flying = world->isFlying(mPtr); Ogre::Vector3 vec = cls.getMovementVector(mPtr); + vec.normalise(); if(mHitState != CharState_None && mJumpState == JumpState_None) vec = Ogre::Vector3(0.0f); Ogre::Vector3 rot = cls.getRotationVector(mPtr); @@ -1119,9 +1120,11 @@ void CharacterController::update(float duration) else moved = Ogre::Vector3(0.0f); - // Ensure we're moving in generally the right direction + // Ensure we're moving in generally the right direction... if(mMovementSpeed > 0.f) { + float l = moved.length(); + if((movement.x < 0.0f && movement.x < moved.x*2.0f) || (movement.x > 0.0f && movement.x > moved.x*2.0f)) moved.x = movement.x; @@ -1131,7 +1134,12 @@ void CharacterController::update(float duration) if((movement.z < 0.0f && movement.z < moved.z*2.0f) || (movement.z > 0.0f && movement.z > moved.z*2.0f)) moved.z = movement.z; + // but keep the original speed + float newLength = moved.length(); + if (newLength > 0) + moved *= (l / newLength); } + // Update movement if(moved.squaredLength() > 1.0f) world->queueMovement(mPtr, moved); From f78b846f9e88347097c1de8e7cacdd09627be2f1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Jan 2014 10:21:49 +0100 Subject: [PATCH 478/889] Handle CasterLinked magic effect flag --- apps/openmw/mwmechanics/activespells.cpp | 18 ++++++++++++++++++ apps/openmw/mwmechanics/activespells.hpp | 3 +++ apps/openmw/mwmechanics/actors.cpp | 7 +++++++ apps/openmw/mwmechanics/spellcasting.cpp | 5 +++-- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 2a7165974..994798b0b 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -205,4 +205,22 @@ namespace MWMechanics } mSpellsChanged = true; } + + void ActiveSpells::purge(const std::string &actorHandle) + { + for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it) + { + for (std::vector::iterator effectIt = it->second.mEffects.begin(); + effectIt != it->second.mEffects.end();) + { + const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get().find(effectIt->mKey.mId); + if (effect->mData.mFlags & ESM::MagicEffect::CasterLinked + && it->second.mCasterHandle == actorHandle) + effectIt = it->second.mEffects.erase(effectIt); + else + effectIt++; + } + } + mSpellsChanged = true; + } } diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 2ddb4ec55..7a40afb4c 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -90,6 +90,9 @@ namespace MWMechanics /// Remove all active effects, if roll succeeds (for each effect) void purgeAll (float chance); + /// Remove all effects with CASTER_LINKED flag that were cast by \a actorHandle + void purge (const std::string& actorHandle); + bool isSpellActive (std::string id) const; ///< case insensitive diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 468a21892..6f710988e 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -814,6 +814,13 @@ namespace MWMechanics stats.setMagicEffects(MWMechanics::MagicEffects()); calculateCreatureStatModifiers(iter->first, 0); + // Make sure spell effects with CasterLinked flag are removed + for(PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2) + { + MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells(); + spells.purge(iter->first.getRefData().getHandle()); + } + ++mDeathCount[cls.getId(iter->first)]; if(cls.isEssential(iter->first)) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index be67b6592..a0e91791b 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -164,8 +164,9 @@ namespace MWMechanics ActiveSpells::Effect effect_ = effect; effect_.mMagnitude *= -1; effects.push_back(effect_); + // Also make sure to set casterHandle = target, so that the effect on the caster gets purged when the target dies caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true, - effects, mSourceName, caster.getRefData().getHandle()); + effects, mSourceName, target.getRefData().getHandle()); } } } @@ -224,7 +225,7 @@ namespace MWMechanics target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects, mSourceName, caster.getRefData().getHandle()); - if (anyHarmfulEffect && target.getClass().isActor() + if (anyHarmfulEffect && target.getClass().isActor() && target != caster && target.getClass().getCreatureStats(target).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) MWBase::Environment::get().getMechanicsManager()->commitCrime(caster, target, MWBase::MechanicsManager::OT_Assault); } From 7066844e52f10752ca77db00f8e1dbaaa20b0d43 Mon Sep 17 00:00:00 2001 From: gus Date: Sun, 12 Jan 2014 11:38:58 +0100 Subject: [PATCH 479/889] Follow you until a certain cell is reached --- apps/openmw/mwmechanics/aifollow.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index e9495f859..6e19cc182 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -6,7 +6,7 @@ #include "movement.hpp" MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) -: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mTimer(0), mStuckTimer(0) +: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), mTimer(0), mStuckTimer(0) { } MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) @@ -19,7 +19,7 @@ MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const return new AiFollow(*this); } - bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) +bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) { const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mActorId, false); @@ -29,11 +29,24 @@ MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const ESM::Position pos = actor.getRefData().getPosition(); - if(mTotalTime > mDuration || - (pos.pos[0]-mX)*(pos.pos[0]-mX) + + if(mTotalTime > mDuration) + return true; + + if((pos.pos[0]-mX)*(pos.pos[0]-mX) + (pos.pos[1]-mY)*(pos.pos[1]-mY) + (pos.pos[2]-mZ)*(pos.pos[2]-mZ) < 100*100) - return true; + { + if(actor.getCell()->isExterior()) + { + if(mCellId == "") + return true; + } + else + { + if(mCellId == actor.getCell()->mCell->mName) + return true; + } + } ESM::Pathgrid::Point dest; dest.mX = target.getRefData().getPosition().pos[0]; From 7e96a391da79df0fa4ea5e71e766e6d7140b6d26 Mon Sep 17 00:00:00 2001 From: gus Date: Sun, 12 Jan 2014 11:39:42 +0100 Subject: [PATCH 480/889] FollowCell duration = 0 -> infinite time --- apps/openmw/mwmechanics/aifollow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 6e19cc182..61994a865 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -29,7 +29,7 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) ESM::Position pos = actor.getRefData().getPosition(); - if(mTotalTime > mDuration) + if(mTotalTime > mDuration && mDuration != 0) return true; if((pos.pos[0]-mX)*(pos.pos[0]-mX) + From 2446abe07633675a8cf3180b187a6bc1812abb45 Mon Sep 17 00:00:00 2001 From: gus Date: Sun, 12 Jan 2014 14:01:54 +0100 Subject: [PATCH 481/889] Allow getting current active package --- apps/openmw/mwmechanics/aisequence.cpp | 8 ++++++++ apps/openmw/mwmechanics/aisequence.hpp | 3 +++ 2 files changed, 11 insertions(+) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 139f54489..616e54bd8 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -102,6 +102,14 @@ void MWMechanics::AiSequence::queue (const AiPackage& package) mPackages.push_back (package.clone()); } +MWMechanics::AiPackage* MWMechanics::AiSequence::getActivePackage() +{ + if(mPackages.empty()) + throw std::runtime_error(std::string("No AI Package!")); + else + return mPackages.front(); +} + void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list) { for (std::vector::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it) diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 0976ef099..d44a4975e 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -52,6 +52,9 @@ namespace MWMechanics ///< Add \a package to the end of the sequence (executed after all other packages have been /// completed) + AiPackage* getActivePackage(); + ///< return the current active package. If there is no active package, throw an exeption + void fill (const ESM::AIPackageList& list); }; } From dd870e35dbd19463d7f16af7216b80be9f8d1abd Mon Sep 17 00:00:00 2001 From: gus Date: Sun, 12 Jan 2014 14:02:15 +0100 Subject: [PATCH 482/889] get all actors following a given actor --- apps/openmw/mwbase/mechanicsmanager.hpp | 4 ++++ apps/openmw/mwmechanics/actors.cpp | 19 +++++++++++++++++++ apps/openmw/mwmechanics/actors.hpp | 3 +++ .../mwmechanics/mechanicsmanagerimp.cpp | 5 +++++ .../mwmechanics/mechanicsmanagerimp.hpp | 2 ++ 5 files changed, 33 insertions(+) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 24e955cdf..ce5c62ee4 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace Ogre { @@ -151,6 +152,9 @@ namespace MWBase virtual void toggleAI() = 0; virtual bool isAIActive() = 0; + + ///return the list of actors which are following the given actor (ie AiFollow is active and the target is the actor) + virtual std::list getActorsFollowing(const MWWorld::Ptr& actor) = 0; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0a4adb6e2..a019aeb30 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -27,6 +27,7 @@ #include "../mwbase/mechanicsmanager.hpp" #include "aicombat.hpp" +#include "aifollow.hpp" namespace { @@ -877,4 +878,22 @@ namespace MWMechanics return iter->second->isAnimPlaying(groupName); return false; } + + std::list Actors::getActorsFollowing(const MWWorld::Ptr& actor) + { + std::list list; + for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++) + { + const MWWorld::Class &cls = MWWorld::Class::get(iter->first); + CreatureStats &stats = cls.getCreatureStats(iter->first); + + if(stats.getAiSequence().getTypeId() == 3) + { + MWMechanics::AiFollow* package = static_cast(stats.getAiSequence().getActivePackage()); + if(package->getFollowedActor() == actor.getCellRef().mRefID) + list.push_front(iter->first); + } + } + return list; + } } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 7046543e6..c8abd3525 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -91,6 +91,9 @@ namespace MWMechanics void skipAnimation(const MWWorld::Ptr& ptr); bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName); + std::list getActorsFollowing(const MWWorld::Ptr& actor); + ///= target); } + + std::list MechanicsManager::getActorsFollowing(const MWWorld::Ptr& actor) + { + return mActors.getActorsFollowing(actor); + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index cec08fa92..50d9289cc 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -129,6 +129,8 @@ namespace MWMechanics virtual void toggleAI(); virtual bool isAIActive(); + + virtual std::list getActorsFollowing(const MWWorld::Ptr& actor); }; } From 1ae62665d65472507a21a2aa91e582f71547899f Mon Sep 17 00:00:00 2001 From: gus Date: Sun, 12 Jan 2014 14:02:40 +0100 Subject: [PATCH 483/889] get all actors following a given actor --- apps/openmw/mwmechanics/aifollow.cpp | 5 +++++ apps/openmw/mwmechanics/aifollow.hpp | 2 ++ 2 files changed, 7 insertions(+) diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 61994a865..399504ff1 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -122,3 +122,8 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) { return 3; } + +std::string MWMechanics::AiFollow::getFollowedActor() +{ + return mActorId; +} diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 135ba9aac..9d77b903d 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -19,6 +19,8 @@ namespace MWMechanics ///< \return Package completed? virtual int getTypeId() const; + std::string getFollowedActor(); + private: float mDuration; float mX; From 0e46c40cb5812bb40dfa454db9fab4167c1a9c25 Mon Sep 17 00:00:00 2001 From: gus Date: Sun, 12 Jan 2014 14:03:11 +0100 Subject: [PATCH 484/889] Make ACtionTeleport non player specific and teleport actors following the teleporting actor --- apps/openmw/mwworld/actionteleport.cpp | 33 ++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index 773fde81e..b4c572ba9 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -3,6 +3,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "player.hpp" namespace MWWorld @@ -16,11 +17,35 @@ namespace MWWorld void ActionTeleport::executeImp (const Ptr& actor) { MWBase::World* world = MWBase::Environment::get().getWorld(); - world->getPlayer().setTeleported(true); - if (mCellName.empty()) - world->changeToExteriorCell (mPosition); + //find any NPC that is following the actor and teleport him too + std::list followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor); + for(std::list::iterator it = followers.begin();it != followers.end();it++) + { + std::cout << "teleporting someone!" << (*it).getCellRef().mRefID; + executeImp(*it); + } + + if(actor == world->getPlayerPtr()) + { + world->getPlayer().setTeleported(true); + if (mCellName.empty()) + world->changeToExteriorCell (mPosition); + else + world->changeToInteriorCell (mCellName, mPosition); + } else - world->changeToInteriorCell (mCellName, mPosition); + { + if (mCellName.empty()) + { + int cellX; + int cellY; + world->positionToIndex(mPosition.pos[0],mPosition.pos[1],cellX,cellY); + world->moveObject(actor,*world->getExterior(cellX,cellY), + mPosition.pos[0],mPosition.pos[1],mPosition.pos[2]); + } + else + world->moveObject(actor,*world->getInterior(mCellName),mPosition.pos[0],mPosition.pos[1],mPosition.pos[2]); + } } } From 5d038423ec8936b0a84eb469c2cca93b5b78887a Mon Sep 17 00:00:00 2001 From: gus Date: Sun, 12 Jan 2014 18:42:31 +0100 Subject: [PATCH 485/889] attempt to solve Bug #1009 by not building graph every frame --- apps/openmw/mwmechanics/aicombat.cpp | 7 ++---- apps/openmw/mwmechanics/aiescort.cpp | 4 +--- apps/openmw/mwmechanics/aifollow.cpp | 7 ++---- apps/openmw/mwmechanics/aitravel.cpp | 3 +-- apps/openmw/mwmechanics/aiwander.cpp | 2 +- apps/openmw/mwmechanics/pathfinding.cpp | 29 ++++++++++++++++++++----- apps/openmw/mwmechanics/pathfinding.hpp | 19 ++++++++++++++-- 7 files changed, 48 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 6a1ab68d8..19c3fa2f3 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -49,8 +49,6 @@ namespace MWMechanics //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true); } ESM::Position pos = actor.getRefData().getPosition(); - const ESM::Pathgrid *pathgrid = - MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); float xCell = 0; float yCell = 0; @@ -74,20 +72,19 @@ namespace MWMechanics mTimer2 = mTimer2 + duration; if(!mPathFinder.isPathConstructed()) - mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, true); + mPathFinder.buildPath(start, dest, actor.getCell(), true); else { if(mTimer2 > 0.25) { mTimer2 = 0; - mPathFinder2.buildPath(start, dest, pathgrid, xCell, yCell, true); + mPathFinder2.buildPath(start, dest, actor.getCell(), true); ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); if(mPathFinder2.getPathSize() < mPathFinder.getPathSize() || (dest.mX - lastPt.mX)*(dest.mX - lastPt.mX)+(dest.mY - lastPt.mY)*(dest.mY - lastPt.mY)+(dest.mZ - lastPt.mZ)*(dest.mZ - lastPt.mZ) > 200*200) mPathFinder = mPathFinder2; } } - ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 35f9f1993..0d44dd6fc 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -85,8 +85,6 @@ namespace MWMechanics MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); ESM::Position pos = actor.getRefData().getPosition(); bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY; - const ESM::Pathgrid *pathgrid = - MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX) { @@ -136,7 +134,7 @@ namespace MWMechanics start.mY = pos.pos[1]; start.mZ = pos.pos[2]; - mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, true); + mPathFinder.buildPath(start, dest, actor.getCell(), true); } if(mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2])) diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 399504ff1..a711ebe7c 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -81,10 +81,7 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) ESM::Pathgrid::Point start; start.mX = pos.pos[0]; start.mY = pos.pos[1]; - start.mZ = pos.pos[2]; - - const ESM::Pathgrid *pathgrid = - MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); + start.mZ = pos.pos[2]; float xCell = 0; float yCell = 0; @@ -94,7 +91,7 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; } - mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, true); + mPathFinder.buildPath(start, dest, actor.getCell(), true); } mStuckTimer = 0; mStuckPos = pos; diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 73b38dd13..24a61310b 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -61,7 +61,6 @@ namespace MWMechanics } } - const ESM::Pathgrid *pathgrid = world->getStore().get().search(*cell); bool cellChange = cell->mData.mX != cellX || cell->mData.mY != cellY; if(!mPathFinder.isPathConstructed() || cellChange) { @@ -86,7 +85,7 @@ namespace MWMechanics start.mY = pos.pos[1]; start.mZ = pos.pos[2]; - mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, true); + mPathFinder.buildPath(start, dest, actor.getCell(), true); } if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 7df88c076..353121a3e 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -213,7 +213,7 @@ namespace MWMechanics start.mY = pos.pos[1]; start.mZ = pos.pos[2]; - mPathFinder.buildPath(start, dest, mPathgrid, mXCell, mYCell, false); + mPathFinder.buildPath(start, dest, actor.getCell(), false); if(mPathFinder.isPathConstructed()) { diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index c8bc9b49c..86019fe54 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -6,7 +6,6 @@ #include "OgreMath.h" #include -#include namespace { @@ -136,8 +135,8 @@ namespace namespace MWMechanics { PathFinder::PathFinder() + :mIsPathConstructed(false),mIsGraphConstructed(false) { - mIsPathConstructed = false; } void PathFinder::clearPath() @@ -147,10 +146,19 @@ namespace MWMechanics mIsPathConstructed = false; } + void PathFinder::buildPathgridGraph(const ESM::Pathgrid* pathGrid,float xCell, float yCell) + { + mGraph = buildGraph(pathGrid, xCell, yCell); + mIsGraphConstructed = true; + } + void PathFinder::buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, - const ESM::Pathgrid *pathGrid, float xCell, float yCell, bool allowShortcuts) + const MWWorld::CellStore* cell, bool allowShortcuts) { mPath.clear(); + if(mCell != cell) mIsGraphConstructed = false; + mCell = cell; + if(allowShortcuts) { if(MWBase::Environment::get().getWorld()->castRay(startPoint.mX, startPoint.mY, startPoint.mZ, @@ -160,13 +168,24 @@ namespace MWMechanics if(!allowShortcuts) { + const ESM::Pathgrid *pathGrid = + MWBase::Environment::get().getWorld()->getStore().get().search(*mCell->mCell); + float xCell = 0; + float yCell = 0; + + if (mCell->isExterior()) + { + xCell = mCell->mCell->mData.mX * ESM::Land::REAL_SIZE; + yCell = mCell->mCell->mData.mY * ESM::Land::REAL_SIZE; + } int startNode = getClosestPoint(pathGrid, startPoint.mX - xCell, startPoint.mY - yCell,startPoint.mZ); int endNode = getClosestPoint(pathGrid, endPoint.mX - xCell, endPoint.mY - yCell, endPoint.mZ); if(startNode != -1 && endNode != -1) { - PathGridGraph graph = buildGraph(pathGrid, xCell, yCell); - mPath = findPath(startNode, endNode, graph); + if(!mIsGraphConstructed) buildPathgridGraph(pathGrid, xCell, yCell); + + mPath = findPath(startNode, endNode, mGraph); if(!mPath.empty()) { diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index f9c2c5cc7..687031491 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -3,6 +3,12 @@ #include #include +#include + +namespace MWWorld +{ + class CellStore; +} namespace MWMechanics { @@ -12,9 +18,11 @@ namespace MWMechanics PathFinder(); void clearPath(); + + void buildPathgridGraph(const ESM::Pathgrid* pathGrid,float xCell = 0, float yCell = 0); + void buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, - const ESM::Pathgrid* pathGrid, float xCell = 0, float yCell = 0, - bool allowShortcuts = true); + const MWWorld::CellStore* cell, bool allowShortcuts = true); bool checkPathCompleted(float x, float y, float z); ///< \Returns true if the last point of the path has been reached. @@ -45,6 +53,13 @@ namespace MWMechanics private: std::list mPath; bool mIsPathConstructed; + + typedef boost::adjacency_list< boost::vecS, boost::vecS, boost::undirectedS, + boost::property, boost::property > + PathGridGraph; + PathGridGraph mGraph; + bool mIsGraphConstructed; + const MWWorld::CellStore* mCell; }; } From e453468eff18e769b0478fdf9f1fc87bd924d98a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Jan 2014 19:23:08 +0100 Subject: [PATCH 486/889] moved CellRef loading code to the CellRef class --- components/esm/cellref.cpp | 70 ++++++++++++++++++++++++++++++++++++- components/esm/cellref.hpp | 3 ++ components/esm/loadcell.cpp | 69 +----------------------------------- 3 files changed, 73 insertions(+), 69 deletions(-) diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 23a95a4ab..bdb0e23de 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -1,12 +1,80 @@ #include "cellref.hpp" +#include "esmreader.hpp" #include "esmwriter.hpp" +void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) +{ + // NAM0 sometimes appears here, sometimes further on + mNam0 = 0; + if (esm.isNextSub ("NAM0")) + esm.getHT (mNam0); + + if (wideRefNum) + esm.getHNT (mRefNum, "FRMR", 8); + else + esm.getHNT (mRefNum.mIndex, "FRMR"); + + mRefID = esm.getHNString ("NAME"); + + mScale = 1.0; + esm.getHNOT (mScale, "XSCL"); + + mOwner = esm.getHNOString ("ANAM"); + mGlob = esm.getHNOString ("BNAM"); + mSoul = esm.getHNOString ("XSOL"); + + mFaction = esm.getHNOString ("CNAM"); + mFactIndex = -2; + esm.getHNOT (mFactIndex, "INDX"); + + mGoldValue = 1; + mCharge = -1; + mEnchantmentCharge = -1; + + esm.getHNOT (mEnchantmentCharge, "XCHG"); + + esm.getHNOT (mCharge, "INTV"); + + esm.getHNOT (mGoldValue, "NAM9"); + + // Present for doors that teleport you to another cell. + if (esm.isNextSub ("DODT")) + { + mTeleport = true; + esm.getHT (mDoorDest); + mDestCell = esm.getHNOString ("DNAM"); + } + else + mTeleport = false; + + mLockLevel = -1; + esm.getHNOT (mLockLevel, "FLTV"); + mKey = esm.getHNOString ("KNAM"); + mTrap = esm.getHNOString ("TNAM"); + + mReferenceBlocked = -1; + mFltv = 0; + esm.getHNOT (mReferenceBlocked, "UNAM"); + esm.getHNOT (mFltv, "FLTV"); + + esm.getHNOT(mPos, "DATA", 24); + + // Number of references in the cell? Maximum once in each cell, + // but not always at the beginning, and not always right. In other + // words, completely useless. + // Update: Well, maybe not completely useless. This might actually be + // number_of_references + number_of_references_moved_here_Across_boundaries, + // and could be helpful for collecting these weird moved references. + if (esm.isNextSub ("NAM0")) + esm.getHT (mNam0); +} + void ESM::CellRef::save(ESMWriter &esm) const { esm.writeHNT("FRMR", mRefNum.mIndex); - /// \todo read content file index (if present) + esm.writeHNCString("NAME", mRefID); if (mScale != 1.0) { diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 60c2bc625..3d80a51bd 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -8,6 +8,7 @@ namespace ESM { class ESMWriter; + class ESMReader; /* Cell reference. This represents ONE object (of many) inside the cell. The cell references are not loaded as part of the normal @@ -86,6 +87,8 @@ namespace ESM // Position and rotation of this object within the cell Position mPos; + void load (ESMReader& esm, bool wideRefNum = false); + void save(ESMWriter &esm) const; void blank(); diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 1fe92ffb1..efd6979b4 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -177,78 +177,11 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted) // That should be it, I haven't seen any other fields yet. } - // NAM0 sometimes appears here, sometimes further on - ref.mNam0 = 0; - if (esm.isNextSub("NAM0")) - { - esm.getHT(ref.mNam0); - //esm.getHNOT(NAM0, "NAM0"); - } - - esm.getHNT (ref.mRefNum.mIndex, "FRMR"); - ref.mRefID = esm.getHNString("NAME"); + ref.load (esm); // Identify references belonging to a parent file and adapt the ID accordingly. adjustRefNum (ref.mRefNum, esm); - // getHNOT will not change the existing value if the subrecord is - // missing - ref.mScale = 1.0; - esm.getHNOT(ref.mScale, "XSCL"); - - ref.mOwner = esm.getHNOString("ANAM"); - ref.mGlob = esm.getHNOString("BNAM"); - ref.mSoul = esm.getHNOString("XSOL"); - - ref.mFaction = esm.getHNOString("CNAM"); - ref.mFactIndex = -2; - esm.getHNOT(ref.mFactIndex, "INDX"); - - ref.mGoldValue = 1; - ref.mCharge = -1; - ref.mEnchantmentCharge = -1; - - esm.getHNOT(ref.mEnchantmentCharge, "XCHG"); - - esm.getHNOT(ref.mCharge, "INTV"); - - esm.getHNOT(ref.mGoldValue, "NAM9"); - - // Present for doors that teleport you to another cell. - if (esm.isNextSub("DODT")) - { - ref.mTeleport = true; - esm.getHT(ref.mDoorDest); - ref.mDestCell = esm.getHNOString("DNAM"); - } else { - ref.mTeleport = false; - } - - // Integer, despite the name suggesting otherwise - ref.mLockLevel = -1; - esm.getHNOT(ref.mLockLevel, "FLTV"); - ref.mKey = esm.getHNOString("KNAM"); - ref.mTrap = esm.getHNOString("TNAM"); - - ref.mReferenceBlocked = -1; - ref.mFltv = 0; - esm.getHNOT(ref.mReferenceBlocked, "UNAM"); - esm.getHNOT(ref.mFltv, "FLTV"); - - esm.getHNOT(ref.mPos, "DATA", 24); - - // Number of references in the cell? Maximum once in each cell, - // but not always at the beginning, and not always right. In other - // words, completely useless. - // Update: Well, maybe not completely useless. This might actually be - // number_of_references + number_of_references_moved_here_Across_boundaries, - // and could be helpful for collecting these weird moved references. - if (esm.isNextSub("NAM0")) - { - esm.getHT(ref.mNam0); - //esm.getHNOT(NAM0, "NAM0"); - } - if (esm.isNextSub("DELE")) { esm.skipHSub(); From 5d4e148063c6e5480d571355872d5fb410f48074 Mon Sep 17 00:00:00 2001 From: gus Date: Sun, 12 Jan 2014 22:47:22 +0100 Subject: [PATCH 487/889] some clean up --- apps/openmw/mwmechanics/aicombat.cpp | 9 --------- apps/openmw/mwmechanics/aiescort.cpp | 21 +++++++-------------- apps/openmw/mwmechanics/aiescort.hpp | 4 ++-- apps/openmw/mwmechanics/aifollow.cpp | 10 +--------- apps/openmw/mwmechanics/pathfinding.cpp | 2 +- 5 files changed, 11 insertions(+), 35 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 19c3fa2f3..b9dc2f7db 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -50,15 +50,6 @@ namespace MWMechanics } ESM::Position pos = actor.getRefData().getPosition(); - float xCell = 0; - float yCell = 0; - - if (actor.getCell()->mCell->isExterior()) - { - xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; - yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; - } - ESM::Pathgrid::Point dest; dest.mX = target.getRefData().getPosition().pos[0]; dest.mY = target.getRefData().getPosition().pos[1]; diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 0d44dd6fc..f31d86140 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -28,8 +28,8 @@ namespace MWMechanics { AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z) : mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration) - , cellX(std::numeric_limits::max()) - , cellY(std::numeric_limits::max()) + , mCellX(std::numeric_limits::max()) + , mCellY(std::numeric_limits::max()) { mMaxDist = 470; @@ -47,8 +47,8 @@ namespace MWMechanics AiEscort::AiEscort(const std::string &actorId, const std::string &cellId,int duration, float x, float y, float z) : mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration) - , cellX(std::numeric_limits::max()) - , cellY(std::numeric_limits::max()) + , mCellX(std::numeric_limits::max()) + , mCellY(std::numeric_limits::max()) { mMaxDist = 470; @@ -84,7 +84,7 @@ namespace MWMechanics MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); ESM::Position pos = actor.getRefData().getPosition(); - bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY; + bool cellChange = actor.getCell()->mCell->mData.mX != mCellX || actor.getCell()->mCell->mData.mY != mCellY; if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX) { @@ -114,15 +114,8 @@ namespace MWMechanics if(!mPathFinder.isPathConstructed() || cellChange) { - cellX = actor.getCell()->mCell->mData.mX; - cellY = actor.getCell()->mCell->mData.mY; - float xCell = 0; - float yCell = 0; - if (actor.getCell()->mCell->isExterior()) - { - xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; - yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; - } + mCellX = actor.getCell()->mCell->mData.mX; + mCellY = actor.getCell()->mCell->mData.mY; ESM::Pathgrid::Point dest; dest.mX = mX; diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index f3f6d2bd9..53b57c058 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -34,8 +34,8 @@ namespace MWMechanics unsigned int mDuration; PathFinder mPathFinder; - int cellX; - int cellY; + int mCellX; + int mCellY; }; } #endif diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index a711ebe7c..898f91e7c 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -82,15 +82,7 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) start.mX = pos.pos[0]; start.mY = pos.pos[1]; start.mZ = pos.pos[2]; - - float xCell = 0; - float yCell = 0; - - if (actor.getCell()->mCell->isExterior()) - { - xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; - yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; - } + mPathFinder.buildPath(start, dest, actor.getCell(), true); } mStuckTimer = 0; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 86019fe54..f8908fbd9 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -106,7 +106,7 @@ namespace return graph; } - std::list findPath(PointID start, PointID end, PathGridGraph graph) + std::list findPath(PointID start, PointID end,const PathGridGraph& graph) { std::vector p(boost::num_vertices(graph)); std::vector d(boost::num_vertices(graph)); From 7983b07b106e6898c14f5277326d88b64384394b Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Jan 2014 06:15:22 +0100 Subject: [PATCH 488/889] Get bk_treasuryreport script to work properly: - OnPcEquip needs to be set on *using* any item, not just equipping - Handle PcSkipEquip - Execute item's script once immediately after setting OnPcEquip - Do not set OnPcEquip when an item that has skipped equipping sets pcskipequip back to 0 and gets equipped --- apps/openmw/mwgui/inventorywindow.cpp | 60 ++++++++++++++++++++------- apps/openmw/mwgui/inventorywindow.hpp | 4 ++ apps/openmw/mwgui/quickkeysmenu.cpp | 14 +------ apps/openmw/mwworld/actionequip.cpp | 6 --- 4 files changed, 50 insertions(+), 34 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 7781c8526..7139c1b2c 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -13,6 +13,8 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/action.hpp" +#include "../mwscript/interpretercontext.hpp" +#include "../mwbase/scriptmanager.hpp" #include "bookwindow.hpp" #include "scrollwindow.hpp" @@ -351,6 +353,48 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->setWeaponVisibility(!mPinned); } + void InventoryWindow::useItem(const MWWorld::Ptr &ptr) + { + const std::string& script = ptr.getClass().getScript(ptr); + + // If the item has a script, set its OnPcEquip to 1 + if (!script.empty() + // Another morrowind oddity: when an item has skipped equipping and pcskipequip is reset to 0 afterwards, + // the next time it is equipped will work normally, but will not set onpcequip + && (ptr != mSkippedToEquip || ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 1)) + ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1); + + // Give the script a chance to run once before we do anything else + // this is important when setting pcskipequip as a reaction to onpcequip being set (bk_treasuryreport does this) + if (!script.empty()) + { + MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr); + MWBase::Environment::get().getScriptManager()->run (script, interpreterContext); + } + + if (script.empty() || ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 0) + { + boost::shared_ptr action = MWWorld::Class::get(ptr).use(ptr); + + action->execute (MWBase::Environment::get().getWorld()->getPlayerPtr()); + + // this is necessary for books/scrolls: if they are already in the player's inventory, + // the "Take" button should not be visible. + // NOTE: the take button is "reset" when the window opens, so we can safely do the following + // without screwing up future book windows + MWBase::Environment::get().getWindowManager()->getBookWindow()->setTakeButtonShow(false); + MWBase::Environment::get().getWindowManager()->getScrollWindow()->setTakeButtonShow(false); + + mSkippedToEquip = MWWorld::Ptr(); + } + else + mSkippedToEquip = ptr; + + mItemView->update(); + + notifyContentChanged(); + } + void InventoryWindow::onAvatarClicked(MyGUI::Widget* _sender) { if (mDragAndDrop->mIsOnDragAndDrop) @@ -369,21 +413,7 @@ namespace MWGui mDragAndDrop->mSourceModel->removeItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount); ptr = *it; } - - boost::shared_ptr action = MWWorld::Class::get(ptr).use(ptr); - - action->execute (MWBase::Environment::get().getWorld()->getPlayerPtr()); - - // this is necessary for books/scrolls: if they are already in the player's inventory, - // the "Take" button should not be visible. - // NOTE: the take button is "reset" when the window opens, so we can safely do the following - // without screwing up future book windows - MWBase::Environment::get().getWindowManager()->getBookWindow()->setTakeButtonShow(false); - MWBase::Environment::get().getWindowManager()->getScrollWindow()->setTakeButtonShow(false); - - mItemView->update(); - - notifyContentChanged(); + useItem(ptr); } else { diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 112e737fa..7e5a0fe10 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -46,6 +46,8 @@ namespace MWGui void updatePlayer(); + void useItem(const MWWorld::Ptr& ptr); + void setGuiMode(GuiMode mode); private: @@ -74,6 +76,8 @@ namespace MWGui MyGUI::Button* mFilterMagic; MyGUI::Button* mFilterMisc; + MWWorld::Ptr mSkippedToEquip; + GuiMode mGuiMode; int mLastXSize; diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 77127f59b..61e414fc4 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -308,19 +308,7 @@ namespace MWGui { MWWorld::Ptr item = *button->getChildAt (0)->getUserData(); - boost::shared_ptr action = MWWorld::Class::get(item).use(item); - - action->execute (MWBase::Environment::get().getWorld()->getPlayerPtr()); - - // this is necessary for books/scrolls: if they are already in the player's inventory, - // the "Take" button should not be visible. - // NOTE: the take button is "reset" when the window opens, so we can safely do the following - // without screwing up future book windows - MWBase::Environment::get().getWindowManager()->getBookWindow()->setTakeButtonShow(false); - MWBase::Environment::get().getWindowManager()->getScrollWindow()->setTakeButtonShow(false); - - // since we changed equipping status, update the inventory window - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->useItem(item); } else if (type == Type_MagicItem) { diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index 6a0b4eec7..348b09ad9 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -80,11 +80,5 @@ namespace MWWorld break; } } - - std::string script = MWWorld::Class::get(object).getScript(object); - - /* Set OnPCEquip Variable on item's script, if the player is equipping it, and it has a script with that variable declared */ - if(equipped && actor == MWBase::Environment::get().getWorld()->getPlayerPtr() && script != "") - object.getRefData().getLocals().setVarByInt(script, "onpcequip", 1); } } From 63cd70f810ea12f1f9ba16141b6e36921bcf33a0 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 13 Jan 2014 10:03:25 +0100 Subject: [PATCH 489/889] some junk removal --- apps/openmw/mwworld/actionequip.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index 348b09ad9..d34773bd5 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -54,8 +54,6 @@ namespace MWWorld assert(it != invStore.end()); - bool equipped = false; - // equip the item in the first free slot for (std::vector::const_iterator slot=slots_.first.begin(); slot!=slots_.first.end(); ++slot) @@ -68,7 +66,6 @@ namespace MWWorld if (slot == --slots_.first.end()) { invStore.equip(*slot, it, actor); - equipped = true; break; } @@ -76,7 +73,6 @@ namespace MWWorld { // slot is not occupied invStore.equip(*slot, it, actor); - equipped = true; break; } } From 83872f6bf58f143b2bd97d833b5f0f870cc72766 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Jan 2014 01:42:19 +0100 Subject: [PATCH 490/889] Knockdown / hit recovery improvements. Use formula and GMSTs from research wiki for knockdown determination. Hand-to-hand automatically knocks out when fatigue empty. --- apps/openmw/mwclass/npc.cpp | 30 +++++++++++++- apps/openmw/mwclass/npc.hpp | 3 ++ apps/openmw/mwmechanics/character.cpp | 48 +++++++++++------------ apps/openmw/mwmechanics/creaturestats.cpp | 22 ++++++++++- apps/openmw/mwmechanics/creaturestats.hpp | 9 ++++- 5 files changed, 85 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4aa27e38a..e08a3c07e 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -242,6 +242,9 @@ namespace MWClass fJumpAcroMultiplier = gmst.find("fJumpAcroMultiplier"); fJumpRunMultiplier = gmst.find("fJumpRunMultiplier"); fWereWolfRunMult = gmst.find("fWereWolfRunMult"); + fKnockDownMult = gmst.find("fKnockDownMult"); + iKnockDownOddsMult = gmst.find("iKnockDownOddsMult"); + iKnockDownOddsBase = gmst.find("iKnockDownOddsBase"); inited = true; } @@ -651,7 +654,20 @@ namespace MWClass { MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); } - getCreatureStats(ptr).setAttacked(true);//used in CharacterController + getCreatureStats(ptr).setAttacked(true); + + // Check for knockdown + float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat(); + float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() + * iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt(); + roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (ishealth && agilityTerm <= damage && knockdownTerm <= roll) + { + getCreatureStats(ptr).setKnockedDown(true); + + } + else + getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? if(object.isEmpty()) { @@ -726,6 +742,15 @@ namespace MWClass fatigue.setCurrent(fatigue.getCurrent() - damage, true); getCreatureStats(ptr).setFatigue(fatigue); } + + if (object.isEmpty()) + { + // Hand-to-hand automatically knocks down when running out of fatigue + if (getCreatureStats(ptr).getFatigue().getCurrent() < 0) + { + getCreatureStats(ptr).setKnockedDown(true); + } + } } void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const @@ -1286,4 +1311,7 @@ namespace MWClass const ESM::GameSetting *Npc::fJumpAcroMultiplier; const ESM::GameSetting *Npc::fJumpRunMultiplier; const ESM::GameSetting *Npc::fWereWolfRunMult; + const ESM::GameSetting *Npc::fKnockDownMult; + const ESM::GameSetting *Npc::iKnockDownOddsMult; + const ESM::GameSetting *Npc::iKnockDownOddsBase; } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index c39ca42ef..22a9632e8 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -33,6 +33,9 @@ namespace MWClass static const ESM::GameSetting *fJumpAcroMultiplier; static const ESM::GameSetting *fJumpRunMultiplier; static const ESM::GameSetting *fWereWolfRunMult; + static const ESM::GameSetting *fKnockDownMult; + static const ESM::GameSetting *iKnockDownOddsMult; + static const ESM::GameSetting *iKnockDownOddsBase; public: diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 86785ec22..07859d57c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -157,40 +157,40 @@ public: void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force) { - //hit recoils/knockdown animations handling - if(MWWorld::Class::get(mPtr).isActor()) + // hit recoils/knockdown animations handling + if(mPtr.getClass().isActor()) { - if(MWWorld::Class::get(mPtr).getCreatureStats(mPtr).getAttacked()) + bool recovery = mPtr.getClass().getCreatureStats(mPtr).getHitRecovery(); + bool knockdown = mPtr.getClass().getCreatureStats(mPtr).getKnockedDown(); + if(mHitState == CharState_None) { - MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setAttacked(false); - - if(mHitState == CharState_None) + if(knockdown) { - if(mJumpState != JumpState_None && !MWBase::Environment::get().getWorld()->isFlying(mPtr) - && !MWBase::Environment::get().getWorld()->isSwimming(mPtr) ) + mHitState = CharState_KnockDown; + mCurrentHit = sHitList[sHitListSize-1]; + mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); + } + else if (recovery) + { + mHitState = CharState_Hit; + int iHit = rand() % (sHitListSize-1); + mCurrentHit = sHitList[iHit]; + if(mPtr.getRefData().getHandle()=="player" && !mAnimation->hasAnimation(mCurrentHit)) { - mHitState = CharState_KnockDown; - mCurrentHit = sHitList[sHitListSize-1]; - mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); - } - else - { - mHitState = CharState_Hit; - int iHit = rand() % (sHitListSize-1); + //only 3 different hit animations if player is in 1st person + int iHit = rand() % (sHitListSize-3); mCurrentHit = sHitList[iHit]; - if(mPtr.getRefData().getHandle()=="player" && !mAnimation->hasAnimation(mCurrentHit)) - { - //only 3 different hit animations if player is in 1st person - int iHit = rand() % (sHitListSize-3); - mCurrentHit = sHitList[iHit]; - } - mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } + mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } } - else if(mHitState != CharState_None && !mAnimation->isPlaying(mCurrentHit)) + else if(!mAnimation->isPlaying(mCurrentHit)) { mCurrentHit.erase(); + if (knockdown) + mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(false); + if (recovery) + mPtr.getClass().getCreatureStats(mPtr).setHitRecovery(false); mHitState = CharState_None; } } diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index b5b9b7156..aadce499e 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -15,7 +15,7 @@ namespace MWMechanics mAttacked (false), mHostile (false), mAttackingOrSpell(false), mAttackType(AT_Chop), mIsWerewolf(false), - mFallHeight(0), mRecalcDynamicStats(false) + mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false) { for (int i=0; i<4; ++i) mAiSettings[i] = 0; @@ -402,4 +402,24 @@ namespace MWMechanics } return false; } + + void CreatureStats::setKnockedDown(bool value) + { + mKnockdown = value; + } + + bool CreatureStats::getKnockedDown() const + { + return mKnockdown; + } + + void CreatureStats::setHitRecovery(bool value) + { + mHitRecovery = value; + } + + bool CreatureStats::getHitRecovery() const + { + return mHitRecovery; + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 6e0804638..1f82a9b5c 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -34,7 +34,9 @@ namespace MWMechanics bool mAlarmed; bool mAttacked; bool mHostile; - bool mAttackingOrSpell;//for the player, this is true if the left mouse button is pressed, false if not. + bool mAttackingOrSpell; + bool mKnockdown; + bool mHitRecovery; float mFallHeight; @@ -186,6 +188,11 @@ namespace MWMechanics float getEvasion() const; + void setKnockedDown(bool value); + bool getKnockedDown() const; + void setHitRecovery(bool value); + bool getHitRecovery() const; + void setLastHitObject(const std::string &objectid); const std::string &getLastHitObject() const; From 3a1b6dd35455d0f0ee33bbfa76493306ee997287 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Jan 2014 01:47:10 +0100 Subject: [PATCH 491/889] Handle fCombatKODamageMult and fCombatCriticalStrikeMult. Fix SelectWrapper Function_Detected. --- apps/openmw/mwclass/npc.cpp | 22 ++++++++++------------ apps/openmw/mwdialogue/filter.cpp | 2 +- apps/openmw/mwworld/class.cpp | 5 ----- apps/openmw/mwworld/class.hpp | 5 ----- 4 files changed, 11 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index e08a3c07e..19561e53d 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -518,12 +518,6 @@ namespace MWClass weapon.getCellRef().mCharge = weapmaxhealth; damage *= float(weapon.getCellRef().mCharge) / weapmaxhealth; } - if(!othercls.hasDetected(victim, ptr)) - { - damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); - MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); - MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); - } if (!MWBase::Environment::get().getWorld()->getGodModeState()) weapon.getCellRef().mCharge -= std::min(std::max(1, @@ -545,12 +539,6 @@ namespace MWClass float maxstrike = gmst.find("fMaxHandToHandMult")->getFloat(); damage = stats.getSkill(weapskill).getModified(); damage *= minstrike + ((maxstrike-minstrike)*stats.getAttackStrength()); - if(!othercls.hasDetected(victim, ptr)) - { - damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); - MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); - MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); - } healthdmg = (otherstats.getFatigue().getCurrent() < 1.0f) || (otherstats.getMagicEffects().get(ESM::MagicEffect::Paralyze).mMagnitude > 0); @@ -577,6 +565,16 @@ namespace MWClass if(ptr.getRefData().getHandle() == "player") skillUsageSucceeded(ptr, weapskill, 0); + bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim); + if(!detected) + { + damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); + MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); + MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); + } + if (othercls.getCreatureStats(victim).getKnockedDown()) + damage *= gmst.find("fCombatKODamageMult")->getFloat(); + // Apply "On hit" enchanted weapons std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : ""; if (!enchantmentName.empty()) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 4f478afce..d7b7df983 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -525,7 +525,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co case SelectWrapper::Function_Detected: - return MWWorld::Class::get (mActor).hasDetected (mActor, player); + return MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, mActor); case SelectWrapper::Function_Attacked: diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 0119cdea5..934dae015 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -232,11 +232,6 @@ namespace MWWorld return false; } - bool Class::hasDetected (const MWWorld::Ptr& ptr, const MWWorld::Ptr& ptr2) const - { - return true; - } - float Class::getArmorRating (const MWWorld::Ptr& ptr) const { throw std::runtime_error("Class does not support armor rating"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 08d769fbe..c0b010eae 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -240,11 +240,6 @@ namespace MWWorld /// /// (default implementation: return false) - virtual bool hasDetected (const MWWorld::Ptr& ptr, const MWWorld::Ptr& ptr2) const; - ///< Has \æ ptr detected \a ptr2? - /// - /// (default implementation: return false) - virtual std::string getUpSoundId (const Ptr& ptr) const; ///< Return the up sound ID of \a ptr or throw an exception, if class does not support ID retrieval /// (default implementation: throw an exception) From 413bf127de15af153a28849bbefa7d57f4c62a6d Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Jan 2014 02:54:54 +0100 Subject: [PATCH 492/889] Allow drain fatigue effect to reduce below zero --- apps/openmw/mwmechanics/actors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 6f710988e..e2255f8cb 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -334,7 +334,7 @@ namespace MWMechanics float currentDiff = creatureStats.getMagicEffects().get(ESM::MagicEffect::RestoreHealth+i).mMagnitude - creatureStats.getMagicEffects().get(ESM::MagicEffect::DamageHealth+i).mMagnitude - creatureStats.getMagicEffects().get(ESM::MagicEffect::AbsorbHealth+i).mMagnitude; - stat.setCurrent(stat.getCurrent() + currentDiff * duration); + stat.setCurrent(stat.getCurrent() + currentDiff * duration, i == 2); creatureStats.setDynamic(i, stat); } From cd06b2177dc1344acedd51adcdc22bce0f512110 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Jan 2014 02:55:18 +0100 Subject: [PATCH 493/889] Automatically knock down when fatigue goes below zero --- apps/openmw/mwclass/npc.cpp | 9 --------- apps/openmw/mwmechanics/creaturestats.cpp | 3 +++ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 19561e53d..a7ff92cf5 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -740,15 +740,6 @@ namespace MWClass fatigue.setCurrent(fatigue.getCurrent() - damage, true); getCreatureStats(ptr).setFatigue(fatigue); } - - if (object.isEmpty()) - { - // Hand-to-hand automatically knocks down when running out of fatigue - if (getCreatureStats(ptr).getFatigue().getCurrent() < 0) - { - getCreatureStats(ptr).setKnockedDown(true); - } - } } void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index aadce499e..ba6f0ba04 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -207,6 +207,9 @@ namespace MWMechanics mDynamic[index] = value; + if (index == 2 && value.getCurrent() < 0) + setKnockedDown(true); + if (index==0 && mDynamic[index].getCurrent()<1) { if (!mDead) From 73268a8606df642fe360e028b491eccebdc2f608 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Jan 2014 07:05:52 +0100 Subject: [PATCH 494/889] Fix skill progress not working --- apps/openmw/mwgui/statswindow.cpp | 2 +- apps/openmw/mwmechanics/npcstats.cpp | 2 +- apps/openmw/mwmechanics/stat.hpp | 12 ++++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 17bb24e83..37128c43f 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -185,7 +185,7 @@ namespace MWGui MyGUI::TextBox* widget = mSkillWidgetMap[(int)parSkill]; if (widget) { - float modified = value.getModified(), base = value.getBase(); + int modified = value.getModified(), base = value.getBase(); std::string text = boost::lexical_cast(std::floor(modified)); std::string state = "normal"; if (modified > base) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 289fcc70f..f77b04271 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -207,7 +207,7 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, if(mIsWerewolf) return; - MWMechanics::SkillValue value = getSkill (skillIndex); + MWMechanics::SkillValue& value = getSkill (skillIndex); value.setProgress(value.getProgress() + getSkillGain (skillIndex, class_, usageType)); diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index e66cf86de..75ac6939a 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -246,6 +246,18 @@ namespace MWMechanics { return !(left == right); } + + inline bool operator== (const SkillValue& left, const SkillValue& right) + { + return left.getBase() == right.getBase() + && left.getModifier() == right.getModifier() + && left.getDamage() == right.getDamage() + && left.getProgress() == right.getProgress(); + } + inline bool operator!= (const SkillValue& left, const SkillValue& right) + { + return !(left == right); + } } #endif From ba27b693f80091241f4979ef6876883afa69912b Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Jan 2014 07:27:08 +0100 Subject: [PATCH 495/889] Increase sneak skill on successful pickpocket --- apps/openmw/mwgui/container.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index d22842b57..858378c03 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -349,6 +349,8 @@ namespace MWGui mPickpocketDetected = true; return false; } + else + player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, 1); } else { From 17cc6a695cb2adbbb2cd2c6e41a0651d7bd87c11 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 13 Jan 2014 18:34:28 +0100 Subject: [PATCH 496/889] fixed bug resposnsible for exception throwed. --- apps/opencs/model/tools/referenceablecheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 36878d44b..d5064d272 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -39,7 +39,7 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str stage -= activatorSize; - const int potionSize(mReferencables.getActivators().getSize()); + const int potionSize(mReferencables.getPotions().getSize()); if (stage < potionSize) { From 59de794e58023edeaf218b739bb578fe80cd8150 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 13 Jan 2014 19:02:23 +0100 Subject: [PATCH 497/889] Creature check was not invoked. --- apps/opencs/model/tools/referenceablecheck.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index d5064d272..f89c641d4 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -206,7 +206,16 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str staticCheck(stage, mReferencables.getStatics(), messages); return; } + + stage -= staticSize; + const int creatureSize(mReferencables.getCreatures().getSize()); + + if (stage < creatureSize) + { + creatureCheck(stage, mReferencables.getCreatures(), messages); + return; + } // if we come that far, we are about to perform our last, final check. finalCheck(messages); return; @@ -215,7 +224,7 @@ void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::str int CSMTools::ReferenceableCheckStage::setup() { mPlayerPresent = false; - return mReferencables.getSize() + 2; //DANGER, final check is not performed if it is just +1 + return mReferencables.getSize() + 1; } void CSMTools::ReferenceableCheckStage::bookCheck( From e34cb9e93196745519aa2e5340a04a698d1af680 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 13 Jan 2014 19:17:03 +0100 Subject: [PATCH 498/889] changed according to the scrawl sugestion --- apps/opencs/model/tools/referenceablecheck.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index f89c641d4..3be594d3e 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -2,6 +2,7 @@ #include "../world/record.hpp" #include "../world/universalid.hpp" #include +#include "../../../../openmw/apps/openmw/mwclass/misc.hpp" CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, @@ -660,7 +661,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck( int gold(npc.mNpdt52.mGold); //Detect if player is present - if ( boost::algorithm::iequals(npc.mId, "player") ) + if (Misc::StringUtils::ciEqual(npc.mId, "player")) { mPlayerPresent = true; } From 0d0005c433b5b17ea0647c2c769030e4c21f8084 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 02:20:13 +0100 Subject: [PATCH 499/889] Fix fatigue not restoring when waiting --- apps/openmw/mwbase/mechanicsmanager.hpp | 5 +++-- apps/openmw/mwgui/trainingwindow.cpp | 2 ++ apps/openmw/mwgui/travelwindow.cpp | 2 +- apps/openmw/mwgui/waitdialog.cpp | 3 +-- apps/openmw/mwmechanics/actors.cpp | 12 +++++------- apps/openmw/mwmechanics/actors.hpp | 4 ++-- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 4 ++-- apps/openmw/mwmechanics/mechanicsmanagerimp.hpp | 5 +++-- 8 files changed, 19 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 85fcfc75b..d1472de38 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -76,8 +76,9 @@ namespace MWBase virtual void setPlayerClass (const ESM::Class& class_) = 0; ///< Set player class to custom class. - virtual void restoreDynamicStats() = 0; - ///< If the player is sleeping, this should be called every hour. + virtual void rest(bool sleep) = 0; + ///< If the player is sleeping or waiting, this should be called every hour. + /// @param sleep is the player sleeping or waiting? virtual int getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying) = 0; ///< This is used by every service to determine the price of objects given the trading skills of the player and NPC. diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 24be5363d..bee76992a 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -154,6 +154,8 @@ namespace MWGui // advance time MWBase::Environment::get().getWorld ()->advanceTime (2); + MWBase::Environment::get().getMechanicsManager()->rest(false); + MWBase::Environment::get().getMechanicsManager()->rest(false); MWBase::Environment::get().getWorld ()->getFader()->fadeOut(0.25); mFadeTimeRemaining = 0.5; diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index dcf54d25a..c314ce1fd 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -148,7 +148,7 @@ namespace MWGui int hours = static_cast(d /MWBase::Environment::get().getWorld()->getStore().get().find("fTravelTimeMult")->getFloat()); for(int i = 0;i < hours;i++) { - MWBase::Environment::get().getMechanicsManager ()->restoreDynamicStats (); + MWBase::Environment::get().getMechanicsManager ()->rest (true); } MWBase::Environment::get().getWorld()->advanceTime(hours); diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index e71ed4247..5058e53ee 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -253,8 +253,7 @@ namespace MWGui if (mCurHour <= mHours) { MWBase::Environment::get().getWorld ()->advanceTime (1); - if (mSleeping) - MWBase::Environment::get().getMechanicsManager ()->restoreDynamicStats (); + MWBase::Environment::get().getMechanicsManager ()->rest (mSleeping); } } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index e2255f8cb..0fc7210b2 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -199,7 +199,7 @@ namespace MWMechanics } // fatigue restoration - calculateRestoration(ptr, duration); + calculateRestoration(ptr, duration, false); } void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused) @@ -257,7 +257,7 @@ namespace MWMechanics creatureStats.setFatigue(fatigue); } - void Actors::calculateRestoration (const MWWorld::Ptr& ptr, float duration) + void Actors::calculateRestoration (const MWWorld::Ptr& ptr, float duration, bool sleep) { if (ptr.getClass().getCreatureStats(ptr).isDead()) return; @@ -272,10 +272,9 @@ namespace MWMechanics if (normalizedEncumbrance > 1) normalizedEncumbrance = 1; - if (duration == 3600) + if (sleep) { // the actor is sleeping, restore health and magicka - bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).mMagnitude > 0; DynamicStat health = stats.getHealth(); @@ -294,7 +293,6 @@ namespace MWMechanics } // restore fatigue - float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); float fEndFatigueMult = settings.find("fEndFatigueMult")->getFloat (); @@ -847,10 +845,10 @@ namespace MWMechanics } } } - void Actors::restoreDynamicStats() + void Actors::restoreDynamicStats(bool sleep) { for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) - calculateRestoration(iter->first, 3600); + calculateRestoration(iter->first, 3600, sleep); } int Actors::countDeaths (const std::string& id) const diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 7046543e6..382aa5400 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -36,7 +36,7 @@ namespace MWMechanics void calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration); void calculateNpcStatModifiers (const MWWorld::Ptr& ptr); - void calculateRestoration (const MWWorld::Ptr& ptr, float duration); + void calculateRestoration (const MWWorld::Ptr& ptr, float duration, bool sleep); void updateDrowning (const MWWorld::Ptr& ptr, float duration); @@ -79,7 +79,7 @@ namespace MWMechanics ///< This function is normally called automatically during the update process, but it can /// also be called explicitly at any time to force an update. - void restoreDynamicStats(); + void restoreDynamicStats(bool sleep); ///< If the player is sleeping, this should be called every hour. int countDeaths (const std::string& id) const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 76ba2ff16..456b3a8f9 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -370,9 +370,9 @@ namespace MWMechanics mObjects.update(duration, paused); } - void MechanicsManager::restoreDynamicStats() + void MechanicsManager::rest(bool sleep) { - mActors.restoreDynamicStats (); + mActors.restoreDynamicStats (sleep); } void MechanicsManager::setPlayerName (const std::string& name) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 569cd2fca..25e556e6a 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -81,8 +81,9 @@ namespace MWMechanics virtual void setPlayerClass (const ESM::Class& class_); ///< Set player class to custom class. - virtual void restoreDynamicStats(); - ///< If the player is sleeping, this should be called every hour. + virtual void rest(bool sleep); + ///< If the player is sleeping or waiting, this should be called every hour. + /// @param sleep is the player sleeping or waiting? virtual int getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying); ///< This is used by every service to determine the price of objects given the trading skills of the player and NPC. From 95651857f3eb90a5c6e0ee1879c7f2154c471508 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 02:52:34 +0100 Subject: [PATCH 500/889] Fix code duplication --- apps/openmw/mwbase/mechanicsmanager.hpp | 3 + apps/openmw/mwgui/waitdialog.cpp | 38 +-------- apps/openmw/mwmechanics/actors.cpp | 77 +++++++++++++------ apps/openmw/mwmechanics/actors.hpp | 3 + .../mwmechanics/mechanicsmanagerimp.cpp | 5 ++ .../mwmechanics/mechanicsmanagerimp.hpp | 3 + 6 files changed, 69 insertions(+), 60 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index d1472de38..726c8cf04 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -80,6 +80,9 @@ namespace MWBase ///< If the player is sleeping or waiting, this should be called every hour. /// @param sleep is the player sleeping or waiting? + virtual int getHoursToRest() const = 0; + ///< Calculate how many hours the player needs to rest in order to be fully healed + virtual int getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying) = 0; ///< This is used by every service to determine the price of objects given the trading skills of the player and NPC. diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 5058e53ee..1ad703790 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -144,43 +144,7 @@ namespace MWGui void WaitDialog::onUntilHealedButtonClicked(MyGUI::Widget* sender) { - // we need to sleep for a specific time, and since that isn't calculated yet, we'll do it here - // I'm making the assumption here that the # of hours rested is calculated when rest is started - // TODO: the rougher logic here (calculating the hourly deltas) should really go into helper funcs elsewhere - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - const MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); - const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - - float hourlyHealthDelta = stats.getAttribute(ESM::Attribute::Endurance).getModified() * 0.1; - - bool stunted = (stats.getMagicEffects().get(ESM::MagicEffect::StuntedMagicka).mMagnitude > 0); - float fRestMagicMult = store.get().find("fRestMagicMult")->getFloat(); - float hourlyMagickaDelta = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified(); - - // this massive duplication is why it has to be put into helper functions instead - float fFatigueReturnBase = store.get().find("fFatigueReturnBase")->getFloat(); - float fFatigueReturnMult = store.get().find("fFatigueReturnMult")->getFloat(); - float fEndFatigueMult = store.get().find("fEndFatigueMult")->getFloat(); - float capacity = MWWorld::Class::get(player).getCapacity(player); - float encumbrance = MWWorld::Class::get(player).getEncumbrance(player); - float normalizedEncumbrance = (capacity == 0 ? 1 : encumbrance/capacity); - if (normalizedEncumbrance > 1) - normalizedEncumbrance = 1; - float hourlyFatigueDelta = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); - hourlyFatigueDelta *= 3600 * fEndFatigueMult * stats.getAttribute(ESM::Attribute::Endurance).getModified(); - - float healthHours = hourlyHealthDelta >= 0.0 - ? (stats.getHealth().getBase() - stats.getHealth().getCurrent()) / hourlyHealthDelta - : 1.0f; - float magickaHours = stunted ? 0.0 : - hourlyMagickaDelta >= 0.0 - ? (stats.getMagicka().getBase() - stats.getMagicka().getCurrent()) / hourlyMagickaDelta - : 1.0f; - float fatigueHours = hourlyFatigueDelta >= 0.0 - ? (stats.getFatigue().getBase() - stats.getFatigue().getCurrent()) / hourlyFatigueDelta - : 1.0f; - - int autoHours = int(std::ceil( std::max(std::max(healthHours, magickaHours), std::max(fatigueHours, 1.0f)) )); // this should use a variadic max if possible + int autoHours = MWBase::Environment::get().getMechanicsManager()->getHoursToRest(); startWaiting(autoHours); } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0fc7210b2..a0be8357d 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -82,6 +82,23 @@ bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate) return false; } +void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float& magicka) +{ + MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); + const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); + + bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).mMagnitude > 0; + int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + + health = 0.1 * endurance; + + magicka = 0; + if (!stunted) + { + float fRestMagicMult = settings.find("fRestMagicMult")->getFloat (); + magicka = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified(); + } +} } @@ -261,37 +278,32 @@ namespace MWMechanics { if (ptr.getClass().getCreatureStats(ptr).isDead()) return; - CreatureStats& stats = MWWorld::Class::get (ptr).getCreatureStats (ptr); + + MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); - int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); - - float capacity = MWWorld::Class::get(ptr).getCapacity(ptr); - float encumbrance = MWWorld::Class::get(ptr).getEncumbrance(ptr); - float normalizedEncumbrance = (capacity == 0 ? 1 : encumbrance/capacity); - if (normalizedEncumbrance > 1) - normalizedEncumbrance = 1; - if (sleep) { - // the actor is sleeping, restore health and magicka - bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).mMagnitude > 0; + float health, magicka; + getRestorationPerHourOfSleep(ptr, health, magicka); - DynamicStat health = stats.getHealth(); - health.setCurrent (health.getCurrent() + 0.1 * endurance); - stats.setHealth (health); + DynamicStat stat = stats.getHealth(); + stat.setCurrent(stat.getCurrent() + health); + stats.setHealth(stat); - if (!stunted) - { - float fRestMagicMult = settings.find("fRestMagicMult")->getFloat (); - - DynamicStat magicka = stats.getMagicka(); - magicka.setCurrent (magicka.getCurrent() - + fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified()); - stats.setMagicka (magicka); - } + stat = stats.getMagicka(); + stat.setCurrent(stat.getCurrent() + magicka); + stats.setMagicka(stat); } + int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + + float capacity = ptr.getClass().getCapacity(ptr); + float encumbrance = ptr.getClass().getEncumbrance(ptr); + float normalizedEncumbrance = (capacity == 0 ? 1 : encumbrance/capacity); + if (normalizedEncumbrance > 1) + normalizedEncumbrance = 1; + // restore fatigue float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); @@ -303,6 +315,7 @@ namespace MWMechanics DynamicStat fatigue = stats.getFatigue(); fatigue.setCurrent (fatigue.getCurrent() + duration * x); stats.setFatigue (fatigue); + } void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration) @@ -851,6 +864,24 @@ namespace MWMechanics calculateRestoration(iter->first, 3600, sleep); } + int Actors::getHoursToRest(const MWWorld::Ptr &ptr) const + { + float healthPerHour, magickaPerHour; + getRestorationPerHourOfSleep(ptr, healthPerHour, magickaPerHour); + + CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); + + float healthHours = healthPerHour >= 0 + ? (stats.getHealth().getModified() - stats.getHealth().getCurrent()) / healthPerHour + : 1.0f; + float magickaHours = magickaPerHour >= 0 + ? (stats.getMagicka().getModified() - stats.getMagicka().getCurrent()) / magickaPerHour + : 1.0f; + + int autoHours = std::ceil(std::max(1.f, std::max(healthHours, magickaHours))); + return autoHours; + } + int Actors::countDeaths (const std::string& id) const { std::map::const_iterator iter = mDeathCount.find(id); diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 382aa5400..b7544dad4 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -81,6 +81,9 @@ namespace MWMechanics void restoreDynamicStats(bool sleep); ///< If the player is sleeping, this should be called every hour. + + int getHoursToRest(const MWWorld::Ptr& ptr) const; + ///< Calculate how many hours the given actor needs to rest in order to be fully healed int countDeaths (const std::string& id) const; ///< Return the number of deaths for actors with the given ID. diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 456b3a8f9..d6dd73bd0 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -375,6 +375,11 @@ namespace MWMechanics mActors.restoreDynamicStats (sleep); } + int MechanicsManager::getHoursToRest() const + { + return mActors.getHoursToRest(mWatched); + } + void MechanicsManager::setPlayerName (const std::string& name) { MWBase::World *world = MWBase::Environment::get().getWorld(); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 25e556e6a..469123df9 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -85,6 +85,9 @@ namespace MWMechanics ///< If the player is sleeping or waiting, this should be called every hour. /// @param sleep is the player sleeping or waiting? + virtual int getHoursToRest() const; + ///< Calculate how many hours the player needs to rest in order to be fully healed + virtual int getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying); ///< This is used by every service to determine the price of objects given the trading skills of the player and NPC. From 2196ce427aa7b2eb0d40cae7ccf82c060f5b7103 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 03:08:37 +0100 Subject: [PATCH 501/889] Closes #556: Link movie volume to 'master' volume slider, instead of 'music'. --- apps/openmw/mwsound/soundmanagerimp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 28a3aae37..a7ee96831 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -156,11 +156,12 @@ namespace MWSound volume *= mFootstepsVolume; break; case Play_TypeMusic: - case Play_TypeMovie: volume *= mMusicVolume; break; case Play_TypeMask: break; + default: + break; } return volume; } From 396efd580b74dadb34fe3b5156e3d79d3949321e Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 03:26:56 +0100 Subject: [PATCH 502/889] Fix a leftover of the old coordinate system --- apps/openmw/mwsound/openal_output.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 4ee754b35..9dc0b8c5d 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -403,7 +403,7 @@ void OpenAL_SoundStream::update() alSourcef(mSource, AL_GAIN, gain); alSourcef(mSource, AL_PITCH, pitch); - alSource3f(mSource, AL_POSITION, mPos[0], mPos[2], -mPos[1]); + alSource3f(mSource, AL_POSITION, mPos[0], mPos[1], mPos[2]); alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f); alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f); throwALerror(); From 9de3abcb5f8cd5a5f7486cdf73b5729fc870761d Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 03:34:11 +0100 Subject: [PATCH 503/889] Closes #1105: Do not reduce magicka if unable to cast --- apps/openmw/mwworld/worldimp.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 620058c65..f9899c3a2 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2075,8 +2075,11 @@ namespace MWWorld } // Reduce mana - magicka.setCurrent(magicka.getCurrent() - spell->mData.mCost); - stats.setMagicka(magicka); + if (!fail) + { + magicka.setCurrent(magicka.getCurrent() - spell->mData.mCost); + stats.setMagicka(magicka); + } } if (isPlayer && fail) From 6aa56354c0eaba9f7c6a2aa4a892cc8d65f8832e Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 05:24:58 +0100 Subject: [PATCH 504/889] Revert "Bug #991: Don't autoequip items with harmful permanent enchantments" This is no longer needed, since merchants no longer equip items sold to them (2f35e5a04ef828d4e99e28e0be74b175c766d13d). Also, items with harmful enchantments that are initially in the NPCs inventory *must* be equipped (e.g. slave bracers) This reverts commit 71d9755ef167a25ea3ce8098325b44e0811b6bf8. --- apps/openmw/mwworld/inventorystore.cpp | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 3e81da212..e8938b2c0 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -195,29 +195,6 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) std::pair, bool> itemsSlots = MWWorld::Class::get (*iter).getEquipmentSlots (*iter); - // Skip items that have *only* harmful permanent effects - if (!test.getClass().getEnchantment(test).empty()) - { - const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - const ESM::Enchantment* enchantment = store.get().find(test.getClass().getEnchantment(test)); - bool harmfulEffect = false; - bool usefulEffect = false; - if (enchantment->mData.mType == ESM::Enchantment::ConstantEffect) - { - for (std::vector::const_iterator it = enchantment->mEffects.mList.begin(); - it != enchantment->mEffects.mList.end(); ++it) - { - const ESM::MagicEffect* effect = store.get().find(it->mEffectID); - if (effect->mData.mFlags & ESM::MagicEffect::Harmful) - harmfulEffect = true; - else - usefulEffect = true; - } - } - if (harmfulEffect && !usefulEffect) - continue; - } - for (std::vector::const_iterator iter2 (itemsSlots.first.begin()); iter2!=itemsSlots.first.end(); ++iter2) { From 90b92a8f41a95cde1bf239052fe4a61e63dfc9e9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 05:37:06 +0100 Subject: [PATCH 505/889] Move levelled list code out of ContainerStore --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwmechanics/levelledlist.hpp | 78 +++++++++++++++++++++++ apps/openmw/mwworld/containerstore.cpp | 79 ++++++------------------ apps/openmw/mwworld/containerstore.hpp | 2 +- 4 files changed, 98 insertions(+), 63 deletions(-) create mode 100644 apps/openmw/mwmechanics/levelledlist.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index eb5b71ec3..4da5e2997 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -74,7 +74,7 @@ add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting - disease pickpocket + disease pickpocket levelledlist ) add_openmw_dir (mwbase diff --git a/apps/openmw/mwmechanics/levelledlist.hpp b/apps/openmw/mwmechanics/levelledlist.hpp new file mode 100644 index 000000000..af5f71f51 --- /dev/null +++ b/apps/openmw/mwmechanics/levelledlist.hpp @@ -0,0 +1,78 @@ +#ifndef OPENMW_MECHANICS_LEVELLEDLIST_H +#define OPENMW_MECHANICS_LEVELLEDLIST_H + +#include "../mwworld/ptr.hpp" +#include "../mwworld/manualref.hpp" +#include "../mwworld/class.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + +namespace MWMechanics +{ + + /// @return ID of resulting item, or empty if none + std::string getLevelledItem (const ESM::LeveledListBase* levItem, unsigned char failChance=0) + { + const std::vector& items = levItem->mList; + + const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerLevel = player.getClass().getCreatureStats(player).getLevel(); + + failChance += levItem->mChanceNone; + + float random = static_cast (std::rand()) / RAND_MAX; + if (random < failChance/100.f) + return std::string(); + + std::vector candidates; + int highestLevel = 0; + for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) + { + if (it->mLevel > highestLevel) + highestLevel = it->mLevel; + } + + std::pair highest = std::make_pair(-1, ""); + for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) + { + if (playerLevel >= it->mLevel + && (levItem->mFlags & ESM::LeveledListBase::AllLevels || it->mLevel == highestLevel)) + { + candidates.push_back(it->mId); + if (it->mLevel >= highest.first) + highest = std::make_pair(it->mLevel, it->mId); + } + + } + if (candidates.empty()) + return std::string(); + std::string item = candidates[std::rand()%candidates.size()]; + + // Is this another levelled item or a real item? + try + { + MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), item, 1); + if (ref.getPtr().getTypeName() != typeid(ESM::ItemLevList).name() + && ref.getPtr().getTypeName() != typeid(ESM::CreatureLevList).name()) + { + return item; + } + else + { + if (ref.getPtr().getTypeName() == typeid(ESM::ItemLevList).name()) + return getLevelledItem(ref.getPtr().get()->mBase, failChance); + else + return getLevelledItem(ref.getPtr().get()->mBase, failChance); + } + } + catch (std::logic_error& e) + { + // Vanilla doesn't fail on nonexistent items in levelled lists + std::cerr << "Warning: ignoring nonexistent item '" << item << "'" << std::endl; + return std::string(); + } + } + +} + +#endif diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 744971985..864bb2a0d 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -5,17 +5,11 @@ #include #include -#include - -#include -#include -#include - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/scriptmanager.hpp" #include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/levelledlist.hpp" #include "manualref.hpp" #include "refdata.hpp" @@ -309,72 +303,35 @@ void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std:: } void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, - int count, unsigned char failChance, bool topLevel) + int count, bool topLevel) { count = std::abs(count); /// \todo implement item restocking (indicated by negative count) - try + ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count); + + if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) { - ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count); + const ESM::ItemLevList* levItem = ref.getPtr().get()->mBase; - if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) + if (topLevel && count > 1 && levItem->mFlags & ESM::ItemLevList::Each) { - const ESM::ItemLevList* levItem = ref.getPtr().get()->mBase; - const std::vector& items = levItem->mList; - - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - int playerLevel = player.getClass().getCreatureStats(player).getLevel(); - - failChance += levItem->mChanceNone; - - if (topLevel && count > 1 && levItem->mFlags & ESM::ItemLevList::Each) - { - for (int i=0; i (std::rand()) / RAND_MAX; - if (random >= failChance/100.f) - { - std::vector candidates; - int highestLevel = 0; - for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) - { - if (it->mLevel > highestLevel) - highestLevel = it->mLevel; - } - - std::pair highest = std::make_pair(-1, ""); - for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) - { - if (playerLevel >= it->mLevel - && (levItem->mFlags & ESM::ItemLevList::AllLevels || it->mLevel == highestLevel)) - { - candidates.push_back(it->mId); - if (it->mLevel >= highest.first) - highest = std::make_pair(it->mLevel, it->mId); - } - - } - if (candidates.empty()) - return; - std::string item = candidates[std::rand()%candidates.size()]; - addInitialItem(item, owner, faction, count, failChance, false); - } + for (int i=0; i()->mBase); + if (id.empty()) + return; + addInitialItem(id, owner, faction, count, false); } } - catch (std::logic_error& e) + else { - // Vanilla doesn't fail on nonexistent items in levelled lists - std::cerr << "Warning: ignoring nonexistent item '" << id << "'" << std::endl; - return; + ref.getPtr().getCellRef().mOwner = owner; + ref.getPtr().getCellRef().mFaction = faction; + addImp (ref.getPtr(), count); } } diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 936468f8d..0a1728740 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -54,7 +54,7 @@ namespace MWWorld mutable float mCachedWeight; mutable bool mWeightUpToDate; ContainerStoreIterator addImp (const Ptr& ptr, int count); - void addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, int count, unsigned char failChance=0, bool topLevel=true); + void addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, int count, bool topLevel=true); public: From 26d972280f7f555855b5782066a830373cee9486 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 06:03:34 +0100 Subject: [PATCH 506/889] Fix a few text defines --- components/interpreter/defines.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/interpreter/defines.cpp b/components/interpreter/defines.cpp index 5774c96ae..6f3b5bb5a 100644 --- a/components/interpreter/defines.cpp +++ b/components/interpreter/defines.cpp @@ -64,7 +64,7 @@ namespace Interpreter{ retval << context.getActionBinding("#{sRestKey}"); } else if((found = Check(temp, "actionmenumode", &i, &start))){ - retval << context.getActionBinding("#{sJournal}"); + retval << context.getActionBinding("#{sInventory}"); } else if((found = Check(temp, "actionactivate", &i, &start))){ retval << context.getActionBinding("#{sActivate}"); @@ -88,10 +88,10 @@ namespace Interpreter{ retval << context.getActionBinding("#{sBack}"); } else if((found = Check(temp, "actionuse", &i, &start))){ - retval << "PLACEHOLDER_ACTION_USE"; + retval << context.getActionBinding("#{sUse}"); } else if((found = Check(temp, "actionrun", &i, &start))){ - retval << "PLACEHOLDER_ACTION_RUN"; + retval << context.getActionBinding("#{sRun}"); } else if((found = Check(temp, "pcclass", &i, &start))){ retval << context.getPCClass(); From b8583124e0f49097f362b1b9e5a2054f246813be Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 06:13:30 +0100 Subject: [PATCH 507/889] Correction for RemoveSoulgem instruction --- apps/openmw/mwscript/containerextensions.cpp | 2 +- apps/openmw/mwscript/miscextensions.cpp | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 202ec6464..7bac7cdbe 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -282,7 +282,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - const std::string &name = runtime.getStringLiteral (runtime[0].mInteger); + const std::string &name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore (ptr); diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index bb3600a27..637159475 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -371,15 +371,20 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - MWWorld::Ptr ptr = R()(runtime); std::string soul = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); - - store.remove(soul, 1, ptr); + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + if (::Misc::StringUtils::ciEqual(it->getCellRef().mSoul, soul)) + { + store.remove(*it, 1, ptr); + return; + } + } } }; From 69381c49c71f18cd7e58bb71682c5cbbb7abf215 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 07:39:44 +0100 Subject: [PATCH 508/889] Added a todo comment --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index d6dd73bd0..1738f9fdd 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -901,6 +901,9 @@ namespace MWMechanics static float fSneakSkillMult = store.find("fSneakSkillMult")->getFloat(); static float fSneakBootMult = store.find("fSneakBootMult")->getFloat(); float sneak = 0; + // TODO: According to Hrnchamd Research:Movement, "Creatures have generalized combat, magic and stealth + // stats which substitute for the specific skills (in the same way as specializations)." + // This probably applies to a large part of the code base. if (ptr.getClass().isNpc()) sneak = ptr.getClass().getNpcStats(ptr).getSkill(ESM::Skill::Sneak).getModified(); int agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); From 52b9ebff9dc40545efa4e3676b5f42bd40ce7d7f Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 07:40:17 +0100 Subject: [PATCH 509/889] Closes #1092: Implement sleep interruption. Fix levelled list flags for creatures. Change World::copyObjectToCell to search for the correct cell. --- apps/esmtool/labels.cpp | 22 ++++++--- apps/esmtool/labels.hpp | 3 +- apps/esmtool/record.cpp | 8 ++-- apps/openmw/mwbase/world.hpp | 3 ++ apps/openmw/mwgui/waitdialog.cpp | 35 ++++++++++++++- apps/openmw/mwgui/waitdialog.hpp | 3 ++ apps/openmw/mwmechanics/actors.cpp | 2 +- apps/openmw/mwmechanics/character.cpp | 1 + apps/openmw/mwmechanics/levelledlist.hpp | 16 ++++--- apps/openmw/mwworld/containerstore.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 57 ++++++++++++++++++++---- apps/openmw/mwworld/worldimp.hpp | 5 ++- components/esm/loadlevlist.hpp | 36 +++++++++------ 13 files changed, 150 insertions(+), 43 deletions(-) diff --git a/apps/esmtool/labels.cpp b/apps/esmtool/labels.cpp index 56c9a2fad..d270a9534 100644 --- a/apps/esmtool/labels.cpp +++ b/apps/esmtool/labels.cpp @@ -717,16 +717,26 @@ std::string landFlags(int flags) return properties; } -std::string leveledListFlags(int flags) +std::string itemListFlags(int flags) { std::string properties = ""; if (flags == 0) properties += "[None] "; - if (flags & ESM::LeveledListBase::AllLevels) properties += "AllLevels "; - // This flag apparently not present on creature lists... - if (flags & ESM::LeveledListBase::Each) properties += "Each "; + if (flags & ESM::ItemLevList::AllLevels) properties += "AllLevels "; + if (flags & ESM::ItemLevList::Each) properties += "Each "; int unused = (0xFFFFFFFF ^ - (ESM::LeveledListBase::AllLevels| - ESM::LeveledListBase::Each)); + (ESM::ItemLevList::AllLevels| + ESM::ItemLevList::Each)); + if (flags & unused) properties += "Invalid "; + properties += str(boost::format("(0x%08X)") % flags); + return properties; +} + +std::string creatureListFlags(int flags) +{ + std::string properties = ""; + if (flags == 0) properties += "[None] "; + if (flags & ESM::CreatureLevList::AllLevels) properties += "AllLevels "; + int unused = (0xFFFFFFFF ^ ESM::CreatureLevList::AllLevels); if (flags & unused) properties += "Invalid "; properties += str(boost::format("(0x%08X)") % flags); return properties; diff --git a/apps/esmtool/labels.hpp b/apps/esmtool/labels.hpp index 48d7b249b..007f93316 100644 --- a/apps/esmtool/labels.hpp +++ b/apps/esmtool/labels.hpp @@ -50,7 +50,8 @@ std::string cellFlags(int flags); std::string containerFlags(int flags); std::string creatureFlags(int flags); std::string landFlags(int flags); -std::string leveledListFlags(int flags); +std::string creatureListFlags(int flags); +std::string itemListFlags(int flags); std::string lightFlags(int flags); std::string magicEffectFlags(int flags); std::string npcFlags(int flags); diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index a5664c1c8..b68004f1f 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -834,7 +834,8 @@ template<> void Record::print() { std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl; - std::cout << " Flags: " << leveledListFlags(mData.mFlags) << std::endl; + std::cout << " Flags: " << creatureListFlags(mData.mFlags) << std::endl; + std::cout << " Chance none: " << mData.mChanceNone << std::endl; std::cout << " Number of items: " << mData.mList.size() << std::endl; std::vector::iterator iit; for (iit = mData.mList.begin(); iit != mData.mList.end(); iit++) @@ -846,11 +847,12 @@ template<> void Record::print() { std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl; - std::cout << " Flags: " << leveledListFlags(mData.mFlags) << std::endl; + std::cout << " Flags: " << itemListFlags(mData.mFlags) << std::endl; + std::cout << " Chance none: " << mData.mChanceNone << std::endl; std::cout << " Number of items: " << mData.mList.size() << std::endl; std::vector::iterator iit; for (iit = mData.mList.begin(); iit != mData.mList.end(); iit++) - std::cout << " Inventory: Count: " << iit->mLevel + std::cout << " Inventory: Level: " << iit->mLevel << " Item: " << iit->mId << std::endl; } diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index fe40fab24..83fcba87c 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -460,6 +460,9 @@ namespace MWBase virtual void confiscateStolenItems(const MWWorld::Ptr& ptr) = 0; virtual void goToJail () = 0; + + /// Spawn a random creature from a levelled list next to the player + virtual void spawnRandomCreature(const std::string& creatureList) = 0; }; } diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 1ad703790..f52771ffe 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -48,6 +48,7 @@ namespace MWGui , mRemainingTime(0.05) , mCurHour(0) , mManualHours(1) + , mInterruptAt(-1) { getWidget(mDateTimeText, "DateTimeText"); getWidget(mRestText, "RestText"); @@ -156,7 +157,8 @@ namespace MWGui void WaitDialog::startWaiting(int hoursToWait) { - MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0.2); + MWBase::World* world = MWBase::Environment::get().getWorld(); + world->getFader ()->fadeOut(0.2); setVisible(false); mProgressBar.setVisible (true); @@ -164,6 +166,30 @@ namespace MWGui mCurHour = 0; mHours = hoursToWait; + // FIXME: move this somewhere else? + mInterruptAt = -1; + MWWorld::Ptr player = world->getPlayerPtr(); + if (mSleeping && player.getCell()->isExterior()) + { + std::string regionstr = player.getCell()->mCell->mRegion; + if (!regionstr.empty()) + { + const ESM::Region *region = world->getStore().get().find (regionstr); + if (!region->mSleepList.empty()) + { + float fSleepRandMod = world->getStore().get().find("fSleepRandMod")->getFloat(); + int x = std::rand()/ (static_cast (RAND_MAX) + 1) * hoursToWait; // [0, hoursRested] + float y = fSleepRandMod * hoursToWait; + if (x > y) + { + float fSleepRestMod = world->getStore().get().find("fSleepRestMod")->getFloat(); + mInterruptAt = int(fSleepRestMod * hoursToWait); + mInterruptCreatureList = region->mSleepList; + } + } + } + } + mRemainingTime = 0.05; mProgressBar.setProgress (0, mHours); } @@ -206,6 +232,13 @@ namespace MWGui if (!mWaiting) return; + if (mCurHour == mInterruptAt) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sSleepInterrupt}"); + MWBase::Environment::get().getWorld()->spawnRandomCreature(mInterruptCreatureList); + stopWaiting(); + } + mRemainingTime -= dt; while (mRemainingTime < 0) diff --git a/apps/openmw/mwgui/waitdialog.hpp b/apps/openmw/mwgui/waitdialog.hpp index 2723f7a80..d96649af6 100644 --- a/apps/openmw/mwgui/waitdialog.hpp +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -51,6 +51,9 @@ namespace MWGui int mManualHours; // stores the hours to rest selected via slider float mRemainingTime; + int mInterruptAt; + std::string mInterruptCreatureList; + WaitDialogProgressBar mProgressBar; void onUntilHealedButtonClicked(MyGUI::Widget* sender); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index a0be8357d..99381a204 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -515,7 +515,7 @@ namespace MWMechanics if (magnitude > 0) { ESM::Position ipos = ptr.getRefData().getPosition(); - Ogre::Vector3 pos(ipos.pos[0],ipos.pos[1],ipos.pos[2]); + Ogre::Vector3 pos(ipos.pos); Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z); const float distance = 50; pos = pos + distance*rot.yAxis(); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 07859d57c..9a3983b07 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -860,6 +860,7 @@ void CharacterController::update(float duration) bool onground = world->isOnGround(mPtr); bool inwater = world->isSwimming(mPtr); bool isrunning = cls.getStance(mPtr, MWWorld::Class::Run); + isrunning = true; bool sneak = cls.getStance(mPtr, MWWorld::Class::Sneak); bool flying = world->isFlying(mPtr); Ogre::Vector3 vec = cls.getMovementVector(mPtr); diff --git a/apps/openmw/mwmechanics/levelledlist.hpp b/apps/openmw/mwmechanics/levelledlist.hpp index af5f71f51..d65503011 100644 --- a/apps/openmw/mwmechanics/levelledlist.hpp +++ b/apps/openmw/mwmechanics/levelledlist.hpp @@ -11,7 +11,7 @@ namespace MWMechanics { /// @return ID of resulting item, or empty if none - std::string getLevelledItem (const ESM::LeveledListBase* levItem, unsigned char failChance=0) + inline std::string getLevelledItem (const ESM::LeveledListBase* levItem, bool creature, unsigned char failChance=0) { const std::vector& items = levItem->mList; @@ -20,29 +20,33 @@ namespace MWMechanics failChance += levItem->mChanceNone; - float random = static_cast (std::rand()) / RAND_MAX; - if (random < failChance/100.f) + int random = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (random < failChance) return std::string(); std::vector candidates; int highestLevel = 0; for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) { - if (it->mLevel > highestLevel) + if (it->mLevel > highestLevel && it->mLevel <= playerLevel) highestLevel = it->mLevel; } + // For levelled creatures, the flags are swapped. This file format just makes so much sense. + bool allLevels = levItem->mFlags & ESM::ItemLevList::AllLevels; + if (creature) + allLevels = levItem->mFlags & ESM::CreatureLevList::AllLevels; + std::pair highest = std::make_pair(-1, ""); for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) { if (playerLevel >= it->mLevel - && (levItem->mFlags & ESM::LeveledListBase::AllLevels || it->mLevel == highestLevel)) + && (allLevels || it->mLevel == highestLevel)) { candidates.push_back(it->mId); if (it->mLevel >= highest.first) highest = std::make_pair(it->mLevel, it->mId); } - } if (candidates.empty()) return std::string(); diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 864bb2a0d..475eeb8f4 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -321,7 +321,7 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: } else { - std::string id = MWMechanics::getLevelledItem(ref.getPtr().get()->mBase); + std::string id = MWMechanics::getLevelledItem(ref.getPtr().get()->mBase, false); if (id.empty()) return; addInitialItem(id, owner, faction, count, false); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f9899c3a2..ebc1044bc 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -27,6 +27,7 @@ #include "../mwmechanics/movement.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/levelledlist.hpp" #include "../mwrender/sky.hpp" @@ -1538,23 +1539,28 @@ namespace MWWorld } - Ptr World::copyObjectToCell(const Ptr &object, CellStore &cell, const ESM::Position &pos, bool adjustPos) + Ptr World::copyObjectToCell(const Ptr &object, CellStore &cell, ESM::Position pos, bool adjustPos) { - /// \todo add searching correct cell for position specified - MWWorld::Ptr dropped = - MWWorld::Class::get(object).copyToCell(object, cell, pos); - if (object.getClass().isActor() || adjustPos) { Ogre::Vector3 min, max; if (mPhysics->getObjectAABB(object, min, max)) { - float *pos = dropped.getRefData().getPosition().pos; - pos[0] -= (min.x + max.x) / 2; - pos[1] -= (min.y + max.y) / 2; - pos[2] -= min.z; + pos.pos[0] -= (min.x + max.x) / 2; + pos.pos[1] -= (min.y + max.y) / 2; + pos.pos[2] -= min.z; } } + if (cell.isExterior()) + { + int cellX, cellY; + positionToIndex(pos.pos[0], pos.pos[1], cellX, cellY); + cell = *mCells.getExterior(cellX, cellY); + } + + MWWorld::Ptr dropped = + MWWorld::Class::get(object).copyToCell(object, cell, pos); + if (mWorldScene->isCellActive(cell)) { if (dropped.getRefData().isEnabled()) { mWorldScene->addObjectToScene(dropped); @@ -2571,4 +2577,37 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->messageBox(message, buttons); } } + + void World::spawnRandomCreature(const std::string &creatureList) + { + const ESM::CreatureLevList* list = getStore().get().find(creatureList); + + int iNumberCreatures = getStore().get().find("iNumberCreatures")->getInt(); + int numCreatures = 1 + std::rand()/ (static_cast (RAND_MAX) + 1) * iNumberCreatures; // [1, iNumberCreatures] + + for (int i=0; igetPlayer().getRefData().getPosition(); + Ogre::Vector3 pos(ipos.pos); + Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z); + const float distance = 50; + pos = pos + distance*rot.yAxis(); + ipos.pos[0] = pos.x; + ipos.pos[1] = pos.y; + ipos.pos[2] = pos.z; + ipos.rot[0] = 0; + ipos.rot[1] = 0; + ipos.rot[2] = 0; + + MWWorld::CellStore* cell = mPlayer->getPlayer().getCell(); + MWWorld::ManualRef ref(getStore(), selectedCreature, 1); + ref.getPtr().getCellRef().mPos = ipos; + + safePlaceObject(ref.getPtr(),*cell,ipos); + } + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 634cc8d6b..92975400a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -114,7 +114,7 @@ namespace MWWorld bool moveObjectImp (const Ptr& ptr, float x, float y, float z); ///< @return true if the active cell (cell player is in) changed - Ptr copyObjectToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos, bool adjustPos=true); + Ptr copyObjectToCell(const Ptr &ptr, CellStore &cell, ESM::Position pos, bool adjustPos=true); void updateWindowManager (); void performUpdateSceneQueries (); @@ -546,6 +546,9 @@ namespace MWWorld virtual void confiscateStolenItems(const MWWorld::Ptr& ptr); virtual void goToJail (); + + /// Spawn a random creature from a levelled list next to the player + virtual void spawnRandomCreature(const std::string& creatureList); }; } diff --git a/components/esm/loadlevlist.hpp b/components/esm/loadlevlist.hpp index 9dcc6177a..a4e1b85c2 100644 --- a/components/esm/loadlevlist.hpp +++ b/components/esm/loadlevlist.hpp @@ -20,20 +20,6 @@ class ESMWriter; struct LeveledListBase { - enum Flags - { - - Each = 0x01, // Select a new item each time this - // list is instantiated, instead of - // giving several identical items - // (used when a container has more - // than one instance of one leveled - // list.) - AllLevels = 0x02 // Calculate from all levels <= player - // level, not just the closest below - // player. - }; - int mFlags; unsigned char mChanceNone; // Chance that none are selected (0-100) std::string mId; @@ -61,6 +47,14 @@ struct CreatureLevList: LeveledListBase { static unsigned int sRecordId; + enum Flags + { + + AllLevels = 0x01 // Calculate from all levels <= player + // level, not just the closest below + // player. + }; + CreatureLevList() { mRecName = "CNAM"; @@ -71,6 +65,20 @@ struct ItemLevList: LeveledListBase { static unsigned int sRecordId; + enum Flags + { + + Each = 0x01, // Select a new item each time this + // list is instantiated, instead of + // giving several identical items + // (used when a container has more + // than one instance of one leveled + // list.) + AllLevels = 0x02 // Calculate from all levels <= player + // level, not just the closest below + // player. + }; + ItemLevList() { mRecName = "INAM"; From 2c1ef610b9251fa40cc0a4a6e08813ed886865b8 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 14 Jan 2014 09:16:59 +0100 Subject: [PATCH 510/889] Moving back to boost for case insensitive comparsion. --- apps/opencs/model/tools/referenceablecheck.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 3be594d3e..f89c641d4 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -2,7 +2,6 @@ #include "../world/record.hpp" #include "../world/universalid.hpp" #include -#include "../../../../openmw/apps/openmw/mwclass/misc.hpp" CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, @@ -661,7 +660,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck( int gold(npc.mNpdt52.mGold); //Detect if player is present - if (Misc::StringUtils::ciEqual(npc.mId, "player")) + if ( boost::algorithm::iequals(npc.mId, "player") ) { mPlayerPresent = true; } From c981a2a6f86c2a5938bcba902cae4c8abf417925 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 14 Jan 2014 09:25:03 +0100 Subject: [PATCH 511/889] Removed boost in favor of #include . Hopefully this will make scrawl happy. :P --- apps/opencs/model/tools/referenceablecheck.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index f89c641d4..5624480af 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -1,7 +1,7 @@ #include "referenceablecheck.hpp" #include "../world/record.hpp" #include "../world/universalid.hpp" -#include +#include CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, @@ -660,7 +660,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck( int gold(npc.mNpdt52.mGold); //Detect if player is present - if ( boost::algorithm::iequals(npc.mId, "player") ) + if (Misc::StringUtils::ciEqual(npc.mId, "player")) //Happy now, scrawl? { mPlayerPresent = true; } From 15d946415e0110993efe8fca1610356f9c72cfaf Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 14 Jan 2014 12:46:53 +0400 Subject: [PATCH 512/889] minor cleanup Removed case folding via std::transform, excessive lowerCase() replaced with ciEqual(). --- apps/launcher/unshieldthread.cpp | 6 ++--- apps/opencs/model/world/columns.cpp | 2 +- apps/opencs/model/world/infocollection.cpp | 6 ++--- apps/openmw/mwdialogue/filter.cpp | 27 +++++++++---------- apps/openmw/mwgui/dialogue.cpp | 2 +- apps/openmw/mwgui/messagebox.cpp | 2 +- .../mwmechanics/mechanicsmanagerimp.cpp | 16 ++++++----- apps/openmw/mwworld/cells.cpp | 5 ++-- apps/openmw/mwworld/cellstore.cpp | 8 +----- apps/openmw/mwworld/containerstore.cpp | 2 +- apps/openmw/mwworld/store.cpp | 4 --- 11 files changed, 36 insertions(+), 44 deletions(-) diff --git a/apps/launcher/unshieldthread.cpp b/apps/launcher/unshieldthread.cpp index d0dbeb1bd..52f935710 100644 --- a/apps/launcher/unshieldthread.cpp +++ b/apps/launcher/unshieldthread.cpp @@ -235,7 +235,7 @@ namespace { for ( bfs::recursive_directory_iterator end, dir(in); dir != end; ++dir ) { - if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename) + if(Misc::StringUtils::ciEqual(dir->path().filename().string(), filename)) return dir->path(); } } @@ -243,7 +243,7 @@ namespace { for ( bfs::directory_iterator end, dir(in); dir != end; ++dir ) { - if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename) + if(Misc::StringUtils::ciEqual(dir->path().filename().string(), filename)) return dir->path(); } } @@ -255,7 +255,7 @@ namespace { for(bfs::directory_iterator end, dir(in); dir != end; ++dir) { - if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename) + if(Misc::StringUtils::ciEqual(dir->path().filename().string(), filename)) return true; } diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index 9c0d5b0fd..2f3911270 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -220,7 +220,7 @@ int CSMWorld::Columns::getId (const std::string& name) std::string name2 = Misc::StringUtils::lowerCase (name); for (int i=0; sNames[i].mName; ++i) - if (name2==Misc::StringUtils::lowerCase (sNames[i].mName)) + if (Misc::StringUtils::ciEqual(sNames[i].mName, name2)) return sNames[i].mId; return -1; diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp index 87bb925c2..50d09f313 100644 --- a/apps/opencs/model/world/infocollection.cpp +++ b/apps/opencs/model/world/infocollection.cpp @@ -67,7 +67,7 @@ int CSMWorld::InfoCollection::getIndex (const std::string& id, const std::string std::pair range = getTopicRange (topic); for (; range.first!=range.second; ++range.first) - if (Misc::StringUtils::lowerCase (range.first->get().mId)==fullId) + if (Misc::StringUtils::ciEqual(range.first->get().mId, fullId)) return std::distance (getRecords().begin(), range.first); return -1; @@ -177,8 +177,8 @@ CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const s RecordConstIterator end = begin; for (; end!=getRecords().end(); ++end) - if (Misc::StringUtils::lowerCase (end->get().mTopicId)!=topic2) + if (!Misc::StringUtils::ciEqual(end->get().mTopicId, topic2)) break; return Range (begin, end); -} \ No newline at end of file +} diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index d7b7df983..f132d13a3 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -24,7 +24,7 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const // actor id if (!info.mActor.empty()) { - if ( Misc::StringUtils::lowerCase (info.mActor)!=MWWorld::Class::get (mActor).getId (mActor)) + if ( !Misc::StringUtils::ciEqual(info.mActor, MWWorld::Class::get (mActor).getId (mActor))) return false; } else if (isCreature) @@ -41,7 +41,7 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const MWWorld::LiveCellRef *cellRef = mActor.get(); - if (Misc::StringUtils::lowerCase (info.mRace)!= Misc::StringUtils::lowerCase (cellRef->mBase->mRace)) + if (!Misc::StringUtils::ciEqual(info.mRace, cellRef->mBase->mRace)) return false; } @@ -53,7 +53,7 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const MWWorld::LiveCellRef *cellRef = mActor.get(); - if ( Misc::StringUtils::lowerCase (info.mClass)!= Misc::StringUtils::lowerCase (cellRef->mBase->mClass)) + if ( !Misc::StringUtils::ciEqual(info.mClass, cellRef->mBase->mClass)) return false; } @@ -110,7 +110,7 @@ bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const // check cell if (!info.mCell.empty()) - if (Misc::StringUtils::lowerCase (player.getCell()->mCell->mName) != Misc::StringUtils::lowerCase (info.mCell)) + if (!Misc::StringUtils::ciEqual(player.getCell()->mCell->mName, info.mCell)) return false; return true; @@ -188,7 +188,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c int i = 0; for (; i (script->mVarNames.size()); ++i) - if (Misc::StringUtils::lowerCase(script->mVarNames[i]) == name) + if (Misc::StringUtils::ciEqual(script->mVarNames[i], name)) break; if (i>=static_cast (script->mVarNames.size())) @@ -262,7 +262,7 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con std::string name = select.getName(); for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) - if (Misc::StringUtils::lowerCase(iter->getCellRef().mRefID) == name) + if (Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, name)) sum += iter->getRefData().getCount(); return sum; @@ -429,23 +429,23 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co case SelectWrapper::Function_NotId: - return select.getName()!=Misc::StringUtils::lowerCase (MWWorld::Class::get (mActor).getId (mActor)); + return !Misc::StringUtils::ciEqual(MWWorld::Class::get (mActor).getId (mActor), select.getName()); case SelectWrapper::Function_NotFaction: - return Misc::StringUtils::lowerCase (mActor.get()->mBase->mFaction)!=select.getName(); + return !Misc::StringUtils::ciEqual(mActor.get()->mBase->mFaction, select.getName()); case SelectWrapper::Function_NotClass: - return Misc::StringUtils::lowerCase (mActor.get()->mBase->mClass)!=select.getName(); + return !Misc::StringUtils::ciEqual(mActor.get()->mBase->mClass, select.getName()); case SelectWrapper::Function_NotRace: - return Misc::StringUtils::lowerCase (mActor.get()->mBase->mRace)!=select.getName(); + return !Misc::StringUtils::ciEqual(mActor.get()->mBase->mRace, select.getName()); case SelectWrapper::Function_NotCell: - return Misc::StringUtils::lowerCase (mActor.getCell()->mCell->mName)!=select.getName(); + return !Misc::StringUtils::ciEqual(mActor.getCell()->mCell->mName, select.getName()); case SelectWrapper::Function_NotLocal: { @@ -462,7 +462,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co int i = 0; for (; i < static_cast (script->mVarNames.size()); ++i) - if (Misc::StringUtils::lowerCase(script->mVarNames[i]) == name) + if (Misc::StringUtils::ciEqual(script->mVarNames[i], name)) break; if (i >= static_cast (script->mVarNames.size())) @@ -478,8 +478,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co case SelectWrapper::Function_SameRace: - return Misc::StringUtils::lowerCase (mActor.get()->mBase->mRace)!= - Misc::StringUtils::lowerCase (player.get()->mBase->mRace); + return !Misc::StringUtils::ciEqual(mActor.get()->mBase->mRace, player.get()->mBase->mRace); case SelectWrapper::Function_SameFaction: diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 8269e8364..481c98314 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -545,7 +545,7 @@ namespace MWGui for (size_t i=0; igetItemCount(); ++i) { std::string item = mTopicsList->getItemNameAt(i); - if (Misc::StringUtils::lowerCase(item) == title) + if (Misc::StringUtils::ciEqual(item, title)) { realTitle = item; break; diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index ae01f4293..644b8f66a 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -333,7 +333,7 @@ namespace MWGui std::vector::const_iterator button; for(button = mButtons.begin(); button != mButtons.end(); ++button) { - if(Misc::StringUtils::lowerCase((*button)->getCaption()) == ok) + if(Misc::StringUtils::ciEqual((*button)->getCaption(), ok)) { MyGUI::InputManager::getInstance().setKeyFocusWidget(*button); (*button)->eventKeyButtonPressed += MyGUI::newDelegate(this, &InteractiveMessageBox::onKeyPressed); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 1738f9fdd..79a038dff 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -472,21 +472,23 @@ namespace MWMechanics std::string npcFaction = ""; if(!npcSkill.getFactionRanks().empty()) npcFaction = npcSkill.getFactionRanks().begin()->first; - if (playerStats.getFactionRanks().find(Misc::StringUtils::lowerCase(npcFaction)) != playerStats.getFactionRanks().end()) + Misc::StringUtils::toLower(npcFaction); + + if (playerStats.getFactionRanks().find(npcFaction) != playerStats.getFactionRanks().end()) { - for(std::vector::const_iterator it = MWBase::Environment::get().getWorld()->getStore().get().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.begin(); - it != MWBase::Environment::get().getWorld()->getStore().get().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.end(); ++it) + for(std::vector::const_iterator it = MWBase::Environment::get().getWorld()->getStore().get().find(npcFaction)->mReactions.begin(); + it != MWBase::Environment::get().getWorld()->getStore().get().find(npcFaction)->mReactions.end(); ++it) { - if(Misc::StringUtils::lowerCase(it->mFaction) == Misc::StringUtils::lowerCase(npcFaction) + if(Misc::StringUtils::ciEqual(it->mFaction, npcFaction) && !playerStats.getExpelled(it->mFaction)) reaction = it->mReaction; } - rank = playerStats.getFactionRanks().find(Misc::StringUtils::lowerCase(npcFaction))->second; + rank = playerStats.getFactionRanks().find(npcFaction)->second; } else if (npcFaction != "") { - for(std::vector::const_iterator it = MWBase::Environment::get().getWorld()->getStore().get().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.begin(); - it != MWBase::Environment::get().getWorld()->getStore().get().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.end();++it) + for(std::vector::const_iterator it = MWBase::Environment::get().getWorld()->getStore().get().find(npcFaction)->mReactions.begin(); + it != MWBase::Environment::get().getWorld()->getStore().get().find(npcFaction)->mReactions.end();++it) { if(playerStats.getFactionRanks().find(Misc::StringUtils::lowerCase(it->mFaction)) != playerStats.getFactionRanks().end() ) { diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 575e10f04..c8a070e21 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -11,11 +11,12 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) { if (cell->mData.mFlags & ESM::Cell::Interior) { - std::map::iterator result = mInteriors.find (Misc::StringUtils::lowerCase(cell->mName)); + std::string lowerName(Misc::StringUtils::lowerCase(cell->mName)); + std::map::iterator result = mInteriors.find (lowerName); if (result==mInteriors.end()) { - result = mInteriors.insert (std::make_pair (Misc::StringUtils::lowerCase(cell->mName), Ptr::CellStore (cell))).first; + result = mInteriors.insert (std::make_pair (lowerName, Ptr::CellStore (cell))).first; } return &result->second; diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 0c145ab60..b492c6f32 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -204,14 +204,8 @@ namespace MWWorld ESM::CellRef &ref = const_cast(*it); //ESM::CellRef &ref = const_cast(it->second); - std::string lowerCase; - - std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); - int rec = store.find(ref.mRefID); - - ref.mRefID = lowerCase; + Misc::StringUtils::toLower(ref.mRefID); /* We can optimize this further by storing the pointer to the record itself in store.all, so that we don't need to look it diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 475eeb8f4..154fa1999 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -45,7 +45,7 @@ namespace for (typename MWWorld::CellRefList::List::iterator iter (list.mList.begin()); iter!=list.mList.end(); ++iter) { - if (Misc::StringUtils::lowerCase (iter->mBase->mId)==id2) + if (Misc::StringUtils::ciEqual(iter->mBase->mId, id2)) { MWWorld::Ptr ptr (&*iter, 0); ptr.setContainerStore (store); diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 512883f1a..ca37cc591 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -31,10 +31,6 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) // Get regular moved reference data. Adapted from CellStore::loadRefs. Maybe we can optimize the following // implementation when the oher implementation works as well. cell->getNextRef(esm, ref); - std::string lowerCase; - - std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); // Add data required to make reference appear in the correct cell. // We should not need to test for duplicates, as this part of the code is pre-cell merge. From 6397d9d40e4716f1e93886c6ad9e55fea92191c2 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 14 Jan 2014 13:12:15 +0100 Subject: [PATCH 513/889] Added mCloneAction member --- apps/opencs/view/world/table.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 71bdb9000..9bc5ef038 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -199,6 +199,9 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q mCreateAction = new QAction (tr ("Add Record"), this); connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest())); addAction (mCreateAction); + + mCloneAction = new QAction(tr("Clone Record"), this); + addAction(mCloneAction); } mRevertAction = new QAction (tr ("Revert Record"), this); From 61707694e8bf5878b8f7243722bdc6d5a92aa76e Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 14 Jan 2014 16:22:50 +0400 Subject: [PATCH 514/889] fix memory leak in AISequence --- apps/openmw/mwmechanics/aisequence.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 916ba1b49..79a953e38 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -67,7 +67,10 @@ bool MWMechanics::AiSequence::getCombatTarget(std::string &targetActorId) const void MWMechanics::AiSequence::stopCombat() { while (getTypeId() == AiPackage::TypeIdCombat) + { + delete *mPackages.begin(); mPackages.erase (mPackages.begin()); + } } bool MWMechanics::AiSequence::isPackageDone() const @@ -83,6 +86,7 @@ void MWMechanics::AiSequence::execute (const MWWorld::Ptr& actor,float duration) { if (mPackages.front()->execute (actor,duration)) { + delete *mPackages.begin(); mPackages.erase (mPackages.begin()); mDone = true; } From 8c5f3135462e0b6e44de4733d917d66c2dfdaed1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 14 Jan 2014 12:25:35 +0100 Subject: [PATCH 515/889] added savedgame-specifc record structs for objects state --- components/CMakeLists.txt | 2 +- components/esm/cellid.cpp | 26 +++++++++++++++++++ components/esm/cellid.hpp | 28 ++++++++++++++++++++ components/esm/defs.hpp | 1 + components/esm/loadcell.cpp | 24 +++++++++++++++++ components/esm/loadcell.hpp | 3 +++ components/esm/objectstate.cpp | 47 ++++++++++++++++++++++++++++++++++ components/esm/objectstate.hpp | 36 ++++++++++++++++++++++++++ components/esm/player.cpp | 44 +++++++++++++++++++++++++++++++ components/esm/player.hpp | 32 +++++++++++++++++++++++ 10 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 components/esm/cellid.cpp create mode 100644 components/esm/cellid.hpp create mode 100644 components/esm/objectstate.cpp create mode 100644 components/esm/objectstate.hpp create mode 100644 components/esm/player.cpp create mode 100644 components/esm/player.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index dbf8c1132..4c0bff59d 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -40,7 +40,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame journalentry queststate locals globalscript + savedgame journalentry queststate locals globalscript player objectstate cellid ) add_component_dir (misc diff --git a/components/esm/cellid.cpp b/components/esm/cellid.cpp new file mode 100644 index 000000000..5bc8b7aef --- /dev/null +++ b/components/esm/cellid.cpp @@ -0,0 +1,26 @@ + +#include "cellid.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::CellId::load (ESMReader &esm) +{ + mWorldspace = esm.getHNString ("SPAC"); + + if (esm.isNextSub ("CIDX")) + { + esm.getHT (mIndex, 8); + mPaged = true; + } + else + mPaged = false; +} + +void ESM::CellId::save (ESMWriter &esm) const +{ + esm.writeHNString ("SPAC", mWorldspace); + + if (mPaged) + esm.writeHNT ("CIDX", mIndex, 8); +} \ No newline at end of file diff --git a/components/esm/cellid.hpp b/components/esm/cellid.hpp new file mode 100644 index 000000000..54dbdae78 --- /dev/null +++ b/components/esm/cellid.hpp @@ -0,0 +1,28 @@ +#ifndef OPENMW_ESM_CELLID_H +#define OPENMW_ESM_CELLID_H + +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + struct CellId + { + struct CellIndex + { + int mX; + int mY; + }; + + std::string mWorldspace; + CellIndex mIndex; + bool mPaged; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif \ No newline at end of file diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 4cf0b1dac..2b956d216 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -88,6 +88,7 @@ enum RecNameInts REC_JOUR = 0x524f55a4, REC_QUES = 0x53455551, REC_GSCR = 0x52435347, + REC_PLAY = 0x504c4159, // format 1 REC_FILT = 0x544C4946 diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index efd6979b4..649e3175d 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -3,11 +3,15 @@ #include #include #include + #include +#include + #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" +#include "cellid.hpp" namespace { @@ -221,4 +225,24 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) mAmbi.mFog = 0; mAmbi.mFogDensity = 0; } + + CellId Cell::getCellId() const + { + CellId id; + + id.mPaged = (mData.mFlags & Interior); + + if (id.mPaged) + { + id.mWorldspace = "default"; + id.mIndex.mX = mData.mX; + id.mIndex.mY = mData.mY; + } + else + { + id.mWorldspace = Misc::StringUtils::lowerCase (mName); + } + + return id; + } } diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 85b3d8954..643119e67 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -18,6 +18,7 @@ namespace ESM { class ESMReader; class ESMWriter; + class CellId; /* Moved cell reference tracking object. This mainly stores the target cell of the reference, so we can easily know where it has been moved when another @@ -150,6 +151,8 @@ struct Cell void blank(); ///< Set record to default state (does not touch the ID/index). + + CellId getCellId() const; }; } #endif diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp new file mode 100644 index 000000000..b13b6c226 --- /dev/null +++ b/components/esm/objectstate.cpp @@ -0,0 +1,47 @@ + +#include "objectstate.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::ObjectState::load (ESMReader &esm) +{ + mRef.load (esm, true); + + mHasLocals = 0; + esm.getHNOT (mHasLocals, "HLOC"); + + if (mHasLocals) + mLocals.load (esm); + + mEnabled = 1; + esm.getHNOT (mEnabled, "ENAB"); + + mCount = 1; + esm.getHNOT (mCount, "COUN"); + + esm.getHNT (mPosition, "POS_", 24); + + esm.getHNT (mLocalRotation, "LROT", 12); +} + +void ESM::ObjectState::save (ESMWriter &esm) const +{ + mRef.save (esm); + + if (mHasLocals) + { + esm.writeHNT ("HLOC", mHasLocals); + mLocals.save (esm); + } + + if (!mEnabled) + esm.writeHNT ("ENAB", mEnabled); + + if (mCount!=1) + esm.writeHNT ("COUN", mCount); + + esm.writeHNT ("POS_", mPosition, 24); + + esm.writeHNT ("LROT", mLocalRotation, 12); +} \ No newline at end of file diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp new file mode 100644 index 000000000..bbbc4798f --- /dev/null +++ b/components/esm/objectstate.hpp @@ -0,0 +1,36 @@ +#ifndef OPENMW_ESM_OBJECTSTATE_H +#define OPENMW_ESM_OBJECTSTATE_H + +#include +#include + +#include "cellref.hpp" +#include "locals.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + ///< \brief Save state for objects, that do not use custom data + struct ObjectState + { + std::string mId; + + CellRef mRef; + + unsigned char mHasLocals; + Locals mLocals; + unsigned char mEnabled; + int mCount; + ESM::Position mPosition; + float mLocalRotation[3]; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif \ No newline at end of file diff --git a/components/esm/player.cpp b/components/esm/player.cpp new file mode 100644 index 000000000..13602fb67 --- /dev/null +++ b/components/esm/player.cpp @@ -0,0 +1,44 @@ + +#include "player.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::Player::load (ESMReader &esm) +{ + mObject.load (esm); + + mCellId.load (esm); + + esm.getHNT (mLastKnownExteriorPosition, "LKEP", 12); + + if (esm.isNextSub ("MARK")) + { + mHasMark = true; + esm.getHT (mMarkedPosition, 24); + mMarkedCell.load (esm); + } + else + mHasMark = false; + + mAutoMove = 0; + esm.getHNOT (mAutoMove, "AMOV"); +} + +void ESM::Player::save (ESMWriter &esm) const +{ + mObject.save (esm); + + mCellId.save (esm); + + esm.writeHNT ("LKEP", mLastKnownExteriorPosition, 12); + + if (mHasMark) + { + esm.writeHNT ("MARK", mMarkedPosition, 24); + mMarkedCell.save (esm); + } + + if (mAutoMove) + esm.writeHNT ("AMOV", mAutoMove); +} \ No newline at end of file diff --git a/components/esm/player.hpp b/components/esm/player.hpp new file mode 100644 index 000000000..3f7db17f7 --- /dev/null +++ b/components/esm/player.hpp @@ -0,0 +1,32 @@ +#ifndef OPENMW_ESM_PLAYER_H +#define OPENMW_ESM_PLAYER_H + +#include + +#include "objectstate.hpp" +#include "cellid.hpp" +#include "defs.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + struct Player + { + ObjectState mObject; + CellId mCellId; + float mLastKnownExteriorPosition[3]; + unsigned char mHasMark; + ESM::Position mMarkedPosition; + CellId mMarkedCell; + unsigned char mAutoMove; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif \ No newline at end of file From 344cae8f993d0ae4c52552fafd832f26090cd6bc Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 14 Jan 2014 15:44:04 +0100 Subject: [PATCH 516/889] added new entry to the context menu --- apps/opencs/view/world/table.cpp | 20 ++++++++++++++++++-- apps/opencs/view/world/table.hpp | 4 ++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 9bc5ef038..31a7d8b58 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -28,7 +28,11 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) if (!mEditLock) { if (selectedRows.size()==1) + { menu.addAction (mEditAction); + if (mCreateAction) + menu.addAction(mCloneAction); + } if (mCreateAction) menu.addAction (mCreateAction); @@ -155,7 +159,7 @@ std::vector CSVWorld::Table::listDeletableSelectedIds() const CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, bool sorting) - : mUndoStack (undoStack), mCreateAction (0), mEditLock (false), mRecordStatusDisplay (0) + : mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0) { mModel = &dynamic_cast (*data.getTableModel (id)); @@ -200,7 +204,8 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest())); addAction (mCreateAction); - mCloneAction = new QAction(tr("Clone Record"), this); + mCloneAction = new QAction (tr ("Clone Record"), this); + connect(mCloneAction, SIGNAL (triggered()), this, SLOT (cloneRecord())); addAction(mCloneAction); } @@ -298,6 +303,17 @@ void CSVWorld::Table::editRecord() } } +void CSVWorld::Table::cloneRecord() +{ + if (!mEditLock) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size()==1) + emit cloneRequest (selectedRows.begin()->row()); + } +} + void CSVWorld::Table::moveUpRecord() { QModelIndexList selectedRows = selectionModel()->selectedRows(); diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 889e2847a..f4a5d3c9e 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -32,6 +32,7 @@ namespace CSVWorld QUndoStack& mUndoStack; QAction *mEditAction; QAction *mCreateAction; + QAction *mCloneAction; QAction *mRevertAction; QAction *mDeleteAction; QAction *mMoveUpAction; @@ -73,6 +74,7 @@ namespace CSVWorld /// \param modified Number of added and modified records void createRequest(); + void cloneRequest(int row); private slots: @@ -82,6 +84,8 @@ namespace CSVWorld void editRecord(); + void cloneRecord(); + void moveUpRecord(); void moveDownRecord(); From 89d4a90c063bcc90acf1bfe085ad924e81622246 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 15 Jan 2014 10:30:43 +0100 Subject: [PATCH 517/889] Localised version of morrowind will no longer spam false positives. Don't check diff, please. --- apps/opencs/model/tools/referenceablecheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 5624480af..ed0fec1da 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -769,7 +769,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck( for (int i = 0; i < mRaces.getSize(); ++i) { - if (dynamic_cast(mRaces.getRecord(i).get()).mName == npc.mRace) //mId in class, mName for race. Stupid. + if (Misc::StringUtils::ciEqual(dynamic_cast(mRaces.getRecord(i).get()).mId, npc.mRace)) { noSuchRace = false; break; From fbcb1a14fc1db7ec495f9cba0970c807dd53bf15 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 15 Jan 2014 11:12:56 +0100 Subject: [PATCH 518/889] ooops, using search id --- apps/opencs/model/tools/referenceablecheck.cpp | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index ed0fec1da..62b2a5172 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -765,18 +765,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck( } else //checking if there is a such race { - bool noSuchRace(true); - - for (int i = 0; i < mRaces.getSize(); ++i) - { - if (Misc::StringUtils::ciEqual(dynamic_cast(mRaces.getRecord(i).get()).mId, npc.mRace)) - { - noSuchRace = false; - break; - } - } - - if (noSuchRace) + if ((!mRaces.searchId(npc.mRace))) { messages.push_back(id.toString() + "|" + npc.mId + " has invalid race"); } From 3d722ba1043fa400e578a7688714cf84aa3c5842 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 15 Jan 2014 11:59:11 +0100 Subject: [PATCH 519/889] Corrected brackets. --- apps/opencs/model/tools/referenceablecheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 62b2a5172..6615d0a57 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -765,7 +765,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck( } else //checking if there is a such race { - if ((!mRaces.searchId(npc.mRace))) + if (!mRaces.searchId(npc.mRace)) { messages.push_back(id.toString() + "|" + npc.mId + " has invalid race"); } From 6224344957f1c5eff6f88cd94166ffc8491272e6 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 15 Jan 2014 12:52:38 +0100 Subject: [PATCH 520/889] Being any idiot is hard. --- apps/opencs/model/tools/referenceablecheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 6615d0a57..dab61bfff 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -765,7 +765,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck( } else //checking if there is a such race { - if (!mRaces.searchId(npc.mRace)) + if (mRaces.searchId(npc.mRace) == -1) { messages.push_back(id.toString() + "|" + npc.mId + " has invalid race"); } From a4602326f3f565cca8e44ec7f402841a00249b32 Mon Sep 17 00:00:00 2001 From: Sandy Date: Wed, 15 Jan 2014 07:30:07 -0500 Subject: [PATCH 521/889] openmw is now available in [community] --- readme.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.txt b/readme.txt index 5b9aafcb3..6b388dc72 100644 --- a/readme.txt +++ b/readme.txt @@ -23,8 +23,8 @@ Ubuntu (and most others) Download the .deb file and install it in the usual way. Arch Linux -There's an OpenMW package available in the AUR Repository: -http://aur.archlinux.org/packages.php?ID=21419 +There's an OpenMW package available in the [community] Repository: +https://www.archlinux.org/packages/?sort=&q=openmw OS X: Open DMG file, copy OpenMW folder anywhere, for example in /Applications From de64c5717961dc247cdbd16e6de371e65c33c681 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 09:07:57 +0100 Subject: [PATCH 522/889] Fix some typos and accidental commit --- apps/esmtool/record.cpp | 4 +--- apps/openmw/mwmechanics/character.cpp | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index b68004f1f..184d11bb4 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -835,7 +835,6 @@ void Record::print() { std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl; std::cout << " Flags: " << creatureListFlags(mData.mFlags) << std::endl; - std::cout << " Chance none: " << mData.mChanceNone << std::endl; std::cout << " Number of items: " << mData.mList.size() << std::endl; std::vector::iterator iit; for (iit = mData.mList.begin(); iit != mData.mList.end(); iit++) @@ -848,7 +847,6 @@ void Record::print() { std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl; std::cout << " Flags: " << itemListFlags(mData.mFlags) << std::endl; - std::cout << " Chance none: " << mData.mChanceNone << std::endl; std::cout << " Number of items: " << mData.mList.size() << std::endl; std::vector::iterator iit; for (iit = mData.mList.begin(); iit != mData.mList.end(); iit++) @@ -960,7 +958,7 @@ void Record::print() std::cout << " RGB Color: " << "(" << mData.mData.mRed << "," << mData.mData.mGreen << "," - << mData.mData.mGreen << ")" << std::endl; + << mData.mData.mBlue << ")" << std::endl; } template<> diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 9a3983b07..07859d57c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -860,7 +860,6 @@ void CharacterController::update(float duration) bool onground = world->isOnGround(mPtr); bool inwater = world->isSwimming(mPtr); bool isrunning = cls.getStance(mPtr, MWWorld::Class::Run); - isrunning = true; bool sneak = cls.getStance(mPtr, MWWorld::Class::Sneak); bool flying = world->isFlying(mPtr); Ogre::Vector3 vec = cls.getMovementVector(mPtr); From a432661b8afe05898d7de02f9051a637482c7d2e Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 09:10:09 +0100 Subject: [PATCH 523/889] Minor correction --- apps/openmw/mwgui/waitdialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index f52771ffe..0ead54d9d 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -183,7 +183,7 @@ namespace MWGui if (x > y) { float fSleepRestMod = world->getStore().get().find("fSleepRestMod")->getFloat(); - mInterruptAt = int(fSleepRestMod * hoursToWait); + mInterruptAt = hoursToWait - int(fSleepRestMod * hoursToWait); mInterruptCreatureList = region->mSleepList; } } From 0c4b6ea89f31f3cb5523eda963cf0d979336ec80 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 09:35:38 +0100 Subject: [PATCH 524/889] Minor fix --- apps/openmw/mwclass/npc.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index a7ff92cf5..7ba5f340c 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -471,10 +471,6 @@ namespace MWClass if(ptr.getRefData().getHandle() == "player") MWBase::Environment::get().getWindowManager()->setEnemy(victim); - // Attacking peaceful NPCs is a crime - if (victim.getClass().isNpc() && victim.getClass().getCreatureStats(victim).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) - MWBase::Environment::get().getMechanicsManager()->commitCrime(ptr, victim, MWBase::MechanicsManager::OT_Assault); - int weapskill = ESM::Skill::HandToHand; if(!weapon.isEmpty()) weapskill = get(weapon).getEquipmentSkill(weapon); @@ -617,6 +613,10 @@ namespace MWClass // NOTE: 'object' and/or 'attacker' may be empty. + // Attacking peaceful NPCs is a crime + if (!attacker.isEmpty() && ptr.getClass().isNpc() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) + MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); + if(!successful) { // TODO: Handle HitAttemptOnMe script function @@ -631,7 +631,7 @@ namespace MWClass if(!attacker.isEmpty() && attacker.getRefData().getHandle() == "player") { - const std::string &script = ptr.get()->mBase->mScript; + const std::string &script = ptr.getClass().getScript(ptr); /* Set the OnPCHitMe script variable. The script is responsible for clearing it. */ if(!script.empty()) ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); From d0500e8124aacbe640fdea6364f3c2c0c85d0df3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 09:47:31 +0100 Subject: [PATCH 525/889] Some unneeded includes cleanup --- apps/openmw/mwclass/npc.cpp | 2 -- apps/openmw/mwgui/birth.cpp | 7 +++---- apps/openmw/mwgui/class.cpp | 8 +++----- apps/openmw/mwgui/race.cpp | 7 +++---- apps/openmw/mwmechanics/enchanting.cpp | 3 +-- apps/openmw/mwscript/guiextensions.cpp | 2 -- apps/openmw/mwscript/statsextensions.cpp | 2 -- apps/openmw/mwscript/transformationextensions.cpp | 5 +---- apps/openmw/mwworld/weather.cpp | 1 - components/bsa/bsa_archive.cpp | 2 ++ components/bsa/bsa_archive.hpp | 2 -- components/nifogre/material.cpp | 2 -- 12 files changed, 13 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 7ba5f340c..ef5665397 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -3,8 +3,6 @@ #include -#include - #include #include diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 965606709..9c8e07f3e 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -1,6 +1,5 @@ #include "birth.hpp" -#include #include #include "../mwbase/environment.hpp" @@ -77,7 +76,7 @@ namespace MWGui size_t count = mBirthList->getItemCount(); for (size_t i = 0; i < count; ++i) { - if (boost::iequals(*mBirthList->getItemDataAt(i), birthId)) + if (Misc::StringUtils::ciEqual(*mBirthList->getItemDataAt(i), birthId)) { mBirthList->setIndexSelected(i); MyGUI::Button* okButton; @@ -112,7 +111,7 @@ namespace MWGui getWidget(okButton, "OKButton"); const std::string *birthId = mBirthList->getItemDataAt(_index); - if (boost::iequals(mCurrentBirthId, *birthId)) + if (Misc::StringUtils::ciEqual(mCurrentBirthId, *birthId)) return; mCurrentBirthId = *birthId; @@ -148,7 +147,7 @@ namespace MWGui mBirthList->setIndexSelected(index); mCurrentBirthId = it2->first; } - else if (boost::iequals(it2->first, mCurrentBirthId)) + else if (Misc::StringUtils::ciEqual(it2->first, mCurrentBirthId)) { mBirthList->setIndexSelected(index); } diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 7965669f1..1e1aebd95 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -1,7 +1,5 @@ #include "class.hpp" -#include - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" @@ -128,7 +126,7 @@ namespace MWGui size_t count = mClassList->getItemCount(); for (size_t i = 0; i < count; ++i) { - if (boost::iequals(*mClassList->getItemDataAt(i), classId)) + if (Misc::StringUtils::ciEqual(*mClassList->getItemDataAt(i), classId)) { mClassList->setIndexSelected(i); MyGUI::Button* okButton; @@ -163,7 +161,7 @@ namespace MWGui getWidget(okButton, "OKButton"); const std::string *classId = mClassList->getItemDataAt(_index); - if (boost::iequals(mCurrentClassId, *classId)) + if (Misc::StringUtils::ciEqual(mCurrentClassId, *classId)) return; mCurrentClassId = *classId; @@ -193,7 +191,7 @@ namespace MWGui mCurrentClassId = id; mClassList->setIndexSelected(index); } - else if (boost::iequals(id, mCurrentClassId)) + else if (Misc::StringUtils::ciEqual(id, mCurrentClassId)) { mClassList->setIndexSelected(index); } diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 2c73226e3..299b34b51 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -1,6 +1,5 @@ #include "race.hpp" -#include #include #include @@ -140,7 +139,7 @@ namespace MWGui size_t count = mRaceList->getItemCount(); for (size_t i = 0; i < count; ++i) { - if (boost::iequals(*mRaceList->getItemDataAt(i), raceId)) + if (Misc::StringUtils::ciEqual(*mRaceList->getItemDataAt(i), raceId)) { mRaceList->setIndexSelected(i); MyGUI::Button* okButton; @@ -230,7 +229,7 @@ namespace MWGui MyGUI::Button* okButton; getWidget(okButton, "OKButton"); const std::string *raceId = mRaceList->getItemDataAt(_index); - if (boost::iequals(mCurrentRaceId, *raceId)) + if (Misc::StringUtils::ciEqual(mCurrentRaceId, *raceId)) return; mCurrentRaceId = *raceId; @@ -320,7 +319,7 @@ namespace MWGui continue; mRaceList->addItem(it->mName, it->mId); - if (boost::iequals(it->mId, mCurrentRaceId)) + if (Misc::StringUtils::ciEqual(it->mId, mCurrentRaceId)) mRaceList->setIndexSelected(index); ++index; } diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 9ccc69f90..6c765aa41 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -6,7 +6,6 @@ #include "creaturestats.hpp" #include "npcstats.hpp" -#include namespace MWMechanics { @@ -60,7 +59,7 @@ namespace MWMechanics store.remove(mSoulGemPtr, 1, player); //Exception for Azura Star, new one will be added after enchanting - if(boost::iequals(mSoulGemPtr.get()->mBase->mId, "Misc_SoulGem_Azura")) + if(Misc::StringUtils::ciEqual(mSoulGemPtr.get()->mBase->mId, "Misc_SoulGem_Azura")) store.add("Misc_SoulGem_Azura", 1, player); if(mSelfEnchanting) diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index b5e2bd293..ab8901881 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -1,8 +1,6 @@ #include "guiextensions.hpp" -#include - #include #include diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index d6c2cb894..095fad7ab 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -3,8 +3,6 @@ #include -#include - #include #include "../mwworld/esmstore.hpp" diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 81aff9958..43a0fedce 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -1,9 +1,5 @@ -#include - -#include #include -#include "../mwworld/esmstore.hpp" #include #include @@ -18,6 +14,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/esmstore.hpp" #include "interpretercontext.hpp" #include "ref.hpp" diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index c34beb3f2..b00ad15ca 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -1,6 +1,5 @@ #include "weather.hpp" -#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index 8f07b9e50..eb741fb10 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -23,6 +23,8 @@ #include "bsa_archive.hpp" +#include + #include #include #include diff --git a/components/bsa/bsa_archive.hpp b/components/bsa/bsa_archive.hpp index 18f7377ff..7f9ebaae1 100644 --- a/components/bsa/bsa_archive.hpp +++ b/components/bsa/bsa_archive.hpp @@ -22,8 +22,6 @@ */ #include -#include -#include #include #ifndef BSA_BSA_ARCHIVE_H diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index e2cc3712b..4dae1a93d 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -10,8 +10,6 @@ #include #include -#include -#include #include From 264736c1391470cc690722f959a722d2c41a476b Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 10:24:49 +0100 Subject: [PATCH 526/889] Remove hopelessly outdated nifogre tests --- components/nifogre/tests/.gitignore | 5 - components/nifogre/tests/Makefile | 19 ---- components/nifogre/tests/ogre_common.cpp | 97 ------------------- .../tests/ogre_manualresource_test.cpp | 40 -------- components/nifogre/tests/ogre_mesh_common.cpp | 69 ------------- components/nifogre/tests/ogre_nif_test.cpp | 50 ---------- .../nifogre/tests/ogre_skeleton_test.cpp | 21 ---- .../tests/output/ogre_manualresource_test.out | 3 - .../nifogre/tests/output/ogre_nif_test.out | 0 .../tests/output/ogre_skeleton_test.out | 1 - components/nifogre/tests/plugins.cfg | 12 --- components/nifogre/tests/test.sh | 18 ---- 12 files changed, 335 deletions(-) delete mode 100644 components/nifogre/tests/.gitignore delete mode 100644 components/nifogre/tests/Makefile delete mode 100644 components/nifogre/tests/ogre_common.cpp delete mode 100644 components/nifogre/tests/ogre_manualresource_test.cpp delete mode 100644 components/nifogre/tests/ogre_mesh_common.cpp delete mode 100644 components/nifogre/tests/ogre_nif_test.cpp delete mode 100644 components/nifogre/tests/ogre_skeleton_test.cpp delete mode 100644 components/nifogre/tests/output/ogre_manualresource_test.out delete mode 100644 components/nifogre/tests/output/ogre_nif_test.out delete mode 100644 components/nifogre/tests/output/ogre_skeleton_test.out delete mode 100644 components/nifogre/tests/plugins.cfg delete mode 100755 components/nifogre/tests/test.sh diff --git a/components/nifogre/tests/.gitignore b/components/nifogre/tests/.gitignore deleted file mode 100644 index 1a5569983..000000000 --- a/components/nifogre/tests/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -*.png -meshlist.txt -ogre.cfg -*_test -chris* diff --git a/components/nifogre/tests/Makefile b/components/nifogre/tests/Makefile deleted file mode 100644 index a7c50d100..000000000 --- a/components/nifogre/tests/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -GCC=g++ - -all: ogre_manualresource_test ogre_nif_test ogre_skeleton_test - -I_OGRE=$(shell pkg-config --cflags OGRE) -L_OGRE=$(shell pkg-config --libs OGRE) - -ogre_manualresource_test: ogre_manualresource_test.cpp - $(GCC) $^ -o $@ $(I_OGRE) $(L_OGRE) - -ogre_skeleton_test: ogre_skeleton_test.cpp - $(GCC) $^ -o $@ $(I_OGRE) $(L_OGRE) - -ogre_nif_test: ogre_nif_test.cpp ../../nif/nif_file.cpp ../../bsa/bsa_file.cpp ../../bsa/bsa_archive.cpp ../../tools/stringops.cpp ../../mangle/vfs/servers/ogre_vfs.cpp ../ogre_nif_loader.cpp - $(GCC) $^ -o $@ $(I_OGRE) $(L_OGRE) - -clean: - rm *_test - diff --git a/components/nifogre/tests/ogre_common.cpp b/components/nifogre/tests/ogre_common.cpp deleted file mode 100644 index 657913f30..000000000 --- a/components/nifogre/tests/ogre_common.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include -#include - -using namespace std; -using namespace Ogre; - -Root *root; -RenderWindow *window; -SceneManager *mgr; - -int shot = 0; - -// Lets you quit by closing the window -struct QuitListener : FrameListener -{ - bool frameStarted(const FrameEvent& evt) - { -#ifdef SCREENSHOT - if(shot == 1) window->writeContentsToFile("nif.png"); - if(shot < 2) shot++; -#endif - - if(window->isClosed()) - return false; - return true; - } -} qlistener; - -// This has to be packaged in a struct because C++ sucks -struct C -{ - static void doTest(); -}; - -int main(int argc, char**args) -{ - // Disable Ogre logging - new LogManager; - Log *log = LogManager::getSingleton().createLog(""); - log->setDebugOutputEnabled(false); - - // Set up Root. - root = new Root("plugins.cfg","ogre.cfg",""); - - if(!root->restoreConfig()) - { - cout << "WARNING: we do NOT recommend fullscreen mode!\n"; - if(!root->showConfigDialog()) - return 1; - } - - mgr = root->createSceneManager(ST_GENERIC); - - // Only render if there are arguments on the command line (we don't - // care what they are.) - bool render = (argc>=2); - - // Create a window - window = root->initialise(true, "Test"); - if(render) - { - // More initialization - Camera *cam = mgr->createCamera("cam"); - Viewport *vp = window->addViewport(cam); - cam->setAspectRatio(Real(vp->getActualWidth()) / Real(vp->getActualHeight())); - cam->setFOVy(Degree(55)); - cam->setPosition(0,0,0); - cam->lookAt(0,0,10); - cam->setNearClipDistance(1); - - root->addFrameListener(&qlistener); - - // Background color - vp->setBackgroundColour(ColourValue(0.5,0.5,0.5)); - - mgr->setAmbientLight(ColourValue(1,1,1)); - } - - // Run the actual test - C::doTest(); - - // Render loop - if(render) - { - cout << "Rendering. Close the window to exit.\n"; - root->startRendering(); - } - - // Cleanup - delete root; - return 0; -} - -void doTest() -{ - cout << "hello\n"; -} diff --git a/components/nifogre/tests/ogre_manualresource_test.cpp b/components/nifogre/tests/ogre_manualresource_test.cpp deleted file mode 100644 index 75e169d54..000000000 --- a/components/nifogre/tests/ogre_manualresource_test.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - This is a test of the manual resource loader interface to Ogre, - applied to manually created meshes. It defines a simple mesh - consisting of two triangles, and creates three instances of it as - different meshes using the same loader. It is a precursor to the NIF - loading code. If the Ogre interface changes and you have to change - this test, then you will also have to change parts of the NIF - loader. - */ - -#include "ogre_mesh_common.cpp" - -void C::doTest() -{ - // Create a couple of manual meshes - makeMesh("mesh1.mm"); - makeMesh("mesh2.mm"); - makeMesh("mesh3.mm"); - - // Display the meshes - { - SceneNode *node = mgr->getRootSceneNode()->createChildSceneNode("node"); - Entity *ent = mgr->createEntity("Mesh1", "mesh1.mm"); - node->attachObject(ent); - node->setPosition(3,1,8); - } - - { - SceneNode *node = mgr->getRootSceneNode()->createChildSceneNode("node2"); - Entity *ent = mgr->createEntity("Mesh2", "mesh2.mm"); - node->attachObject(ent); - node->setPosition(-3,1,8); - } - { - SceneNode *node = mgr->getRootSceneNode()->createChildSceneNode("node3"); - Entity *ent = mgr->createEntity("Mesh3", "mesh3.mm"); - node->attachObject(ent); - node->setPosition(0,-2,8); - } -} diff --git a/components/nifogre/tests/ogre_mesh_common.cpp b/components/nifogre/tests/ogre_mesh_common.cpp deleted file mode 100644 index 72e51e331..000000000 --- a/components/nifogre/tests/ogre_mesh_common.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "ogre_common.cpp" - -struct MyMeshLoader : ManualResourceLoader -{ - void loadResource(Resource *resource) - { - Mesh *mesh = dynamic_cast(resource); - assert(mesh); - - const String& name = mesh->getName(); - cout << "Manually loading mesh " << name << endl; - - // Create the mesh here - int numVerts = 4; - int numFaces = 2*3; - const float vertices[] = - { -1,-1,0, 1,-1,0, - 1,1,0, -1,1,0 }; - - const short faces[] = - { 0,2,1, 0,3,2 }; - - mesh->sharedVertexData = new VertexData(); - mesh->sharedVertexData->vertexCount = numVerts; - - VertexDeclaration* decl = mesh->sharedVertexData->vertexDeclaration; - - decl->addElement(0, 0, VET_FLOAT3, VES_POSITION); - - HardwareVertexBufferSharedPtr vbuf = - HardwareBufferManager::getSingleton().createVertexBuffer( - VertexElement::getTypeSize(VET_FLOAT3), - numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY); - - // Upload the vertex data to the card - vbuf->writeData(0, vbuf->getSizeInBytes(), vertices, true); - - // Set vertex buffer binding so buffer 0 is bound to our vertex buffer - VertexBufferBinding* bind = mesh->sharedVertexData->vertexBufferBinding; - bind->setBinding(0, vbuf); - - /// Allocate index buffer of the requested number of faces - HardwareIndexBufferSharedPtr ibuf = HardwareBufferManager::getSingleton(). - createIndexBuffer(HardwareIndexBuffer::IT_16BIT, - numFaces, - HardwareBuffer::HBU_STATIC_WRITE_ONLY); - - /// Upload the index data to the card - ibuf->writeData(0, ibuf->getSizeInBytes(), faces, true); - - SubMesh* sub = mesh->createSubMesh(name+"tris"); - sub->useSharedVertices = true; - - /// Set parameters of the submesh - sub->indexData->indexBuffer = ibuf; - sub->indexData->indexCount = numFaces; - sub->indexData->indexStart = 0; - - mesh->_setBounds(AxisAlignedBox(-1.1,-1.1,-1.1,1.1,1.1,1.1)); - mesh->_setBoundingSphereRadius(2); - } -}; - -MyMeshLoader mml; - -MeshPtr makeMesh(const string &name) -{ - return MeshManager::getSingleton().createManual(name, "General", &mml); -} diff --git a/components/nifogre/tests/ogre_nif_test.cpp b/components/nifogre/tests/ogre_nif_test.cpp deleted file mode 100644 index decd43df5..000000000 --- a/components/nifogre/tests/ogre_nif_test.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "../ogre_nif_loader.hpp" -#include "../../bsa/bsa_archive.hpp" - -//#define SCREENSHOT - -#include "ogre_common.cpp" - -//const char* mesh = "meshes\\a\\towershield_steel.nif"; -//const char* mesh = "meshes\\r\\bonelord.nif"; -//const char* mesh = "meshes\\m\\text_scroll_open_01.nif"; -const char* mesh = "meshes\\f\\ex_ashl_a_banner_r.nif"; - -void C::doTest() -{ - // Add Morrowind.bsa resource location - Bsa::addBSA("../../data/Morrowind.bsa"); - - // Insert the mesh - NifOgre::NIFLoader::load(mesh); - NifOgre::NIFLoader::load(mesh); - - /* - SceneNode *node = mgr->getRootSceneNode()->createChildSceneNode("node"); - Entity *ent = mgr->createEntity("Mesh1", mesh); - node->attachObject(ent); - - // Works great for the scroll - node->setPosition(0,4,50); - node->pitch(Degree(20)); - node->roll(Degree(10)); - node->yaw(Degree(-10)); - - /* Bone lord - node->setPosition(0,-70,170); - node->pitch(Degree(-90)); - */ - - // Display it from two different angles - shield and banner - const int sep = 45; - SceneNode *node = mgr->getRootSceneNode()->createChildSceneNode("node"); - Entity *ent = mgr->createEntity("Mesh1", mesh); - node->attachObject(ent); - node->setPosition(sep,0,130); - node = node->createChildSceneNode("node2"); - ent = mgr->createEntity("Mesh2", mesh); - node->attachObject(ent); - node->setPosition(-2*sep,0,0); - node->yaw(Degree(180)); - //*/ -} diff --git a/components/nifogre/tests/ogre_skeleton_test.cpp b/components/nifogre/tests/ogre_skeleton_test.cpp deleted file mode 100644 index df9139b95..000000000 --- a/components/nifogre/tests/ogre_skeleton_test.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "ogre_common.cpp" - -void C::doTest() -{ - SkeletonManager &skm = SkeletonManager::getSingleton(); - - SkeletonPtr skp = skm.create("MySkel", "General"); - - cout << "hello\n"; - /* - MeshPtr msh = makeMesh("mesh1"); - - // Display the mesh - { - SceneNode *node = mgr->getRootSceneNode()->createChildSceneNode("node"); - Entity *ent = mgr->createEntity("Mesh1", "mesh1"); - node->attachObject(ent); - node->setPosition(0,0,4); - } - */ -} diff --git a/components/nifogre/tests/output/ogre_manualresource_test.out b/components/nifogre/tests/output/ogre_manualresource_test.out deleted file mode 100644 index 2eab2d50d..000000000 --- a/components/nifogre/tests/output/ogre_manualresource_test.out +++ /dev/null @@ -1,3 +0,0 @@ -Manually loading mesh mesh1.mm -Manually loading mesh mesh2.mm -Manually loading mesh mesh3.mm diff --git a/components/nifogre/tests/output/ogre_nif_test.out b/components/nifogre/tests/output/ogre_nif_test.out deleted file mode 100644 index e69de29bb..000000000 diff --git a/components/nifogre/tests/output/ogre_skeleton_test.out b/components/nifogre/tests/output/ogre_skeleton_test.out deleted file mode 100644 index ce0136250..000000000 --- a/components/nifogre/tests/output/ogre_skeleton_test.out +++ /dev/null @@ -1 +0,0 @@ -hello diff --git a/components/nifogre/tests/plugins.cfg b/components/nifogre/tests/plugins.cfg deleted file mode 100644 index 9133aec32..000000000 --- a/components/nifogre/tests/plugins.cfg +++ /dev/null @@ -1,12 +0,0 @@ -# Defines plugins to load - -# Define plugin folder -PluginFolder=/usr/local/lib/OGRE - -# Define plugins -Plugin=RenderSystem_GL -Plugin=Plugin_ParticleFX -Plugin=Plugin_OctreeSceneManager - - - diff --git a/components/nifogre/tests/test.sh b/components/nifogre/tests/test.sh deleted file mode 100755 index 2d07708ad..000000000 --- a/components/nifogre/tests/test.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -make || exit - -mkdir -p output - -PROGS=*_test - -for a in $PROGS; do - if [ -f "output/$a.out" ]; then - echo "Running $a:" - ./$a | diff output/$a.out - - else - echo "Creating $a.out" - ./$a > "output/$a.out" - git add "output/$a.out" - fi -done From d4aeb177f9c63377fcf5eb2788ce4e5af9d3651b Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 10:47:56 +0100 Subject: [PATCH 527/889] Remove unused btKinematicCharacterController --- CMakeLists.txt | 2 - .../bullet/btKinematicCharacterController.cpp | 643 ------------------ .../bullet/btKinematicCharacterController.h | 168 ----- libs/openengine/bullet/physic.cpp | 1 - 4 files changed, 814 deletions(-) delete mode 100644 libs/openengine/bullet/btKinematicCharacterController.cpp delete mode 100644 libs/openengine/bullet/btKinematicCharacterController.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5cb2fd5b2..b365e162f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,8 +93,6 @@ set(OENGINE_GUI ) set(OENGINE_BULLET - ${LIBDIR}/openengine/bullet/btKinematicCharacterController.cpp - ${LIBDIR}/openengine/bullet/btKinematicCharacterController.h ${LIBDIR}/openengine/bullet/BtOgre.cpp ${LIBDIR}/openengine/bullet/BtOgreExtras.h ${LIBDIR}/openengine/bullet/BtOgreGP.h diff --git a/libs/openengine/bullet/btKinematicCharacterController.cpp b/libs/openengine/bullet/btKinematicCharacterController.cpp deleted file mode 100644 index fc4f3278f..000000000 --- a/libs/openengine/bullet/btKinematicCharacterController.cpp +++ /dev/null @@ -1,643 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -#include "LinearMath/btIDebugDraw.h" -#include "BulletCollision/CollisionDispatch/btGhostObject.h" -#include "BulletCollision/CollisionShapes/btMultiSphereShape.h" -#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h" -#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" -#include "BulletCollision/CollisionDispatch/btCollisionWorld.h" -#include "LinearMath/btDefaultMotionState.h" -#include "btKinematicCharacterController.h" - -///@todo Interact with dynamic objects, -///Ride kinematicly animated platforms properly -///Support ducking -class btKinematicClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback -{ -public: - btKinematicClosestNotMeRayResultCallback (btCollisionObject* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) - { - m_me[0] = me; - count = 1; - } - - btKinematicClosestNotMeRayResultCallback (btCollisionObject* me[], int count_) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) - { - count = count_; - - for(int i = 0; i < count; i++) - m_me[i] = me[i]; - } - - virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) - { - for(int i = 0; i < count; i++) - if (rayResult.m_collisionObject == m_me[i]) - return 1.0; - - return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace); - } -protected: - btCollisionObject* m_me[10]; - int count; -}; - -class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback -{ -public: - btKinematicClosestNotMeConvexResultCallback( btCollisionObject* me, const btVector3& up, btScalar minSlopeDot ) - : btCollisionWorld::ClosestConvexResultCallback( btVector3( 0.0, 0.0, 0.0 ), btVector3( 0.0, 0.0, 0.0 ) ), - m_me( me ), m_up( up ), m_minSlopeDot( minSlopeDot ) - { - } - - virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace) - { - if( convexResult.m_hitCollisionObject == m_me ) - return btScalar( 1 ); - - btVector3 hitNormalWorld; - if( normalInWorldSpace ) - { - hitNormalWorld = convexResult.m_hitNormalLocal; - } - else - { - ///need to transform normal into worldspace - hitNormalWorld = m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal; - } - - // NOTE : m_hitNormalLocal is not always vertical on the ground with a capsule or a box... - - btScalar dotUp = m_up.dot(hitNormalWorld); - if( dotUp < m_minSlopeDot ) - return btScalar( 1 ); - - return ClosestConvexResultCallback::addSingleResult (convexResult, normalInWorldSpace); - } - -protected: - btCollisionObject* m_me; - const btVector3 m_up; - btScalar m_minSlopeDot; -}; - - - -btKinematicCharacterController::btKinematicCharacterController( btPairCachingGhostObject* externalGhostObject_, - btPairCachingGhostObject* internalGhostObject_, - btScalar stepHeight, - btScalar constantScale, - btScalar gravity, - btScalar fallVelocity, - btScalar jumpVelocity, - btScalar recoveringFactor ) -{ - m_upAxis = btKinematicCharacterController::Y_AXIS; - - m_walkDirection.setValue( btScalar( 0 ), btScalar( 0 ), btScalar( 0 ) ); - - m_useGhostObjectSweepTest = true; - - externalGhostObject = externalGhostObject_; - internalGhostObject = internalGhostObject_; - - m_recoveringFactor = recoveringFactor; - - m_stepHeight = stepHeight; - - m_useWalkDirection = true; // use walk direction by default, legacy behavior - m_velocityTimeInterval = btScalar( 0 ); - m_verticalVelocity = btScalar( 0 ); - m_verticalOffset = btScalar( 0 ); - - m_gravity = constantScale * gravity; - m_fallSpeed = constantScale * fallVelocity; // Terminal velocity of a sky diver in m/s. - - m_jumpSpeed = constantScale * jumpVelocity; // ? - m_wasJumping = false; - - setMaxSlope( btRadians( 45.0 ) ); - - mCollision = true; -} - - -btKinematicCharacterController::~btKinematicCharacterController () -{ -} - -void btKinematicCharacterController::setVerticalVelocity(float z) -{ - m_verticalVelocity = z; -} - -bool btKinematicCharacterController::recoverFromPenetration( btCollisionWorld* collisionWorld ) -{ - bool penetration = false; - - if(!mCollision) return penetration; - - collisionWorld->getDispatcher()->dispatchAllCollisionPairs( internalGhostObject->getOverlappingPairCache(), - collisionWorld->getDispatchInfo(), - collisionWorld->getDispatcher() ); - - btVector3 currentPosition = internalGhostObject->getWorldTransform().getOrigin(); - - btScalar maxPen = btScalar( 0 ); - - for( int i = 0; i < internalGhostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++ ) - { - m_manifoldArray.resize(0); - - btBroadphasePair* collisionPair = &internalGhostObject->getOverlappingPairCache()->getOverlappingPairArray()[i]; - - if( collisionPair->m_algorithm ) - collisionPair->m_algorithm->getAllContactManifolds( m_manifoldArray ); - - - for( int j = 0; j < m_manifoldArray.size(); j++ ) - { - btPersistentManifold* manifold = m_manifoldArray[j]; - - btScalar directionSign = manifold->getBody0() == internalGhostObject ? btScalar( -1.0 ) : btScalar( 1.0 ); - - for( int p = 0; p < manifold->getNumContacts(); p++ ) - { - const btManifoldPoint&pt = manifold->getContactPoint( p ); - if( (manifold->getBody1() == externalGhostObject && manifold->getBody0() == internalGhostObject) - ||(manifold->getBody0() == externalGhostObject && manifold->getBody1() == internalGhostObject) ) - { - } - else - { - btScalar dist = pt.getDistance(); - - if( dist < 0.0 ) - { - if( dist < maxPen ) - maxPen = dist; - - // NOTE : btScalar affects the stairs but the parkinson... - // 0.0 , the capsule can break the walls... - currentPosition += pt.m_normalWorldOnB * directionSign * dist * m_recoveringFactor; - - penetration = true; - } - } - } - - // ??? - //manifold->clearManifold(); - } - } - - btTransform transform = internalGhostObject->getWorldTransform(); - - transform.setOrigin( currentPosition ); - - internalGhostObject->setWorldTransform( transform ); - externalGhostObject->setWorldTransform( transform ); - - return penetration; -} - - -btVector3 btKinematicCharacterController::stepUp( btCollisionWorld* world, const btVector3& currentPosition, btScalar& currentStepOffset ) -{ - btVector3 targetPosition = currentPosition + getUpAxisDirections()[ m_upAxis ] * ( m_stepHeight + ( m_verticalOffset > btScalar( 0.0 ) ? m_verticalOffset : 0.0 ) ); - - //if the no collisions mode is on, no need to go any further - if(!mCollision) - { - currentStepOffset = m_stepHeight; - return targetPosition; - } - - // Retrieve the collision shape - // - btCollisionShape* collisionShape = externalGhostObject->getCollisionShape(); - btAssert( collisionShape->isConvex() ); - - btConvexShape* convexShape = ( btConvexShape* )collisionShape; - - // FIXME: Handle penetration properly - // - btTransform start; - start.setIdentity(); - start.setOrigin( currentPosition + getUpAxisDirections()[ m_upAxis ] * ( convexShape->getMargin() ) ); - - btTransform end; - end.setIdentity(); - end.setOrigin( targetPosition ); - - btKinematicClosestNotMeConvexResultCallback callback( externalGhostObject, -getUpAxisDirections()[ m_upAxis ], m_maxSlopeCosine ); - callback.m_collisionFilterGroup = externalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup; - callback.m_collisionFilterMask = externalGhostObject->getBroadphaseHandle()->m_collisionFilterMask; - - // Sweep test - // - if( m_useGhostObjectSweepTest ) - externalGhostObject->convexSweepTest( convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration ); - - else - world->convexSweepTest( convexShape, start, end, callback ); - - if( callback.hasHit() ) - { - // Only modify the position if the hit was a slope and not a wall or ceiling. - // - if( callback.m_hitNormalWorld.dot(getUpAxisDirections()[m_upAxis]) > btScalar( 0.0 ) ) - { - // We moved up only a fraction of the step height - // - currentStepOffset = m_stepHeight * callback.m_closestHitFraction; - - return currentPosition.lerp( targetPosition, callback.m_closestHitFraction ); - } - - m_verticalVelocity = btScalar( 0.0 ); - m_verticalOffset = btScalar( 0.0 ); - - return currentPosition; - } - else - { - currentStepOffset = m_stepHeight; - return targetPosition; - } -} - - -///Reflect the vector d around the vector r -inline btVector3 reflect( const btVector3& d, const btVector3& r ) -{ - return d - ( btScalar( 2.0 ) * d.dot( r ) ) * r; -} - - -///Project a vector u on another vector v -inline btVector3 project( const btVector3& u, const btVector3& v ) -{ - return v * u.dot( v ); -} - - -///Helper for computing the character sliding -inline btVector3 slide( const btVector3& direction, const btVector3& planeNormal ) -{ - return direction - project( direction, planeNormal ); -} - - - -btVector3 slideOnCollision( const btVector3& fromPosition, const btVector3& toPosition, const btVector3& hitNormal ) -{ - btVector3 moveDirection = toPosition - fromPosition; - btScalar moveLength = moveDirection.length(); - - if( moveLength <= btScalar( SIMD_EPSILON ) ) - return toPosition; - - moveDirection.normalize(); - - btVector3 reflectDir = reflect( moveDirection, hitNormal ); - reflectDir.normalize(); - - return fromPosition + slide( reflectDir, hitNormal ) * moveLength; -} - - -btVector3 btKinematicCharacterController::stepForwardAndStrafe( btCollisionWorld* collisionWorld, const btVector3& currentPosition, const btVector3& walkMove ) -{ - // We go to ! - // - btVector3 targetPosition = currentPosition + walkMove; - - //if the no collisions mode is on, no need to go any further - if(!mCollision) return targetPosition; - - // Retrieve the collision shape - // - btCollisionShape* collisionShape = externalGhostObject->getCollisionShape(); - btAssert( collisionShape->isConvex() ); - - btConvexShape* convexShape = ( btConvexShape* )collisionShape; - - btTransform start; - start.setIdentity(); - - btTransform end; - end.setIdentity(); - - btScalar fraction = btScalar( 1.0 ); - - // This optimization scheme suffers in the corners. - // It basically jumps from a wall to another, then fails to find a new - // position (after 4 iterations here) and finally don't move at all. - // - // The stepping algorithm adds some problems with stairs. It seems - // the treads create some fake corner using capsules for collisions. - // - for( int i = 0; i < 4 && fraction > btScalar( 0.01 ); i++ ) - { - start.setOrigin( currentPosition ); - end.setOrigin( targetPosition ); - - btVector3 sweepDirNegative = currentPosition - targetPosition; - - btKinematicClosestNotMeConvexResultCallback callback( externalGhostObject, sweepDirNegative, btScalar( 0.0 ) ); - callback.m_collisionFilterGroup = externalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup; - callback.m_collisionFilterMask = externalGhostObject->getBroadphaseHandle()->m_collisionFilterMask; - - if( m_useGhostObjectSweepTest ) - externalGhostObject->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration ); - - else - collisionWorld->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration ); - - if( callback.hasHit() ) - { - // Try another target position - // - targetPosition = slideOnCollision( currentPosition, targetPosition, callback.m_hitNormalWorld ); - fraction = callback.m_closestHitFraction; - } - else - - // Move to the valid target position - // - return targetPosition; - } - - // Don't move if you can't find a valid target position... - // It prevents some flickering. - // - return currentPosition; -} - - -///Handle the gravity -btScalar btKinematicCharacterController::addFallOffset( bool wasOnGround, btScalar currentStepOffset, btScalar dt ) -{ - btScalar downVelocity = ( m_verticalVelocity < 0.0 ? -m_verticalVelocity : btScalar( 0.0 ) ) * dt; - - if( downVelocity > btScalar( 0.0 ) && downVelocity < m_stepHeight && ( wasOnGround || !m_wasJumping ) ) - downVelocity = m_stepHeight; - - return currentStepOffset + downVelocity; -} - - -btVector3 btKinematicCharacterController::stepDown( btCollisionWorld* collisionWorld, const btVector3& currentPosition, btScalar currentStepOffset ) -{ - btVector3 stepDrop = getUpAxisDirections()[ m_upAxis ] * currentStepOffset; - - // Be sure we are falling from the last m_currentPosition - // It prevents some flickering - // - btVector3 targetPosition = currentPosition - stepDrop; - - //if the no collisions mode is on, no need to go any further - if(!mCollision) return targetPosition; - - btTransform start; - start.setIdentity(); - start.setOrigin( currentPosition ); - - btTransform end; - end.setIdentity(); - end.setOrigin( targetPosition ); - - btKinematicClosestNotMeConvexResultCallback callback( internalGhostObject, getUpAxisDirections()[ m_upAxis ], m_maxSlopeCosine ); - callback.m_collisionFilterGroup = internalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup; - callback.m_collisionFilterMask = internalGhostObject->getBroadphaseHandle()->m_collisionFilterMask; - - // Retrieve the collision shape - // - btCollisionShape* collisionShape = internalGhostObject->getCollisionShape(); - btAssert( collisionShape->isConvex() ); - btConvexShape* convexShape = ( btConvexShape* )collisionShape; - - if( m_useGhostObjectSweepTest ) - externalGhostObject->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration ); - - else - collisionWorld->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration ); - - if( callback.hasHit() ) - { - m_verticalVelocity = btScalar( 0.0 ); - m_verticalOffset = btScalar( 0.0 ); - m_wasJumping = false; - - // We dropped a fraction of the height -> hit floor - // - return currentPosition.lerp( targetPosition, callback.m_closestHitFraction ); - } - else - - // We dropped the full height - // - return targetPosition; -} - - - -void btKinematicCharacterController::setWalkDirection( const btVector3& walkDirection ) -{ - m_useWalkDirection = true; - m_walkDirection = walkDirection; -} - - -void btKinematicCharacterController::setVelocityForTimeInterval( const btVector3& velocity, btScalar timeInterval ) -{ - m_useWalkDirection = false; - m_walkDirection = velocity; - m_velocityTimeInterval = timeInterval; -} - - -void btKinematicCharacterController::reset() -{ -} - - -void btKinematicCharacterController::warp( const btVector3& origin ) -{ - btTransform transform; - transform.setIdentity(); - transform.setOrigin( -origin ); - - externalGhostObject->setWorldTransform( transform ); - internalGhostObject->setWorldTransform( transform ); -} - - -void btKinematicCharacterController::preStep( btCollisionWorld* collisionWorld ) -{ - BT_PROFILE( "preStep" ); - - for( int i = 0; i < 4 && recoverFromPenetration ( collisionWorld ); i++ ); -} - - -void btKinematicCharacterController::playerStep( btCollisionWorld* collisionWorld, btScalar dt ) -{ - BT_PROFILE( "playerStep" ); - - if( !m_useWalkDirection && m_velocityTimeInterval <= btScalar( 0.0 ) ) - return; - - bool wasOnGround = onGround(); - - // Handle the gravity - // - m_verticalVelocity -= m_gravity * dt; - - if( m_verticalVelocity > 0.0 && m_verticalVelocity > m_jumpSpeed ) - m_verticalVelocity = m_jumpSpeed; - - if( m_verticalVelocity < 0.0 && btFabs( m_verticalVelocity ) > btFabs( m_fallSpeed ) ) - m_verticalVelocity = -btFabs( m_fallSpeed ); - - m_verticalOffset = m_verticalVelocity * dt; - - // This forced stepping up can cause problems when the character - // walks (jump in fact...) under too low ceilings. - // - btVector3 currentPosition = externalGhostObject->getWorldTransform().getOrigin(); - btScalar currentStepOffset; - - currentPosition = stepUp( collisionWorld, currentPosition, currentStepOffset ); - - // Move in the air and slide against the walls ignoring the stair steps. - // - if( m_useWalkDirection ) - currentPosition = stepForwardAndStrafe( collisionWorld, currentPosition, m_walkDirection ); - - else - { - btScalar dtMoving = ( dt < m_velocityTimeInterval ) ? dt : m_velocityTimeInterval; - m_velocityTimeInterval -= dt; - - // How far will we move while we are moving ? - // - btVector3 moveDirection = m_walkDirection * dtMoving; - - currentPosition = stepForwardAndStrafe( collisionWorld, currentPosition, moveDirection ); - } - - // Finally find the ground. - // - currentStepOffset = addFallOffset( wasOnGround, currentStepOffset, dt ); - - currentPosition = stepDown( collisionWorld, currentPosition, currentStepOffset ); - - // Apply the new position to the collision objects. - // - btTransform tranform; - tranform = externalGhostObject->getWorldTransform(); - tranform.setOrigin( currentPosition ); - - externalGhostObject->setWorldTransform( tranform ); - internalGhostObject->setWorldTransform( tranform ); -} - - -void btKinematicCharacterController::setFallSpeed( btScalar fallSpeed ) -{ - m_fallSpeed = fallSpeed; -} - - -void btKinematicCharacterController::setJumpSpeed( btScalar jumpSpeed ) -{ - m_jumpSpeed = jumpSpeed; -} - - -void btKinematicCharacterController::setMaxJumpHeight( btScalar maxJumpHeight ) -{ - m_maxJumpHeight = maxJumpHeight; -} - - -bool btKinematicCharacterController::canJump() const -{ - return onGround(); -} - - -void btKinematicCharacterController::jump() -{ - if( !canJump() ) - return; - - m_verticalVelocity = m_jumpSpeed; - m_wasJumping = true; -} - - -void btKinematicCharacterController::setGravity( btScalar gravity ) -{ - m_gravity = gravity; -} - - -btScalar btKinematicCharacterController::getGravity() const -{ - return m_gravity; -} - - -void btKinematicCharacterController::setMaxSlope( btScalar slopeRadians ) -{ - m_maxSlopeRadians = slopeRadians; - m_maxSlopeCosine = btCos( slopeRadians ); -} - - -btScalar btKinematicCharacterController::getMaxSlope() const -{ - return m_maxSlopeRadians; -} - - -bool btKinematicCharacterController::onGround() const -{ - return btFabs( m_verticalVelocity ) < btScalar( SIMD_EPSILON ) && - btFabs( m_verticalOffset ) < btScalar( SIMD_EPSILON ); -} - - -btVector3* btKinematicCharacterController::getUpAxisDirections() -{ - static btVector3 sUpAxisDirection[] = - { - btVector3( btScalar( 0.0 ), btScalar( 0.0 ), btScalar( 0.0 ) ), - btVector3( btScalar( 0.0 ), btScalar( 1.0 ), btScalar( 0.0 ) ), - btVector3( btScalar( 0.0 ), btScalar( 0.0 ), btScalar( 1.0 ) ) - }; - - return sUpAxisDirection; -} - - -void btKinematicCharacterController::debugDraw( btIDebugDraw* debugDrawer ) -{ -} diff --git a/libs/openengine/bullet/btKinematicCharacterController.h b/libs/openengine/bullet/btKinematicCharacterController.h deleted file mode 100644 index d24cd9722..000000000 --- a/libs/openengine/bullet/btKinematicCharacterController.h +++ /dev/null @@ -1,168 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef KINEMATIC_CHARACTER_CONTROLLER_H -#define KINEMATIC_CHARACTER_CONTROLLER_H - -#include "LinearMath/btVector3.h" -#include "LinearMath/btQuickprof.h" - -#include "BulletDynamics/Character/btCharacterControllerInterface.h" - -#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" - - -class btCollisionShape; -class btRigidBody; -class btCollisionWorld; -class btCollisionDispatcher; -class btPairCachingGhostObject; - -///btKinematicCharacterController is an object that supports a sliding motion in a world. -///It uses a ghost object and convex sweep test to test for upcoming collisions. This is combined with discrete collision detection to recover from penetrations. -///Interaction between btKinematicCharacterController and dynamic rigid bodies needs to be explicity implemented by the user. -class btKinematicCharacterController : public btCharacterControllerInterface -{ -public: - enum UpAxis - { - X_AXIS = 0, - Y_AXIS = 1, - Z_AXIS = 2 - }; - -private: - btPairCachingGhostObject* externalGhostObject; // use this for querying collisions for sliding and move - btPairCachingGhostObject* internalGhostObject; // and this for recoreving from penetrations - - btScalar m_verticalVelocity; - btScalar m_verticalOffset; - btScalar m_fallSpeed; - btScalar m_jumpSpeed; - btScalar m_maxJumpHeight; - btScalar m_maxSlopeRadians; // Slope angle that is set (used for returning the exact value) - btScalar m_maxSlopeCosine; // Cosine equivalent of m_maxSlopeRadians (calculated once when set, for optimization) - btScalar m_gravity; - btScalar m_recoveringFactor; - - btScalar m_stepHeight; - - ///this is the desired walk direction, set by the user - btVector3 m_walkDirection; - - ///keep track of the contact manifolds - btManifoldArray m_manifoldArray; - - ///Gravity attributes - bool m_wasJumping; - - bool m_useGhostObjectSweepTest; - bool m_useWalkDirection; - btScalar m_velocityTimeInterval; - - UpAxis m_upAxis; - - static btVector3* getUpAxisDirections(); - - bool recoverFromPenetration ( btCollisionWorld* collisionWorld ); - - btVector3 stepUp( btCollisionWorld* collisionWorld, const btVector3& currentPosition, btScalar& currentStepOffset ); - btVector3 stepForwardAndStrafe( btCollisionWorld* collisionWorld, const btVector3& currentPosition, const btVector3& walkMove ); - btScalar addFallOffset( bool wasJumping, btScalar currentStepOffset, btScalar dt ); - btVector3 stepDown( btCollisionWorld* collisionWorld, const btVector3& currentPosition, btScalar currentStepOffset ); - -public: - /// externalGhostObject is used for querying the collisions for sliding along the wall, - /// and internalGhostObject is used for querying the collisions for recovering from large penetrations. - /// These parameters can point on the same object. - /// Using a smaller internalGhostObject can help for removing some flickering but create some - /// stopping artefacts when sliding along stairs or small walls. - /// Don't forget to scale gravity and fallSpeed if you scale the world. - btKinematicCharacterController( btPairCachingGhostObject* externalGhostObject, - btPairCachingGhostObject* internalGhostObject, - btScalar stepHeight, - btScalar constantScale = btScalar( 1.0 ), - btScalar gravity = btScalar( 9.8 ), - btScalar fallVelocity = btScalar( 55.0 ), - btScalar jumpVelocity = btScalar( 9.8 ), - btScalar recoveringFactor = btScalar( 0.2 ) ); - - ~btKinematicCharacterController (); - - void setVerticalVelocity(float z); - - ///btActionInterface interface - virtual void updateAction( btCollisionWorld* collisionWorld, btScalar deltaTime ) - { - preStep( collisionWorld ); - playerStep( collisionWorld, deltaTime ); - } - - ///btActionInterface interface - void debugDraw( btIDebugDraw* debugDrawer ); - - void setUpAxis( UpAxis axis ) - { - m_upAxis = axis; - } - - /// This should probably be called setPositionIncrementPerSimulatorStep. - /// This is neither a direction nor a velocity, but the amount to - /// increment the position each simulation iteration, regardless - /// of dt. - /// This call will reset any velocity set by setVelocityForTimeInterval(). - virtual void setWalkDirection(const btVector3& walkDirection); - - /// Caller provides a velocity with which the character should move for - /// the given time period. After the time period, velocity is reset - /// to zero. - /// This call will reset any walk direction set by setWalkDirection(). - /// Negative time intervals will result in no motion. - virtual void setVelocityForTimeInterval(const btVector3& velocity, - btScalar timeInterval); - - void reset(); - void warp( const btVector3& origin ); - - void preStep( btCollisionWorld* collisionWorld ); - void playerStep( btCollisionWorld* collisionWorld, btScalar dt ); - - void setFallSpeed( btScalar fallSpeed ); - void setJumpSpeed( btScalar jumpSpeed ); - void setMaxJumpHeight( btScalar maxJumpHeight ); - bool canJump() const; - - void jump(); - - void setGravity( btScalar gravity ); - btScalar getGravity() const; - - /// The max slope determines the maximum angle that the controller can walk up. - /// The slope angle is measured in radians. - void setMaxSlope( btScalar slopeRadians ); - btScalar getMaxSlope() const; - - void setUseGhostSweepTest( bool useGhostObjectSweepTest ) - { - m_useGhostObjectSweepTest = useGhostObjectSweepTest; - } - - bool onGround() const; - - //if set to false, there will be no collision. - bool mCollision; -}; - -#endif // KINEMATIC_CHARACTER_CONTROLLER_H diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 4e80088bf..481b99bad 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -4,7 +4,6 @@ #include #include #include "OgreRoot.h" -#include "btKinematicCharacterController.h" #include "BtOgrePG.h" #include "BtOgreGP.h" #include "BtOgreExtras.h" From 3ea1407ed330efbf96a83bf981e55884a7a8386a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 15 Jan 2014 03:56:59 +0100 Subject: [PATCH 528/889] Closes #1109: Don't reset water level when loading a plugin that does include water level records --- components/esm/loadcell.cpp | 2 -- components/esm/loadcell.hpp | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index c22c1b22b..f6bc29ae1 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -36,8 +36,6 @@ void Cell::load(ESMReader &esm, bool saveContext) esm.getHNT(mData, "DATA", 12); - // Water level - mWater = -1; mNAM0 = 0; if (mData.mFlags & Interior) diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 61d586b9d..b066f497e 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -77,6 +77,8 @@ struct Cell float mFogDensity; }; + Cell() : mWater(-1) {} + // Interior cells are indexed by this (it's the 'id'), for exterior // cells it is optional. std::string mName; From 79a6ffd21645166ce51d398178b66e95333940b5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 15 Jan 2014 06:41:16 +0100 Subject: [PATCH 529/889] Closes #1107: Do not create box shapes unless the box collision flag is enabled --- components/nifbullet/bulletnifloader.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 9c4fee7a0..65f90c8c1 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -229,9 +229,12 @@ void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node * { if(node->hasBounds) { - mShape->mBoxTranslation = node->boundPos; - mShape->mBoxRotation = node->boundRot; - mBoundingBox = new btBoxShape(getbtVector(node->boundXYZ)); + if (node->flags & Nif::NiNode::Flag_BBoxCollision) + { + mShape->mBoxTranslation = node->boundPos; + mShape->mBoxRotation = node->boundRot; + mBoundingBox = new btBoxShape(getbtVector(node->boundXYZ)); + } } else if(node->recType == Nif::RC_NiTriShape) { From 03b2e99802e9f214bb130d746796e29b35800518 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 15 Jan 2014 07:06:18 +0100 Subject: [PATCH 530/889] Remove unused Combat stance --- apps/openmw/mwclass/npc.cpp | 14 -------------- apps/openmw/mwworld/class.hpp | 2 +- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index ef5665397..e6e1e1892 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -821,10 +821,6 @@ namespace MWClass stats.setMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak, force); break; - - case Combat: - - throw std::runtime_error ("combat stance not enforcable for NPCs"); } } @@ -843,12 +839,6 @@ namespace MWClass stats.setMovementFlag (MWMechanics::NpcStats::Flag_Sneak, set); break; - - case Combat: - - // Combat stance ignored for now; need to be determined based on draw state instead of - // being maunally set. - break; } } @@ -871,10 +861,6 @@ namespace MWClass return true; return stats.getMovementFlag (MWMechanics::NpcStats::Flag_Sneak); - - case Combat: - - return false; } return false; diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index c0b010eae..a752f76a7 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -70,7 +70,7 @@ namespace MWWorld /// NPC-stances. enum Stance { - Run, Sneak, Combat + Run, Sneak }; virtual ~Class(); From 0a8c61a7fe298b9d7f75490753e4dc3031f92e85 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 15 Jan 2014 07:42:51 +0100 Subject: [PATCH 531/889] Bug #1107: Reverted previous fix, which caused problems with some actors not using a box shape as expected. Instead, do not create a bounding box collision shape for hidden nodes. --- components/nifbullet/bulletnifloader.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 65f90c8c1..cdddb94d0 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -229,7 +229,8 @@ void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node * { if(node->hasBounds) { - if (node->flags & Nif::NiNode::Flag_BBoxCollision) + // Checking for BBoxCollision flag causes issues with some actors :/ + if (!(node->flags & Nif::NiNode::Flag_Hidden)) { mShape->mBoxTranslation = node->boundPos; mShape->mBoxRotation = node->boundRot; From da3295d69c8b10142d990937ddac874a2cdf1f4a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 15 Jan 2014 07:47:21 +0100 Subject: [PATCH 532/889] Closes #1106: Move stance to CreatureStats, since creatures also have separate run/walk animations. --- apps/openmw/mwclass/npc.cpp | 73 ++----------------- apps/openmw/mwclass/npc.hpp | 10 --- apps/openmw/mwmechanics/aicombat.cpp | 8 +- apps/openmw/mwmechanics/character.cpp | 4 +- apps/openmw/mwmechanics/creaturestats.cpp | 29 +++++++- apps/openmw/mwmechanics/creaturestats.hpp | 20 +++++ .../mwmechanics/mechanicsmanagerimp.cpp | 2 +- apps/openmw/mwmechanics/npcstats.cpp | 20 +---- apps/openmw/mwmechanics/npcstats.hpp | 17 ----- apps/openmw/mwrender/renderingmanager.cpp | 2 +- apps/openmw/mwscript/controlextensions.cpp | 42 +++++------ apps/openmw/mwworld/class.cpp | 15 ---- apps/openmw/mwworld/class.hpp | 9 --- apps/openmw/mwworld/player.cpp | 4 +- 14 files changed, 88 insertions(+), 167 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index e6e1e1892..c89822843 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -775,7 +775,7 @@ namespace MWClass } if(getCreatureStats(ptr).isDead()) return boost::shared_ptr(new MWWorld::ActionOpen(ptr, true)); - if(get(actor).getStance(actor, MWWorld::Class::Sneak)) + if(getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak)) return boost::shared_ptr(new MWWorld::ActionOpen(ptr)); // stealing if(get(ptr).getCreatureStats(ptr).isHostile()) return boost::shared_ptr(new MWWorld::FailedAction("#{sActorInCombat}")); @@ -806,66 +806,6 @@ namespace MWClass return ref->mBase->mScript; } - void Npc::setForceStance (const MWWorld::Ptr& ptr, Stance stance, bool force) const - { - MWMechanics::NpcStats& stats = getNpcStats (ptr); - - switch (stance) - { - case Run: - - stats.setMovementFlag (MWMechanics::NpcStats::Flag_ForceRun, force); - break; - - case Sneak: - - stats.setMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak, force); - break; - } - } - - void Npc::setStance (const MWWorld::Ptr& ptr, Stance stance, bool set) const - { - MWMechanics::NpcStats& stats = getNpcStats (ptr); - - switch (stance) - { - case Run: - - stats.setMovementFlag (MWMechanics::NpcStats::Flag_Run, set); - break; - - case Sneak: - - stats.setMovementFlag (MWMechanics::NpcStats::Flag_Sneak, set); - break; - } - } - - bool Npc::getStance (const MWWorld::Ptr& ptr, Stance stance, bool ignoreForce) const - { - MWMechanics::NpcStats& stats = getNpcStats (ptr); - - switch (stance) - { - case Run: - - if (!ignoreForce && stats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceRun)) - return true; - - return stats.getMovementFlag (MWMechanics::NpcStats::Flag_Run); - - case Sneak: - - if (!ignoreForce && stats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak)) - return true; - - return stats.getMovementFlag (MWMechanics::NpcStats::Flag_Sneak); - } - - return false; - } - float Npc::getSpeed(const MWWorld::Ptr& ptr) const { const MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -874,11 +814,14 @@ namespace MWClass const float normalizedEncumbrance = Npc::getEncumbrance(ptr) / Npc::getCapacity(ptr); + bool sneaking = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak); + bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); + float walkSpeed = fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* (fMaxWalkSpeed->getFloat() - fMinWalkSpeed->getFloat()); walkSpeed *= 1.0f - fEncumberedMoveEffect->getFloat()*normalizedEncumbrance; walkSpeed = std::max(0.0f, walkSpeed); - if(Npc::getStance(ptr, Sneak, false)) + if(sneaking) walkSpeed *= fSneakSpeedMultiplier->getFloat(); float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() * @@ -902,14 +845,14 @@ namespace MWClass else if(world->isSwimming(ptr)) { float swimSpeed = walkSpeed; - if(Npc::getStance(ptr, Run, false)) + if(running) swimSpeed = runSpeed; swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude; swimSpeed *= fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()* fSwimRunAthleticsMult->getFloat(); moveSpeed = swimSpeed; } - else if(Npc::getStance(ptr, Run, false) && !Npc::getStance(ptr, Sneak, false)) + else if(running && !sneaking) moveSpeed = runSpeed; else moveSpeed = walkSpeed; @@ -941,7 +884,7 @@ namespace MWClass x += mageffects.get(ESM::MagicEffect::Jump).mMagnitude * 64; x *= encumbranceTerm; - if(Npc::getStance(ptr, Run, false)) + if(ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run)) x *= fJumpRunMultiplier->getFloat(); x *= npcdata->mNpcStats.getFatigueTerm(); x -= -627.2f;/*gravity constant*/ diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 22a9632e8..9977866cd 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -84,16 +84,6 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr - virtual void setForceStance (const MWWorld::Ptr& ptr, Stance stance, bool force) const; - ///< Force or unforce a stance. - - virtual void setStance (const MWWorld::Ptr& ptr, Stance stance, bool set) const; - ///< Set or unset a stance. - - virtual bool getStance (const MWWorld::Ptr& ptr, Stance stance, bool ignoreForce = false) - const; - ///< Check if a stance is active or not. - virtual float getSpeed (const MWWorld::Ptr& ptr) const; ///< Return movement speed. diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 62158fb12..42b618143 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -40,13 +40,13 @@ namespace MWMechanics if(MWWorld::Class::get(actor).getCreatureStats(actor).getHealth().getCurrent() <= 0) return true; + actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); + if(actor.getTypeName() == typeid(ESM::NPC).name()) { - MWWorld::Class::get(actor). - MWWorld::Class::get(actor).setStance(actor, MWWorld::Class::Run,true); - MWMechanics::DrawState_ state = MWWorld::Class::get(actor).getNpcStats(actor).getDrawState(); + MWMechanics::DrawState_ state = actor.getClass().getNpcStats(actor).getDrawState(); if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) - MWWorld::Class::get(actor).getNpcStats(actor).setDrawState(MWMechanics::DrawState_Weapon); + actor.getClass().getNpcStats(actor).setDrawState(MWMechanics::DrawState_Weapon); //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true); } ESM::Position pos = actor.getRefData().getPosition(); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 07859d57c..580b71731 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -859,8 +859,8 @@ void CharacterController::update(float duration) { bool onground = world->isOnGround(mPtr); bool inwater = world->isSwimming(mPtr); - bool isrunning = cls.getStance(mPtr, MWWorld::Class::Run); - bool sneak = cls.getStance(mPtr, MWWorld::Class::Sneak); + bool isrunning = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run); + bool sneak = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak); bool flying = world->isFlying(mPtr); Ogre::Vector3 vec = cls.getMovementVector(mPtr); vec.normalise(); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index ba6f0ba04..8d37e34c8 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -15,7 +15,8 @@ namespace MWMechanics mAttacked (false), mHostile (false), mAttackingOrSpell(false), mAttackType(AT_Chop), mIsWerewolf(false), - mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false) + mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false), + mMovementFlags(0) { for (int i=0; i<4; ++i) mAiSettings[i] = 0; @@ -425,4 +426,30 @@ namespace MWMechanics { return mHitRecovery; } + + bool CreatureStats::getMovementFlag (Flag flag) const + { + return mMovementFlags & flag; + } + + void CreatureStats::setMovementFlag (Flag flag, bool state) + { + if (state) + mMovementFlags |= flag; + else + mMovementFlags &= ~flag; + } + + bool CreatureStats::getStance(Stance flag) const + { + switch (flag) + { + case Stance_Run: + return getMovementFlag (Flag_Run) || getMovementFlag (Flag_ForceRun); + case Stance_Sneak: + return getMovementFlag (Flag_Sneak) || getMovementFlag (Flag_ForceSneak); + } + return false; // shut up, compiler + } + } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 1f82a9b5c..26856c966 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -37,6 +37,7 @@ namespace MWMechanics bool mAttackingOrSpell; bool mKnockdown; bool mHitRecovery; + unsigned int mMovementFlags; float mFallHeight; @@ -47,6 +48,7 @@ namespace MWMechanics // Do we need to recalculate stats derived from attributes or other factors? bool mRecalcDynamicStats; + std::map mUsedPowers; protected: bool mIsWerewolf; @@ -193,6 +195,24 @@ namespace MWMechanics void setHitRecovery(bool value); bool getHitRecovery() const; + enum Flag + { + Flag_ForceRun = 1, + Flag_ForceSneak = 2, + Flag_Run = 4, + Flag_Sneak = 8 + }; + enum Stance + { + Stance_Run, + Stance_Sneak + }; + + bool getMovementFlag (Flag flag) const; + void setMovementFlag (Flag flag, bool state); + /// Like getMovementFlag, but also takes into account if the flag is Forced + bool getStance (Stance flag) const; + void setLastHitObject(const std::string &objectid); const std::string &getLastHitObject() const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 79a038dff..4514c077c 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -896,7 +896,7 @@ namespace MWMechanics return false; float sneakTerm = 0; - if (ptr.getClass().getStance(ptr, MWWorld::Class::Sneak) + if (ptr.getClass().getCreatureStats(ptr).getStance(CreatureStats::Stance_Sneak) && !MWBase::Environment::get().getWorld()->isSwimming(ptr) && MWBase::Environment::get().getWorld()->isOnGround(ptr)) { diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index f77b04271..293b078da 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -22,8 +22,7 @@ #include "../mwbase/soundmanager.hpp" MWMechanics::NpcStats::NpcStats() -: mMovementFlags (0) -, mDrawState (DrawState_Nothing) +: mDrawState (DrawState_Nothing) , mBounty (0) , mLevelProgress(0) , mDisposition(0) @@ -34,9 +33,7 @@ MWMechanics::NpcStats::NpcStats() , mTimeToStartDrowning(20.0) , mLastDrowningHit(0) { - mSkillIncreases.resize (ESM::Attribute::Length); - for (int i=0; i=ESM::Skill::Length) diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 5fd358c85..b89a2b4b3 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -25,18 +25,6 @@ namespace MWMechanics class NpcStats : public CreatureStats { - public: - - enum Flag - { - Flag_ForceRun = 1, - Flag_ForceSneak = 2, - Flag_Run = 4, - Flag_Sneak = 8 - }; - - private: - /// NPCs other than the player can only have one faction. But for the sake of consistency /// we use the same data structure for the PC and the NPCs. /// \note the faction key must be in lowercase @@ -44,7 +32,6 @@ namespace MWMechanics DrawState_ mDrawState; int mDisposition; - unsigned int mMovementFlags; SkillValue mSkill[27]; SkillValue mWerewolfSkill[27]; int mBounty; @@ -89,10 +76,6 @@ namespace MWMechanics void setReputation(int reputation); - bool getMovementFlag (Flag flag) const; - - void setMovementFlag (Flag flag, bool state); - const SkillValue& getSkill (int index) const; SkillValue& getSkill (int index); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 0c5e053c9..47cf3ee41 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -347,7 +347,7 @@ void RenderingManager::update (float duration, bool paused) } // Sink the camera while sneaking - bool isSneaking = MWWorld::Class::get(player).getStance(player, MWWorld::Class::Sneak); + bool isSneaking = player.getClass().getCreatureStats(player).getStance(MWMechanics::CreatureStats::Stance_Sneak); bool isInAir = !world->isOnGround(player); bool isSwimming = world->isSwimming(player); diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp index 7697ab619..d2e774859 100644 --- a/apps/openmw/mwscript/controlextensions.cpp +++ b/apps/openmw/mwscript/controlextensions.cpp @@ -76,34 +76,34 @@ namespace MWScript template class OpClearMovementFlag : public Interpreter::Opcode0 { - MWMechanics::NpcStats::Flag mFlag; + MWMechanics::CreatureStats::Flag mFlag; public: - OpClearMovementFlag (MWMechanics::NpcStats::Flag flag) : mFlag (flag) {} + OpClearMovementFlag (MWMechanics::CreatureStats::Flag flag) : mFlag (flag) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); - MWWorld::Class::get (ptr).getNpcStats (ptr).setMovementFlag (mFlag, false); + ptr.getClass().getCreatureStats(ptr).setMovementFlag (mFlag, false); } }; template class OpSetMovementFlag : public Interpreter::Opcode0 { - MWMechanics::NpcStats::Flag mFlag; + MWMechanics::CreatureStats::Flag mFlag; public: - OpSetMovementFlag (MWMechanics::NpcStats::Flag flag) : mFlag (flag) {} + OpSetMovementFlag (MWMechanics::CreatureStats::Flag flag) : mFlag (flag) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); - MWWorld::Class::get (ptr).getNpcStats (ptr).setMovementFlag (mFlag, true); + ptr.getClass().getCreatureStats(ptr).setMovementFlag (mFlag, true); } }; @@ -116,9 +116,8 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - MWMechanics::NpcStats& npcStats = MWWorld::Class::get(ptr).getNpcStats (ptr); - - runtime.push (npcStats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceRun)); + MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); + runtime.push (stats.getMovementFlag (MWMechanics::CreatureStats::Flag_ForceRun)); } }; @@ -131,9 +130,8 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - MWMechanics::NpcStats& npcStats = MWWorld::Class::get(ptr).getNpcStats (ptr); - - runtime.push (npcStats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak)); + MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); + runtime.push (stats.getMovementFlag (MWMechanics::CreatureStats::Flag_ForceSneak)); } }; @@ -144,7 +142,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); - runtime.push (MWWorld::Class::get(ptr).getStance (ptr, MWWorld::Class::Run)); + runtime.push (ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run)); } }; @@ -155,7 +153,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); - runtime.push (MWWorld::Class::get(ptr).getStance (ptr, MWWorld::Class::Sneak)); + runtime.push (ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak)); } }; @@ -172,22 +170,22 @@ namespace MWScript interpreter.installSegment5 (Compiler::Control::opcodeToggleCollision, new OpToggleCollision); interpreter.installSegment5 (Compiler::Control::opcodeClearForceRun, - new OpClearMovementFlag (MWMechanics::NpcStats::Flag_ForceRun)); + new OpClearMovementFlag (MWMechanics::CreatureStats::Flag_ForceRun)); interpreter.installSegment5 (Compiler::Control::opcodeForceRun, - new OpSetMovementFlag (MWMechanics::NpcStats::Flag_ForceRun)); + new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceRun)); interpreter.installSegment5 (Compiler::Control::opcodeClearForceSneak, - new OpClearMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak)); + new OpClearMovementFlag (MWMechanics::CreatureStats::Flag_ForceSneak)); interpreter.installSegment5 (Compiler::Control::opcodeForceSneak, - new OpSetMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak)); + new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceSneak)); interpreter.installSegment5 (Compiler::Control::opcodeClearForceRunExplicit, - new OpClearMovementFlag (MWMechanics::NpcStats::Flag_ForceRun)); + new OpClearMovementFlag (MWMechanics::CreatureStats::Flag_ForceRun)); interpreter.installSegment5 (Compiler::Control::opcodeForceRunExplicit, - new OpSetMovementFlag (MWMechanics::NpcStats::Flag_ForceRun)); + new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceRun)); interpreter.installSegment5 (Compiler::Control::opcodeClearForceSneakExplicit, - new OpClearMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak)); + new OpClearMovementFlag (MWMechanics::CreatureStats::Flag_ForceSneak)); interpreter.installSegment5 (Compiler::Control::opcodeForceSneakExplicit, - new OpSetMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak)); + new OpSetMovementFlag (MWMechanics::CreatureStats::Flag_ForceSneak)); interpreter.installSegment5 (Compiler::Control::opcodeGetPcRunning, new OpGetPcRunning); interpreter.installSegment5 (Compiler::Control::opcodeGetPcSneaking, new OpGetPcSneaking); interpreter.installSegment5 (Compiler::Control::opcodeGetForceRun, new OpGetForceRun); diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 934dae015..07bd90571 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -147,21 +147,6 @@ namespace MWWorld return ""; } - void Class::setForceStance (const Ptr& ptr, Stance stance, bool force) const - { - throw std::runtime_error ("stance not supported by class"); - } - - void Class::setStance (const Ptr& ptr, Stance stance, bool set) const - { - throw std::runtime_error ("stance not supported by class"); - } - - bool Class::getStance (const Ptr& ptr, Stance stance, bool ignoreForce) const - { - return false; - } - float Class::getSpeed (const Ptr& ptr) const { return 0; diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index a752f76a7..5c1c063a3 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -168,15 +168,6 @@ namespace MWWorld ///< Return name of the script attached to ptr (default implementation: return an empty /// string). - virtual void setForceStance (const Ptr& ptr, Stance stance, bool force) const; - ///< Force or unforce a stance. - - virtual void setStance (const Ptr& ptr, Stance stance, bool set) const; - ///< Set or unset a stance. - - virtual bool getStance (const Ptr& ptr, Stance stance, bool ignoreForce = false) const; - ///< Check if a stance is active or not. - virtual float getSpeed (const Ptr& ptr) const; ///< Return movement speed. diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index c59445402..3ea6c62f8 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -114,14 +114,14 @@ namespace MWWorld void Player::setRunState(bool run) { MWWorld::Ptr ptr = getPlayer(); - MWWorld::Class::get(ptr).setStance(ptr, MWWorld::Class::Run, run); + ptr.getClass().getCreatureStats(ptr).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, run); } void Player::setSneak(bool sneak) { MWWorld::Ptr ptr = getPlayer(); - MWWorld::Class::get (ptr).setStance (ptr, MWWorld::Class::Sneak, sneak); + ptr.getClass().getCreatureStats(ptr).setMovementFlag(MWMechanics::CreatureStats::Flag_Sneak, sneak); // TODO show sneak indicator only when the player is not detected by any actor MWBase::Environment::get().getWindowManager()->setSneakVisibility(sneak); From dff67bb0b6b7a16c8fb8cae05ad4eae1b2285210 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 15 Jan 2014 08:16:44 +0100 Subject: [PATCH 533/889] StopCombat: mark as non-hostile --- apps/openmw/mwscript/aiextensions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 7d0562767..09b1ed447 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -451,6 +451,7 @@ namespace MWScript MWWorld::Ptr actor = R()(runtime); MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); creatureStats.getAiSequence().stopCombat(); + creatureStats.setHostile(false); } }; From 28a2585106af29d510e709c3cce2704d5ada0c80 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 15 Jan 2014 08:44:35 +0100 Subject: [PATCH 534/889] Unsheath weapon in AiWander --- apps/openmw/mwmechanics/aiwander.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 915595c25..bf6c1dc38 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -6,6 +6,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwmechanics/npcstats.hpp" #include @@ -64,6 +65,8 @@ namespace MWMechanics bool AiWander::execute (const MWWorld::Ptr& actor,float duration) { + if (actor.getClass().isNpc()) + actor.getClass().getNpcStats(actor).setDrawState(DrawState_Nothing); MWBase::World *world = MWBase::Environment::get().getWorld(); if(mDuration) { From 7534fc968dcdee874377826a1a475dbb74f12d71 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 15 Jan 2014 12:08:12 +0100 Subject: [PATCH 535/889] Minor acrobatics fixes --- apps/openmw/mwmechanics/character.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 580b71731..39c421907 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1017,14 +1017,16 @@ void CharacterController::update(float duration) cls.getCreatureStats(mPtr).setHealth(health); cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), true); - // report acrobatics progression - if (mPtr.getRefData().getHandle() == "player") - cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 1); - const float acrobaticsSkill = cls.getNpcStats(mPtr).getSkill(ESM::Skill::Acrobatics).getModified(); if (healthLost > (acrobaticsSkill * fatigueTerm)) { - //TODO: actor falls over + cls.getCreatureStats(mPtr).setKnockedDown(true); + } + else + { + // report acrobatics progression + if (mPtr.getRefData().getHandle() == "player") + cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 1); } } } From d544551f61f51605d59ddc0bc1bbefbb320dcaa0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 15 Jan 2014 15:50:45 +0100 Subject: [PATCH 536/889] Added getSkill to Class interface, since creatures also have skills (which are provided by generalized Combat, Magic and Stealth attributes which substitute for the specific skills, in the same way as specialization) Information provided by Hrnchamd. --- apps/openmw/mwclass/creature.cpp | 20 +++++++++++++++++++ apps/openmw/mwclass/creature.hpp | 2 ++ apps/openmw/mwclass/npc.cpp | 5 +++++ apps/openmw/mwclass/npc.hpp | 2 ++ apps/openmw/mwgui/pickpocketitemmodel.cpp | 2 +- apps/openmw/mwgui/tradewindow.cpp | 4 ++-- apps/openmw/mwmechanics/character.cpp | 2 +- .../mwmechanics/mechanicsmanagerimp.cpp | 11 ++-------- apps/openmw/mwmechanics/pickpocket.cpp | 5 ++--- apps/openmw/mwmechanics/spellcasting.hpp | 11 +++++----- apps/openmw/mwscript/statsextensions.cpp | 4 +--- apps/openmw/mwworld/class.cpp | 5 +++++ apps/openmw/mwworld/class.hpp | 2 ++ apps/openmw/mwworld/inventorystore.cpp | 16 +++++++-------- components/esm/loadcrea.hpp | 4 +++- 15 files changed, 60 insertions(+), 35 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 480c335c2..ac8f85d13 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -461,6 +461,26 @@ namespace MWClass throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); } + int Creature::getSkill(const MWWorld::Ptr &ptr, int skill) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + const ESM::Skill* skillRecord = MWBase::Environment::get().getWorld()->getStore().get().find(skill); + + switch (skillRecord->mData.mSpecialization) + { + case ESM::Class::Combat: + return ref->mBase->mData.mCombat; + case ESM::Class::Magic: + return ref->mBase->mData.mMagic; + case ESM::Class::Stealth: + return ref->mBase->mData.mStealth; + default: + throw std::runtime_error("invalid specialisation"); + } + } + const ESM::GameSetting* Creature::fMinWalkSpeedCreature; const ESM::GameSetting* Creature::fMaxWalkSpeedCreature; } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 34e19ebdc..461410a49 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -101,6 +101,8 @@ namespace MWClass } virtual bool isFlying (const MWWorld::Ptr &ptr) const; + + virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const; }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index c89822843..8d51fd1cf 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1211,6 +1211,11 @@ namespace MWClass return MWWorld::Ptr(&cell.mNpcs.insert(*ref), &cell); } + int Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const + { + return ptr.getClass().getNpcStats(ptr).getSkill(skill).getModified(); + } + const ESM::GameSetting *Npc::fMinWalkSpeed; const ESM::GameSetting *Npc::fMaxWalkSpeed; const ESM::GameSetting *Npc::fEncumberedMoveEffect; diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 9977866cd..b729d0151 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -140,6 +140,8 @@ namespace MWClass virtual std::string getModel(const MWWorld::Ptr &ptr) const; + virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; + virtual bool isActor() const { return true; } diff --git a/apps/openmw/mwgui/pickpocketitemmodel.cpp b/apps/openmw/mwgui/pickpocketitemmodel.cpp index 13ee4396d..0196bf02d 100644 --- a/apps/openmw/mwgui/pickpocketitemmodel.cpp +++ b/apps/openmw/mwgui/pickpocketitemmodel.cpp @@ -9,7 +9,7 @@ namespace MWGui PickpocketItemModel::PickpocketItemModel(const MWWorld::Ptr& thief, ItemModel *sourceModel) { mSourceModel = sourceModel; - int chance = MWWorld::Class::get(thief).getNpcStats(thief).getSkill(ESM::Skill::Sneak).getModified(); + int chance = thief.getClass().getSkill(thief, ESM::Skill::Sneak); mSourceModel->update(); for (size_t i = 0; igetItemCount(); ++i) diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 14024dec6..f86044841 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -296,10 +296,10 @@ namespace MWGui const MWMechanics::NpcStats &sellerStats = mPtr.getClass().getNpcStats(mPtr); const MWMechanics::NpcStats &playerStats = player.getClass().getNpcStats(player); - float a1 = std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100); + float a1 = std::min(player.getClass().getSkill(player, ESM::Skill::Mercantile), 100); float b1 = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float c1 = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); - float d1 = std::min(sellerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100); + float d1 = std::min(mPtr.getClass().getSkill(mPtr, ESM::Skill::Mercantile), 100); float e1 = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float f1 = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 39c421907..5104a0f57 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1017,7 +1017,7 @@ void CharacterController::update(float duration) cls.getCreatureStats(mPtr).setHealth(health); cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), true); - const float acrobaticsSkill = cls.getNpcStats(mPtr).getSkill(ESM::Skill::Acrobatics).getModified(); + const float acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics); if (healthLost > (acrobaticsSkill * fatigueTerm)) { cls.getCreatureStats(mPtr).setKnockedDown(true); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 4514c077c..0dee4706a 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -902,12 +902,7 @@ namespace MWMechanics { static float fSneakSkillMult = store.find("fSneakSkillMult")->getFloat(); static float fSneakBootMult = store.find("fSneakBootMult")->getFloat(); - float sneak = 0; - // TODO: According to Hrnchamd Research:Movement, "Creatures have generalized combat, magic and stealth - // stats which substitute for the specific skills (in the same way as specializations)." - // This probably applies to a large part of the code base. - if (ptr.getClass().isNpc()) - sneak = ptr.getClass().getNpcStats(ptr).getSkill(ESM::Skill::Sneak).getModified(); + float sneak = ptr.getClass().getSkill(ptr, ESM::Skill::Sneak); int agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); int luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float bootWeight = 0; @@ -935,9 +930,7 @@ namespace MWMechanics int obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified(); int obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified(); float obsBlind = observerStats.getMagicEffects().get(ESM::MagicEffect::Blind).mMagnitude; - int obsSneak = 0; - if (observer.getClass().isNpc()) - obsSneak = observer.getClass().getNpcStats(observer).getSkill(ESM::Skill::Sneak).getModified(); + int obsSneak = observer.getClass().getSkill(observer, ESM::Skill::Sneak); float obsTerm = obsSneak + 0.2 * obsAgility + 0.1 * obsLuck - obsBlind; diff --git a/apps/openmw/mwmechanics/pickpocket.cpp b/apps/openmw/mwmechanics/pickpocket.cpp index 8e8a70d88..53681caf8 100644 --- a/apps/openmw/mwmechanics/pickpocket.cpp +++ b/apps/openmw/mwmechanics/pickpocket.cpp @@ -19,7 +19,7 @@ namespace MWMechanics NpcStats& stats = ptr.getClass().getNpcStats(ptr); float agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); - float sneak = stats.getSkill(ESM::Skill::Sneak).getModified(); + float sneak = ptr.getClass().getSkill(ptr, ESM::Skill::Sneak); return (add + 0.2 * agility + 0.1 * luck + sneak) * stats.getFatigueTerm(); } @@ -30,8 +30,7 @@ namespace MWMechanics float t = 2*x - y; - NpcStats& pcStats = mThief.getClass().getNpcStats(mThief); - float pcSneak = pcStats.getSkill(ESM::Skill::Sneak).getModified(); + float pcSneak = mThief.getClass().getSkill(mThief, ESM::Skill::Sneak); int iPickMinChance = MWBase::Environment::get().getWorld()->getStore().get() .find("iPickMinChance")->getInt(); int iPickMaxChance = MWBase::Environment::get().getWorld()->getStore().get() diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index a1ae254f6..52af26ad1 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -16,9 +16,9 @@ namespace MWMechanics { - inline int spellSchoolToSkill(int school) + inline ESM::Skill::SkillEnum spellSchoolToSkill(int school) { - std::map schoolSkillMap; // maps spell school to skill id + std::map schoolSkillMap; // maps spell school to skill id schoolSkillMap[0] = ESM::Skill::Alteration; schoolSkillMap[1] = ESM::Skill::Conjuration; schoolSkillMap[3] = ESM::Skill::Illusion; @@ -38,10 +38,9 @@ namespace MWMechanics */ inline float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = NULL) { - NpcStats& stats = MWWorld::Class::get(actor).getNpcStats(actor); - CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); + CreatureStats& stats = actor.getClass().getCreatureStats(actor); - if (creatureStats.getMagicEffects().get(ESM::MagicEffect::Silence).mMagnitude) + if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).mMagnitude) return 0; float y = FLT_MAX; @@ -63,7 +62,7 @@ namespace MWMechanics "fEffectCostMult")->getFloat(); x *= fEffectCostMult; - float s = 2 * stats.getSkill(spellSchoolToSkill(magicEffect->mData.mSchool)).getModified(); + float s = 2 * actor.getClass().getSkill(actor, spellSchoolToSkill(magicEffect->mData.mSchool)); if (s - x < y) { y = s - x; diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 095fad7ab..7a59e9689 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -306,9 +306,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = - MWWorld::Class::get (ptr).getNpcStats (ptr).getSkill (mIndex). - getModified(); + Interpreter::Type_Integer value = ptr.getClass().getSkill(ptr, mIndex); runtime.push (value); } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 07bd90571..f3128780b 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -358,4 +358,9 @@ namespace MWWorld return false; } + int Class::getSkill(const MWWorld::Ptr& ptr, int skill) const + { + throw std::runtime_error("class does not support skills"); + } + } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 5c1c063a3..bbc74323c 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -294,6 +294,8 @@ namespace MWWorld virtual bool isFlying(const MWWorld::Ptr& ptr) const; + virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; + static const Class& get (const std::string& key); ///< If there is no class for this \a key, an exception is thrown. diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index e8938b2c0..82b827e75 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -164,8 +164,6 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot) void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) { - const MWMechanics::NpcStats& stats = MWWorld::Class::get(actor).getNpcStats(actor); - TSlots slots_; initSlots (slots_); @@ -190,10 +188,10 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) !actor.getRefData().getLocals().getIntVar(actor.getClass().getScript(actor), "companion"))) continue; - int testSkill = MWWorld::Class::get (test).getEquipmentSkill (test); + int testSkill = test.getClass().getEquipmentSkill (test); std::pair, bool> itemsSlots = - MWWorld::Class::get (*iter).getEquipmentSlots (*iter); + iter->getClass().getEquipmentSlots (*iter); for (std::vector::const_iterator iter2 (itemsSlots.first.begin()); iter2!=itemsSlots.first.end(); ++iter2) @@ -210,16 +208,16 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) { // check skill int oldSkill = - MWWorld::Class::get (old).getEquipmentSkill (old); + old.getClass().getEquipmentSkill (old); if (testSkill!=-1 && oldSkill==-1) use = true; else if (testSkill!=-1 && oldSkill!=-1 && testSkill!=oldSkill) { - if (stats.getSkill (oldSkill).getModified()>stats.getSkill (testSkill).getModified()) + if (actor.getClass().getSkill(actor, oldSkill) > actor.getClass().getSkill (actor, testSkill)) continue; // rejected, because old item better matched the NPC's skills. - if (stats.getSkill (oldSkill).getModified()= - MWWorld::Class::get (test).getValue (test)) + if (old.getClass().getValue (old)>= + test.getClass().getValue (test)) { continue; } diff --git a/components/esm/loadcrea.hpp b/components/esm/loadcrea.hpp index b5ea49508..c0523025b 100644 --- a/components/esm/loadcrea.hpp +++ b/components/esm/loadcrea.hpp @@ -63,7 +63,9 @@ struct Creature int mHealth, mMana, mFatigue; // Stats int mSoul; // The creatures soul value (used with soul gems.) - int mCombat, mMagic, mStealth; // Don't know yet. + // Creatures have generalized combat, magic and stealth stats which substitute for + // the specific skills (in the same way as specializations). + int mCombat, mMagic, mStealth; int mAttack[6]; // AttackMin1, AttackMax1, ditto2, ditto3 int mGold; }; // 96 byte From ea8f60eddf614c04330a5befdf310a98eea28c6a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 15 Jan 2014 16:30:16 +0100 Subject: [PATCH 537/889] Implement movement speed formula for creatures. Still moving a bit too slow. --- apps/openmw/mwclass/creature.cpp | 62 ++++++++++++++++++++++++++++++-- apps/openmw/mwclass/creature.hpp | 8 +++++ 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index ac8f85d13..a18df6d9f 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -61,6 +61,14 @@ namespace MWClass fMinWalkSpeedCreature = gmst.find("fMinWalkSpeedCreature"); fMaxWalkSpeedCreature = gmst.find("fMaxWalkSpeedCreature"); + fEncumberedMoveEffect = gmst.find("fEncumberedMoveEffect"); + fSneakSpeedMultiplier = gmst.find("fSneakSpeedMultiplier"); + fAthleticsRunBonus = gmst.find("fAthleticsRunBonus"); + fBaseRunMultiplier = gmst.find("fBaseRunMultiplier"); + fMinFlySpeed = gmst.find("fMinFlySpeed"); + fMaxFlySpeed = gmst.find("fMaxFlySpeed"); + fSwimRunBase = gmst.find("fSwimRunBase"); + fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult"); inited = true; } @@ -286,10 +294,51 @@ namespace MWClass float Creature::getSpeed(const MWWorld::Ptr &ptr) const { MWMechanics::CreatureStats& stats = getCreatureStats(ptr); + float walkSpeed = fMinWalkSpeedCreature->getFloat() + 0.01 * stats.getAttribute(ESM::Attribute::Speed).getModified() * (fMaxWalkSpeedCreature->getFloat() - fMinWalkSpeedCreature->getFloat()); - /// \todo what about the rest? - return walkSpeed; + + const MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); + + const float normalizedEncumbrance = 0; //getEncumbrance(ptr) / getCapacity(ptr); + + bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); + + float runSpeed = walkSpeed*(0.01f * getSkill(ptr, ESM::Skill::Athletics) * + fAthleticsRunBonus->getFloat() + fBaseRunMultiplier->getFloat()); + + float moveSpeed; + if(normalizedEncumbrance >= 1.0f) + moveSpeed = 0.0f; + else if(mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 && + world->isLevitationEnabled()) + { + float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() + + mageffects.get(ESM::MagicEffect::Levitate).mMagnitude); + flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat()); + flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; + flySpeed = std::max(0.0f, flySpeed); + moveSpeed = flySpeed; + } + else if(world->isSwimming(ptr)) + { + float swimSpeed = walkSpeed; + if(running) + swimSpeed = runSpeed; + swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude; + swimSpeed *= fSwimRunBase->getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) * + fSwimRunAthleticsMult->getFloat(); + moveSpeed = swimSpeed; + } + else if(running) + moveSpeed = runSpeed; + else + moveSpeed = walkSpeed; + if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) + moveSpeed *= 0.75f; + + return moveSpeed; } MWMechanics::Movement& Creature::getMovementSettings (const MWWorld::Ptr& ptr) const @@ -483,4 +532,13 @@ namespace MWClass const ESM::GameSetting* Creature::fMinWalkSpeedCreature; const ESM::GameSetting* Creature::fMaxWalkSpeedCreature; + const ESM::GameSetting *Creature::fEncumberedMoveEffect; + const ESM::GameSetting *Creature::fSneakSpeedMultiplier; + const ESM::GameSetting *Creature::fAthleticsRunBonus; + const ESM::GameSetting *Creature::fBaseRunMultiplier; + const ESM::GameSetting *Creature::fMinFlySpeed; + const ESM::GameSetting *Creature::fMaxFlySpeed; + const ESM::GameSetting *Creature::fSwimRunBase; + const ESM::GameSetting *Creature::fSwimRunAthleticsMult; + } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 461410a49..7d2f95fae 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -16,6 +16,14 @@ namespace MWClass static const ESM::GameSetting *fMinWalkSpeedCreature; static const ESM::GameSetting *fMaxWalkSpeedCreature; + static const ESM::GameSetting *fEncumberedMoveEffect; + static const ESM::GameSetting *fSneakSpeedMultiplier; + static const ESM::GameSetting *fAthleticsRunBonus; + static const ESM::GameSetting *fBaseRunMultiplier; + static const ESM::GameSetting *fMinFlySpeed; + static const ESM::GameSetting *fMaxFlySpeed; + static const ESM::GameSetting *fSwimRunBase; + static const ESM::GameSetting *fSwimRunAthleticsMult; public: From d5f794d4fbd16ccf3d82e877e98f1d731ce84061 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Wed, 15 Jan 2014 22:56:55 +0200 Subject: [PATCH 538/889] update to combat ai behaviour --- apps/openmw/mwmechanics/actors.cpp | 3 +- apps/openmw/mwmechanics/aicombat.cpp | 392 ++++++++++++++++++++------ apps/openmw/mwmechanics/aicombat.hpp | 28 +- apps/openmw/mwmechanics/character.cpp | 5 +- apps/openmw/mwmechanics/character.hpp | 10 +- 5 files changed, 344 insertions(+), 94 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0a4adb6e2..8ce03f2b9 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -13,6 +13,7 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/actionequip.hpp" +#include "../mwworld/player.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -190,7 +191,7 @@ namespace MWMechanics && LOS ) { - creatureStats.getAiSequence().stack(AiCombat("player")); + creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayer().getPlayer())); creatureStats.setHostile(true); } } diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 32b0063b6..5588e8482 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -4,14 +4,19 @@ #include "../mwworld/class.hpp" #include "../mwworld/timestamp.hpp" -#include "../mwbase/world.hpp" + #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/dialoguemanager.hpp" + +#include "character.hpp" +#include "../mwworld/inventorystore.hpp" #include "creaturestats.hpp" #include "npcstats.hpp" #include +#include namespace { @@ -25,111 +30,309 @@ namespace namespace MWMechanics { - - AiCombat::AiCombat(const std::string &targetId) - :mTargetId(targetId),mTimer(0),mTimer2(0) + AiCombat::AiCombat(const MWWorld::Ptr& actor) : + mTarget(actor), + mTimerAttack(0), + mTimerReact(0), + mTimerCombatMove(0), + mCloseUp(false), + mReadyToAttack(false), + mStrike(false), + mCombatMove(false), + mMovement() { } bool AiCombat::execute (const MWWorld::Ptr& actor,float duration) { - if(!MWWorld::Class::get(actor).getCreatureStats(actor).isHostile()) return true; + //General description + if(!actor.getClass().getCreatureStats(actor).isHostile()) + return true; + if(actor.getClass().getCreatureStats(actor).getHealth().getCurrent() <= 0) + return true; - const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mTargetId, false); + //update every frame + determineAttackType(actor, mMovement); - if(MWWorld::Class::get(actor).getCreatureStats(actor).getHealth().getCurrent() <= 0) return true; - - if(actor.getTypeName() == typeid(ESM::NPC).name()) + if(mCombatMove) { - MWWorld::Class::get(actor). - MWWorld::Class::get(actor).setStance(actor, MWWorld::Class::Run,true); - MWMechanics::DrawState_ state = MWWorld::Class::get(actor).getNpcStats(actor).getDrawState(); - if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) - MWWorld::Class::get(actor).getNpcStats(actor).setDrawState(MWMechanics::DrawState_Weapon); - //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true); + mTimerCombatMove -= duration; + if( mTimerCombatMove <= 0) + { + mTimerCombatMove = 0; + mMovement.mPosition[1] = mMovement.mPosition[0] = 0; + mCombatMove = false; + } } - ESM::Position pos = actor.getRefData().getPosition(); - const ESM::Pathgrid *pathgrid = - MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); + actor.getClass().getMovementSettings(actor) = mMovement; + - float xCell = 0; - float yCell = 0; + //actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mReadyToAttack); + mTimerAttack -= duration; + actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mStrike); - if (actor.getCell()->mCell->isExterior()) + float tReaction = 0.25f; + if(mTimerReact < tReaction) { - xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; - yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; + mTimerReact += duration; + return false; } - - ESM::Pathgrid::Point dest; - dest.mX = target.getRefData().getPosition().pos[0]; - dest.mY = target.getRefData().getPosition().pos[1]; - dest.mZ = target.getRefData().getPosition().pos[2]; - - ESM::Pathgrid::Point start; - start.mX = pos.pos[0]; - start.mY = pos.pos[1]; - start.mZ = pos.pos[2]; - - mTimer2 = mTimer2 + duration; - - if(!mPathFinder.isPathConstructed()) - mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, true); else { - mPathFinder2.buildPath(start, dest, pathgrid, xCell, yCell, true); - ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); - if((mTimer2 > 0.25)&&(mPathFinder2.getPathSize() < mPathFinder.getPathSize() || - (dest.mX - lastPt.mX)*(dest.mX - lastPt.mX)+(dest.mY - lastPt.mY)*(dest.mY - lastPt.mY)+(dest.mZ - lastPt.mZ)*(dest.mZ - lastPt.mZ) > 200*200)) - { - mTimer2 = 0; - mPathFinder = mPathFinder2; - } + mTimerReact = 0; } - mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); - - float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - - // TODO: use movement settings instead of rotating directly - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; - - - float range = 100; - MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); - if((dest.mX - start.mX)*(dest.mX - start.mX)+(dest.mY - start.mY)*(dest.mY - start.mY)+(dest.mZ - start.mZ)*(dest.mZ - start.mZ) - < range*range) + //actual attacking logic + //TODO: Some skills affect period of strikes.For berserk-like style period ~ 0.25f + float attackPeriod = 1.0f; + if(mReadyToAttack) { - float directionX = dest.mX - start.mX; - float directionY = dest.mY - start.mY; - float directionResult = sqrt(directionX * directionX + directionY * directionY); - - zAngle = Ogre::Radian( Ogre::Math::ACos(directionY / directionResult) * sgn(Ogre::Math::ASin(directionX / directionResult)) ).valueDegrees(); - // TODO: use movement settings instead of rotating directly - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); - - mPathFinder.clearPath(); - - if(mTimer == 0) + if(mTimerAttack <= -attackPeriod) { - MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); - //mTimer = mTimer + duration; + //TODO: should depend on time between 'start' to 'min attack' + //for better controlling of NPCs' attack strength. + //Also it seems that this time is different for slash/thrust/chop + mTimerAttack = 0.35f * static_cast(rand())/RAND_MAX; + mStrike = true; + + //say a provoking combat phrase + if (actor.getClass().isNpc()) + { + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + int chance = store.get().find("iVoiceAttackOdds")->getInt(); + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (roll < chance) + { + MWBase::Environment::get().getDialogueManager()->say(actor, "attack"); + } + } } - if( mTimer > 1) + else if (mTimerAttack <= 0) + mStrike = false; + } + else + { + mTimerAttack = -attackPeriod; + mStrike = false; + } + + const MWWorld::Class &cls = actor.getClass(); + const ESM::Weapon *weapon = NULL; + MWMechanics::WeaponType weaptype; + float weapRange, weapSpeed = 1.0f; + if(actor.getTypeName() == typeid(ESM::NPC).name()) + { + actor.getClass().setStance(actor, MWWorld::Class::Run,true); + MWMechanics::DrawState_ state = actor.getClass().getNpcStats(actor).getDrawState(); + if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) + actor.getClass().getNpcStats(actor).setDrawState(MWMechanics::DrawState_Weapon); + + //Get weapon speed and range + MWWorld::ContainerStoreIterator weaponSlot = + MWMechanics::getActiveWeapon(cls.getNpcStats(actor), cls.getInventoryStore(actor), &weaptype); + if (weaptype == WeapType_HandToHand) { - MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true); - mTimer = 0; + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + weapRange = gmst.find("fHandToHandReach")->getFloat(); } else { - mTimer = mTimer + duration; + weapon = weaponSlot->get()->mBase; + weapRange = weapon->mData.mReach; + weapSpeed = weapon->mData.mSpeed; + } + weapRange *= 100.0f; + } + + //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); + + ESM::Position pos = actor.getRefData().getPosition(); + + float zAngle; + + float rangeMelee; + float rangeCloseUp; + bool distantCombat = false; + if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_ThowWeapon) // || WeapType_Spell_OnTarget + { + rangeMelee = 1000; // TODO: should depend on archer skill + rangeCloseUp = 0; //doesn't needed when attacking from distance + distantCombat = true; + } + else + { + rangeMelee = weapRange; + rangeCloseUp = 300; + } + + Ogre::Vector3 vStart(pos.pos[0], pos.pos[1], pos.pos[2]); + ESM::Position targetPos = mTarget.getRefData().getPosition(); + Ogre::Vector3 vDest(targetPos.pos[0], targetPos.pos[1], targetPos.pos[2]); + Ogre::Vector3 vDir = vDest - vStart; + float distBetween = vDir.length(); + + if(distBetween < rangeMelee || (distBetween <= rangeCloseUp && mCloseUp) ) + { + //Melee and Close-up combat + vDir.z = 0; + float dirLen = vDir.length(); + zAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / dirLen) * sgn(Ogre::Math::ASin(vDir.x / dirLen)) ).valueDegrees(); + + // TODO: use movement settings instead of rotating directly + MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + + if(mPathFinder.isPathConstructed()) + mPathFinder.clearPath(); + + //MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + + //bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget); + if (mCloseUp && distBetween > rangeMelee) + { + //Close-up combat: just run up on target + mMovement.mPosition[1] = 1; + } + else + { + //Melee: stop running and attack + mMovement.mPosition[1] = 0; + chooseBestAttack(weapon, mMovement); + + if(mMovement.mPosition[0] != 0 || mMovement.mPosition[1]) + { + mTimerCombatMove = 0.1f + 0.1f * static_cast(rand())/RAND_MAX; + mCombatMove = true; + } + else if(!distantCombat || (distantCombat && rangeMelee/5)) + { + //apply sideway movement (kind of dodging) with some probability + if(static_cast(rand())/RAND_MAX < 0.25) + { + mMovement.mPosition[0] = static_cast(rand())/RAND_MAX < 0.5? 1: -1; + mTimerCombatMove = 0.05f + 0.15f * static_cast(rand())/RAND_MAX; + mCombatMove = true; + } + } + + if(distantCombat && distBetween < rangeMelee/4) + { + mMovement.mPosition[1] = -1; + } + + mReadyToAttack = true; + //only once got in melee combat, actor is allowed to use close-up shortcutting + mCloseUp = true; + } + } + else + { + //target is at far distance: build & follow the path + mCloseUp = false; + + buildNewPath(actor); + + //delete visited path node + mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); + + zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + + // TODO: use movement settings instead of rotating directly + MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + //MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + mMovement.mPosition[1] = 1; + mReadyToAttack = false; + } + + if(distBetween > rangeMelee) + { + //special run attack; it shouldn't affect melee combat tactics + if(actor.getClass().getMovementSettings(actor).mPosition[1] == 1) + { + //check if actor can overcome the distance = distToTarget - attackerWeapRange + //less than in time of playing weapon anim from 'start' to 'hit' tags (t_swing) + //then start attacking + float speed1 = cls.getSpeed(actor); + float speed2 = cls.getSpeed(mTarget); + if(actor.getClass().getMovementSettings(mTarget).mPosition[0] == 0 + && actor.getClass().getMovementSettings(mTarget).mPosition[1] == 0) + speed2 = 0; + + float s1 = distBetween - weapRange; + float t = s1/speed1; + float s2 = speed2 * t; + float t_swing = 0.17f/weapSpeed;//0.17 should be the time of playing weapon anim from 'start' to 'hit' tags + if (t + s2/speed1 <= t_swing) + { + mReadyToAttack = true; + if(mTimerAttack <= -attackPeriod) + { + mTimerAttack = 0.45f*static_cast(rand())/RAND_MAX; + mStrike = true; + } + } + } + } + + return false; + } + + void AiCombat::buildNewPath(const MWWorld::Ptr& actor) + { + //Construct path to target + ESM::Pathgrid::Point dest; + dest.mX = mTarget.getRefData().getPosition().pos[0]; + dest.mY = mTarget.getRefData().getPosition().pos[1]; + dest.mZ = mTarget.getRefData().getPosition().pos[2]; + Ogre::Vector3 newPathTarget = Ogre::Vector3(dest.mX, dest.mY, dest.mZ); + + ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); + Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); + float dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); + + float targetPosThreshold; + bool isOutside = actor.getCell()->mCell->isExterior(); + if (isOutside) + targetPosThreshold = 300; + else + targetPosThreshold = 100; + + if(dist > targetPosThreshold) + { + //construct new path only if target has moved away more than on + ESM::Position pos = actor.getRefData().getPosition(); + + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; + start.mZ = pos.pos[2]; + + const ESM::Pathgrid *pathgrid = + MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); + + float xCell = 0; + float yCell = 0; + + if (actor.getCell()->mCell->isExterior()) + { + xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; + yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; } - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; - //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(!MWWorld::Class::get(actor).getCreatureStats(actor).getAttackingOrSpell()); + if(!mPathFinder.isPathConstructed()) + mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, isOutside); + else + { + PathFinder newPathFinder; + newPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, isOutside); + + //TO EXPLORE: + //maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path, + //not the actual path length. Here we should know if the new path is actually more effective. + //if(pathFinder2.getPathSize() < mPathFinder.getPathSize()) + mPathFinder = newPathFinder; + } } - return false; } int AiCombat::getTypeId() const @@ -146,5 +349,36 @@ namespace MWMechanics { return new AiCombat(*this); } + + static void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) + { + if (movement.mPosition[0] && !movement.mPosition[1]) //sideway + actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Slash); + else if (movement.mPosition[1]) //forward + actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Thrust); + else + actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Chop); + } + + static void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) + { + if (weapon == NULL) + return; + + int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; + int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; + int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; + + float total = slash + chop + thrust; + + if(static_cast(rand())/RAND_MAX <= static_cast(slash)/total) + { + movement.mPosition[0] = (static_cast(rand())/RAND_MAX < 0.5f)? 1: -1; + movement.mPosition[1] = 0; + } + if (static_cast(rand())/RAND_MAX <= static_cast(thrust)/total) + movement.mPosition[1] = 1; + //else chop + } } diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index fa71e261f..f6a0c85c3 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -7,12 +7,14 @@ #include "movement.hpp" +#include "../mwbase/world.hpp" + namespace MWMechanics { class AiCombat : public AiPackage { public: - AiCombat(const std::string &targetId); + AiCombat(const MWWorld::Ptr& actor); virtual AiCombat *clone() const; @@ -24,13 +26,27 @@ namespace MWMechanics virtual unsigned int getPriority() const; private: - std::string mTargetId; - PathFinder mPathFinder; - PathFinder mPathFinder2; - float mTimer; - float mTimer2; + //controls duration of the actual strike + float mTimerAttack; + float mTimerReact; + //controls duration of the sideway & forward moves + //when mCombatMove is true + float mTimerCombatMove; + + bool mReadyToAttack, mStrike; + bool mCloseUp; + bool mCombatMove; + + MWMechanics::Movement mMovement; + MWWorld::Ptr mTarget; + + void buildNewPath(const MWWorld::Ptr& actor); }; + + static void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement); + //chooses an attack depending on probability to avoid uniformity + static void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement); } #endif \ No newline at end of file diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 2a077abc7..03d131c74 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -315,7 +315,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat } -void CharacterController::getWeaponGroup(WeaponType weaptype, std::string &group) +void getWeaponGroup(WeaponType weaptype, std::string &group) { const WeaponInfo *info = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(weaptype)); if(info != sWeaponTypeListEnd) @@ -323,7 +323,7 @@ void CharacterController::getWeaponGroup(WeaponType weaptype, std::string &group } -MWWorld::ContainerStoreIterator CharacterController::getActiveWeapon(NpcStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype) +MWWorld::ContainerStoreIterator getActiveWeapon(NpcStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype) { if(stats.getDrawState() == DrawState_Spell) { @@ -441,6 +441,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim { getWeaponGroup(mWeaponType, mCurrentWeapon); mUpperBodyState = UpperCharState_WeapEquiped; + mAnimation->showWeapons(true); } } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 438f542f0..d1930b77a 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -168,12 +168,6 @@ class CharacterController void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false); - static void getWeaponGroup(WeaponType weaptype, std::string &group); - - static MWWorld::ContainerStoreIterator getActiveWeapon(NpcStats &stats, - MWWorld::InventoryStore &inv, - WeaponType *weaptype); - void clearAnimQueue(); bool updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak); @@ -205,6 +199,10 @@ public: void forceStateUpdate(); }; + void getWeaponGroup(WeaponType weaptype, std::string &group); + MWWorld::ContainerStoreIterator getActiveWeapon(NpcStats &stats, + MWWorld::InventoryStore &inv, + WeaponType *weaptype); } #endif /* GAME_MWMECHANICS_CHARACTER_HPP */ From 278876d6fb9df8ae1d48063647af508b87d06213 Mon Sep 17 00:00:00 2001 From: nobrakal Date: Thu, 16 Jan 2014 08:41:29 +0100 Subject: [PATCH 539/889] Update opencs.desktop Fix bad output of desktop-file-validate for opencs.dektop --- files/opencs.desktop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/opencs.desktop b/files/opencs.desktop index 80afa26bc..638de6ebf 100644 --- a/files/opencs.desktop +++ b/files/opencs.desktop @@ -3,7 +3,7 @@ Type=Application Name=OpenMW Content Editor GenericName=Content Editor Comment=A replacement for the Morrowind Construction Set. -Keywords=Morrowind;Construction Set;Creation Kit Editor;Set;Kit +Keywords=Morrowind;Construction Set;Creation Kit Editor;Set;Kit; TryExec=opencs Exec=opencs Icon=opencs From e638b8b8cbeb5bdbc57cb01ba8526f426d3ef70b Mon Sep 17 00:00:00 2001 From: nobrakal Date: Thu, 16 Jan 2014 08:44:56 +0100 Subject: [PATCH 540/889] Update openmw.desktop Fix bad output of desktop-file-validate for openmw.dektop --- files/openmw.desktop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/openmw.desktop b/files/openmw.desktop index 3e26018d0..4a3a76f52 100644 --- a/files/openmw.desktop +++ b/files/openmw.desktop @@ -3,7 +3,7 @@ Type=Application Name=OpenMW Launcher GenericName=Role Playing Game Comment=An engine replacement for The Elder Scrolls III: Morrowind -Keywords=Morrowind;Reimplementation Mods;esm;bsa +Keywords=Morrowind;Reimplementation Mods;esm;bsa; TryExec=omwlauncher Exec=omwlauncher Icon=openmw From d8d4f1a15e0bd49155031dd7fffdd3012e99af31 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 16 Jan 2014 12:02:45 +0100 Subject: [PATCH 541/889] some fixes to record structs --- components/esm/cellref.cpp | 7 +++++-- components/esm/cellref.hpp | 2 +- components/esm/loadcell.cpp | 2 +- components/esm/objectstate.cpp | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index bdb0e23de..b9f630290 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -71,9 +71,12 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) esm.getHT (mNam0); } -void ESM::CellRef::save(ESMWriter &esm) const +void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum) const { - esm.writeHNT("FRMR", mRefNum.mIndex); + if (wideRefNum) + esm.writeHNT ("FRMR", mRefNum, 8); + else + esm.writeHNT ("FRMR", mRefNum.mIndex, 4); esm.writeHNCString("NAME", mRefID); diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 3d80a51bd..01b546d5a 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -89,7 +89,7 @@ namespace ESM void load (ESMReader& esm, bool wideRefNum = false); - void save(ESMWriter &esm) const; + void save(ESMWriter &esm, bool wideRefNum = false) const; void blank(); }; diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 649e3175d..cfd73554a 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -230,7 +230,7 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) { CellId id; - id.mPaged = (mData.mFlags & Interior); + id.mPaged = !(mData.mFlags & Interior); if (id.mPaged) { diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index b13b6c226..56289acae 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -27,7 +27,7 @@ void ESM::ObjectState::load (ESMReader &esm) void ESM::ObjectState::save (ESMWriter &esm) const { - mRef.save (esm); + mRef.save (esm, true); if (mHasLocals) { From c300cd93752ad23dd3db600433ba2e7c9e447720 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 16 Jan 2014 12:03:23 +0100 Subject: [PATCH 542/889] loading/saving of some player state (cell/coordinates and some other bits) --- apps/openmw/mwbase/world.hpp | 7 ++- apps/openmw/mwstate/statemanagerimp.cpp | 13 +++--- apps/openmw/mwworld/livecellref.cpp | 21 +++++++++ apps/openmw/mwworld/livecellref.hpp | 57 +++++++++++++++++++++++ apps/openmw/mwworld/player.cpp | 60 +++++++++++++++++++++++-- apps/openmw/mwworld/player.hpp | 6 +++ apps/openmw/mwworld/refdata.cpp | 21 +++++++++ apps/openmw/mwworld/refdata.hpp | 9 ++++ apps/openmw/mwworld/worldimp.cpp | 21 ++++++++- apps/openmw/mwworld/worldimp.hpp | 6 ++- 10 files changed, 210 insertions(+), 11 deletions(-) create mode 100644 apps/openmw/mwworld/livecellref.cpp diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 06f6d6fac..eaf411d20 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -37,6 +37,7 @@ namespace ESM struct Potion; struct Spell; struct NPC; + struct CellId; } namespace MWRender @@ -105,12 +106,14 @@ namespace MWBase virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0; virtual OEngine::Render::Fader* getFader() = 0; - ///< \ŧodo remove this function. Rendering details should not be exposed. + ///< \todo remove this function. Rendering details should not be exposed. virtual MWWorld::CellStore *getExterior (int x, int y) = 0; virtual MWWorld::CellStore *getInterior (const std::string& name) = 0; + virtual MWWorld::CellStore *getCell (const ESM::CellId& id) = 0; + virtual void useDeathCamera() = 0; virtual void setWaterHeight(const float height) = 0; @@ -236,6 +239,8 @@ namespace MWBase virtual void changeToExteriorCell (const ESM::Position& position) = 0; ///< Move to exterior cell. + virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position) = 0; + virtual const ESM::Cell *getExterior (const std::string& cellName) const = 0; ///< Return a cell matching the given name or a 0-pointer, if there is no such cell. diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 11b2d546f..2eb54a125 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -3,6 +3,8 @@ #include #include +#include +#include #include @@ -216,6 +218,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl case ESM::REC_SPEL: case ESM::REC_WEAP: case ESM::REC_GLOB: + case ESM::REC_PLAY: MWBase::Environment::get().getWorld()->readRecord (reader, n.val); break; @@ -245,11 +248,11 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl MWBase::Environment::get().getWindowManager()->updatePlayer(); MWBase::Environment::get().getMechanicsManager()->playerLoaded(); - // for testing purpose only - ESM::Position pos; - pos.pos[0] = pos.pos[1] = pos.pos[2] = 0; - pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; - MWBase::Environment::get().getWorld()->changeToExteriorCell (pos); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + + ESM::CellId cellId = ptr.getCell()->mCell->getCellId(); + + MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition()); } MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) diff --git a/apps/openmw/mwworld/livecellref.cpp b/apps/openmw/mwworld/livecellref.cpp new file mode 100644 index 000000000..a12d20e6a --- /dev/null +++ b/apps/openmw/mwworld/livecellref.cpp @@ -0,0 +1,21 @@ + +#include "livecellref.hpp" + +#include + +void MWWorld::LiveCellRefBase::loadImp (const ESM::ObjectState& state) +{ + mRef = state.mRef; + mData = RefData (state); +} + +void MWWorld::LiveCellRefBase::saveImp (ESM::ObjectState& state) const +{ + state.mRef = mRef; + mData.write (state); +} + +bool MWWorld::LiveCellRefBase::checkStateImp (const ESM::ObjectState& state) +{ + return true; +} \ No newline at end of file diff --git a/apps/openmw/mwworld/livecellref.hpp b/apps/openmw/mwworld/livecellref.hpp index 558639a3b..46f49df78 100644 --- a/apps/openmw/mwworld/livecellref.hpp +++ b/apps/openmw/mwworld/livecellref.hpp @@ -7,6 +7,11 @@ #include "refdata.hpp" +namespace ESM +{ + class ObjectState; +} + namespace MWWorld { class Ptr; @@ -29,6 +34,24 @@ namespace MWWorld LiveCellRefBase(std::string type, const ESM::CellRef &cref=ESM::CellRef()); /* Need this for the class to be recognized as polymorphic */ virtual ~LiveCellRefBase() { } + + protected: + + void loadImp (const ESM::ObjectState& state); + ///< Load state into a LiveCellRef, that has already been initialised with base and + /// class. + /// + /// \attention Must not be called with an invalid \a state. + + void saveImp (ESM::ObjectState& state) const; + ///< Save LiveCellRef state into \a state. + + static bool checkStateImp (const ESM::ObjectState& state); + ///< Check if state is valid and report errors. + /// + /// \return Valid? + /// + /// \note Does not check if the RefId exists. }; inline bool operator== (const LiveCellRefBase& cellRef, const ESM::CellRef::RefNum refNum) @@ -55,7 +78,41 @@ namespace MWWorld // The object that this instance is based on. const X* mBase; + + void load (const ESM::ObjectState& state); + ///< Load state into a LiveCellRef, that has already been initialised with base and class. + /// + /// \attention Must not be called with an invalid \a state. + + void save (ESM::ObjectState& state) const; + ///< Save LiveCellRef state into \a state. + + static bool checkState (const ESM::ObjectState& state); + ///< Check if state is valid and report errors. + /// + /// \return Valid? + /// + /// \note Does not check if the RefId exists. }; + + template + void LiveCellRef::load (const ESM::ObjectState& state) + { + loadImp (state); + } + + template + void LiveCellRef::save (ESM::ObjectState& state) const + { + saveImp (state); + } + + template + bool LiveCellRef::checkState (const ESM::ObjectState& state) + { + return checkStateImp (state); + } + } #endif diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index a2777d489..14e310432 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -1,6 +1,13 @@ #include "player.hpp" +#include + +#include +#include +#include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" @@ -34,9 +41,6 @@ namespace MWWorld void Player::set(const ESM::NPC *player) { mPlayer.mBase = player; - - float* playerPos = mPlayer.mData.getPosition().pos; - playerPos[0] = playerPos[1] = playerPos[2] = 0; } void Player::setCell (MWWorld::CellStore *cellStore) @@ -181,4 +185,54 @@ namespace MWWorld mForwardBackward = 0; mTeleported = false; } + + void Player::write (ESM::ESMWriter& writer) const + { + ESM::Player player; + + mPlayer.save (player.mObject); + player.mCellId = mCellStore->mCell->getCellId(); + + /// \todo sign + /// \todo last know exterior position + /// \todo mark + + player.mAutoMove = mAutoMove ? 1 : 0; + + writer.startRecord (ESM::REC_PLAY); + player.save (writer); + writer.endRecord (ESM::REC_PLAY); + } + + bool Player::readRecord (ESM::ESMReader& reader, int32_t type) + { + if (type==ESM::REC_PLAY) + { + ESM::Player player; + player.load (reader); + + if (!mPlayer.checkState (player.mObject)) + { + // this is the one object we can not silently drop. + throw std::runtime_error ("invalid player state record"); + } + + mPlayer.load (player.mObject); + + mCellStore = MWBase::Environment::get().getWorld()->getCell (player.mCellId); + + /// \todo sign + /// \todo last know exterior position + /// \todo mark + + mAutoMove = player.mAutoMove!=0; + + mForwardBackward = 0; + mTeleported = false; + + return true; + } + + return false; + } } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index fef577cec..7eb023a2b 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -11,6 +11,8 @@ namespace ESM { struct NPC; + class ESMWriter; + class ESMReader; } namespace MWBase @@ -88,6 +90,10 @@ namespace MWWorld void setTeleported(bool teleported); void clear(); + + void write (ESM::ESMWriter& writer) const; + + bool readRecord (ESM::ESMReader& reader, int32_t type); }; } #endif diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index 87d0efe19..8d48078b1 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -3,6 +3,8 @@ #include +#include + #include "customdata.hpp" #include "cellstore.hpp" @@ -52,6 +54,14 @@ namespace MWWorld mLocalRotation.rot[2]=0; } + RefData::RefData (const ESM::ObjectState& objectState) + : mBaseNode (0), mHasLocals (false), mEnabled (objectState.mEnabled), + mCount (objectState.mCount), mPosition (objectState.mPosition), mCustomData (0) + { + for (int i=0; i<3; ++i) + mLocalRotation.rot[i] = objectState.mLocalRotation[i]; + } + RefData::RefData (const RefData& refData) : mBaseNode(0), mCustomData (0) { @@ -66,6 +76,17 @@ namespace MWWorld } } + void RefData::write (ESM::ObjectState& objectState) const + { + objectState.mHasLocals = false; + objectState.mEnabled = mEnabled; + objectState.mCount = mCount; + objectState.mPosition = mPosition; + + for (int i=0; i<3; ++i) + objectState.mLocalRotation[i] = mLocalRotation.rot[i]; + } + RefData& RefData::operator= (const RefData& refData) { try diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index 07841e470..d9f5697bd 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -14,6 +14,7 @@ namespace ESM { class Script; class CellRef; + class ObjectState; } namespace MWWorld @@ -55,10 +56,18 @@ namespace MWWorld /// to reset the position as the orignal data is still held in the CellRef RefData (const ESM::CellRef& cellRef); + RefData (const ESM::ObjectState& objectState); + ///< Ignores local variables and custom data (not enough context available here to + /// perform these operations). + RefData (const RefData& refData); ~RefData(); + void write (ESM::ObjectState& objectState) const; + ///< Ignores local variables and custom data (not enough context available here to + /// perform these operations). + RefData& operator= (const RefData& refData); /// Return OGRE handle (may be empty). diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5224ffdce..8edba0892 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -309,12 +310,14 @@ namespace MWWorld { mStore.write (writer); mGlobalVariables.write (writer); + mPlayer->write (writer); } void World::readRecord (ESM::ESMReader& reader, int32_t type) { if (!mStore.readRecord (reader, type) && - !mGlobalVariables.readRecord (reader, type)) + !mGlobalVariables.readRecord (reader, type) && + !mPlayer->readRecord (reader, type)) { throw std::runtime_error ("unknown record in saved game"); } @@ -402,6 +405,14 @@ namespace MWWorld return mCells.getInterior (name); } + CellStore *World::getCell (const ESM::CellId& id) + { + if (id.mPaged) + return getExterior (id.mIndex.mX, id.mIndex.mY); + else + return getInterior (id.mWorldspace); + } + void World::useDeathCamera() { if(mRendering->getCamera()->isVanityOrPreviewModeEnabled() ) @@ -802,6 +813,14 @@ namespace MWWorld addContainerScripts(getPlayer().getPlayer(), getPlayer().getPlayer().getCell()); } + void World::changeToCell (const ESM::CellId& cellId, const ESM::Position& position) + { + if (cellId.mPaged) + changeToExteriorCell (position); + else + changeToInteriorCell (cellId.mWorldspace, position); + } + void World::markCellAsUnchanged() { return mWorldScene->markCellAsUnchanged(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index d7befcc6e..58a6111c5 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -181,12 +181,14 @@ namespace MWWorld virtual void readRecord (ESM::ESMReader& reader, int32_t type); virtual OEngine::Render::Fader* getFader(); - ///< \ŧodo remove this function. Rendering details should not be exposed. + ///< \todo remove this function. Rendering details should not be exposed. virtual CellStore *getExterior (int x, int y); virtual CellStore *getInterior (const std::string& name); + virtual CellStore *getCell (const ESM::CellId& id); + //switch to POV before showing player's death animation virtual void useDeathCamera(); @@ -314,6 +316,8 @@ namespace MWWorld virtual void changeToExteriorCell (const ESM::Position& position); ///< Move to exterior cell. + virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position); + virtual const ESM::Cell *getExterior (const std::string& cellName) const; ///< Return a cell matching the given name or a 0-pointer, if there is no such cell. From ddf72c06deb64c89622b2483ebdb92c4effbe3f6 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Thu, 16 Jan 2014 12:09:50 +0100 Subject: [PATCH 543/889] Update statswindow.cpp Fixes a build issue on MSVC ('floor' : ambiguous call to overloaded function) --- apps/openmw/mwgui/statswindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 37128c43f..40eb2d3b1 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -186,7 +186,7 @@ namespace MWGui if (widget) { int modified = value.getModified(), base = value.getBase(); - std::string text = boost::lexical_cast(std::floor(modified)); + std::string text = boost::lexical_cast(modified); std::string state = "normal"; if (modified > base) state = "increased"; From ce00639d31eea587cc8e921c94914479ccd711bf Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 17 Jan 2014 10:51:52 +0100 Subject: [PATCH 544/889] added missing birthsign field to player state record --- components/esm/player.cpp | 4 ++++ components/esm/player.hpp | 1 + 2 files changed, 5 insertions(+) diff --git a/components/esm/player.cpp b/components/esm/player.cpp index 13602fb67..d5ddc74d0 100644 --- a/components/esm/player.cpp +++ b/components/esm/player.cpp @@ -23,6 +23,8 @@ void ESM::Player::load (ESMReader &esm) mAutoMove = 0; esm.getHNOT (mAutoMove, "AMOV"); + + mBirthsign = esm.getHNString ("SIGN"); } void ESM::Player::save (ESMWriter &esm) const @@ -41,4 +43,6 @@ void ESM::Player::save (ESMWriter &esm) const if (mAutoMove) esm.writeHNT ("AMOV", mAutoMove); + + esm.writeHNString ("SIGN", mBirthsign); } \ No newline at end of file diff --git a/components/esm/player.hpp b/components/esm/player.hpp index 3f7db17f7..bd618457e 100644 --- a/components/esm/player.hpp +++ b/components/esm/player.hpp @@ -23,6 +23,7 @@ namespace ESM ESM::Position mMarkedPosition; CellId mMarkedCell; unsigned char mAutoMove; + std::string mBirthsign; void load (ESMReader &esm); void save (ESMWriter &esm) const; From f070d9966db366d5c4364d0dc95f102cae20d3d5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 15 Jan 2014 16:30:16 +0100 Subject: [PATCH 545/889] Implement movement speed formula for creatures. Still moving a bit too slow. --- apps/openmw/mwclass/creature.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index a18df6d9f..f2a4f9ccd 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -301,7 +301,7 @@ namespace MWClass const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); - const float normalizedEncumbrance = 0; //getEncumbrance(ptr) / getCapacity(ptr); + const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr); bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); From fe66012bcda70a3d1f61bdf726f9b6abc5133ee4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 16 Jan 2014 06:57:50 +0100 Subject: [PATCH 546/889] Closes #1115: Fix a bug causing number of AI packages to grow exponentially when adding an AI package. Not sure if adding the same package twice should even be allowed. --- apps/openmw/mwmechanics/aisequence.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 79a953e38..73caa6ca7 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -109,7 +109,10 @@ void MWMechanics::AiSequence::stack (const AiPackage& package) for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); it++) { if(mPackages.front()->getPriority() <= package.getPriority()) + { mPackages.insert(it,package.clone()); + return; + } } if(mPackages.empty()) From 2836c7c927ede12d6d993831fae3410ba07c7449 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 16 Jan 2014 07:11:34 +0100 Subject: [PATCH 547/889] Do not set owner when adding items to a container, as opposed to NPC/creatures --- apps/openmw/mwworld/containerstore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 154fa1999..0c4226f9b 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -149,7 +149,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr item.getCellRef().mPos.pos[1] = 0; item.getCellRef().mPos.pos[2] = 0; - if (setOwner) + if (setOwner && actorPtr.getClass().isActor()) item.getCellRef().mOwner = actorPtr.getCellRef().mRefID; std::string script = MWWorld::Class::get(item).getScript(item); From e410eb527310b8ca13a121248e1f09e3f77a5081 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 16 Jan 2014 13:49:26 +0100 Subject: [PATCH 548/889] Play 'Idle' voiced dialogue entries in AIWander. Tweak voice max distance. --- apps/openmw/mwmechanics/aiwander.cpp | 9 +++++++++ apps/openmw/mwsound/soundmanagerimp.cpp | 3 +-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index bf6c1dc38..853d0ff7b 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -6,6 +6,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/dialoguemanager.hpp" #include "../mwmechanics/npcstats.hpp" #include @@ -185,6 +186,14 @@ namespace MWMechanics playIdle(actor, mPlayedIdle); mChooseAction = false; mIdleNow = true; + + // Play idle voiced dialogue entries randomly + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + float chance = store.get().find("fVoiceIdleOdds")->getFloat(); + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + // TODO: do not show subtitle messagebox if player is too far away? or do not say at all? + if (roll < chance) + MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); } } diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index a7ee96831..bdd03a8b4 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -248,14 +248,13 @@ namespace MWSound return; try { - // The range values are not tested float basevol = volumeFromType(Play_TypeVoice); std::string filePath = "Sound/"+filename; const ESM::Position &pos = ptr.getRefData().getPosition(); const Ogre::Vector3 objpos(pos.pos); MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, 1.0f, basevol, 1.0f, - 20.0f, 12750.0f, Play_Normal|Play_TypeVoice, 0); + 20.0f, 1500.0f, Play_Normal|Play_TypeVoice, 0); mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound")); } catch(std::exception &e) From ddc432c7ef8c49c6d380ce7b229ab27c098f1c53 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 06:18:37 +0100 Subject: [PATCH 549/889] Fix stealing bug --- apps/openmw/mwclass/npc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 8d51fd1cf..57de52338 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -775,10 +775,10 @@ namespace MWClass } if(getCreatureStats(ptr).isDead()) return boost::shared_ptr(new MWWorld::ActionOpen(ptr, true)); - if(getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak)) - return boost::shared_ptr(new MWWorld::ActionOpen(ptr)); // stealing if(get(ptr).getCreatureStats(ptr).isHostile()) return boost::shared_ptr(new MWWorld::FailedAction("#{sActorInCombat}")); + if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak)) + return boost::shared_ptr(new MWWorld::ActionOpen(ptr)); // stealing return boost::shared_ptr(new MWWorld::ActionTalk(ptr)); } From 11394d83c5006efe11c1a43484f532dec25938ee Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 08:54:24 +0100 Subject: [PATCH 550/889] Feature #1086: Import blood models/textures in MWIniImporter --- apps/mwiniimporter/importer.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index cf1589114..648ab3ebe 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -623,6 +623,17 @@ MwIniImporter::MwIniImporter() "Moons:Masser Fade Out Finish", "Moons:Script Color", + // blood + "Blood:Model 0", + "Blood:Model 1", + "Blood:Model 2", + "Blood:Texture 0", + "Blood:Texture 1", + "Blood:Texture 2", + "Blood:Texture Name 0", + "Blood:Texture Name 1", + "Blood:Texture Name 2", + 0 }; From 240d96a0f139d77a74e54635586600b20be2356a Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 09:41:23 +0100 Subject: [PATCH 551/889] Renamed AnimationValue to AnimationTime --- apps/openmw/mwrender/animation.cpp | 34 +++++++++++++-------------- apps/openmw/mwrender/animation.hpp | 14 +++++------ apps/openmw/mwrender/npcanimation.cpp | 9 +++---- apps/openmw/mwrender/npcanimation.hpp | 6 ++--- 4 files changed, 32 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index d4a3533a1..f0650fd82 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -30,7 +30,7 @@ namespace MWRender { -Ogre::Real Animation::AnimationValue::getValue() const +Ogre::Real Animation::AnimationTime::getValue() const { AnimStateMap::const_iterator iter = mAnimation->mStates.find(mAnimationName); if(iter != mAnimation->mStates.end()) @@ -38,16 +38,16 @@ Ogre::Real Animation::AnimationValue::getValue() const return 0.0f; } -void Animation::AnimationValue::setValue(Ogre::Real) +void Animation::AnimationTime::setValue(Ogre::Real) { } -Ogre::Real Animation::EffectAnimationValue::getValue() const +Ogre::Real Animation::EffectAnimationTime::getValue() const { return mTime; } -void Animation::EffectAnimationValue::setValue(Ogre::Real) +void Animation::EffectAnimationTime::setValue(Ogre::Real) { } @@ -60,10 +60,10 @@ Animation::Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node) , mNonAccumRoot(NULL) , mNonAccumCtrl(NULL) , mAccumulate(0.0f) - , mNullAnimationValuePtr(OGRE_NEW NullAnimationValue) + , mNullAnimationTimePtr(OGRE_NEW NullAnimationTime) { for(size_t i = 0;i < sNumGroups;i++) - mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this)); + mAnimationTimePtr[i].bind(OGRE_NEW AnimationTime(this)); } Animation::~Animation() @@ -139,7 +139,7 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly) for(size_t i = 0;i < mObjectRoot->mControllers.size();i++) { if(mObjectRoot->mControllers[i].getSource().isNull()) - mObjectRoot->mControllers[i].setSource(mAnimationValuePtr[0]); + mObjectRoot->mControllers[i].setSource(mAnimationTimePtr[0]); } } @@ -286,7 +286,7 @@ void Animation::addAnimSource(const std::string &model) } } - ctrls[i].setSource(mAnimationValuePtr[grp]); + ctrls[i].setSource(mAnimationTimePtr[grp]); grpctrls[grp].push_back(ctrls[i]); } } @@ -296,7 +296,7 @@ void Animation::clearAnimSources() mStates.clear(); for(size_t i = 0;i < sNumGroups;i++) - mAnimationValuePtr[i]->setAnimName(std::string()); + mAnimationTimePtr[i]->setAnimName(std::string()); mNonAccumCtrl = NULL; @@ -789,7 +789,7 @@ void Animation::resetActiveGroups() active = state; } - mAnimationValuePtr[grp]->setAnimName((active == mStates.end()) ? + mAnimationTimePtr[grp]->setAnimName((active == mStates.end()) ? std::string() : active->first); } mNonAccumCtrl = NULL; @@ -797,7 +797,7 @@ void Animation::resetActiveGroups() if(!mNonAccumRoot || mAccumulate == Ogre::Vector3(0.0f)) return; - AnimStateMap::const_iterator state = mStates.find(mAnimationValuePtr[0]->getAnimName()); + AnimStateMap::const_iterator state = mStates.find(mAnimationTimePtr[0]->getAnimName()); if(state == mStates.end()) return; @@ -869,13 +869,13 @@ Ogre::Vector3 Animation::runAnimation(float duration) targetTime = state.mTime + timepassed; if(textkey == textkeys.end() || textkey->first > targetTime) { - if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) + if(mNonAccumCtrl && stateiter->first == mAnimationTimePtr[0]->getAnimName()) updatePosition(state.mTime, targetTime, movement); state.mTime = std::min(targetTime, state.mStopTime); } else { - if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) + if(mNonAccumCtrl && stateiter->first == mAnimationTimePtr[0]->getAnimName()) updatePosition(state.mTime, textkey->first, movement); state.mTime = textkey->first; } @@ -926,7 +926,7 @@ Ogre::Vector3 Animation::runAnimation(float duration) // Apply group controllers for(size_t grp = 0;grp < sNumGroups;grp++) { - const std::string &name = mAnimationValuePtr[grp]->getAnimName(); + const std::string &name = mAnimationTimePtr[grp]->getAnimName(); if(!name.empty() && (stateiter=mStates.find(name)) != mStates.end()) { const Ogre::SharedPtr &src = stateiter->second.mSource; @@ -1052,7 +1052,7 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con for(size_t i = 0;i < params.mObjects->mControllers.size();i++) { if(params.mObjects->mControllers[i].getSource().isNull()) - params.mObjects->mControllers[i].setSource(Ogre::SharedPtr (new EffectAnimationValue())); + params.mObjects->mControllers[i].setSource(Ogre::SharedPtr (new EffectAnimationTime())); } if (!texture.empty()) @@ -1110,7 +1110,7 @@ void Animation::updateEffects(float duration) NifOgre::ObjectScenePtr objects = it->mObjects; for(size_t i = 0; i < objects->mControllers.size() ;i++) { - EffectAnimationValue* value = dynamic_cast(objects->mControllers[i].getSource().get()); + EffectAnimationTime* value = dynamic_cast(objects->mControllers[i].getSource().get()); if (value) value->addTime(duration); @@ -1125,7 +1125,7 @@ void Animation::updateEffects(float duration) float remainder = objects->mControllers[0].getSource()->getValue() - objects->mMaxControllerLength; for(size_t i = 0; i < objects->mControllers.size() ;i++) { - EffectAnimationValue* value = dynamic_cast(objects->mControllers[i].getSource().get()); + EffectAnimationTime* value = dynamic_cast(objects->mControllers[i].getSource().get()); if (value) value->resetTime(remainder); } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 1400cb9a2..22f2e5df9 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -32,14 +32,14 @@ protected: /* This is the number of *discrete* groups. */ static const size_t sNumGroups = 4; - class AnimationValue : public Ogre::ControllerValue + class AnimationTime : public Ogre::ControllerValue { private: Animation *mAnimation; std::string mAnimationName; public: - AnimationValue(Animation *anim) + AnimationTime(Animation *anim) : mAnimation(anim) { } @@ -52,12 +52,12 @@ protected: virtual void setValue(Ogre::Real value); }; - class EffectAnimationValue : public Ogre::ControllerValue + class EffectAnimationTime : public Ogre::ControllerValue { private: float mTime; public: - EffectAnimationValue() : mTime(0) { } + EffectAnimationTime() : mTime(0) { } void addTime(float time) { mTime += time; } void resetTime(float value) { mTime = value; } @@ -67,7 +67,7 @@ protected: - class NullAnimationValue : public Ogre::ControllerValue + class NullAnimationTime : public Ogre::ControllerValue { public: virtual Ogre::Real getValue() const @@ -134,8 +134,8 @@ protected: AnimStateMap mStates; - Ogre::SharedPtr mAnimationValuePtr[sNumGroups]; - Ogre::SharedPtr mNullAnimationValuePtr; + Ogre::SharedPtr mAnimationTimePtr[sNumGroups]; + Ogre::SharedPtr mNullAnimationTimePtr; ObjectAttachMap mAttachedObjects; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 497279bd8..bcb6a374c 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -61,8 +61,9 @@ std::string getVampireHead(const std::string& race, bool female) namespace MWRender { -float SayAnimationValue::getValue() const +float HeadAnimationTime::getValue() const { + // TODO: Handle eye blinking (time is in the text keys) if (MWBase::Environment::get().getSoundManager()->sayDone(mReference)) return 0; else @@ -124,7 +125,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v { mNpc = mPtr.get()->mBase; - mSayAnimationValue = Ogre::SharedPtr(new SayAnimationValue(mPtr)); + mHeadAnimationTime = Ogre::SharedPtr(new HeadAnimationTime(mPtr)); for(size_t i = 0;i < ESM::PRT_Count;i++) { @@ -595,10 +596,10 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g { if(ctrl->getSource().isNull()) { - ctrl->setSource(mNullAnimationValuePtr); + ctrl->setSource(mNullAnimationTimePtr); if (type == ESM::PRT_Head) - ctrl->setSource(mSayAnimationValue); + ctrl->setSource(mHeadAnimationTime); } } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 6f0403b9d..e86ec7d4e 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -13,12 +13,12 @@ namespace ESM namespace MWRender { -class SayAnimationValue : public Ogre::ControllerValue +class HeadAnimationTime : public Ogre::ControllerValue { private: MWWorld::Ptr mReference; public: - SayAnimationValue(MWWorld::Ptr reference) : mReference(reference) {} + HeadAnimationTime(MWWorld::Ptr reference) : mReference(reference) {} virtual Ogre::Real getValue() const; virtual void setValue(Ogre::Real value) @@ -70,7 +70,7 @@ private: Ogre::Vector3 mFirstPersonOffset; - Ogre::SharedPtr mSayAnimationValue; + Ogre::SharedPtr mHeadAnimationTime; float mAlpha; From 805843d7ff8cb15dbe1fb039037e6ae79d34e3d9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 10:52:44 +0100 Subject: [PATCH 552/889] Closes #1086: Implement blood effects --- apps/esmtool/labels.cpp | 4 +- apps/opencs/model/world/refidcollection.cpp | 2 +- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/world.hpp | 3 + apps/openmw/mwclass/creature.cpp | 11 ++ apps/openmw/mwclass/creature.hpp | 3 + apps/openmw/mwclass/npc.cpp | 17 ++- apps/openmw/mwclass/npc.hpp | 3 + apps/openmw/mwrender/animation.cpp | 1 + apps/openmw/mwrender/animation.hpp | 10 +- apps/openmw/mwrender/creatureanimation.cpp | 2 +- apps/openmw/mwrender/effectmanager.cpp | 117 ++++++++++++++++++++ apps/openmw/mwrender/effectmanager.hpp | 31 ++++++ apps/openmw/mwrender/renderingmanager.cpp | 19 +++- apps/openmw/mwrender/renderingmanager.hpp | 8 +- apps/openmw/mwworld/class.cpp | 4 + apps/openmw/mwworld/class.hpp | 3 + apps/openmw/mwworld/worldimp.cpp | 27 +++++ apps/openmw/mwworld/worldimp.hpp | 3 + components/esm/loadcrea.hpp | 16 ++- 20 files changed, 263 insertions(+), 23 deletions(-) create mode 100644 apps/openmw/mwrender/effectmanager.cpp create mode 100644 apps/openmw/mwrender/effectmanager.hpp diff --git a/apps/esmtool/labels.cpp b/apps/esmtool/labels.cpp index d270a9534..7a42e6900 100644 --- a/apps/esmtool/labels.cpp +++ b/apps/esmtool/labels.cpp @@ -680,7 +680,7 @@ std::string creatureFlags(int flags) if (flags & ESM::Creature::Walks) properties += "Walks "; if (flags & ESM::Creature::Swims) properties += "Swims "; if (flags & ESM::Creature::Flies) properties += "Flies "; - if (flags & ESM::Creature::Biped) properties += "Biped "; + if (flags & ESM::Creature::Bipedal) properties += "Bipedal "; if (flags & ESM::Creature::Respawn) properties += "Respawn "; if (flags & ESM::Creature::Weapon) properties += "Weapon "; if (flags & ESM::Creature::Skeleton) properties += "Skeleton "; @@ -691,7 +691,7 @@ std::string creatureFlags(int flags) ESM::Creature::Walks| ESM::Creature::Swims| ESM::Creature::Flies| - ESM::Creature::Biped| + ESM::Creature::Bipedal| ESM::Creature::Respawn| ESM::Creature::Weapon| ESM::Creature::Skeleton| diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 9ed526818..176a19f2f 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -181,7 +181,7 @@ CSMWorld::RefIdCollection::RefIdCollection() unsigned int mFlag; } sCreatureFlagTable[] = { - { Columns::ColumnId_Biped, ESM::Creature::Biped }, + { Columns::ColumnId_Biped, ESM::Creature::Bipedal }, { Columns::ColumnId_HasWeapon, ESM::Creature::Weapon }, { Columns::ColumnId_NoMovement, ESM::Creature::None }, { Columns::ColumnId_Swims, ESM::Creature::Swims }, diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 4da5e2997..3b533b416 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -20,7 +20,7 @@ add_openmw_dir (mwrender renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation actors objects renderinginterface localmap occlusionquery water shadows characterpreview globalmap videoplayer ripplesimulation refraction - terrainstorage renderconst + terrainstorage renderconst effectmanager ) add_openmw_dir (mwinput diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 83fcba87c..3ad716b72 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -463,6 +463,9 @@ namespace MWBase /// Spawn a random creature from a levelled list next to the player virtual void spawnRandomCreature(const std::string& creatureList) = 0; + + /// Spawn a blood effect for \a ptr at \a worldPosition + virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition) = 0; }; } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index f2a4f9ccd..0fc26950d 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -530,6 +530,17 @@ namespace MWClass } } + int Creature::getBloodTexture(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = ptr.get(); + + if (ref->mBase->mFlags & ESM::Creature::Skeleton) + return 1; + if (ref->mBase->mFlags & ESM::Creature::Metal) + return 2; + return 0; + } + const ESM::GameSetting* Creature::fMinWalkSpeedCreature; const ESM::GameSetting* Creature::fMaxWalkSpeedCreature; const ESM::GameSetting *Creature::fEncumberedMoveEffect; diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 7d2f95fae..708999ef0 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -111,6 +111,9 @@ namespace MWClass virtual bool isFlying (const MWWorld::Ptr &ptr) const; virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const; + + /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) + virtual int getBloodTexture (const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 57de52338..93a7ddcf6 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -455,7 +455,9 @@ namespace MWClass weapon.get()->mBase->mData.mReach : gmst.find("fHandToHandReach")->getFloat()); // TODO: Use second to work out the hit angle and where to spawn the blood effect - MWWorld::Ptr victim = world->getHitContact(ptr, dist).first; + std::pair result = world->getHitContact(ptr, dist); + MWWorld::Ptr victim = result.first; + Ogre::Vector3 hitPosition = result.second; if(victim.isEmpty()) // Didn't hit anything return; @@ -602,6 +604,8 @@ namespace MWClass } } + MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); + othercls.onHit(victim, damage, healthdmg, weapon, ptr, true); } @@ -1216,6 +1220,17 @@ namespace MWClass return ptr.getClass().getNpcStats(ptr).getSkill(skill).getModified(); } + int Npc::getBloodTexture(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = ptr.get(); + + if (ref->mBase->mFlags & ESM::NPC::Skeleton) + return 1; + if (ref->mBase->mFlags & ESM::NPC::Metal) + return 2; + return 0; + } + const ESM::GameSetting *Npc::fMinWalkSpeed; const ESM::GameSetting *Npc::fMaxWalkSpeed; const ESM::GameSetting *Npc::fEncumberedMoveEffect; diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index b729d0151..497d0ced8 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -142,6 +142,9 @@ namespace MWClass virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; + /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) + virtual int getBloodTexture (const MWWorld::Ptr& ptr) const; + virtual bool isActor() const { return true; } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index f0650fd82..de89b0207 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1042,6 +1042,7 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con else params.mObjects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); + // TODO: turn off shadow casting setRenderProperties(params.mObjects, RV_Misc, RQG_Main, RQG_Alpha, 0.f, false, NULL); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 22f2e5df9..da1c1628c 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -189,16 +189,18 @@ protected: /** Adds an additional light to the given object list using the specified ESM record. */ void addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectScenePtr objlist, const ESM::Light *light); - static void setRenderProperties(NifOgre::ObjectScenePtr objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, - Ogre::uint8 transqueue, Ogre::Real dist=0.0f, - bool enchantedGlow=false, Ogre::Vector3* glowColor=NULL); - void clearAnimSources(); // TODO: Should not be here Ogre::Vector3 getEnchantmentColor(MWWorld::Ptr item); public: + // FIXME: Move outside of this class + static void setRenderProperties(NifOgre::ObjectScenePtr objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, + Ogre::uint8 transqueue, Ogre::Real dist=0.0f, + bool enchantedGlow=false, Ogre::Vector3* glowColor=NULL); + + Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node); virtual ~Animation(); diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index c3ad512dd..20e5ff8ef 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -24,7 +24,7 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) setObjectRoot(model, false); setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); - if((ref->mBase->mFlags&ESM::Creature::Biped)) + if((ref->mBase->mFlags&ESM::Creature::Bipedal)) addAnimSource("meshes\\base_anim.nif"); addAnimSource(model); } diff --git a/apps/openmw/mwrender/effectmanager.cpp b/apps/openmw/mwrender/effectmanager.cpp new file mode 100644 index 000000000..eb4525a4f --- /dev/null +++ b/apps/openmw/mwrender/effectmanager.cpp @@ -0,0 +1,117 @@ +#include "effectmanager.hpp" + +#include +#include + +#include "animation.hpp" +#include "renderconst.hpp" + +namespace MWRender +{ + +class EffectAnimationTime : public Ogre::ControllerValue +{ +private: + float mTime; +public: + EffectAnimationTime() : mTime(0) { } + void addTime(float time) { mTime += time; } + + virtual Ogre::Real getValue() const { return mTime; } + virtual void setValue(Ogre::Real value) {} +}; + +EffectManager::EffectManager(Ogre::SceneManager *sceneMgr) + : mSceneMgr(sceneMgr) +{ +} + +void EffectManager::addEffect(const std::string &model, std::string textureOverride, const Ogre::Vector3 &worldPosition) +{ + Ogre::SceneNode* sceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(worldPosition); + + // fix texture extension to .dds + if (textureOverride.size() > 4) + { + textureOverride[textureOverride.size()-3] = 'd'; + textureOverride[textureOverride.size()-2] = 'd'; + textureOverride[textureOverride.size()-1] = 's'; + } + + + NifOgre::ObjectScenePtr scene = NifOgre::Loader::createObjects(sceneNode, model); + + // TODO: turn off shadow casting + MWRender::Animation::setRenderProperties(scene, RV_Misc, + RQG_Main, RQG_Alpha, 0.f, false, NULL); + + for(size_t i = 0;i < scene->mControllers.size();i++) + { + if(scene->mControllers[i].getSource().isNull()) + scene->mControllers[i].setSource(Ogre::SharedPtr (new EffectAnimationTime())); + } + + if (!textureOverride.empty()) + { + for(size_t i = 0;i < scene->mParticles.size(); ++i) + { + Ogre::ParticleSystem* partSys = scene->mParticles[i]; + + Ogre::MaterialPtr mat = scene->mMaterialControllerMgr.getWritableMaterial(partSys); + + for (int t=0; tgetNumTechniques(); ++t) + { + Ogre::Technique* tech = mat->getTechnique(t); + for (int p=0; pgetNumPasses(); ++p) + { + Ogre::Pass* pass = tech->getPass(p); + for (int tex=0; texgetNumTextureUnitStates(); ++tex) + { + Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex); + tus->setTextureName("textures\\" + textureOverride); + } + } + } + } + } + + mEffects.push_back(std::make_pair(sceneNode, scene)); +} + +void EffectManager::update(float dt) +{ + for (std::vector >::iterator it = mEffects.begin(); it != mEffects.end(); ) + { + NifOgre::ObjectScenePtr objects = it->second; + for(size_t i = 0; i < objects->mControllers.size() ;i++) + { + EffectAnimationTime* value = dynamic_cast(objects->mControllers[i].getSource().get()); + if (value) + value->addTime(dt); + + objects->mControllers[i].update(); + } + + // Finished playing? + if (objects->mControllers[0].getSource()->getValue() >= objects->mMaxControllerLength) + { + Ogre::SceneNode* node = it->first; + it = mEffects.erase(it); + mSceneMgr->destroySceneNode(node); + continue; + } + ++it; + } +} + +void EffectManager::clear() +{ + for (std::vector >::iterator it = mEffects.begin(); it != mEffects.end(); ) + { + Ogre::SceneNode* node = it->first; + it = mEffects.erase(it); + mSceneMgr->destroySceneNode(node); + } +} + +} diff --git a/apps/openmw/mwrender/effectmanager.hpp b/apps/openmw/mwrender/effectmanager.hpp new file mode 100644 index 000000000..0c8bc3857 --- /dev/null +++ b/apps/openmw/mwrender/effectmanager.hpp @@ -0,0 +1,31 @@ +#ifndef OPENMW_MWRENDER_EFFECTMANAGER_H +#define OPENMW_MWRENDER_EFFECTMANAGER_H + +#include + +namespace MWRender +{ + // Note: effects attached to another object should be managed by MWRender::Animation::addEffect. + // This class manages "free" effects, i.e. attached to a dedicated scene node in the world. + class EffectManager + { + public: + EffectManager(Ogre::SceneManager* sceneMgr); + ~EffectManager() { clear(); } + + /// Add an effect. When it's finished playing, it will be removed automatically. + void addEffect (const std::string& model, std::string textureOverride, const Ogre::Vector3& worldPosition); + + void update(float dt); + + /// Remove all effects + void clear(); + + private: + std::vector > mEffects; + Ogre::SceneManager* mSceneMgr; + }; + +} + +#endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 47cf3ee41..8a2ab1c7a 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -41,6 +41,7 @@ #include "globalmap.hpp" #include "videoplayer.hpp" #include "terrainstorage.hpp" +#include "effectmanager.hpp" using namespace MWRender; using namespace Ogre; @@ -57,9 +58,11 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b , mSunEnabled(0) , mPhysicsEngine(engine) , mTerrain(NULL) + , mEffectManager(NULL) { mActors = new MWRender::Actors(mRendering, this); mObjects = new MWRender::Objects(mRendering); + mEffectManager = new EffectManager(mRendering.getScene()); // select best shader mode bool openGL = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL") != std::string::npos); bool glES = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL ES") != std::string::npos); @@ -193,6 +196,7 @@ RenderingManager::~RenderingManager () delete mVideoPlayer; delete mActors; delete mObjects; + delete mEffectManager; delete mFactory; } @@ -374,6 +378,8 @@ void RenderingManager::update (float duration, bool paused) if(paused) return; + mEffectManager->update(duration); + mActors->update (mRendering.getCamera()); mPlayerAnimation->preRender(mRendering.getCamera()); mObjects->update (duration, mRendering.getCamera()); @@ -675,14 +681,14 @@ Shadows* RenderingManager::getShadows() void RenderingManager::switchToInterior() { - // causes light flicker in opengl when moving.. - //mRendering.getScene()->setCameraRelativeRendering(false); + // TODO: also do this when switching worldspace + mEffectManager->clear(); } void RenderingManager::switchToExterior() { - // causes light flicker in opengl when moving.. - //mRendering.getScene()->setCameraRelativeRendering(true); + // TODO: also do this when switching worldspace + mEffectManager->clear(); } Ogre::Vector4 RenderingManager::boundingBoxToScreen(Ogre::AxisAlignedBox bounds) @@ -1020,4 +1026,9 @@ float RenderingManager::getCameraDistance() const return mCamera->getCameraDistance(); } +void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const Vector3 &worldPosition) +{ + mEffectManager->addEffect(model, texture, worldPosition); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 9f77f0a3c..b6379bee4 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -21,10 +21,7 @@ namespace Ogre { - class SceneManager; class SceneNode; - class Quaternion; - class Vector3; } namespace MWWorld @@ -51,6 +48,7 @@ namespace MWRender class GlobalMap; class VideoPlayer; class Animation; + class EffectManager; class RenderingManager: private RenderingInterface, public Ogre::RenderTargetListener, public OEngine::Render::WindowSizeListener { @@ -209,6 +207,8 @@ public: void stopVideo(); void frameStarted(float dt, bool paused); + void spawnEffect (const std::string& model, const std::string& texture, const Ogre::Vector3& worldPosition); + protected: virtual void windowResized(int x, int y); @@ -239,6 +239,8 @@ private: MWRender::Objects* mObjects; MWRender::Actors* mActors; + MWRender::EffectManager* mEffectManager; + MWRender::NpcAnimation *mPlayerAnimation; // 0 normal, 1 more bright, 2 max diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index f3128780b..6c00b949c 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -363,4 +363,8 @@ namespace MWWorld throw std::runtime_error("class does not support skills"); } + int Class::getBloodTexture (const MWWorld::Ptr& ptr) const + { + throw std::runtime_error("class does not support gore"); + } } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index bbc74323c..ec22d0306 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -278,6 +278,9 @@ namespace MWWorld virtual bool isKey (const MWWorld::Ptr& ptr) const { return false; } + /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) + virtual int getBloodTexture (const MWWorld::Ptr& ptr) const; + virtual Ptr copyToCell(const Ptr &ptr, CellStore &cell) const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ebc1044bc..a40f20696 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2610,4 +2610,31 @@ namespace MWWorld safePlaceObject(ref.getPtr(),*cell,ipos); } } + + void World::spawnBloodEffect(const Ptr &ptr, const Vector3 &worldPosition) + { + int type = ptr.getClass().getBloodTexture(ptr); + std::string texture; + switch (type) + { + case 2: + texture = getFallback()->getFallbackString("Blood_Texture_2"); + break; + case 1: + texture = getFallback()->getFallbackString("Blood_Texture_1"); + break; + case 0: + default: + texture = getFallback()->getFallbackString("Blood_Texture_0"); + break; + } + + std::stringstream modelName; + modelName << "Blood_Model_"; + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 3; // [0, 2] + modelName << roll; + std::string model = "meshes\\" + getFallback()->getFallbackString(modelName.str()); + + mRendering->spawnEffect(model, texture, worldPosition); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 92975400a..38766e74f 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -549,6 +549,9 @@ namespace MWWorld /// Spawn a random creature from a levelled list next to the player virtual void spawnRandomCreature(const std::string& creatureList); + + /// Spawn a blood effect for \a ptr at \a worldPosition + virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition); }; } diff --git a/components/esm/loadcrea.hpp b/components/esm/loadcrea.hpp index c0523025b..817c0e43c 100644 --- a/components/esm/loadcrea.hpp +++ b/components/esm/loadcrea.hpp @@ -25,16 +25,20 @@ struct Creature // Default is 0x48? enum Flags { - Biped = 0x001, - Respawn = 0x002, - Weapon = 0x004, // Has weapon and shield - None = 0x008, // ?? + // Movement types + Bipedal = 0x001, Swims = 0x010, Flies = 0x020, // Don't know what happens if several Walks = 0x040, // of these are set + + Respawn = 0x002, + Weapon = 0x004, // Has weapon and shield + None = 0x008, // ?? Essential = 0x080, - Skeleton = 0x400, // Does not have normal blood - Metal = 0x800 // Has 'golden' blood + + // Blood types + Skeleton = 0x400, + Metal = 0x800 }; enum Type From 1b7697a4b21dd6b1af49eedcbfdf8598a9b4c732 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 17 Jan 2014 13:07:57 +0100 Subject: [PATCH 553/889] handle missing player specific state during load/save --- apps/openmw/mwworld/player.cpp | 54 +++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 14e310432..f03abe5bc 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -193,9 +193,20 @@ namespace MWWorld mPlayer.save (player.mObject); player.mCellId = mCellStore->mCell->getCellId(); - /// \todo sign - /// \todo last know exterior position - /// \todo mark + player.mBirthsign = mSign; + + player.mLastKnownExteriorPosition[0] = mLastKnownExteriorPosition.x; + player.mLastKnownExteriorPosition[1] = mLastKnownExteriorPosition.y; + player.mLastKnownExteriorPosition[2] = mLastKnownExteriorPosition.z; + + if (mMarkedCell) + { + player.mHasMark = true; + player.mMarkedPosition = mMarkedPosition; + player.mMarkedCell = mMarkedCell->mCell->getCellId(); + } + else + player.mHasMark = false; player.mAutoMove = mAutoMove ? 1 : 0; @@ -214,16 +225,43 @@ namespace MWWorld if (!mPlayer.checkState (player.mObject)) { // this is the one object we can not silently drop. - throw std::runtime_error ("invalid player state record"); + throw std::runtime_error ("invalid player state record (object state)"); } mPlayer.load (player.mObject); - mCellStore = MWBase::Environment::get().getWorld()->getCell (player.mCellId); + MWBase::World& world = *MWBase::Environment::get().getWorld(); - /// \todo sign - /// \todo last know exterior position - /// \todo mark + mCellStore = world.getCell (player.mCellId); + + if (!player.mBirthsign.empty() && + !world.getStore().get().search (player.mBirthsign)) + throw std::runtime_error ("invalid player state record (birthsign)"); + + mSign = player.mBirthsign; + + mLastKnownExteriorPosition.x = player.mLastKnownExteriorPosition[0]; + mLastKnownExteriorPosition.y = player.mLastKnownExteriorPosition[1]; + mLastKnownExteriorPosition.z = player.mLastKnownExteriorPosition[2]; + + if (player.mHasMark && !player.mMarkedCell.mPaged) + { + // interior cell -> need to check if it exists (exterior cell will be + // generated on the fly) + + if (!world.getStore().get().search (player.mMarkedCell.mWorldspace)) + player.mHasMark = false; // drop mark silently + } + + if (player.mHasMark) + { + mMarkedPosition = player.mMarkedPosition; + mMarkedCell = world.getCell (player.mMarkedCell); + } + else + { + mMarkedCell = 0; + } mAutoMove = player.mAutoMove!=0; From c548dcee13e6262a32148a4892178d9429456dc8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 13:13:58 +0100 Subject: [PATCH 554/889] Quick keys menu: make sure selected spell still exists --- apps/openmw/mwgui/quickkeysmenu.cpp | 6 ++++++ apps/openmw/mwmechanics/character.cpp | 6 ++++-- apps/openmw/mwmechanics/spells.hpp | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 61e414fc4..e1d430307 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -301,6 +301,12 @@ namespace MWGui if (type == Type_Magic) { std::string spellId = button->getChildAt(0)->getUserString("Spell"); + + // Make sure the player still has this spell + MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); + MWMechanics::Spells& spells = stats.getSpells(); + if (!spells.hasSpell(spellId)) + return; store.setSelectedEnchantItem(store.end()); MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 5104a0f57..755dbc093 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -583,8 +583,10 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun // This has to be done at the start of the casting animation, // *not* when selecting a spell in the GUI (otherwise you could change the spell mid-animation) if (mPtr.getRefData().getHandle() == "player") - stats.getSpells().setSelectedSpell(MWBase::Environment::get().getWindowManager()->getSelectedSpell()); - + { + std::string selectedSpell = MWBase::Environment::get().getWindowManager()->getSelectedSpell(); + stats.getSpells().setSelectedSpell(selectedSpell); + } std::string spellid = stats.getSpells().getSelectedSpell(); if(!spellid.empty() && MWBase::Environment::get().getWorld()->startSpellCast(mPtr)) diff --git a/apps/openmw/mwmechanics/spells.hpp b/apps/openmw/mwmechanics/spells.hpp index cf9b66091..facf02da8 100644 --- a/apps/openmw/mwmechanics/spells.hpp +++ b/apps/openmw/mwmechanics/spells.hpp @@ -44,6 +44,8 @@ namespace MWMechanics TIterator end() const; + bool hasSpell(const std::string& spell) { return mSpells.find(Misc::StringUtils::lowerCase(spell)) != mSpells.end(); } + void add (const std::string& spell); ///< Adding a spell that is already listed in *this is a no-op. From 659d790048adf5643d15419fd8a250d7a5663f64 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 13:14:50 +0100 Subject: [PATCH 555/889] uuid is not used --- CMakeLists.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b365e162f..fd5de4c39 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,8 +186,6 @@ if (WIN32) add_definitions(-DSDL_MAIN_HANDLED) else (WIN32) set(PLATFORM_INCLUDE_DIR "") - find_path (UUID_INCLUDE_DIR uuid/uuid.h) - include_directories(${UUID_INCLUDE_DIR}) endif (WIN32) if (MSVC10) set(PLATFORM_INCLUDE_DIR "") @@ -239,7 +237,6 @@ include_directories("." ${MYGUI_INCLUDE_DIRS} ${MYGUI_PLATFORM_INCLUDE_DIRS} ${OPENAL_INCLUDE_DIR} - ${UUID_INCLUDE_DIR} ${LIBDIR} ) @@ -434,7 +431,7 @@ IF(NOT WIN32 AND NOT APPLE) SET(CPACK_DEBIAN_PACKAGE_NAME "openmw") SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}") SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW opencs;OpenCS bsatool;Bsatool esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter") - SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libuuid1 (>= 2.17.2), libqtgui4 (>= 4.7.0)") + SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libqtgui4 (>= 4.7.0)") SET(CPACK_DEBIAN_PACKAGE_SECTION "Games") From 5fc38e7ac480d2b9b922391126d31f17adecee44 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 13:20:41 +0100 Subject: [PATCH 556/889] Don't use blood effects for fatigue damage --- apps/openmw/mwclass/npc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 93a7ddcf6..cfd981088 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -604,7 +604,8 @@ namespace MWClass } } - MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); + if (healthdmg) + MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); othercls.onHit(victim, damage, healthdmg, weapon, ptr, true); } From 37a59a37c6d2c6c227b7b9fd029d0d1a618d8028 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 14:11:46 +0100 Subject: [PATCH 557/889] Remove cpack (no longer used, according to BrotherBrick) --- CMakeLists.txt | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fd5de4c39..8eb8b44c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -406,46 +406,6 @@ IF(NOT WIN32 AND NOT APPLE) # Install resources INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT "Resources") INSTALL(DIRECTORY DESTINATION "${DATADIR}/data" COMPONENT "Resources") - - IF (DPKG_PROGRAM) - ## Debian Specific - IF(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.git") - EXEC_PROGRAM("git" ${CMAKE_CURRENT_SOURCE_DIR} ARGS "describe" OUTPUT_VARIABLE GIT_VERSION ) - STRING(REGEX REPLACE "openmw-" "" VERSION_STRING "${GIT_VERSION}") - EXEC_PROGRAM("git" ARGS "config --get user.name" OUTPUT_VARIABLE GIT_NAME ) - EXEC_PROGRAM("git" ARGS "config --get user.email" OUTPUT_VARIABLE GIT_EMAIL) - SET(PACKAGE_MAINTAINER "${GIT_NAME} <${GIT_EMAIL}>") - ELSE() - SET(VERSION_STRING "${OPENMW_VERSION}") - SET(PACKAGE_MAINTAINER "unknown") - ENDIF() - - SET(CPACK_GENERATOR "DEB") - SET(CPACK_PACKAGE_NAME "openmw") - SET(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://openmw.org") - SET(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") - SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "${PACKAGE_MAINTAINER}") - SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "A reimplementation of The Elder Scrolls III: Morrowind - OpenMW is a reimplementation of the Bethesda Game Studios game The Elder Scrolls III: Morrowind. - Data files from the original game is required to run it.") - SET(CPACK_DEBIAN_PACKAGE_NAME "openmw") - SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}") - SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW opencs;OpenCS bsatool;Bsatool esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter") - SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libqtgui4 (>= 4.7.0)") - - SET(CPACK_DEBIAN_PACKAGE_SECTION "Games") - - STRING(TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_PACKAGE_NAME_LOWERCASE) - EXECUTE_PROCESS( - COMMAND ${DPKG_PROGRAM} --print-architecture - OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME_LOWERCASE}_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}") - - - INCLUDE(CPack) - ENDIF(DPKG_PROGRAM) ENDIF(NOT WIN32 AND NOT APPLE) if(WIN32) From 27d0d9c592a22448c64a129824f9bbcc268ee4b4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 15:27:59 +0100 Subject: [PATCH 558/889] Fix exception when clicking on statics when in the inventory window --- apps/openmw/mwgui/hud.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 7cd9e2256..a6ad43ce5 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -267,7 +267,8 @@ namespace MWGui else if ((mode == GM_Container) || (mode == GM_Inventory)) { // pick up object - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object); + if (!object.isEmpty()) + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object); } } } From c76a0448a3f6e9ad9954cc879b6164aba2f54e8f Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 15:47:34 +0100 Subject: [PATCH 559/889] Closes #1123: Implement SlowFall magic effect --- apps/openmw/mwworld/physicssystem.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 2a7f5948e..a7103b991 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -108,7 +108,7 @@ namespace MWWorld } static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, - bool isFlying, float waterlevel, OEngine::Physic::PhysicEngine *engine) + bool isFlying, float waterlevel, float slowFall, OEngine::Physic::PhysicEngine *engine) { const ESM::Position &refpos = ptr.getRefData().getPosition(); Ogre::Vector3 position(refpos.pos); @@ -229,7 +229,10 @@ namespace MWWorld physicActor->setInertialForce(Ogre::Vector3(0.0f)); else { - inertia.z += time*-627.2f; + float diff = time*-627.2f; + if (inertia.z < 0) + diff *= slowFall; + inertia.z += diff; physicActor->setInertialForce(inertia); } physicActor->setOnGround(isOnGround); @@ -577,9 +580,10 @@ namespace MWWorld float oldHeight = iter->first.getRefData().getPosition().pos[2]; + const MWMechanics::MagicEffects& effects = iter->first.getClass().getCreatureStats(iter->first).getMagicEffects(); + bool waterCollision = false; - if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects() - .get(ESM::MagicEffect::WaterWalking).mMagnitude + if (effects.get(ESM::MagicEffect::WaterWalking).mMagnitude && cell->hasWater() && !world->isUnderwater(iter->first.getCell(), Ogre::Vector3(iter->first.getRefData().getPosition().pos))) @@ -592,9 +596,12 @@ namespace MWWorld if (waterCollision) mEngine->dynamicsWorld->addCollisionObject(&object); + // 100 points of slowfall reduce gravity by 90% (this is just a guess) + float slowFall = 1-std::min(std::max(0.f, (effects.get(ESM::MagicEffect::SlowFall).mMagnitude / 100.f) * 0.9f), 0.9f); + Ogre::Vector3 newpos = MovementSolver::move(iter->first, iter->second, mTimeAccum, world->isFlying(iter->first), - waterlevel, mEngine); + waterlevel, slowFall, mEngine); if (waterCollision) mEngine->dynamicsWorld->removeCollisionObject(&object); From 228254c890e6b90f1bcb2070b3e356c854efeeb1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 16:31:27 +0100 Subject: [PATCH 560/889] Handle creature attack animations in character controller --- apps/openmw/mwmechanics/character.cpp | 46 +++++++++++++++++++++++++-- apps/openmw/mwmechanics/character.hpp | 3 +- apps/openmw/mwworld/worldimp.cpp | 3 +- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 755dbc093..fe650237a 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -479,9 +479,47 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr) mPtr = ptr; } -bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak) +bool CharacterController::updateCreatureState() { - const MWWorld::Class &cls = MWWorld::Class::get(mPtr); + const MWWorld::Class &cls = mPtr.getClass(); + CreatureStats &stats = cls.getCreatureStats(mPtr); + + if(stats.getAttackingOrSpell()) + { + if(mUpperBodyState == UpperCharState_Nothing && mHitState == CharState_None) + { + MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); + + switch (stats.getAttackType()) + { + case CreatureStats::AT_Chop: + mCurrentWeapon = "attack1"; + break; + case CreatureStats::AT_Thrust: + mCurrentWeapon = "attack2"; + break; + case CreatureStats::AT_Slash: + mCurrentWeapon = "attack3"; + break; + } + + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + 1, "start", "stop", + 0.0f, 0); + mUpperBodyState = UpperCharState_StartToMinAttack; + } + } + + bool animPlaying = mAnimation->getInfo(mCurrentWeapon); + if (!animPlaying) + mUpperBodyState = UpperCharState_Nothing; + return false; +} + +bool CharacterController::updateNpcState(bool inwater, bool isrunning) +{ + const MWWorld::Class &cls = MWWorld::Class::get(mPtr); NpcStats &stats = cls.getNpcStats(mPtr); WeaponType weaptype = WeapType_None; MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); @@ -1091,7 +1129,9 @@ void CharacterController::update(float duration) } if(cls.isNpc()) - forcestateupdate = updateNpcState(onground, inwater, isrunning, sneak) || forcestateupdate; + forcestateupdate = updateNpcState(inwater, isrunning) || forcestateupdate; + else + forcestateupdate = updateCreatureState() || forcestateupdate; refreshCurrentAnims(idlestate, movestate, forcestateupdate); diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 438f542f0..d3d4b4435 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -176,7 +176,8 @@ class CharacterController void clearAnimQueue(); - bool updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak); + bool updateNpcState(bool inwater, bool isrunning); + bool updateCreatureState(); void updateVisibility(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index a40f20696..0d7802081 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2282,7 +2282,8 @@ namespace MWWorld void World::breakInvisibility(const Ptr &actor) { actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Invisibility); - actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility); + if (actor.getClass().isNpc()) + actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility); } bool World::isDark() const From 9b32b1403ba63c63ef5d9261a2c641f78eff7e36 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 17:19:08 +0100 Subject: [PATCH 561/889] Feature #960: Implement Creature::hit --- apps/openmw/mwclass/creature.cpp | 56 +++++++++++++++++++++++ apps/openmw/mwclass/npc.cpp | 3 +- apps/openmw/mwmechanics/character.cpp | 4 +- apps/openmw/mwmechanics/creaturestats.hpp | 4 +- apps/openmw/mwrender/animation.cpp | 17 +++++-- 5 files changed, 75 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 0fc26950d..e6d2965fa 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -173,6 +173,62 @@ namespace MWClass void Creature::hit(const MWWorld::Ptr& ptr, int type) const { + MWWorld::LiveCellRef *ref = + ptr.get(); + + // TODO: where is the distance defined? + std::pair result = MWBase::Environment::get().getWorld()->getHitContact(ptr, 100); + if (result.first.isEmpty()) + return; // Didn't hit anything + + MWWorld::Ptr victim = result.first; + + if (!victim.getClass().isActor()) + return; // Can't hit non-actors + + Ogre::Vector3 hitPosition = result.second; + + MWMechanics::CreatureStats &stats = getCreatureStats(ptr); + MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim); + const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); + float hitchance = ref->mBase->mData.mCombat + + (stats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) + + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f); + hitchance *= stats.getFatigueTerm(); + hitchance += mageffects.get(ESM::MagicEffect::FortifyAttack).mMagnitude - + mageffects.get(ESM::MagicEffect::Blind).mMagnitude; + hitchance -= otherstats.getEvasion(); + + if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f) + { + victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, false); + return; + } + + int min,max; + switch (type) + { + case 0: + min = ref->mBase->mData.mAttack[0]; + max = ref->mBase->mData.mAttack[1]; + break; + case 1: + min = ref->mBase->mData.mAttack[2]; + max = ref->mBase->mData.mAttack[3]; + break; + case 2: + default: + min = ref->mBase->mData.mAttack[4]; + max = ref->mBase->mData.mAttack[5]; + break; + } + + float damage = min + (max - min) * ::rand()/(RAND_MAX+1.0); + + // TODO: do not do this if the attack is blocked + MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); + + victim.getClass().onHit(victim, damage, true, MWWorld::Ptr(), ptr, true); } void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index cfd981088..f93a3e342 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -454,7 +454,7 @@ namespace MWClass float dist = 100.0f * (!weapon.isEmpty() ? weapon.get()->mBase->mData.mReach : gmst.find("fHandToHandReach")->getFloat()); - // TODO: Use second to work out the hit angle and where to spawn the blood effect + // TODO: Use second to work out the hit angle std::pair result = world->getHitContact(ptr, dist); MWWorld::Ptr victim = result.first; Ogre::Vector3 hitPosition = result.second; @@ -604,6 +604,7 @@ namespace MWClass } } + // TODO: do not do this if the attack is blocked if (healthdmg) MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index fe650237a..013af5b3a 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -495,10 +495,10 @@ bool CharacterController::updateCreatureState() case CreatureStats::AT_Chop: mCurrentWeapon = "attack1"; break; - case CreatureStats::AT_Thrust: + case CreatureStats::AT_Slash: mCurrentWeapon = "attack2"; break; - case CreatureStats::AT_Slash: + case CreatureStats::AT_Thrust: mCurrentWeapon = "attack3"; break; } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 26856c966..308883fc5 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -116,9 +116,9 @@ namespace MWMechanics enum AttackType { + AT_Chop, AT_Slash, - AT_Thrust, - AT_Chop + AT_Thrust }; void setAttackType(int attackType) { mAttackType = attackType; } int getAttackType() { return mAttackType; } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index de89b0207..a7623efea 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -660,13 +660,22 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co else if(evt.compare(off, len, "unequip detach") == 0) showWeapons(false); else if(evt.compare(off, len, "chop hit") == 0) - MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Chop); + mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Chop); else if(evt.compare(off, len, "slash hit") == 0) - MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Slash); + mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Slash); else if(evt.compare(off, len, "thrust hit") == 0) - MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Thrust); + mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Thrust); else if(evt.compare(off, len, "hit") == 0) - MWWorld::Class::get(mPtr).hit(mPtr); + { + if (groupname == "attack1") + mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Chop); + else if (groupname == "attack2") + mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Slash); + else if (groupname == "attack3") + mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Thrust); + else + mPtr.getClass().hit(mPtr); + } else if (groupname == "spellcast" && evt.substr(evt.size()-7, 7) == "release") MWBase::Environment::get().getWorld()->castSpell(mPtr); From 149d77dedf4cb89a46db80248c6f16b73f3a345c Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 17:40:58 +0100 Subject: [PATCH 562/889] Feature #960: Add knockdown and hit recovery for creatures --- apps/openmw/mwclass/creature.cpp | 19 +++++++++++++++++++ apps/openmw/mwclass/creature.hpp | 4 ++++ 2 files changed, 23 insertions(+) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index e6d2965fa..a97268318 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -69,6 +69,9 @@ namespace MWClass fMaxFlySpeed = gmst.find("fMaxFlySpeed"); fSwimRunBase = gmst.find("fSwimRunBase"); fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult"); + fKnockDownMult = gmst.find("fKnockDownMult"); + iKnockDownOddsMult = gmst.find("iKnockDownOddsMult"); + iKnockDownOddsBase = gmst.find("iKnockDownOddsBase"); inited = true; } @@ -255,6 +258,19 @@ namespace MWClass ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); } + // Check for knockdown + float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat(); + float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() + * iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt(); + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (ishealth && agilityTerm <= damage && knockdownTerm <= roll) + { + getCreatureStats(ptr).setKnockedDown(true); + + } + else + getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? + if(ishealth) { if(damage > 0.0f) @@ -607,5 +623,8 @@ namespace MWClass const ESM::GameSetting *Creature::fMaxFlySpeed; const ESM::GameSetting *Creature::fSwimRunBase; const ESM::GameSetting *Creature::fSwimRunAthleticsMult; + const ESM::GameSetting *Creature::fKnockDownMult; + const ESM::GameSetting *Creature::iKnockDownOddsMult; + const ESM::GameSetting *Creature::iKnockDownOddsBase; } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 708999ef0..d518d0056 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -24,6 +24,10 @@ namespace MWClass static const ESM::GameSetting *fMaxFlySpeed; static const ESM::GameSetting *fSwimRunBase; static const ESM::GameSetting *fSwimRunAthleticsMult; + static const ESM::GameSetting *fKnockDownMult; + static const ESM::GameSetting *iKnockDownOddsMult; + static const ESM::GameSetting *iKnockDownOddsBase; + public: From 5357f569e629e5b108ae21016f63ac2e87956780 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 17 Jan 2014 19:36:22 +0200 Subject: [PATCH 563/889] fix for scrawl --- apps/openmw/mwmechanics/aicombat.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index e3cce0f95..4ab358a52 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -358,7 +358,7 @@ namespace MWMechanics return new AiCombat(*this); } - static void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) + void MWMechanics::determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) { if (movement.mPosition[0] && !movement.mPosition[1]) //sideway actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Slash); @@ -368,7 +368,7 @@ namespace MWMechanics actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Chop); } - static void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) + void MWMechanics::chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) { //the more damage attackType deals the more probability it has From 3dcb292bc13d170e65e72b52825c4df8a03e04e7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 18:43:45 +0100 Subject: [PATCH 564/889] Do not soul trap creatures without a soul (which apparently exist) --- apps/openmw/mwmechanics/actors.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 99381a204..1b65b1472 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -131,6 +131,8 @@ namespace MWMechanics static const float fSoulgemMult = world->getStore().get().find("fSoulgemMult")->getFloat(); float creatureSoulValue = mCreature.get()->mBase->mData.mSoul; + if (creatureSoulValue == 0) + return; // Use the smallest soulgem that is large enough to hold the soul MWWorld::ContainerStore& container = caster.getClass().getContainerStore(caster); From 7df8273d717d71187ecb2774d3e9b0863fa8166b Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 17 Jan 2014 20:01:15 +0200 Subject: [PATCH 565/889] fix for everybody --- apps/openmw/mwmechanics/aicombat.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 4ab358a52..0f3536f0a 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -358,7 +358,7 @@ namespace MWMechanics return new AiCombat(*this); } - void MWMechanics::determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) + void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) { if (movement.mPosition[0] && !movement.mPosition[1]) //sideway actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Slash); @@ -368,7 +368,7 @@ namespace MWMechanics actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Chop); } - void MWMechanics::chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) + void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) { //the more damage attackType deals the more probability it has From 46a4790cb1213a626de2a11ff4a5d7410c0a96af Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 17 Jan 2014 20:33:49 +0200 Subject: [PATCH 566/889] final warnings removal --- apps/openmw/mwmechanics/aicombat.cpp | 83 +++++++++++++++------------- apps/openmw/mwmechanics/aicombat.hpp | 4 -- 2 files changed, 45 insertions(+), 42 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 0f3536f0a..487a4773e 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -26,6 +26,10 @@ namespace return 1.0; return -1.0; } + + void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement); + //chooses an attack depending on probability to avoid uniformity + void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement); } namespace MWMechanics @@ -256,7 +260,7 @@ namespace MWMechanics //less than in time of playing weapon anim from 'start' to 'hit' tags (t_swing) //then start attacking float speed1 = cls.getSpeed(actor); - float speed2 = cls.getSpeed(mTarget); + float speed2 = mTarget.getClass().getSpeed(mTarget); if(actor.getClass().getMovementSettings(mTarget).mPosition[0] == 0 && actor.getClass().getMovementSettings(mTarget).mPosition[1] == 0) speed2 = 0; @@ -357,51 +361,54 @@ namespace MWMechanics { return new AiCombat(*this); } +} - void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) +namespace +{ +void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) +{ + if (movement.mPosition[0] && !movement.mPosition[1]) //sideway + actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Slash); + else if (movement.mPosition[1]) //forward + actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Thrust); + else + actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Chop); +} + +void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) +{ + //the more damage attackType deals the more probability it has + + if (weapon == NULL) { - if (movement.mPosition[0] && !movement.mPosition[1]) //sideway - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Slash); - else if (movement.mPosition[1]) //forward - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Thrust); - else - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Chop); - } - - void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) - { - //the more damage attackType deals the more probability it has - - if (weapon == NULL) - { - //hand-to-hand deals equal damage - float roll = static_cast(rand())/RAND_MAX; - if(roll <= 0.333f) //side punch - { - movement.mPosition[0] = (static_cast(rand())/RAND_MAX < 0.5f)? 1: -1; - movement.mPosition[1] = 0; - } - else if(roll <= 0.666f) //forward punch - movement.mPosition[1] = 1; - - return; - } - - int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; - int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; - int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; - - float total = slash + chop + thrust; - + //hand-to-hand deals equal damage float roll = static_cast(rand())/RAND_MAX; - if(roll <= static_cast(slash)/total) + if(roll <= 0.333f) //side punch { movement.mPosition[0] = (static_cast(rand())/RAND_MAX < 0.5f)? 1: -1; movement.mPosition[1] = 0; } - else if(roll <= (static_cast(slash) + static_cast(thrust))/total) + else if(roll <= 0.666f) //forward punch movement.mPosition[1] = 1; - //else chop + + return; } + + int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; + int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; + int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; + + float total = slash + chop + thrust; + + float roll = static_cast(rand())/RAND_MAX; + if(roll <= static_cast(slash)/total) + { + movement.mPosition[0] = (static_cast(rand())/RAND_MAX < 0.5f)? 1: -1; + movement.mPosition[1] = 0; + } + else if(roll <= (static_cast(slash) + static_cast(thrust))/total) + movement.mPosition[1] = 1; + //else chop } +} \ No newline at end of file diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 8c494648b..cb9eee973 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -45,10 +45,6 @@ namespace MWMechanics void buildNewPath(const MWWorld::Ptr& actor); }; - - static void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement); - //chooses an attack depending on probability to avoid uniformity - static void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement); } #endif From 435bbdd53070f17c48fd1d8e393ae381f355e917 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 17 Jan 2014 20:55:21 +0200 Subject: [PATCH 567/889] one more mismatch fixed --- apps/openmw/mwmechanics/aicombat.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 487a4773e..9a78e44ff 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -261,8 +261,8 @@ namespace MWMechanics //then start attacking float speed1 = cls.getSpeed(actor); float speed2 = mTarget.getClass().getSpeed(mTarget); - if(actor.getClass().getMovementSettings(mTarget).mPosition[0] == 0 - && actor.getClass().getMovementSettings(mTarget).mPosition[1] == 0) + if(mTarget.getClass().getMovementSettings(mTarget).mPosition[0] == 0 + && mTarget.getClass().getMovementSettings(mTarget).mPosition[1] == 0) speed2 = 0; float s1 = distBetween - weapRange; From 5049f3b1346ee80eb9f0bede5b24eb5329d7e976 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 20:18:35 +0100 Subject: [PATCH 568/889] Fix the group for creature attack animations --- apps/openmw/mwmechanics/character.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 013af5b3a..6ff947306 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -504,7 +504,7 @@ bool CharacterController::updateCreatureState() } mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, true, + MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); mUpperBodyState = UpperCharState_StartToMinAttack; From df4df5b0946708a5e8d98de8bac7c998a964e1fd Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 17 Jan 2014 23:30:28 +0200 Subject: [PATCH 569/889] fixed weapRange for creatures/startcombat script(?) --- apps/openmw/mwmechanics/aicombat.cpp | 6 ++++++ apps/openmw/mwscript/aiextensions.cpp | 8 ++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 9a78e44ff..6bf97f6ac 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -151,6 +151,11 @@ namespace MWMechanics } weapRange *= 100.0f; } + else //is creature + { + weaptype = WeapType_HandToHand; //doesn't matter, should only reflect if it is melee or distant weapon + weapRange = 100; //TODO: use true attack range (the same problem in Creature::hit) + } //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); @@ -381,6 +386,7 @@ void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement if (weapon == NULL) { + //hand-to-hand and creatures' attacks handled here //hand-to-hand deals equal damage float roll = static_cast(rand())/RAND_MAX; if(roll <= 0.333f) //side punch diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index d09a72acc..53b757d76 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -435,10 +435,14 @@ namespace MWScript std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); - creatureStats.getAiSequence().stack(MWMechanics::AiCombat(actor)); + MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); + if (actorID == "player") + { creatureStats.setHostile(true); + creatureStats.getAiSequence().stack( + MWMechanics::AiCombat(MWBase::Environment::get().getWorld()->getPtr(actorID,true))); + } } }; From 9653355cf1f5e78ea131c5e4187d8052c8e86776 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 18 Jan 2014 07:26:27 +0100 Subject: [PATCH 570/889] Feature #701: Spawn levelled creatures in cells --- apps/openmw/mwclass/creaturelevlist.cpp | 47 ++++++++++++++++++++++++ apps/openmw/mwclass/creaturelevlist.hpp | 5 +++ apps/openmw/mwmechanics/levelledlist.hpp | 1 + apps/openmw/mwworld/scene.cpp | 3 +- 4 files changed, 55 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/creaturelevlist.cpp b/apps/openmw/mwclass/creaturelevlist.cpp index 53dd34bb4..ea586e5b6 100644 --- a/apps/openmw/mwclass/creaturelevlist.cpp +++ b/apps/openmw/mwclass/creaturelevlist.cpp @@ -3,6 +3,24 @@ #include +#include "../mwmechanics/levelledlist.hpp" + +#include "../mwworld/customdata.hpp" + +namespace +{ + struct CustomData : public MWWorld::CustomData + { + // TODO: save the creature we spawned here + virtual MWWorld::CustomData *clone() const; + }; + + MWWorld::CustomData *CustomData::clone() const + { + return new CustomData (*this); + } +} + namespace MWClass { std::string CreatureLevList::getName (const MWWorld::Ptr& ptr) const @@ -16,4 +34,33 @@ namespace MWClass registerClass (typeid (ESM::CreatureLevList).name(), instance); } + + void CreatureLevList::insertObjectRendering(const MWWorld::Ptr &ptr, MWRender::RenderingInterface &renderingInterface) const + { + ensureCustomData(ptr); + } + + void CreatureLevList::ensureCustomData(const MWWorld::Ptr &ptr) const + { + if (!ptr.getRefData().getCustomData()) + { + std::auto_ptr data (new CustomData); + + MWWorld::LiveCellRef *ref = + ptr.get(); + + std::string id = MWMechanics::getLevelledItem(ref->mBase, true); + + if (!id.empty()) + { + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + MWWorld::ManualRef ref(store, id); + ref.getPtr().getCellRef().mPos = ptr.getCellRef().mPos; + // TODO: hold on to this for respawn purposes later + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), *ptr.getCell() , ptr.getCellRef().mPos); + } + + ptr.getRefData().setCustomData(data.release()); + } + } } diff --git a/apps/openmw/mwclass/creaturelevlist.hpp b/apps/openmw/mwclass/creaturelevlist.hpp index 81965efd5..d2c02043e 100644 --- a/apps/openmw/mwclass/creaturelevlist.hpp +++ b/apps/openmw/mwclass/creaturelevlist.hpp @@ -7,6 +7,8 @@ namespace MWClass { class CreatureLevList : public MWWorld::Class { + void ensureCustomData (const MWWorld::Ptr& ptr) const; + public: virtual std::string getName (const MWWorld::Ptr& ptr) const; @@ -14,6 +16,9 @@ namespace MWClass /// can return an empty string. static void registerSelf(); + + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + ///< Add reference into a cell for rendering }; } diff --git a/apps/openmw/mwmechanics/levelledlist.hpp b/apps/openmw/mwmechanics/levelledlist.hpp index d65503011..120616f9f 100644 --- a/apps/openmw/mwmechanics/levelledlist.hpp +++ b/apps/openmw/mwmechanics/levelledlist.hpp @@ -6,6 +6,7 @@ #include "../mwworld/class.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwmechanics/creaturestats.hpp" namespace MWMechanics { diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 3607b8276..0d5fb0560 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -487,7 +487,6 @@ namespace MWWorld insertCellRefList(mRendering, cell.mContainers, cell, *mPhysics, rescale, loadingListener); insertCellRefList(mRendering, cell.mDoors, cell, *mPhysics, rescale, loadingListener); insertCellRefList(mRendering, cell.mIngreds, cell, *mPhysics, rescale, loadingListener); - insertCellRefList(mRendering, cell.mCreatureLists, cell, *mPhysics, rescale, loadingListener); insertCellRefList(mRendering, cell.mItemLists, cell, *mPhysics, rescale, loadingListener); insertCellRefList(mRendering, cell.mLights, cell, *mPhysics, rescale, loadingListener); insertCellRefList(mRendering, cell.mLockpicks, cell, *mPhysics, rescale, loadingListener); @@ -499,6 +498,8 @@ namespace MWWorld // Load NPCs and creatures _after_ everything else (important for adjustPosition to work correctly) insertCellRefList(mRendering, cell.mCreatures, cell, *mPhysics, rescale, loadingListener); insertCellRefList(mRendering, cell.mNpcs, cell, *mPhysics, rescale, loadingListener); + // Since this adds additional creatures, load afterwards, or they would be loaded twice + insertCellRefList(mRendering, cell.mCreatureLists, cell, *mPhysics, rescale, loadingListener); } void Scene::addObjectToScene (const Ptr& ptr) From c04a8afc8bb1757f64886627877a1be6d7658ee3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 18 Jan 2014 10:51:52 +0100 Subject: [PATCH 571/889] Make sure onPcEquip is also set for Equip script instruction --- apps/openmw/mwscript/containerextensions.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 7bac7cdbe..6674481f2 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -171,6 +171,9 @@ namespace MWScript MWWorld::ActionEquip action (*it); action.execute(ptr); + + if (ptr.getRefData().getHandle() == "player" && !ptr.getClass().getScript(ptr).empty()) + ptr.getRefData().getLocals().setVarByInt(ptr.getClass().getScript(ptr), "onpcequip", 1); } }; From 525ce2f04273a76d59045a2d669fa6a58dc2f7da Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 18 Jan 2014 10:52:16 +0100 Subject: [PATCH 572/889] Some cleanup - move definitions to implementation file --- apps/openmw/mwgui/quickkeysmenu.cpp | 18 ++- apps/openmw/mwgui/spellcreationdialog.cpp | 6 +- apps/openmw/mwgui/spellwindow.cpp | 17 +-- apps/openmw/mwmechanics/spellcasting.cpp | 164 ++++++++++++++++++++- apps/openmw/mwmechanics/spellcasting.hpp | 171 ++-------------------- apps/openmw/mwmechanics/spells.hpp | 2 +- 6 files changed, 197 insertions(+), 181 deletions(-) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index e1d430307..ff13ae1af 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -3,8 +3,15 @@ #include #include "../mwworld/inventorystore.hpp" -#include "../mwworld/actionequip.hpp" +#include "../mwworld/class.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/spells.hpp" +#include "../mwmechanics/creaturestats.hpp" + #include "../mwgui/inventorywindow.hpp" #include "../mwgui/bookwindow.hpp" #include "../mwgui/scrollwindow.hpp" @@ -17,8 +24,8 @@ namespace { bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right) { - int cmp = MWWorld::Class::get(left).getName(left).compare( - MWWorld::Class::get(right).getName(right)); + int cmp = left.getClass().getName(left).compare( + right.getClass().getName(right)); return cmp < 0; } @@ -334,10 +341,7 @@ namespace MWGui // equip, if it can be equipped if (!MWWorld::Class::get(item).getEquipmentSlots(item).first.empty()) { - // Note: can't use Class::use here because enchanted scrolls for example would then open the scroll window instead of equipping - - MWWorld::ActionEquip action(item); - action.execute (MWBase::Environment::get().getWorld ()->getPlayerPtr()); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->useItem(item); } store.setSelectedEnchantItem(it); diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 857dd76d5..469d7188e 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -3,13 +3,17 @@ #include #include "../mwbase/windowmanager.hpp" - #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/containerstore.hpp" +#include "../mwworld/class.hpp" #include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/spells.hpp" +#include "../mwmechanics/creaturestats.hpp" #include "tooltips.hpp" #include "class.hpp" diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 21257ef9c..07076159c 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -4,11 +4,15 @@ #include #include "../mwbase/windowmanager.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwworld/actionequip.hpp" +#include "../mwworld/class.hpp" #include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/spells.hpp" +#include "../mwmechanics/creaturestats.hpp" #include "spellicons.hpp" #include "inventorywindow.hpp" @@ -231,8 +235,7 @@ namespace MWGui MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); float enchantCost = enchant->mData.mCost; - MWMechanics::NpcStats &stats = player.getClass().getNpcStats(player); - int eSkill = stats.getSkill(ESM::Skill::Enchant).getModified(); + int eSkill = player.getClass().getSkill(player, ESM::Skill::Enchant); int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10)); std::string cost = boost::lexical_cast(castCost); @@ -316,13 +319,7 @@ namespace MWGui if (_sender->getUserString("Equipped") == "false" && !MWWorld::Class::get(item).getEquipmentSlots(item).first.empty()) { - // Note: can't use Class::use here because enchanted scrolls for example would then open the scroll window instead of equipping - - MWWorld::ActionEquip action(item); - action.execute (MWBase::Environment::get().getWorld ()->getPlayerPtr()); - - // since we changed equipping status, update the inventory window - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->useItem(item); } store.setSelectedEnchantItem(it); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index a0e91791b..f70050628 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -1,20 +1,182 @@ #include "spellcasting.hpp" +#include + #include #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/actionteleport.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" #include "../mwrender/animation.hpp" +#include "magiceffects.hpp" +#include "npcstats.hpp" + namespace MWMechanics { + ESM::Skill::SkillEnum spellSchoolToSkill(int school) + { + std::map schoolSkillMap; // maps spell school to skill id + schoolSkillMap[0] = ESM::Skill::Alteration; + schoolSkillMap[1] = ESM::Skill::Conjuration; + schoolSkillMap[3] = ESM::Skill::Illusion; + schoolSkillMap[2] = ESM::Skill::Destruction; + schoolSkillMap[4] = ESM::Skill::Mysticism; + schoolSkillMap[5] = ESM::Skill::Restoration; + assert(schoolSkillMap.find(school) != schoolSkillMap.end()); + return schoolSkillMap[school]; + } + + float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool) + { + CreatureStats& stats = actor.getClass().getCreatureStats(actor); + + if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).mMagnitude) + return 0; + + float y = FLT_MAX; + float lowestSkill = 0; + + for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != spell->mEffects.mList.end(); ++it) + { + float x = it->mDuration; + const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find( + it->mEffectID); + if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage)) + x = std::max(1.f, x); + x *= 0.1 * magicEffect->mData.mBaseCost; + x *= 0.5 * (it->mMagnMin + it->mMagnMax); + x *= it->mArea * 0.05 * magicEffect->mData.mBaseCost; + if (magicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) + x *= 1.5; + static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get().find( + "fEffectCostMult")->getFloat(); + x *= fEffectCostMult; + + float s = 2 * actor.getClass().getSkill(actor, spellSchoolToSkill(magicEffect->mData.mSchool)); + if (s - x < y) + { + y = s - x; + if (effectiveSchool) + *effectiveSchool = magicEffect->mData.mSchool; + lowestSkill = s; + } + } + + if (spell->mData.mType != ESM::Spell::ST_Spell) + return 100; + + if (spell->mData.mFlags & ESM::Spell::F_Always) + return 100; + + int castBonus = -stats.getMagicEffects().get(ESM::MagicEffect::Sound).mMagnitude; + + int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); + int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + + float castChance = (lowestSkill - spell->mData.mCost + castBonus + 0.2 * actorWillpower + 0.1 * actorLuck) * stats.getFatigueTerm(); + if (MWBase::Environment::get().getWorld()->getGodModeState() && actor.getRefData().getHandle() == "player") + castChance = 100; + + return std::max(0.f, std::min(100.f, castChance)); + } + + float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool) + { + const ESM::Spell* spell = + MWBase::Environment::get().getWorld()->getStore().get().find(spellId); + return getSpellSuccessChance(spell, actor, effectiveSchool); + } + + + int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor) + { + int school = 0; + getSpellSuccessChance(spellId, actor, &school); + return school; + } + + int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor) + { + int school = 0; + getSpellSuccessChance(spell, actor, &school); + return school; + } + + float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell) + { + const ESM::MagicEffect *magicEffect = + MWBase::Environment::get().getWorld()->getStore().get().find ( + effectId); + + const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); + + float resisted = 0; + if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful) + { + + short resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId); + short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId); + + float resistance = 0; + if (resistanceEffect != -1) + resistance += stats.getMagicEffects().get(resistanceEffect).mMagnitude; + if (weaknessEffect != -1) + resistance -= stats.getMagicEffects().get(weaknessEffect).mMagnitude; + + + float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); + float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + float x = (willpower + 0.1 * luck) * stats.getFatigueTerm(); + + // This makes spells that are easy to cast harder to resist and vice versa + if (spell != NULL && caster.getClass().isActor()) + { + float castChance = getSpellSuccessChance(spell, caster); + if (castChance > 0) + x *= 50 / castChance; + } + + float roll = static_cast(std::rand()) / RAND_MAX * 100; + if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) + roll -= resistance; + + if (x <= roll) + x = 0; + else + { + if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) + x = 100; + else + x = roll / std::min(x, 100.f); + } + + x = std::min(x + resistance, 100.f); + + resisted = x; + } + + return resisted; + } + + float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell) + { + float resistance = getEffectResistance(effectId, actor, caster, spell); + if (resistance >= 0) + return 1 - resistance / 100.f; + else + return -(resistance-100) / 100.f; + } + CastSpell::CastSpell(const MWWorld::Ptr &caster, const MWWorld::Ptr &target) : mCaster(caster) , mTarget(target) @@ -230,7 +392,7 @@ namespace MWMechanics MWBase::Environment::get().getMechanicsManager()->commitCrime(caster, target, MWBase::MechanicsManager::OT_Assault); } - void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, MWMechanics::EffectKey effect, float magnitude) + void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, const MWMechanics::EffectKey& effect, float magnitude) { short effectId = effect.mId; if (!target.getClass().isActor()) diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 52af26ad1..b58e7bb09 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -1,33 +1,13 @@ #ifndef MWMECHANICS_SPELLSUCCESS_H #define MWMECHANICS_SPELLSUCCESS_H -#include - -#include "../mwbase/world.hpp" -#include "../mwbase/environment.hpp" - #include "../mwworld/ptr.hpp" -#include "../mwworld/class.hpp" -#include "../mwmechanics/creaturestats.hpp" - -#include "../mwworld/esmstore.hpp" - -#include "npcstats.hpp" namespace MWMechanics { - inline ESM::Skill::SkillEnum spellSchoolToSkill(int school) - { - std::map schoolSkillMap; // maps spell school to skill id - schoolSkillMap[0] = ESM::Skill::Alteration; - schoolSkillMap[1] = ESM::Skill::Conjuration; - schoolSkillMap[3] = ESM::Skill::Illusion; - schoolSkillMap[2] = ESM::Skill::Destruction; - schoolSkillMap[4] = ESM::Skill::Mysticism; - schoolSkillMap[5] = ESM::Skill::Restoration; - assert(schoolSkillMap.find(school) != schoolSkillMap.end()); - return schoolSkillMap[school]; - } + class EffectKey; + + ESM::Skill::SkillEnum spellSchoolToSkill(int school); /** * @param spell spell to cast @@ -36,147 +16,16 @@ namespace MWMechanics * @attention actor has to be an NPC and not a creature! * @return success chance from 0 to 100 (in percent) */ - inline float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = NULL) - { - CreatureStats& stats = actor.getClass().getCreatureStats(actor); + float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = NULL); + float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = NULL); - if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).mMagnitude) - return 0; - - float y = FLT_MAX; - float lowestSkill = 0; - - for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != spell->mEffects.mList.end(); ++it) - { - float x = it->mDuration; - const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find( - it->mEffectID); - if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage)) - x = std::max(1.f, x); - x *= 0.1 * magicEffect->mData.mBaseCost; - x *= 0.5 * (it->mMagnMin + it->mMagnMax); - x *= it->mArea * 0.05 * magicEffect->mData.mBaseCost; - if (magicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) - x *= 1.5; - static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get().find( - "fEffectCostMult")->getFloat(); - x *= fEffectCostMult; - - float s = 2 * actor.getClass().getSkill(actor, spellSchoolToSkill(magicEffect->mData.mSchool)); - if (s - x < y) - { - y = s - x; - if (effectiveSchool) - *effectiveSchool = magicEffect->mData.mSchool; - lowestSkill = s; - } - } - - if (spell->mData.mType != ESM::Spell::ST_Spell) - return 100; - - if (spell->mData.mFlags & ESM::Spell::F_Always) - return 100; - - int castBonus = -stats.getMagicEffects().get(ESM::MagicEffect::Sound).mMagnitude; - - int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); - int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); - - float castChance = (lowestSkill - spell->mData.mCost + castBonus + 0.2 * actorWillpower + 0.1 * actorLuck) * stats.getFatigueTerm(); - if (MWBase::Environment::get().getWorld()->getGodModeState() && actor.getRefData().getHandle() == "player") - castChance = 100; - - return std::max(0.f, std::min(100.f, castChance)); - } - - inline float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = NULL) - { - const ESM::Spell* spell = - MWBase::Environment::get().getWorld()->getStore().get().find(spellId); - return getSpellSuccessChance(spell, actor, effectiveSchool); - } - - inline int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor) - { - int school = 0; - getSpellSuccessChance(spellId, actor, &school); - return school; - } - - inline int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor) - { - int school = 0; - getSpellSuccessChance(spell, actor, &school); - return school; - } + int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor); + int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor); /// @return >=100 for fully resisted. can also return negative value for damage amplification. - inline float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL) - { - const ESM::MagicEffect *magicEffect = - MWBase::Environment::get().getWorld()->getStore().get().find ( - effectId); - - const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); - - float resisted = 0; - if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful) - { - - short resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId); - short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId); - - float resistance = 0; - if (resistanceEffect != -1) - resistance += stats.getMagicEffects().get(resistanceEffect).mMagnitude; - if (weaknessEffect != -1) - resistance -= stats.getMagicEffects().get(weaknessEffect).mMagnitude; - - - float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); - float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); - float x = (willpower + 0.1 * luck) * stats.getFatigueTerm(); - - // This makes spells that are easy to cast harder to resist and vice versa - if (spell != NULL && caster.getClass().isActor()) - { - float castChance = getSpellSuccessChance(spell, caster); - if (castChance > 0) - x *= 50 / castChance; - } - - float roll = static_cast(std::rand()) / RAND_MAX * 100; - if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) - roll -= resistance; - - if (x <= roll) - x = 0; - else - { - if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) - x = 100; - else - x = roll / std::min(x, 100.f); - } - - x = std::min(x + resistance, 100.f); - - resisted = x; - } - - return resisted; - } - - inline float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL) - { - float resistance = getEffectResistance(effectId, actor, caster, spell); - if (resistance >= 0) - return 1 - resistance / 100.f; - else - return -(resistance-100) / 100.f; - } + float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL); + float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL); class CastSpell { @@ -202,7 +51,7 @@ namespace MWMechanics void inflict (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false); - void applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, MWMechanics::EffectKey effect, float magnitude); + void applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const MWMechanics::EffectKey& effect, float magnitude); }; } diff --git a/apps/openmw/mwmechanics/spells.hpp b/apps/openmw/mwmechanics/spells.hpp index facf02da8..cc239a650 100644 --- a/apps/openmw/mwmechanics/spells.hpp +++ b/apps/openmw/mwmechanics/spells.hpp @@ -20,7 +20,7 @@ namespace MWMechanics /// \brief Spell list /// /// This class manages known spells as well as abilities, powers and permanent negative effects like - /// diseaes. + /// diseases. class Spells { public: From 969340d61ba3e2a0fbc8fdf9fa3ab937b4a271c1 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sat, 18 Jan 2014 12:55:17 +0200 Subject: [PATCH 573/889] fixed StartCombat script --- apps/openmw/mwscript/aiextensions.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 53b757d76..759d0ba94 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -432,17 +432,14 @@ namespace MWScript virtual void execute (Interpreter::Runtime &runtime) { MWWorld::Ptr actor = R()(runtime); - std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); + std::string targetID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); - if (actorID == "player") - { - creatureStats.setHostile(true); - creatureStats.getAiSequence().stack( - MWMechanics::AiCombat(MWBase::Environment::get().getWorld()->getPtr(actorID,true))); - } + creatureStats.setHostile(true); + creatureStats.getAiSequence().stack( + MWMechanics::AiCombat(MWBase::Environment::get().getWorld()->getPtr(targetID, true) )); } }; From 6584a01d01190ed9d2bccc6ebdc4ab6dd87f07fa Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 18 Jan 2014 14:53:25 +0100 Subject: [PATCH 574/889] reset reference to player NPC record during cleanup --- apps/openmw/mwworld/worldimp.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8edba0892..9704239d7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -276,15 +276,16 @@ namespace MWWorld mWorldScene->changeToVoid(); + mStore.clearDynamic(); + mStore.setUp(); + if (mPlayer) { mPlayer->setCell (0); mPlayer->getPlayer().getRefData() = RefData(); + mPlayer->set (mStore.get().find ("player")); } - mStore.clearDynamic(); - mStore.setUp(); - mCells.clear(); mProjectiles.clear(); From 14e64c180f0b685327c51a22ba78f81a296e0a28 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 18 Jan 2014 15:06:58 +0100 Subject: [PATCH 575/889] on load check player record for dangling ID references --- apps/openmw/mwworld/esmstore.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index cab10ee51..c5c826d47 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -161,9 +161,9 @@ void ESMStore::setUp() mClasses.write (writer); mClothes.write (writer); mEnchants.write (writer); - mNpcs.write (writer); mSpells.write (writer); mWeapons.write (writer); + mNpcs.write (writer); } bool ESMStore::readRecord (ESM::ESMReader& reader, int32_t type) @@ -176,11 +176,25 @@ void ESMStore::setUp() case ESM::REC_CLAS: case ESM::REC_CLOT: case ESM::REC_ENCH: - case ESM::REC_NPC_: case ESM::REC_SPEL: case ESM::REC_WEAP: + case ESM::REC_NPC_: mStores[type]->read (reader); + + if (type==ESM::REC_NPC_) + { + // NPC record will always be last and we know that there can be only one + // dynamic NPC record (player) -> We are done here with dynamic record laoding + setUp(); + + const ESM::NPC *player = mNpcs.find ("player"); + + if (!mRaces.find (player->mRace) || + !mClasses.find (player->mClass)) + throw std::runtime_error ("Invalid player record (race or class unavilable"); + } + return true; default: From 4c0045b41875d7952ccd47ef1f0fc82701e18e63 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 18 Jan 2014 21:11:12 +0100 Subject: [PATCH 576/889] Bug #1109: Do not reset water level when loading a plugin with no water level record (for real this time) --- apps/openmw/mwworld/store.cpp | 4 ++-- components/esm/loadcell.cpp | 19 ++++++++++++++++++- components/esm/loadcell.hpp | 6 +++++- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index ca37cc591..8e4c5ecef 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -56,7 +56,7 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) // copy list into new cell cell->mContextList = oldcell->mContextList; // have new cell replace old cell - *oldcell = *cell; + ESM::Cell::merge(oldcell, cell); } else mInt[idLower] = *cell; } @@ -83,7 +83,7 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) } cell->mMovedRefs = oldcell->mMovedRefs; // have new cell replace old cell - *oldcell = *cell; + ESM::Cell::merge(oldcell, cell); } else mExt[std::make_pair(cell->mData.mX, cell->mData.mY)] = *cell; } diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index f6bc29ae1..f4bba7f19 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -47,9 +47,13 @@ void Cell::load(ESMReader &esm, bool saveContext) esm.getHT(waterl); mWater = (float) waterl; mWaterInt = true; + mHasWaterLevelRecord = true; } else if (esm.isNextSub("WHGT")) + { esm.getHT(mWater); + mHasWaterLevelRecord = true; + } // Quasi-exterior cells have a region (which determines the // weather), pure interior cells have ambient lighting @@ -94,7 +98,7 @@ void Cell::save(ESMWriter &esm) const esm.writeHNT("DATA", mData, 12); if (mData.mFlags & Interior) { - if (mWater != -1) { + if (mHasWaterLevelRecord) { if (mWaterInt) { int water = (mWater >= 0) ? (int) (mWater + 0.5) : (int) (mWater - 0.5); @@ -301,4 +305,17 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) mAmbi.mFog = 0; mAmbi.mFogDensity = 0; } + + void Cell::merge(Cell *original, Cell *modified) + { + float waterLevel = original->mWater; + if (modified->mHasWaterLevelRecord) + { + waterLevel = modified->mWater; + } + // else: keep original water level, instead of resetting to 0 + + *original = *modified; + original->mWater = waterLevel; + } } diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index b066f497e..71478946f 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -77,7 +77,10 @@ struct Cell float mFogDensity; }; - Cell() : mWater(-1) {} + Cell() : mWater(0), mHasWaterLevelRecord(false) {} + + /// Merge \a modified into \a original + static void merge (Cell* original, Cell* modified); // Interior cells are indexed by this (it's the 'id'), for exterior // cells it is optional. @@ -90,6 +93,7 @@ struct Cell DATAstruct mData; AMBIstruct mAmbi; float mWater; // Water level + bool mHasWaterLevelRecord; bool mWaterInt; int mMapColor; int mNAM0; From 947b6c964541932acf7e28e75709d1f597c17281 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 18 Jan 2014 22:16:31 +0100 Subject: [PATCH 577/889] kOgrePi unused --- apps/openmw/mwclass/npc.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index f93a3e342..7f75d910b 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -36,9 +36,6 @@ namespace { - const Ogre::Radian kOgrePi (Ogre::Math::PI); - const Ogre::Radian kOgrePiOverTwo (Ogre::Math::PI / Ogre::Real(2.0)); - struct CustomData : public MWWorld::CustomData { MWMechanics::NpcStats mNpcStats; From 69ca03c308d84abc7ffbccb0a880d121cad4428c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 09:43:41 +0100 Subject: [PATCH 578/889] Issue #777: Move DrawState to CreatureStats. All creatures can cast spells, and some creatures have weapons. --- apps/openmw/mwmechanics/aiwander.cpp | 6 ++---- apps/openmw/mwmechanics/creaturestats.cpp | 12 +++++++++++- apps/openmw/mwmechanics/creaturestats.hpp | 5 +++++ apps/openmw/mwmechanics/npcstats.cpp | 13 +------------ apps/openmw/mwmechanics/npcstats.hpp | 5 ----- 5 files changed, 19 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 853d0ff7b..9a78f1acd 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -7,8 +7,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.hpp" -#include "../mwmechanics/npcstats.hpp" - +#include "creaturestats.hpp" #include namespace @@ -66,8 +65,7 @@ namespace MWMechanics bool AiWander::execute (const MWWorld::Ptr& actor,float duration) { - if (actor.getClass().isNpc()) - actor.getClass().getNpcStats(actor).setDrawState(DrawState_Nothing); + actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); MWBase::World *world = MWBase::Environment::get().getWorld(); if(mDuration) { diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 8d37e34c8..70e4bd989 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -16,7 +16,7 @@ namespace MWMechanics mAttackingOrSpell(false), mAttackType(AT_Chop), mIsWerewolf(false), mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false), - mMovementFlags(0) + mMovementFlags(0), mDrawState (DrawState_Nothing) { for (int i=0; i<4; ++i) mAiSettings[i] = 0; @@ -452,4 +452,14 @@ namespace MWMechanics return false; // shut up, compiler } + DrawState_ CreatureStats::getDrawState() const + { + return mDrawState; + } + + void CreatureStats::setDrawState(DrawState_ state) + { + mDrawState = state; + } + } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 308883fc5..0981522a5 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -10,6 +10,7 @@ #include "spells.hpp" #include "activespells.hpp" #include "aisequence.hpp" +#include "drawstate.hpp" namespace MWMechanics { @@ -18,6 +19,7 @@ namespace MWMechanics /// class CreatureStats { + DrawState_ mDrawState; AttributeValue mAttributes[8]; DynamicStat mDynamic[3]; // health, magicka, fatigue int mLevel; @@ -57,6 +59,9 @@ namespace MWMechanics public: CreatureStats(); + DrawState_ getDrawState() const; + void setDrawState(DrawState_ state); + bool needToRecalcDynamicStats(); void addToFallHeight(float height); diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 293b078da..d18519168 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -22,8 +22,7 @@ #include "../mwbase/soundmanager.hpp" MWMechanics::NpcStats::NpcStats() -: mDrawState (DrawState_Nothing) -, mBounty (0) + : mBounty (0) , mLevelProgress(0) , mDisposition(0) , mReputation(0) @@ -36,16 +35,6 @@ MWMechanics::NpcStats::NpcStats() mSkillIncreases.resize (ESM::Attribute::Length, 0); } -MWMechanics::DrawState_ MWMechanics::NpcStats::getDrawState() const -{ - return mDrawState; -} - -void MWMechanics::NpcStats::setDrawState (DrawState_ state) -{ - mDrawState = state; -} - float MWMechanics::NpcStats::getAttackStrength() const { return mAttackStrength; diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index b89a2b4b3..bf76b80d6 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -7,7 +7,6 @@ #include #include "stat.hpp" -#include "drawstate.hpp" #include "creaturestats.hpp" @@ -30,7 +29,6 @@ namespace MWMechanics /// \note the faction key must be in lowercase std::map mFactionRank; - DrawState_ mDrawState; int mDisposition; SkillValue mSkill[27]; SkillValue mWerewolfSkill[27]; @@ -61,9 +59,6 @@ namespace MWMechanics int getProfit() const; void modifyProfit(int diff); - DrawState_ getDrawState() const; - void setDrawState (DrawState_ state); - /// When attacking, stores how strong the attack should be (0 = weakest, 1 = strongest) float getAttackStrength() const; void setAttackStrength(float value); From 589fbbd8718f27926cf67365be6e85f9616e0e55 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 11:42:58 +0100 Subject: [PATCH 579/889] Issue #777: Create InventoryStore for creatures with weapons/shields --- apps/openmw/mwclass/armor.cpp | 27 +++++----- apps/openmw/mwclass/clothing.cpp | 25 +++++---- apps/openmw/mwclass/creature.cpp | 52 +++++++++++++++---- apps/openmw/mwclass/creature.hpp | 5 ++ apps/openmw/mwclass/npc.hpp | 2 + apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 2 +- apps/openmw/mwgui/companionitemmodel.cpp | 4 +- apps/openmw/mwgui/inventoryitemmodel.cpp | 4 +- apps/openmw/mwgui/tradeitemmodel.cpp | 2 +- apps/openmw/mwgui/tradewindow.cpp | 5 +- apps/openmw/mwmechanics/actors.cpp | 3 +- .../mwmechanics/mechanicsmanagerimp.cpp | 4 +- apps/openmw/mwrender/actors.cpp | 3 -- apps/openmw/mwrender/characterpreview.cpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 2 - apps/openmw/mwscript/containerextensions.cpp | 2 +- apps/openmw/mwworld/actionequip.cpp | 4 +- apps/openmw/mwworld/class.cpp | 5 ++ apps/openmw/mwworld/class.hpp | 3 ++ apps/openmw/mwworld/containerstore.hpp | 2 + apps/openmw/mwworld/inventorystore.cpp | 4 +- apps/openmw/mwworld/inventorystore.hpp | 2 + apps/openmw/mwworld/worldimp.cpp | 9 ++-- 23 files changed, 111 insertions(+), 62 deletions(-) diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 5e37426c9..83bda25d1 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -289,7 +289,7 @@ namespace MWClass std::pair Armor::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const { - MWWorld::InventoryStore& invStore = MWWorld::Class::get(npc).getInventoryStore(npc); + MWWorld::InventoryStore& invStore = npc.getClass().getInventoryStore(npc); if (ptr.getCellRef().mCharge == 0) return std::make_pair(0, "#{sInventoryMessage1}"); @@ -300,20 +300,23 @@ namespace MWClass if (slots_.first.empty()) return std::make_pair(0, ""); - std::string npcRace = npc.get()->mBase->mRace; - - // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) - const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); - if(race->mData.mFlags & ESM::Race::Beast) + if (npc.getClass().isNpc()) { - std::vector parts = ptr.get()->mBase->mParts.mParts; + std::string npcRace = npc.get()->mBase->mRace; - for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) + // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) + const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); + if(race->mData.mFlags & ESM::Race::Beast) { - if((*itr).mPart == ESM::PRT_Head) - return std::make_pair(0, "#{sNotifyMessage13}"); - if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) - return std::make_pair(0, "#{sNotifyMessage14}"); + std::vector parts = ptr.get()->mBase->mParts.mParts; + + for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) + { + if((*itr).mPart == ESM::PRT_Head) + return std::make_pair(0, "#{sNotifyMessage13}"); + if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) + return std::make_pair(0, "#{sNotifyMessage14}"); + } } } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 62fc26a71..a135585eb 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -238,20 +238,23 @@ namespace MWClass if (slots_.first.empty()) return std::make_pair(0, ""); - std::string npcRace = npc.get()->mBase->mRace; - - // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) - const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); - if(race->mData.mFlags & ESM::Race::Beast) + if (npc.getClass().isNpc()) { - std::vector parts = ptr.get()->mBase->mParts.mParts; + std::string npcRace = npc.get()->mBase->mRace; - for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) + // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) + const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); + if(race->mData.mFlags & ESM::Race::Beast) { - if((*itr).mPart == ESM::PRT_Head) - return std::make_pair(0, "#{sNotifyMessage13}"); - if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) - return std::make_pair(0, "#{sNotifyMessage15}"); + std::vector parts = ptr.get()->mBase->mParts.mParts; + + for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) + { + if((*itr).mPart == ESM::PRT_Head) + return std::make_pair(0, "#{sNotifyMessage13}"); + if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) + return std::make_pair(0, "#{sNotifyMessage15}"); + } } } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index a97268318..1a8f7b5c7 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -26,6 +26,8 @@ #include "../mwgui/tooltips.hpp" +#include "../mwworld/inventorystore.hpp" + #include "../mwmechanics/npcstats.hpp" namespace @@ -33,15 +35,20 @@ namespace struct CustomData : public MWWorld::CustomData { MWMechanics::CreatureStats mCreatureStats; - MWWorld::ContainerStore mContainerStore; + MWWorld::ContainerStore* mContainerStore; // may be InventoryStore for some creatures MWMechanics::Movement mMovement; virtual MWWorld::CustomData *clone() const; + + CustomData() : mContainerStore(0) {} + virtual ~CustomData() { delete mContainerStore; } }; MWWorld::CustomData *CustomData::clone() const { - return new CustomData (*this); + CustomData* cloned = new CustomData (*this); + cloned->mContainerStore = mContainerStore->clone(); + return cloned; } } @@ -106,15 +113,23 @@ namespace MWClass data->mCreatureStats.getSpells().add (*iter); // inventory - data->mContainerStore.fill(ref->mBase->mInventory, getId(ptr), "", + if (ref->mBase->mFlags & ESM::Creature::Weapon) + data->mContainerStore = new MWWorld::InventoryStore(); + else + data->mContainerStore = new MWWorld::ContainerStore(); + + // store + ptr.getRefData().setCustomData (data.release()); + + getContainerStore(ptr).fill(ref->mBase->mInventory, getId(ptr), "", MWBase::Environment::get().getWorld()->getStore()); // TODO: this is not quite correct, in vanilla the merchant's gold pool is not available in his inventory. // (except for gold you gave him) - data->mContainerStore.add(MWWorld::ContainerStore::sGoldId, ref->mBase->mData.mGold, ptr); + getContainerStore(ptr).add(MWWorld::ContainerStore::sGoldId, ref->mBase->mData.mGold, ptr); - // store - ptr.getRefData().setCustomData (data.release()); + if (ref->mBase->mFlags & ESM::Creature::Weapon) + getInventoryStore(ptr).autoEquip(ptr); } } @@ -325,18 +340,33 @@ namespace MWClass return boost::shared_ptr(new MWWorld::ActionTalk(ptr)); } - MWWorld::ContainerStore& Creature::getContainerStore (const MWWorld::Ptr& ptr) - const + MWWorld::ContainerStore& Creature::getContainerStore (const MWWorld::Ptr& ptr) const { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore; + return *dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore; + } + + MWWorld::InventoryStore& Creature::getInventoryStore(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = ptr.get(); + + if (ref->mBase->mFlags & ESM::Creature::Weapon) + return dynamic_cast(getContainerStore(ptr)); + else + throw std::runtime_error("this creature has no inventory store"); + } + + bool Creature::hasInventoryStore(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = ptr.get(); + + return (ref->mBase->mFlags & ESM::Creature::Weapon); } std::string Creature::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->mScript; } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index d518d0056..cfa06ed62 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -68,6 +68,11 @@ namespace MWClass const MWWorld::Ptr& ptr) const; ///< Return container store + virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const; + ///< Return inventory store + + virtual bool hasInventoryStore (const MWWorld::Ptr &ptr) const; + virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 497d0ced8..e658dde5f 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -71,6 +71,8 @@ namespace MWClass virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const; ///< Return inventory store + virtual bool hasInventoryStore(const MWWorld::Ptr &ptr) const { return true; } + virtual void hit(const MWWorld::Ptr& ptr, int type) const; virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index b2107c329..263c101e5 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -420,7 +420,7 @@ namespace MWDialogue MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue); // Apply disposition change to NPC's base disposition - if (mActor.getTypeName() == typeid(ESM::NPC).name()) + if (mActor.getClass().isNpc()) { MWMechanics::NpcStats& npcStats = MWWorld::Class::get(mActor).getNpcStats(mActor); npcStats.setBaseDisposition(npcStats.getBaseDisposition() + mPermanentDispositionChange); diff --git a/apps/openmw/mwgui/companionitemmodel.cpp b/apps/openmw/mwgui/companionitemmodel.cpp index 3212ed701..bb6cf2800 100644 --- a/apps/openmw/mwgui/companionitemmodel.cpp +++ b/apps/openmw/mwgui/companionitemmodel.cpp @@ -12,7 +12,7 @@ namespace MWGui void CompanionItemModel::copyItem (const ItemStack& item, size_t count) { - if (mActor.getTypeName() == typeid(ESM::NPC).name()) + if (mActor.getClass().isNpc()) { MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor); stats.modifyProfit(MWWorld::Class::get(item.mBase).getValue(item.mBase) * count); @@ -23,7 +23,7 @@ namespace MWGui void CompanionItemModel::removeItem (const ItemStack& item, size_t count) { - if (mActor.getTypeName() == typeid(ESM::NPC).name()) + if (mActor.getClass().isNpc()) { MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor); stats.modifyProfit(-MWWorld::Class::get(item.mBase).getValue(item.mBase) * count); diff --git a/apps/openmw/mwgui/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp index d26feba88..97e1e9a2b 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.cpp +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -74,9 +74,9 @@ void InventoryItemModel::update() ItemStack newItem (item, this, item.getRefData().getCount()); - if (mActor.getTypeName() == typeid(ESM::NPC).name()) + if (mActor.getClass().hasInventoryStore(mActor)) { - MWWorld::InventoryStore& store = MWWorld::Class::get(mActor).getInventoryStore(mActor); + MWWorld::InventoryStore& store = mActor.getClass().getInventoryStore(mActor); for (int slot=0; slot(0,std::min(int(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr) + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange()),100)); - const MWMechanics::NpcStats &sellerStats = mPtr.getClass().getNpcStats(mPtr); - const MWMechanics::NpcStats &playerStats = player.getClass().getNpcStats(player); + const MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr); + const MWMechanics::CreatureStats &playerStats = player.getClass().getCreatureStats(player); float a1 = std::min(player.getClass().getSkill(player, ESM::Skill::Mercantile), 100); float b1 = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 1b65b1472..3daecbe1c 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -47,8 +47,7 @@ void adjustBoundItem (const std::string& item, bool bound, const MWWorld::Ptr& a bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate) { - // TODO: remove this check once creatures support inventory store - if (ptr.getTypeName() == typeid(ESM::NPC).name()) + if (ptr.getClass().hasInventoryStore(ptr)) { MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr); MWWorld::ContainerStoreIterator item = diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 0dee4706a..fff3db8a9 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -196,8 +196,8 @@ namespace MWMechanics creatureStats.setDynamic (i, stat); } - // auto-equip again. we need this for when the race is changed to a beast race - MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore(ptr); + // auto-equip again. we need this for when the race is changed to a beast race and shoes are no longer equippable + MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr); for (int i=0; iaddWaterRippleEmitter (ptr); - - // Create CustomData, will do autoEquip and trigger animation parts update - ptr.getClass().getInventoryStore(ptr); } void Actors::insertCreature (const MWWorld::Ptr& ptr) { diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 2dbc72e26..32145928e 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -142,7 +142,7 @@ namespace MWRender { mAnimation->updateParts(); - MWWorld::InventoryStore &inv = MWWorld::Class::get(mCharacter).getInventoryStore(mCharacter); + MWWorld::InventoryStore &inv = mCharacter.getClass().getInventoryStore(mCharacter); MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); std::string groupname; if(iter == inv.end()) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8a2ab1c7a..572d2abdb 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -892,8 +892,6 @@ void RenderingManager::renderPlayer(const MWWorld::Ptr &ptr) mPlayerAnimation->~NpcAnimation(); new(mPlayerAnimation) NpcAnimation(ptr, ptr.getRefData().getBaseNode(), RV_Actors); } - // Ensure CustomData -> autoEquip -> animation update - ptr.getClass().getInventoryStore(ptr); mCamera->setAnimation(mPlayerAnimation); mWater->removeEmitter(ptr); diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 6674481f2..e06505e86 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -159,7 +159,7 @@ namespace MWScript std::string item = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore (ptr); + MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr); MWWorld::ContainerStoreIterator it = invStore.begin(); for (; it != invStore.end(); ++it) { diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index d34773bd5..2a1b7a3aa 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -19,9 +19,9 @@ namespace MWWorld void ActionEquip::executeImp (const Ptr& actor) { MWWorld::Ptr object = getTarget(); - MWWorld::InventoryStore& invStore = MWWorld::Class::get(actor).getInventoryStore(actor); + MWWorld::InventoryStore& invStore = actor.getClass().getInventoryStore(actor); - std::pair result = MWWorld::Class::get (object).canBeEquipped (object, actor); + std::pair result = object.getClass().canBeEquipped (object, actor); // display error message if the player tried to equip something if (!result.second.empty() && actor == MWBase::Environment::get().getWorld()->getPlayerPtr()) diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 6c00b949c..c7b0d2e1f 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -122,6 +122,11 @@ namespace MWWorld throw std::runtime_error ("class does not have an inventory store"); } + bool Class::hasInventoryStore(const Ptr &ptr) const + { + return false; + } + void Class::lock (const Ptr& ptr, int lockLevel) const { throw std::runtime_error ("class does not support locking"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index ec22d0306..823538cf8 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -150,6 +150,9 @@ namespace MWWorld ///< Return inventory store or throw an exception, if class does not have a /// inventory store (default implementation: throw an exceoption) + virtual bool hasInventoryStore (const Ptr& ptr) const; + ///< Does this object have an inventory store, i.e. equipment slots? (default implementation: false) + virtual void lock (const Ptr& ptr, int lockLevel) const; ///< Lock object (default implementation: throw an exception) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 0a1728740..913ec544a 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -62,6 +62,8 @@ namespace MWWorld virtual ~ContainerStore(); + virtual ContainerStore* clone() { return new ContainerStore(*this); } + ContainerStoreIterator begin (int mask = Type_All); ContainerStoreIterator end(); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 82b827e75..ea43314e6 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -81,7 +81,7 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, // Auto-equip items if an armor/clothing or weapon item is added, but not for the player nor werewolves if ((actorPtr.getRefData().getHandle() != "player") - && !(MWWorld::Class::get(actorPtr).getNpcStats(actorPtr).isWerewolf()) + && !(actorPtr.getClass().isNpc() && actorPtr.getClass().getNpcStats(actorPtr).isWerewolf()) && !actorPtr.getClass().getCreatureStats(actorPtr).isDead()) { std::string type = itemPtr.getTypeName(); @@ -457,7 +457,7 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor // If an armor/clothing item is removed, try to find a replacement, // but not for the player nor werewolves. if ((actor.getRefData().getHandle() != "player") - && !(MWWorld::Class::get(actor).getNpcStats(actor).isWerewolf())) + && !(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())) { std::string type = item.getTypeName(); if (((type == typeid(ESM::Armor).name()) || (type == typeid(ESM::Clothing).name())) diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 067c8261e..d97bdf365 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -113,6 +113,8 @@ namespace MWWorld InventoryStore& operator= (const InventoryStore& store); + virtual InventoryStore* clone() { return new InventoryStore(*this); } + virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner=false); ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed) /// Auto-equip items if specific conditions are fulfilled (see the implementation). diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0d7802081..e6569a178 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2097,7 +2097,6 @@ namespace MWWorld void World::castSpell(const Ptr &actor) { MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); - InventoryStore& inv = actor.getClass().getInventoryStore(actor); MWWorld::Ptr target = getFacedObject(); @@ -2111,9 +2110,11 @@ namespace MWWorld cast.cast(spell); } - else if (inv.getSelectedEnchantItem() != inv.end()) + else if (actor.getClass().hasInventoryStore(actor)) { - cast.cast(*inv.getSelectedEnchantItem()); + MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor); + if (inv.getSelectedEnchantItem() != inv.end()) + cast.cast(*inv.getSelectedEnchantItem()); } } @@ -2282,7 +2283,7 @@ namespace MWWorld void World::breakInvisibility(const Ptr &actor) { actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Invisibility); - if (actor.getClass().isNpc()) + if (actor.getClass().hasInventoryStore(actor)) actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility); } From b3e45c55bcfc45d4fe4ee4f9b10d989c32b15a72 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 19 Jan 2014 11:44:47 +0100 Subject: [PATCH 580/889] progressing with the cloning --- apps/opencs/view/world/creator.hpp | 3 +++ apps/opencs/view/world/genericcreator.cpp | 32 +++++++++++++++++------ apps/opencs/view/world/genericcreator.hpp | 6 +++++ apps/opencs/view/world/tablebottombox.cpp | 11 +++++++- apps/opencs/view/world/tablebottombox.hpp | 2 ++ apps/opencs/view/world/tablesubview.cpp | 15 +++++++++-- apps/opencs/view/world/tablesubview.hpp | 4 +++ 7 files changed, 62 insertions(+), 11 deletions(-) diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index df9b116ee..58a5e21ad 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -2,6 +2,7 @@ #define CSV_WORLD_CREATOR_H #include +#include "../../model/world/universalid.hpp" class QUndoStack; @@ -23,6 +24,8 @@ namespace CSVWorld virtual ~Creator(); virtual void reset() = 0; + + virtual void cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type) = 0; virtual void setEditLock (bool locked) = 0; diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index ba2b3665a..b89c2dccf 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -2,6 +2,7 @@ #include "genericcreator.hpp" #include +#include #include #include @@ -58,7 +59,7 @@ const CSMWorld::UniversalId& CSVWorld::GenericCreator::getCollectionId() const CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id, bool relaxedIdRules) -: mData (data), mUndoStack (undoStack), mListId (id), mLocked (false) +: mData (data), mUndoStack (undoStack), mListId (id), mLocked (false), mCloneMode(false), mClonedType(CSMWorld::UniversalId::Type_None) { mLayout = new QHBoxLayout; mLayout->setContentsMargins (0, 0, 0, 0); @@ -89,6 +90,7 @@ void CSVWorld::GenericCreator::setEditLock (bool locked) void CSVWorld::GenericCreator::reset() { + mCloneMode = false; mId->setText (""); update(); } @@ -120,16 +122,30 @@ void CSVWorld::GenericCreator::create() { if (!mLocked) { - std::string id = getId(); + if (mCloneMode) + { + std::string id = getId(); + } else { + std::string id = getId(); - std::auto_ptr command (new CSMWorld::CreateCommand ( + std::auto_ptr command (new CSMWorld::CreateCommand ( dynamic_cast (*mData.getTableModel (mListId)), id)); - configureCreateCommand (*command); + configureCreateCommand (*command); - mUndoStack.push (command.release()); + mUndoStack.push (command.release()); - emit done(); - emit requestFocus (id); + emit done(); + emit requestFocus (id); + } } -} \ No newline at end of file +} + +void CSVWorld::GenericCreator::cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type) +{ + mCloneMode = true; + mClonedId = originid; + mClonedType = type; + + mId->setText(QString::fromStdString(mClonedId)); +} diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 8dd2ca911..a1acb2e79 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -1,6 +1,7 @@ #ifndef CSV_WORLD_GENERICCREATOR_H #define CSV_WORLD_GENERICCREATOR_H +class QString; class QPushButton; class QLineEdit; class QHBoxLayout; @@ -28,6 +29,9 @@ namespace CSVWorld std::string mErrors; QHBoxLayout *mLayout; bool mLocked; + bool mCloneMode; + std::string mClonedId; + CSMWorld::UniversalId::Type mClonedType; protected: @@ -57,6 +61,8 @@ namespace CSVWorld virtual void reset(); + virtual void cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type); + virtual std::string getErrors() const; ///< Return formatted error descriptions for the current state of the creator. if an empty /// string is returned, there is no error. diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index 3edf9af31..b89f5180b 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -156,4 +156,13 @@ void CSVWorld::TableBottomBox::createRequest() mLayout->setCurrentWidget (mCreator); setVisible (true); mCreating = true; -} \ No newline at end of file +} + +void CSVWorld::TableBottomBox::cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type) +{ + mCreator->reset(); + mCreator->cloneMode(id, type); + mLayout->setCurrentWidget(mCreator); + setVisible (true); + mCreating = true; +} diff --git a/apps/opencs/view/world/tablebottombox.hpp b/apps/opencs/view/world/tablebottombox.hpp index a5ae5e0bd..58a6a5a2f 100644 --- a/apps/opencs/view/world/tablebottombox.hpp +++ b/apps/opencs/view/world/tablebottombox.hpp @@ -2,6 +2,7 @@ #define CSV_WORLD_BOTTOMBOX_H #include +#include class QLabel; class QStackedLayout; @@ -76,6 +77,7 @@ namespace CSVWorld /// \param modified Number of added and modified records void createRequest(); + void cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type); }; } diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 55ded09de..36200dbbc 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -46,8 +46,13 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D mTable->selectionSizeUpdate(); if (mBottom->canCreateAndDelete()) + { connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); - + + connect (mTable, SIGNAL (cloneRequest(int)), this, SLOT(cloneRequest(int))); + connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), + mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type))); + } connect (mBottom, SIGNAL (requestFocus (const std::string&)), mTable, SLOT (requestFocus (const std::string&))); @@ -75,4 +80,10 @@ void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, con void CSVWorld::TableSubView::setStatusBar (bool show) { mBottom->setStatusBar (show); -} \ No newline at end of file +} + +void CSVWorld::TableSubView::cloneRequest(int row) +{ + const CSMWorld::UniversalId& toClone(mTable->getUniversalId(row)); + emit cloneRequest(toClone.getId(), toClone.getType()); +} diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 56a441a4d..5b19110b6 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -33,10 +33,14 @@ namespace CSVWorld virtual void updateEditorSetting (const QString& key, const QString& value); virtual void setStatusBar (bool show); + + signals: + void cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type); private slots: void editRequest (int row); + void cloneRequest (int row); }; } From 198bb0de60017bd9663a2eb5046296892be6c577 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 13:05:26 +0100 Subject: [PATCH 581/889] Issue #777: Add CreatureAnimation variant for creatures with weapons/shields --- apps/openmw/mwclass/creature.cpp | 4 +- apps/openmw/mwrender/actors.cpp | 8 +- apps/openmw/mwrender/actors.hpp | 2 +- apps/openmw/mwrender/creatureanimation.cpp | 126 +++++++++++++++++++-- apps/openmw/mwrender/creatureanimation.hpp | 28 ++++- 5 files changed, 156 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 1a8f7b5c7..c272b2825 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -148,8 +148,10 @@ namespace MWClass void Creature::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { + MWWorld::LiveCellRef *ref = ptr.get(); + MWRender::Actors& actors = renderingInterface.getActors(); - actors.insertCreature(ptr); + actors.insertCreature(ptr, ref->mBase->mFlags & ESM::Creature::Weapon); } void Creature::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index be123d39c..4955dd6cb 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -76,10 +76,14 @@ void Actors::insertNPC(const MWWorld::Ptr& ptr) mAllActors[ptr] = anim; mRendering->addWaterRippleEmitter (ptr); } -void Actors::insertCreature (const MWWorld::Ptr& ptr) +void Actors::insertCreature (const MWWorld::Ptr& ptr, bool weaponsShields) { insertBegin(ptr); - CreatureAnimation* anim = new CreatureAnimation(ptr); + Animation* anim = NULL; + if (weaponsShields) + anim = new CreatureWeaponAnimation(ptr); + else + anim = new CreatureAnimation(ptr); delete mAllActors[ptr]; mAllActors[ptr] = anim; mRendering->addWaterRippleEmitter (ptr); diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index af71525fa..d5d6c52bb 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -40,7 +40,7 @@ namespace MWRender void setRootNode(Ogre::SceneNode* root); void insertNPC(const MWWorld::Ptr& ptr); - void insertCreature (const MWWorld::Ptr& ptr); + void insertCreature (const MWWorld::Ptr& ptr, bool weaponsShields); void insertActivator (const MWWorld::Ptr& ptr); bool deleteObject (const MWWorld::Ptr& ptr); ///< \return found? diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 20e5ff8ef..2cffd7f0d 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -1,26 +1,27 @@ #include "creatureanimation.hpp" +#include +#include +#include + #include "renderconst.hpp" #include "../mwbase/world.hpp" +#include "../mwworld/class.hpp" + namespace MWRender { -CreatureAnimation::~CreatureAnimation() -{ -} CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) : Animation(ptr, ptr.getRefData().getBaseNode()) { MWWorld::LiveCellRef *ref = mPtr.get(); - assert (ref->mBase != NULL); - if(!ref->mBase->mModel.empty()) + std::string model = ptr.getClass().getModel(ptr); + if(!model.empty()) { - std::string model = "meshes\\"+ref->mBase->mModel; - setObjectRoot(model, false); setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); @@ -30,4 +31,115 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) } } + +CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr) + : Animation(ptr, ptr.getRefData().getBaseNode()) + , mShowWeapons(true) // TODO: change to false, once charactercontroller handles creature weapons + , mShowCarriedLeft(true) // TODO: change to false, once charactercontroller handles creature weapons +{ + MWWorld::LiveCellRef *ref = mPtr.get(); + + std::string model = ptr.getClass().getModel(ptr); + if(!model.empty()) + { + setObjectRoot(model, false); + setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); + + if((ref->mBase->mFlags&ESM::Creature::Bipedal)) + addAnimSource("meshes\\base_anim.nif"); + addAnimSource(model); + + mPtr.getClass().getInventoryStore(mPtr).setListener(this, mPtr); + + updateParts(); + } +} + +void CreatureWeaponAnimation::showWeapons(bool showWeapon) +{ + if (showWeapon != mShowWeapons) + { + mShowWeapons = showWeapon; + updateParts(); + } +} + +void CreatureWeaponAnimation::showCarriedLeft(bool show) +{ + if (show != mShowCarriedLeft) + { + mShowCarriedLeft = show; + updateParts(); + } +} + +void CreatureWeaponAnimation::updateParts() +{ + mWeapon.setNull(); + mShield.setNull(); + + if (mShowWeapons) + updatePart(mWeapon, MWWorld::InventoryStore::Slot_CarriedRight); + if (mShowCarriedLeft) + updatePart(mShield, MWWorld::InventoryStore::Slot_CarriedLeft); +} + +void CreatureWeaponAnimation::updatePart(NifOgre::ObjectScenePtr& scene, int slot) +{ + MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); + MWWorld::ContainerStoreIterator it = inv.getSlot(slot); + + if (it == inv.end()) + { + scene.setNull(); + return; + } + MWWorld::Ptr item = *it; + + std::string bonename; + if (slot == MWWorld::InventoryStore::Slot_CarriedRight) + bonename = "Weapon Bone"; + else + bonename = "Shield Bone"; + + scene = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, item.getClass().getModel(item)); + Ogre::Vector3 glowColor = getEnchantmentColor(item); + + setRenderProperties(scene, RV_Actors, RQG_Main, RQG_Alpha, 0, + !item.getClass().getEnchantment(item).empty(), &glowColor); + + if(scene->mSkelBase) + { + Ogre::SkeletonInstance *skel = scene->mSkelBase->getSkeleton(); + if(scene->mSkelBase->isParentTagPoint()) + { + Ogre::Node *root = scene->mSkelBase->getParentNode(); + if(skel->hasBone("BoneOffset")) + { + Ogre::Bone *offset = skel->getBone("BoneOffset"); + + root->translate(offset->getPosition()); + + // It appears that the BoneOffset rotation is completely bogus, at least for light models. + //root->rotate(offset->getOrientation()); + root->pitch(Ogre::Degree(-90.0f)); + + root->scale(offset->getScale()); + root->setInitialState(); + } + } + updateSkeletonInstance(mSkelBase->getSkeleton(), skel); + } + + // TODO: + // type == ESM::PRT_Weapon should get an animation source based on the current offset + // of the weapon attack animation (from its beginning, or start marker?) + std::vector >::iterator ctrl(scene->mControllers.begin()); + for(;ctrl != scene->mControllers.end();ctrl++) + { + if(ctrl->getSource().isNull()) + ctrl->setSource(Ogre::SharedPtr(new NullAnimationTime())); + } +} + } diff --git a/apps/openmw/mwrender/creatureanimation.hpp b/apps/openmw/mwrender/creatureanimation.hpp index a902df5d8..37826673d 100644 --- a/apps/openmw/mwrender/creatureanimation.hpp +++ b/apps/openmw/mwrender/creatureanimation.hpp @@ -2,6 +2,7 @@ #define GAME_RENDER_CREATUREANIMATION_H #include "animation.hpp" +#include "../mwworld/inventorystore.hpp" namespace MWWorld { @@ -14,7 +15,32 @@ namespace MWRender { public: CreatureAnimation(const MWWorld::Ptr& ptr); - virtual ~CreatureAnimation(); + virtual ~CreatureAnimation() {} + }; + + // For creatures with weapons and shields + // Animation is already virtual anyway, so might as well make a separate class. + // Most creatures don't need weapons/shields, so this will save some memory. + class CreatureWeaponAnimation : public Animation, public MWWorld::InventoryStoreListener + { + public: + CreatureWeaponAnimation(const MWWorld::Ptr& ptr); + virtual ~CreatureWeaponAnimation() {} + + virtual void equipmentChanged() { updateParts(); } + + virtual void showWeapons(bool showWeapon); + virtual void showCarriedLeft(bool show); + + void updateParts(); + + void updatePart(NifOgre::ObjectScenePtr& scene, int slot); + + private: + NifOgre::ObjectScenePtr mWeapon; + NifOgre::ObjectScenePtr mShield; + bool mShowWeapons; + bool mShowCarriedLeft; }; } From 13646a651be8f27b7c3180d56087cd8f87d06556 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 13:31:17 +0100 Subject: [PATCH 582/889] Issue #777: Handle creatures with weapons in CharacterController. Move attack strength to CreatureStats. --- apps/openmw/mwmechanics/aicombat.cpp | 7 ++++--- apps/openmw/mwmechanics/character.cpp | 19 ++++++++++--------- apps/openmw/mwmechanics/character.hpp | 6 +++--- apps/openmw/mwmechanics/creaturestats.cpp | 12 +++++++++++- apps/openmw/mwmechanics/creaturestats.hpp | 5 +++++ apps/openmw/mwmechanics/npcstats.cpp | 11 ----------- apps/openmw/mwmechanics/npcstats.hpp | 4 ---- apps/openmw/mwrender/creatureanimation.cpp | 4 ++-- 8 files changed, 35 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 42b618143..a0d57e89d 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -42,13 +42,14 @@ namespace MWMechanics actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); - if(actor.getTypeName() == typeid(ESM::NPC).name()) + if (actor.getClass().hasInventoryStore(actor)) { - MWMechanics::DrawState_ state = actor.getClass().getNpcStats(actor).getDrawState(); + MWMechanics::DrawState_ state = actor.getClass().getCreatureStats(actor).getDrawState(); if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) - actor.getClass().getNpcStats(actor).setDrawState(MWMechanics::DrawState_Weapon); + actor.getClass().getCreatureStats(actor).setDrawState(MWMechanics::DrawState_Weapon); //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true); } + ESM::Position pos = actor.getRefData().getPosition(); const ESM::Pathgrid *pathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 6ff947306..6183b65d3 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -323,7 +323,7 @@ void CharacterController::getWeaponGroup(WeaponType weaptype, std::string &group } -MWWorld::ContainerStoreIterator CharacterController::getActiveWeapon(NpcStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype) +MWWorld::ContainerStoreIterator CharacterController::getActiveWeapon(CreatureStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype) { if(stats.getDrawState() == DrawState_Spell) { @@ -434,15 +434,16 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim * handle knockout and death which moves the character down. */ mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f)); - if(mPtr.getTypeName() == typeid(ESM::NPC).name()) + if (cls.hasInventoryStore(mPtr)) { - getActiveWeapon(cls.getNpcStats(mPtr), cls.getInventoryStore(mPtr), &mWeaponType); + getActiveWeapon(cls.getCreatureStats(mPtr), cls.getInventoryStore(mPtr), &mWeaponType); if(mWeaponType != WeapType_None) { getWeaponGroup(mWeaponType, mCurrentWeapon); mUpperBodyState = UpperCharState_WeapEquiped; mAnimation->showWeapons(true); } + mAnimation->showCarriedLeft(mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand); } if(!cls.getCreatureStats(mPtr).isDead()) @@ -517,14 +518,14 @@ bool CharacterController::updateCreatureState() return false; } -bool CharacterController::updateNpcState(bool inwater, bool isrunning) +bool CharacterController::updateWeaponState(bool inwater, bool isrunning) { const MWWorld::Class &cls = MWWorld::Class::get(mPtr); - NpcStats &stats = cls.getNpcStats(mPtr); + CreatureStats &stats = cls.getCreatureStats(mPtr); WeaponType weaptype = WeapType_None; MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); MWWorld::ContainerStoreIterator weapon = getActiveWeapon(stats, inv, &weaptype); - const bool isWerewolf = stats.isWerewolf(); + const bool isWerewolf = cls.isNpc() && cls.getNpcStats(mPtr).isWerewolf(); bool forcestateupdate = false; if(weaptype != mWeaponType && mHitState != CharState_KnockDown) @@ -613,7 +614,7 @@ bool CharacterController::updateNpcState(bool inwater, bool isrunning) { // Unset casting flag, otherwise pressing the mouse button down would // continue casting every frame if there is no animation - mPtr.getClass().getCreatureStats(mPtr).setAttackingOrSpell(false); + stats.setAttackingOrSpell(false); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); @@ -1128,8 +1129,8 @@ void CharacterController::update(float duration) } } - if(cls.isNpc()) - forcestateupdate = updateNpcState(inwater, isrunning) || forcestateupdate; + if(cls.hasInventoryStore(mPtr)) + forcestateupdate = updateWeaponState(inwater, isrunning) || forcestateupdate; else forcestateupdate = updateCreatureState() || forcestateupdate; diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index d3d4b4435..33e6cae52 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -22,7 +22,7 @@ namespace MWMechanics { class Movement; -class NpcStats; +class CreatureStats; enum Priority { Priority_Default, @@ -170,13 +170,13 @@ class CharacterController static void getWeaponGroup(WeaponType weaptype, std::string &group); - static MWWorld::ContainerStoreIterator getActiveWeapon(NpcStats &stats, + static MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype); void clearAnimQueue(); - bool updateNpcState(bool inwater, bool isrunning); + bool updateWeaponState(bool inwater, bool isrunning); bool updateCreatureState(); void updateVisibility(); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 70e4bd989..62bae8ca8 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -16,7 +16,7 @@ namespace MWMechanics mAttackingOrSpell(false), mAttackType(AT_Chop), mIsWerewolf(false), mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false), - mMovementFlags(0), mDrawState (DrawState_Nothing) + mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f) { for (int i=0; i<4; ++i) mAiSettings[i] = 0; @@ -462,4 +462,14 @@ namespace MWMechanics mDrawState = state; } + float CreatureStats::getAttackStrength() const + { + return mAttackStrength; + } + + void CreatureStats::setAttackStrength(float value) + { + mAttackStrength = value; + } + } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 0981522a5..6893f385e 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -40,6 +40,7 @@ namespace MWMechanics bool mKnockdown; bool mHitRecovery; unsigned int mMovementFlags; + float mAttackStrength; // Note only some creatures attack with weapons float mFallHeight; @@ -62,6 +63,10 @@ namespace MWMechanics DrawState_ getDrawState() const; void setDrawState(DrawState_ state); + /// When attacking, stores how strong the attack should be (0 = weakest, 1 = strongest) + float getAttackStrength() const; + void setAttackStrength(float value); + bool needToRecalcDynamicStats(); void addToFallHeight(float height); diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index d18519168..4fff6eac8 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -28,23 +28,12 @@ MWMechanics::NpcStats::NpcStats() , mReputation(0) , mWerewolfKills (0) , mProfit(0) -, mAttackStrength(0.0f) , mTimeToStartDrowning(20.0) , mLastDrowningHit(0) { mSkillIncreases.resize (ESM::Attribute::Length, 0); } -float MWMechanics::NpcStats::getAttackStrength() const -{ - return mAttackStrength; -} - -void MWMechanics::NpcStats::setAttackStrength(float value) -{ - mAttackStrength = value; -} - int MWMechanics::NpcStats::getBaseDisposition() const { return mDisposition; diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index bf76b80d6..8cdeeea5d 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -59,10 +59,6 @@ namespace MWMechanics int getProfit() const; void modifyProfit(int diff); - /// When attacking, stores how strong the attack should be (0 = weakest, 1 = strongest) - float getAttackStrength() const; - void setAttackStrength(float value); - int getBaseDisposition() const; void setBaseDisposition(int disposition); diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 2cffd7f0d..e2aa9a2b8 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -34,8 +34,8 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr) : Animation(ptr, ptr.getRefData().getBaseNode()) - , mShowWeapons(true) // TODO: change to false, once charactercontroller handles creature weapons - , mShowCarriedLeft(true) // TODO: change to false, once charactercontroller handles creature weapons + , mShowWeapons(false) + , mShowCarriedLeft(false) { MWWorld::LiveCellRef *ref = mPtr.get(); From 047bbe43b2ca08a63c8e18d2b4af52e0dbefb37b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 13:46:40 +0100 Subject: [PATCH 583/889] Don't complain about missing jump animations --- apps/openmw/mwmechanics/character.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 6183b65d3..7c2f7472d 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -250,14 +250,16 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat mAnimation->disable(mCurrentJump); mCurrentJump = jump; - mAnimation->play(mCurrentJump, Priority_Jump, jumpgroup, false, + if (mAnimation->hasAnimation("jump")) + mAnimation->play(mCurrentJump, Priority_Jump, jumpgroup, false, 1.0f, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul); } else { mAnimation->disable(mCurrentJump); mCurrentJump.clear(); - mAnimation->play(jump, Priority_Jump, jumpgroup, true, + if (mAnimation->hasAnimation("jump")) + mAnimation->play(jump, Priority_Jump, jumpgroup, true, 1.0f, "loop stop", "stop", 0.0f, 0); } } From 1a00f26390e28fb15153f7910b41e51a614a8a6b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 14:02:39 +0100 Subject: [PATCH 584/889] Bug #1126: Golden saints and many other creatures only have "Bip01 Head" --- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 3ad716b72..3ce98c0bf 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -225,7 +225,7 @@ namespace MWBase /// Returns a pointer to the object the provided object would hit (if within the /// specified distance), and the point where the hit occurs. This will attempt to - /// use the "Head" node as a basis. + /// use the "Head" node, or alternatively the "Bip01 Head" node as a basis. virtual std::pair getHitContact(const MWWorld::Ptr &ptr, float distance) = 0; virtual void adjustPosition (const MWWorld::Ptr& ptr) = 0; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e6569a178..46a1e9fda 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -821,6 +821,8 @@ namespace MWWorld if(anim != NULL) { Ogre::Node *node = anim->getNode("Head"); + if (node == NULL) + node = anim->getNode("Bip01 Head"); if(node != NULL) pos += node->_getDerivedPosition(); } From 9723263730b3c101c2bf38d5009ca06c637dfa91 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 14:47:58 +0100 Subject: [PATCH 585/889] Bug #1126: Tweak creature attack distance a bit (still no idea where this should come from) --- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwmechanics/aicombat.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index c272b2825..7fcc7066b 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -197,7 +197,7 @@ namespace MWClass ptr.get(); // TODO: where is the distance defined? - std::pair result = MWBase::Environment::get().getWorld()->getHitContact(ptr, 100); + std::pair result = MWBase::Environment::get().getWorld()->getHitContact(ptr, 200); if (result.first.isEmpty()) return; // Didn't hit anything diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index c36c10665..f38b7ef75 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -152,7 +152,7 @@ namespace MWMechanics else //is creature { weaptype = WeapType_HandToHand; //doesn't matter, should only reflect if it is melee or distant weapon - weapRange = 100; //TODO: use true attack range (the same problem in Creature::hit) + weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit) } //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); From 0adc0f56ed844c017a574ad2e19095ffa43e9984 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 16:13:16 +0100 Subject: [PATCH 586/889] Issue #777: Consider weapons in Creature::hit --- apps/openmw/mwclass/creature.cpp | 96 +++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 7fcc7066b..4a244f6a9 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -6,6 +6,7 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/magiceffects.hpp" #include "../mwmechanics/movement.hpp" +#include "../mwmechanics/spellcasting.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -195,9 +196,38 @@ namespace MWClass { MWWorld::LiveCellRef *ref = ptr.get(); + const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); + MWMechanics::CreatureStats &stats = getCreatureStats(ptr); + + // Get the weapon used (if hand-to-hand, weapon = inv.end()) + MWWorld::Ptr weapon; + if (ptr.getClass().hasInventoryStore(ptr)) + { + MWWorld::InventoryStore &inv = getInventoryStore(ptr); + MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if (weaponslot != inv.end() && weaponslot->getTypeName() == typeid(ESM::Weapon).name()) + weapon = *weaponslot; + } + + // Reduce fatigue + // somewhat of a guess, but using the weapon weight makes sense + const float fFatigueAttackBase = gmst.find("fFatigueAttackBase")->getFloat(); + const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat(); + const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat(); + MWMechanics::DynamicStat fatigue = stats.getFatigue(); + const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr); + float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult; + if (!weapon.isEmpty()) + fatigueLoss += weapon.getClass().getWeight(weapon) * stats.getAttackStrength() * fWeaponFatigueMult; + fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); + stats.setFatigue(fatigue); // TODO: where is the distance defined? - std::pair result = MWBase::Environment::get().getWorld()->getHitContact(ptr, 200); + float dist = 200.f; + if (!weapon.isEmpty()) + dist = 100.f * weapon.get()->mBase->mData.mReach; + + std::pair result = MWBase::Environment::get().getWorld()->getHitContact(ptr, dist); if (result.first.isEmpty()) return; // Didn't hit anything @@ -208,7 +238,6 @@ namespace MWClass Ogre::Vector3 hitPosition = result.second; - MWMechanics::CreatureStats &stats = getCreatureStats(ptr); MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); float hitchance = ref->mBase->mData.mCombat + @@ -243,8 +272,71 @@ namespace MWClass break; } + // I think this should be random, since attack1-3 animations don't have an attack strength like NPCs do float damage = min + (max - min) * ::rand()/(RAND_MAX+1.0); + if (!weapon.isEmpty()) + { + const bool weaphashealth = get(weapon).hasItemHealth(weapon); + const unsigned char *attack = NULL; + if(type == MWMechanics::CreatureStats::AT_Chop) + attack = weapon.get()->mBase->mData.mChop; + else if(type == MWMechanics::CreatureStats::AT_Slash) + attack = weapon.get()->mBase->mData.mSlash; + else if(type == MWMechanics::CreatureStats::AT_Thrust) + attack = weapon.get()->mBase->mData.mThrust; + if(attack) + { + float weaponDamage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength()); + weaponDamage *= 0.5f + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f); + if(weaphashealth) + { + int weapmaxhealth = weapon.get()->mBase->mData.mHealth; + if(weapon.getCellRef().mCharge == -1) + weapon.getCellRef().mCharge = weapmaxhealth; + weaponDamage *= float(weapon.getCellRef().mCharge) / weapmaxhealth; + } + + if (!MWBase::Environment::get().getWorld()->getGodModeState()) + weapon.getCellRef().mCharge -= std::min(std::max(1, + (int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weapon.getCellRef().mCharge); + + // Weapon broken? unequip it + if (weapon.getCellRef().mCharge == 0) + weapon = *getInventoryStore(ptr).unequipItem(weapon, ptr); + + damage += weaponDamage; + } + + // Apply "On hit" enchanted weapons + std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : ""; + if (!enchantmentName.empty()) + { + const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find( + enchantmentName); + if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes) + { + // Check if we have enough charges + const float enchantCost = enchantment->mData.mCost; + int eSkill = getSkill(ptr, ESM::Skill::Enchant); + const int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10)); + + if (weapon.getCellRef().mEnchantmentCharge == -1) + weapon.getCellRef().mEnchantmentCharge = enchantment->mData.mCharge; + if (weapon.getCellRef().mEnchantmentCharge < castCost) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}"); + } + else + { + weapon.getCellRef().mEnchantmentCharge -= castCost; + MWMechanics::CastSpell cast(ptr, victim); + cast.cast(weapon); + } + } + } + } + // TODO: do not do this if the attack is blocked MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); From 0ea2bb7c4cc835f969fdfe92bba153fd9f3bebdc Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 19 Jan 2014 16:49:39 +0100 Subject: [PATCH 587/889] Working on commands --- apps/opencs/model/world/commands.cpp | 21 +++++++++++++++++++++ apps/opencs/model/world/commands.hpp | 20 ++++++++++++++++++++ apps/opencs/model/world/idtable.cpp | 8 ++++++++ apps/opencs/model/world/idtable.hpp | 2 ++ apps/opencs/view/world/genericcreator.cpp | 2 ++ 5 files changed, 53 insertions(+) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 21feb14be..281afb15f 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -25,6 +25,27 @@ void CSMWorld::ModifyCommand::undo() mModel.setData (mIndex, mOld); } +CSMWorld::CloneCommand::CloneCommand (CSMWorld::IdTable& model, + const std::string& idOrigin, + const std::string& IdDestination, + CSMWorld::UniversalId::Type type, + QUndoCommand* parent) : + QUndoCommand(parent), + mModel(model), + mIdOrigin(idOrigin), + mIdDestination(IdDestination), + mType(type) +{ + setText (("Clone record " + idOrigin + " to the " + IdDestination).c_str()); +} + + +void CSMWorld::CloneCommand::redo() +{ + mModel.cloneRecord(mIdOrigin, mIdDestination, mType); +} + + CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent) : QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None) { diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index f0480a369..1af4bd919 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -39,6 +39,26 @@ namespace CSMWorld virtual void undo(); }; + class CloneCommand : public QUndoCommand + { + IdTable& mModel; + std::string mIdOrigin; + std::string mIdDestination; + UniversalId::Type mType; + std::map mValues; + + public: + + CloneCommand (IdTable& model, const std::string& idOrigin, + const std::string& IdDestination, + UniversalId::Type type, + QUndoCommand* parent = 0); + + virtual void redo(); + +// virtual void undo(); + }; + class CreateCommand : public QUndoCommand { IdTable& mModel; diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 809d64339..b90bf1a9b 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -124,6 +124,14 @@ void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type endInsertRows(); } +void CSMWorld::IdTable::cloneRecord(const std::string& origin, + const std::string& destination, + CSMWorld::UniversalId::Type type) +{ + +} + + QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const { return index (mIdCollection->getIndex (id), column); diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index e4ae58fd0..dfbc04134 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -63,6 +63,8 @@ namespace CSMWorld void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None); ///< \param type Will be ignored, unless the collection supports multiple record types + void cloneRecord(const std::string& origin, const std::string& destination, UniversalId::Type type = UniversalId::Type_None); + QModelIndex getModelIndex (const std::string& id, int column) const; void setRecord (const std::string& id, const RecordBase& record); diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index b89c2dccf..f21a62d97 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -125,6 +125,8 @@ void CSVWorld::GenericCreator::create() if (mCloneMode) { std::string id = getId(); + std::auto_ptr command (new CSMWorld::CloneCommand ( + dynamic_cast (*mData.getTableModel(mListId)), mClonedId, id, mClonedType)); } else { std::string id = getId(); From bea161331c2f1e4d173c8ad70d1125ce3bbd4970 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 17:03:25 +0100 Subject: [PATCH 588/889] Play 'hello' voiced dialogue entries in AIWander --- apps/openmw/mwmechanics/aiwander.cpp | 34 ++++++++++++++++++++++++++++ apps/openmw/mwmechanics/aiwander.hpp | 2 ++ 2 files changed, 36 insertions(+) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 9a78f1acd..185805153 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -7,6 +7,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.hpp" + #include "creaturestats.hpp" #include @@ -31,6 +32,7 @@ namespace MWMechanics , mX(0) , mY(0) , mZ(0) + , mSaidGreeting(false) { for(unsigned short counter = 0; counter < mIdle.size(); counter++) { @@ -197,6 +199,38 @@ namespace MWMechanics if(mIdleNow) { + // Play a random voice greeting if the player gets too close + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + + float hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified(); + float helloDistance = hello; + int iGreetDistanceMultiplier = store.get().find("iGreetDistanceMultiplier")->getInt(); + helloDistance *= iGreetDistanceMultiplier; + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + float playerDist = Ogre::Vector3(player.getRefData().getPosition().pos).distance( + Ogre::Vector3(actor.getRefData().getPosition().pos)); + + if (!mSaidGreeting) + { + // TODO: check if actor is aware / has line of sight + if (playerDist <= helloDistance + // Only play a greeting if the player is not moving + && Ogre::Vector3(player.getClass().getMovementSettings(player).mPosition).squaredLength() == 0) + { + mSaidGreeting = true; + MWBase::Environment::get().getDialogueManager()->say(actor, "hello"); + // TODO: turn to face player and interrupt the idle animation? + } + } + else + { + float fGreetDistanceReset = store.get().find("fGreetDistanceReset")->getFloat(); + if (playerDist >= fGreetDistanceReset * iGreetDistanceMultiplier) + mSaidGreeting = false; + } + + // Check if idle animation finished if(!checkIdle(actor, mPlayedIdle)) { mPlayedIdle = 0; diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 48bc62c62..9a44aa065 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -32,6 +32,8 @@ namespace MWMechanics std::vector mIdle; bool mRepeat; + bool mSaidGreeting; + float mX; float mY; float mZ; From 05e75e121432923ab94ca8fab39a8c266aa674a6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 20:27:33 +0100 Subject: [PATCH 589/889] Closes #1127: Fix for subtitles for idle dialogue appearing outside of hearing range --- apps/openmw/mwmechanics/aiwander.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 185805153..77b52f5be 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -191,8 +191,11 @@ namespace MWMechanics const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); float chance = store.get().find("fVoiceIdleOdds")->getFloat(); int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] - // TODO: do not show subtitle messagebox if player is too far away? or do not say at all? - if (roll < chance) + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + + // Don't bother if the player is out of hearing range + if (roll < chance && Ogre::Vector3(player.getRefData().getPosition().pos).distance(Ogre::Vector3(actor.getRefData().getPosition().pos)) < 1500) MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); } } From 339399f8b1dada292ccb038e4b7f039aecfc122d Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sun, 19 Jan 2014 22:09:51 +0200 Subject: [PATCH 590/889] bug fix/logic fix/future suggestion --- apps/openmw/mwmechanics/aicombat.cpp | 27 +++++++++++++++---------- apps/openmw/mwmechanics/aicombat.hpp | 2 +- apps/openmw/mwmechanics/character.cpp | 2 +- apps/openmw/mwmechanics/pathfinding.cpp | 17 ++++++++++++++++ apps/openmw/mwmechanics/pathfinding.hpp | 5 +++++ 5 files changed, 40 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 6bf97f6ac..f12a10f9b 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -1,4 +1,5 @@ #include "aicombat.hpp" +#include "aifollow.hpp" #include "movement.hpp" @@ -39,7 +40,7 @@ namespace MWMechanics mTimerAttack(0), mTimerReact(0), mTimerCombatMove(0), - mCloseUp(false), + mFollowTarget(false), mReadyToAttack(false), mStrike(false), mCombatMove(false), @@ -184,7 +185,7 @@ namespace MWMechanics Ogre::Vector3 vDir = vDest - vStart; float distBetween = vDir.length(); - if(distBetween < rangeMelee || (distBetween <= rangeCloseUp && mCloseUp) ) + if(distBetween < rangeMelee || (distBetween <= rangeCloseUp && mFollowTarget) ) { //Melee and Close-up combat vDir.z = 0; @@ -194,13 +195,10 @@ namespace MWMechanics // TODO: use movement settings instead of rotating directly MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); - if(mPathFinder.isPathConstructed()) - mPathFinder.clearPath(); - //MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; //bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget); - if (mCloseUp && distBetween > rangeMelee) + if (mFollowTarget && distBetween > rangeMelee) { //Close-up combat: just run up on target mMovement.mPosition[1] = 1; @@ -216,7 +214,7 @@ namespace MWMechanics mTimerCombatMove = 0.1f + 0.1f * static_cast(rand())/RAND_MAX; mCombatMove = true; } - else if(!distantCombat || (distantCombat && rangeMelee/5)) + else if(actor.getClass().isNpc() && (!distantCombat || (distantCombat && rangeMelee/5))) { //apply sideway movement (kind of dodging) with some probability if(static_cast(rand())/RAND_MAX < 0.25) @@ -234,13 +232,19 @@ namespace MWMechanics mReadyToAttack = true; //only once got in melee combat, actor is allowed to use close-up shortcutting - mCloseUp = true; + mFollowTarget = true; } } else { - //target is at far distance: build & follow the path - mCloseUp = false; + //target is at far distance: build path to target OR follow target (if previously actor had reached it once) + + /* + //apply when AIFOLLOW package implementation will be existent + if(mFollowTarget) + actor.getClass().getCreatureStats(actor).getAiSequence().stack(AiFollow(mTarget));*/ + + mFollowTarget = false; buildNewPath(actor); @@ -342,7 +346,8 @@ namespace MWMechanics //maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path, //not the actual path length. Here we should know if the new path is actually more effective. //if(pathFinder2.getPathSize() < mPathFinder.getPathSize()) - mPathFinder = newPathFinder; + newPathFinder.syncStart(mPathFinder.getPath()); + mPathFinder = newPathFinder; } } } diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index cb9eee973..27f7f5d95 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -37,7 +37,7 @@ namespace MWMechanics float mTimerCombatMove; bool mReadyToAttack, mStrike; - bool mCloseUp; + bool mFollowTarget; bool mCombatMove; MWMechanics::Movement mMovement; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 0ffeb52e5..967f7413a 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -504,7 +504,7 @@ bool CharacterController::updateCreatureState() } mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, true, + MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); mUpperBodyState = UpperCharState_StartToMinAttack; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index c8bc9b49c..fd44fcc4c 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -4,6 +4,7 @@ #include "../mwbase/environment.hpp" #include "OgreMath.h" +#include "OgreVector3.h" #include #include @@ -233,5 +234,21 @@ namespace MWMechanics return false; } + + void PathFinder::syncStart(const std::list &path) + { + std::list::const_iterator oldStart = path.begin(); + std::list::iterator iter = ++mPath.begin(); + + if( (*iter).mX == oldStart->mX + && (*iter).mY == oldStart->mY + && (*iter).mZ == oldStart->mZ + && (*iter).mAutogenerated == oldStart->mAutogenerated + && (*iter).mConnectionNum == oldStart->mConnectionNum ) + { + mPath.pop_front(); + } + + } } diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index 916df850b..de58f5db8 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -37,6 +37,11 @@ namespace MWMechanics return mPath; } + //When first point of newly created path is the nearest to actor point, then + //the cituation can occure when this point is undesirable (if the 2nd point of new path == the 1st point of old path) + //This functions deletes that point. + void syncStart(const std::list &path); + private: std::list mPath; bool mIsPathConstructed; From fec26342cd57269bc0801e5d901e00419fbc7cec Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Jan 2014 21:11:48 +0100 Subject: [PATCH 591/889] Query the number of animation groups for death/hit animations, since they can vary a lot (argonian/khajiit, creatures, first person..) --- apps/openmw/mwmechanics/character.cpp | 64 +++++++++------------------ apps/openmw/mwmechanics/character.hpp | 4 ++ 2 files changed, 24 insertions(+), 44 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index ba6cb6449..3f29cbb7c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -62,26 +62,6 @@ struct StateInfo { const char groupname[32]; }; -static const std::string sDeathList[] = { - "death1" , - "death2" , - "death3" , - "death4" , - "death5" , - "swimdeath", -}; -static const int sDeathListSize = sizeof(sDeathList)/sizeof(sDeathList[0]); - -static const std::string sHitList[] = { - "hit1" , - "hit2" , - "hit3" , - "hit4" , - "hit5" , - "knockdown" , -}; -static const int sHitListSize = sizeof(sHitList)/sizeof(sHitList[0]); - static const StateInfo sMovementList[] = { { CharState_WalkForward, "walkforward" }, { CharState_WalkBack, "walkback" }, @@ -154,6 +134,17 @@ public: { return weap.type == type; } }; +std::string CharacterController::chooseRandomGroup (const std::string& prefix, int* num) +{ + int numAnims=0; + while (mAnimation->hasAnimation(prefix + Ogre::StringConverter::toString(numAnims+1))) + ++numAnims; + + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * numAnims + 1; // [1, numAnims] + if (num) + *num = roll; + return prefix + Ogre::StringConverter::toString(roll); +} void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force) { @@ -167,20 +158,13 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat if(knockdown) { mHitState = CharState_KnockDown; - mCurrentHit = sHitList[sHitListSize-1]; + mCurrentHit = "knockdown"; mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } else if (recovery) { mHitState = CharState_Hit; - int iHit = rand() % (sHitListSize-1); - mCurrentHit = sHitList[iHit]; - if(mPtr.getRefData().getHandle()=="player" && !mAnimation->hasAnimation(mCurrentHit)) - { - //only 3 different hit animations if player is in 1st person - int iHit = rand() % (sHitListSize-3); - mCurrentHit = sHitList[iHit]; - } + mCurrentHit = chooseRandomGroup("hit"); mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } } @@ -387,28 +371,20 @@ MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::I void CharacterController::playRandomDeath(float startpoint) { - if(MWWorld::Class::get(mPtr).isNpc()) + if(MWBase::Environment::get().getWorld()->isSwimming(mPtr) && mAnimation->hasAnimation("swimdeath")) { - if(MWBase::Environment::get().getWorld()->isSwimming(mPtr)) - { - mDeathState = CharState_SwimDeath; - mCurrentDeath = sDeathList[sDeathListSize-1]; //last in the list is 'swimdeath' - } - else - { - int num = rand() % (sDeathListSize-1); - mDeathState = static_cast(CharState_Death1 + num); - mCurrentDeath = sDeathList[num]; - } + mDeathState = CharState_SwimDeath; + mCurrentDeath = "swimdeath"; } else { - mDeathState = CharState_Death1; - mCurrentDeath = "death1"; + int selected=0; + mCurrentDeath = chooseRandomGroup("death", &selected); + mDeathState = static_cast(CharState_Death1 + (selected-1)); } mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, - false, 1.0f, "start", "stop", 0.0f, 0); + false, 1.0f, "start", "stop", startpoint, 0); } CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim) diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 1b73670f9..ea12d9b94 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -177,6 +177,10 @@ class CharacterController void playRandomDeath(float startpoint = 0.0f); + /// choose a random animation group with \a prefix and numeric suffix + /// @param num if non-NULL, the chosen animation number will be written here + std::string chooseRandomGroup (const std::string& prefix, int* num = NULL); + public: CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim); virtual ~CharacterController(); From 5dd3cfa09acdad237e507fd2063db5db9c9383b9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 11:50:52 +0100 Subject: [PATCH 592/889] Loop projectile sounds --- apps/openmw/mwworld/worldimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 46a1e9fda..2c825caa6 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2177,7 +2177,7 @@ namespace MWWorld state.mStack = stack; MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - sndMgr->playSound3D(ptr, sound, 1.0f, 1.0f); + sndMgr->playSound3D(ptr, sound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); mProjectiles[ptr] = state; } From ba5300b071b5e5e0f7ea8c9e1c8e06074d8601fd Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 12:05:13 +0100 Subject: [PATCH 593/889] Update the Ptr in SoundManager for references moved to a different cell. Fixes looping sounds not stopping after a moved object was already deleted. --- apps/openmw/mwbase/soundmanager.hpp | 2 ++ apps/openmw/mwsound/soundmanagerimp.cpp | 9 +++++++++ apps/openmw/mwsound/soundmanagerimp.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 1 + 4 files changed, 14 insertions(+) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index 4d764597c..98b733c41 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -147,6 +147,8 @@ namespace MWBase virtual void update(float duration) = 0; virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up) = 0; + + virtual void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated) = 0; }; } diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index bdd03a8b4..d39602516 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -640,6 +640,15 @@ namespace MWSound mListenerUp = up; } + void SoundManager::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &updated) + { + for (SoundMap::iterator snditer = mActiveSounds.begin(); snditer != mActiveSounds.end(); ++snditer) + { + if (snditer->second.first == old) + snditer->second.first = updated; + } + } + // Default readAll implementation, for decoders that can't do anything // better void Sound_Decoder::readAll(std::vector &output) diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index f62e62d50..4fd1d3d48 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -148,6 +148,8 @@ namespace MWSound virtual void update(float duration); virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up); + + virtual void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated); }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2c825caa6..9be7830cf 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -905,6 +905,7 @@ namespace MWWorld MWWorld::Class::get(ptr).copyToCell(ptr, newCell, pos); mRendering->updateObjectCell(ptr, copy); + MWBase::Environment::get().getSoundManager()->updatePtr (ptr, copy); MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); mechMgr->updateCell(ptr, copy); From 205e8aa4e95129db49032db48b2a8fc1c7cc34f1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 13:00:43 +0100 Subject: [PATCH 594/889] Feature #957: Implement area magic --- apps/openmw/mwbase/mechanicsmanager.hpp | 2 + apps/openmw/mwmechanics/actors.cpp | 9 ++ apps/openmw/mwmechanics/actors.hpp | 2 + .../mwmechanics/mechanicsmanagerimp.cpp | 6 ++ .../mwmechanics/mechanicsmanagerimp.hpp | 2 + apps/openmw/mwmechanics/objects.cpp | 9 ++ apps/openmw/mwmechanics/objects.hpp | 2 + apps/openmw/mwrender/effectmanager.cpp | 6 +- apps/openmw/mwrender/effectmanager.hpp | 4 +- apps/openmw/mwrender/renderingmanager.cpp | 6 +- apps/openmw/mwrender/renderingmanager.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 87 ++++++++++++++----- 12 files changed, 108 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 726c8cf04..914fbded2 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -157,6 +157,8 @@ namespace MWBase virtual void toggleAI() = 0; virtual bool isAIActive() = 0; + + virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector& objects) = 0; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index f7d5d3a1b..35783bd9f 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -919,4 +919,13 @@ namespace MWMechanics return iter->second->isAnimPlaying(groupName); return false; } + + void Actors::getObjectsInRange(const Ogre::Vector3& position, float radius, std::vector& out) + { + for (PtrControllerMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter) + { + if (Ogre::Vector3(iter->first.getRefData().getPosition().pos).squaredDistance(position) <= radius*radius) + out.push_back(iter->first); + } + } } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index b7544dad4..78aae6861 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -94,6 +94,8 @@ namespace MWMechanics void skipAnimation(const MWWorld::Ptr& ptr); bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName); + void getObjectsInRange(const Ogre::Vector3& position, float radius, std::vector& out); + private: PtrControllerMap mActors; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index fff3db8a9..a0183e973 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -950,4 +950,10 @@ namespace MWMechanics return (roll >= target); } + + void MechanicsManager::getObjectsInRange(const Ogre::Vector3 &position, float radius, std::vector &objects) + { + mActors.getObjectsInRange(position, radius, objects); + mObjects.getObjectsInRange(position, radius, objects); + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 469123df9..012de2e32 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -135,6 +135,8 @@ namespace MWMechanics virtual void toggleAI(); virtual bool isAIActive(); + + virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector& objects); }; } diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp index 41d6b4ffa..b09574923 100644 --- a/apps/openmw/mwmechanics/objects.cpp +++ b/apps/openmw/mwmechanics/objects.cpp @@ -92,4 +92,13 @@ void Objects::skipAnimation(const MWWorld::Ptr& ptr) iter->second->skipAnim(); } +void Objects::getObjectsInRange(const Ogre::Vector3& position, float radius, std::vector& out) +{ + for (PtrControllerMap::iterator iter = mObjects.begin(); iter != mObjects.end(); ++iter) + { + if (Ogre::Vector3(iter->first.getRefData().getPosition().pos).squaredDistance(position) <= radius*radius) + out.push_back(iter->first); + } +} + } diff --git a/apps/openmw/mwmechanics/objects.hpp b/apps/openmw/mwmechanics/objects.hpp index 32432c130..373a2a105 100644 --- a/apps/openmw/mwmechanics/objects.hpp +++ b/apps/openmw/mwmechanics/objects.hpp @@ -40,6 +40,8 @@ namespace MWMechanics void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); void skipAnimation(const MWWorld::Ptr& ptr); + + void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector& out); }; } diff --git a/apps/openmw/mwrender/effectmanager.cpp b/apps/openmw/mwrender/effectmanager.cpp index eb4525a4f..7d41525b7 100644 --- a/apps/openmw/mwrender/effectmanager.cpp +++ b/apps/openmw/mwrender/effectmanager.cpp @@ -26,9 +26,10 @@ EffectManager::EffectManager(Ogre::SceneManager *sceneMgr) { } -void EffectManager::addEffect(const std::string &model, std::string textureOverride, const Ogre::Vector3 &worldPosition) +void EffectManager::addEffect(const std::string &model, std::string textureOverride, const Ogre::Vector3 &worldPosition, float scale) { Ogre::SceneNode* sceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(worldPosition); + sceneNode->setScale(scale,scale,scale); // fix texture extension to .dds if (textureOverride.size() > 4) @@ -78,7 +79,7 @@ void EffectManager::addEffect(const std::string &model, std::string textureOverr mEffects.push_back(std::make_pair(sceneNode, scene)); } -void EffectManager::update(float dt) +void EffectManager::update(float dt, Ogre::Camera* camera) { for (std::vector >::iterator it = mEffects.begin(); it != mEffects.end(); ) { @@ -91,6 +92,7 @@ void EffectManager::update(float dt) objects->mControllers[i].update(); } + objects->rotateBillboardNodes(camera); // Finished playing? if (objects->mControllers[0].getSource()->getValue() >= objects->mMaxControllerLength) diff --git a/apps/openmw/mwrender/effectmanager.hpp b/apps/openmw/mwrender/effectmanager.hpp index 0c8bc3857..bc9e68d26 100644 --- a/apps/openmw/mwrender/effectmanager.hpp +++ b/apps/openmw/mwrender/effectmanager.hpp @@ -14,9 +14,9 @@ namespace MWRender ~EffectManager() { clear(); } /// Add an effect. When it's finished playing, it will be removed automatically. - void addEffect (const std::string& model, std::string textureOverride, const Ogre::Vector3& worldPosition); + void addEffect (const std::string& model, std::string textureOverride, const Ogre::Vector3& worldPosition, float scale); - void update(float dt); + void update(float dt, Ogre::Camera* camera); /// Remove all effects void clear(); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 572d2abdb..515112668 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -378,7 +378,7 @@ void RenderingManager::update (float duration, bool paused) if(paused) return; - mEffectManager->update(duration); + mEffectManager->update(duration, mRendering.getCamera()); mActors->update (mRendering.getCamera()); mPlayerAnimation->preRender(mRendering.getCamera()); @@ -1024,9 +1024,9 @@ float RenderingManager::getCameraDistance() const return mCamera->getCameraDistance(); } -void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const Vector3 &worldPosition) +void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const Vector3 &worldPosition, float scale) { - mEffectManager->addEffect(model, texture, worldPosition); + mEffectManager->addEffect(model, "", worldPosition, scale); } } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index b6379bee4..ea9a64120 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -207,7 +207,7 @@ public: void stopVideo(); void frameStarted(float dt, bool paused); - void spawnEffect (const std::string& model, const std::string& texture, const Ogre::Vector3& worldPosition); + void spawnEffect (const std::string& model, const std::string& texture, const Ogre::Vector3& worldPosition, float scale=1.f); protected: virtual void windowResized(int x, int y); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9be7830cf..ccb16d5fb 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2174,9 +2174,16 @@ namespace MWWorld state.mId = id; state.mActorHandle = actor.getRefData().getHandle(); state.mSpeed = speed; - state.mEffects = effects; state.mStack = stack; + // Only interested in "on target" effects + for (std::vector::const_iterator iter (effects.mList.begin()); + iter!=effects.mList.end(); ++iter) + { + if (iter->mRange == ESM::RT_Target) + state.mEffects.mList.push_back(*iter); + } + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); sndMgr->playSound3D(ptr, sound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); @@ -2223,30 +2230,68 @@ namespace MWWorld continue; explode = true; - - MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle); - if (caster.isEmpty()) - caster = obstacle; - if (obstacle.isEmpty()) - { - // Terrain - } - else - { - MWMechanics::CastSpell cast(caster, obstacle); - cast.mStack = it->second.mStack; - cast.mId = it->second.mId; - cast.mSourceName = it->second.mSourceName; - cast.inflict(obstacle, caster, it->second.mEffects, ESM::RT_Target, false); - } - - deleteObject(ptr); - mProjectiles.erase(it++); } if (explode) { - // TODO: Explode + std::map > toApply; + for (std::vector::const_iterator effectIt = it->second.mEffects.mList.begin(); + effectIt != it->second.mEffects.mList.end(); ++effectIt) + { + const ESM::MagicEffect* effect = getStore().get().find(effectIt->mEffectID); + + // Spawn the explosion orb effect + const ESM::Static* areaStatic; + if (!effect->mCasting.empty()) + areaStatic = getStore().get().find (effect->mArea); + else + areaStatic = getStore().get().find ("VFX_DefaultArea"); + + mRendering->spawnEffect("meshes\\" + areaStatic->mModel, "", Ogre::Vector3(ptr.getRefData().getPosition().pos), effectIt->mArea); + + // Play explosion sound (make sure to use NoTrack, since we will delete the projectile now) + static const std::string schools[] = { + "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" + }; + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + if(!effect->mAreaSound.empty()) + sndMgr->playSound3D(ptr, effect->mAreaSound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); + else + sndMgr->playSound3D(ptr, schools[effect->mData.mSchool]+" area", 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); + + // Get the actors in range of the effect + std::vector objects; + MWBase::Environment::get().getMechanicsManager()->getObjectsInRange( + Ogre::Vector3(ptr.getRefData().getPosition().pos), feetToGameUnits(effectIt->mArea), objects); + + for (std::vector::iterator affected = objects.begin(); affected != objects.end(); ++affected) + toApply[*affected].push_back(*effectIt); + } + + // Now apply the appropriate effects to each actor in range + for (std::map >::iterator apply = toApply.begin(); apply != toApply.end(); ++apply) + { + MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle); + + // Vanilla-compatible behaviour of never applying the spell to the caster + // (could be changed by mods later) + if (apply->first == caster) + continue; + + if (caster.isEmpty()) + caster = apply->first; + + MWMechanics::CastSpell cast(caster, apply->first); + cast.mId = it->second.mId; + cast.mSourceName = it->second.mSourceName; + cast.mStack = it->second.mStack; + ESM::EffectList effects; + effects.mList = apply->second; + cast.inflict(apply->first, caster, effects, ESM::RT_Target); + } + + deleteObject(ptr); + mProjectiles.erase(it++); continue; } From 9cedd3bb2ee8c67b42b57103ef40997ff54e271f Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 13:05:38 +0100 Subject: [PATCH 595/889] Fix spell area not displaying in tooltip --- apps/openmw/mwgui/tooltips.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index b52c8e3dd..0fe500879 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -211,6 +211,7 @@ namespace MWGui params.mMagnMin = it->mMagnMin; params.mMagnMax = it->mMagnMax; params.mRange = it->mRange; + params.mArea = it->mArea; params.mIsConstant = (spell->mData.mType == ESM::Spell::ST_Ability); params.mNoTarget = false; effects.push_back(params); From bd34b61f2ae2037b4d701cbb4ad0a4cf83d56e7a Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 13:37:34 +0100 Subject: [PATCH 596/889] Set all keyframe-controlled bones as manually controlled --- components/nifogre/ogrenifloader.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 5a76b702e..d036844fc 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -917,6 +917,8 @@ class NIFObjectLoader { int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); + // The keyframe controller will control this bone manually + trgtbone->setManuallyControlled(true); Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : Ogre::ControllerValueRealPtr()); From 42284603f7624337ad900a338e69b1fe89b50dae Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 13:43:21 +0100 Subject: [PATCH 597/889] Properly handle on target effects with mArea=0 --- apps/openmw/mwworld/worldimp.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ccb16d5fb..419558387 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2229,6 +2229,23 @@ namespace MWWorld if (obstacle == ptr) continue; + MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle); + if (caster.isEmpty()) + caster = obstacle; + + if (obstacle.isEmpty()) + { + // Terrain + } + else + { + MWMechanics::CastSpell cast(caster, obstacle); + cast.mId = it->second.mId; + cast.mSourceName = it->second.mSourceName; + cast.mStack = it->second.mStack; + cast.inflict(obstacle, caster, it->second.mEffects, ESM::RT_Target); + } + explode = true; } @@ -2240,6 +2257,9 @@ namespace MWWorld { const ESM::MagicEffect* effect = getStore().get().find(effectIt->mEffectID); + if (effectIt->mArea <= 0) + continue; // Not an area effect + // Spawn the explosion orb effect const ESM::Static* areaStatic; if (!effect->mCasting.empty()) From c8a9e9f7fa467d54971237f653c5e095b1cc6978 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 13:54:10 +0100 Subject: [PATCH 598/889] Stop AiCombat when the target is dead --- apps/openmw/mwmechanics/aicombat.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 4053e640f..c01c4096b 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -52,7 +52,7 @@ namespace MWMechanics //General description if(!actor.getClass().getCreatureStats(actor).isHostile()) return true; - if(actor.getClass().getCreatureStats(actor).getHealth().getCurrent() <= 0) + if (mTarget.getClass().getCreatureStats(mTarget).isDead()) return true; //Update every frame From 33620a001b802ed8c8ae8c61db9afcc4619ad20b Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 20 Jan 2014 13:59:00 +0100 Subject: [PATCH 599/889] Cloning works for single record type tables. Well, kinda. --- apps/opencs/model/world/collection.hpp | 27 ++++- apps/opencs/model/world/collectionbase.hpp | 5 + apps/opencs/model/world/commands.cpp | 126 +++++++++++--------- apps/opencs/model/world/commands.hpp | 6 +- apps/opencs/model/world/idtable.cpp | 5 +- apps/opencs/model/world/idtable.hpp | 5 +- apps/opencs/model/world/refidcollection.cpp | 8 ++ apps/opencs/model/world/refidcollection.hpp | 5 + apps/opencs/view/world/creator.hpp | 2 +- apps/opencs/view/world/genericcreator.cpp | 15 ++- apps/opencs/view/world/genericcreator.hpp | 6 +- apps/opencs/view/world/tablebottombox.cpp | 6 +- apps/opencs/view/world/tablebottombox.hpp | 2 +- apps/opencs/view/world/tablesubview.cpp | 6 +- apps/opencs/view/world/tablesubview.hpp | 4 +- 15 files changed, 150 insertions(+), 78 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index df144ba7b..0b9b44fa9 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -97,7 +97,12 @@ namespace CSMWorld virtual void appendBlankRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None); ///< \param type Will be ignored, unless the collection supports multiple record types - + + virtual void cloneRecord(const std::string& origin, + const std::string& destination, + const UniversalId::Type type, + const UniversalId::ArgumentType argumentType); + virtual int searchId (const std::string& id) const; ////< Search record with \a id. /// \return index of record (if found) or -1 (not found) @@ -193,6 +198,26 @@ namespace CSMWorld return true; } + template + void Collection::cloneRecord(const std::string& origin, + const std::string& destination, + const UniversalId::Type type, + const UniversalId::ArgumentType argumentType) + { + Record copy = getRecord(origin); + if (copy.isDeleted()) + { + return; + } + + if (argumentType == UniversalId::ArgumentType_Id) + { + copy.get().mId = Misc::StringUtils::lowerCase(destination); + } + + insertRecord(copy, getAppendIndex(destination, type)); + } + template Collection::Collection() {} diff --git a/apps/opencs/model/world/collectionbase.hpp b/apps/opencs/model/world/collectionbase.hpp index ab7a9ebec..d32847490 100644 --- a/apps/opencs/model/world/collectionbase.hpp +++ b/apps/opencs/model/world/collectionbase.hpp @@ -74,6 +74,11 @@ namespace CSMWorld UniversalId::Type type = UniversalId::Type_None) = 0; ///< If the record type does not match, an exception is thrown. + virtual void cloneRecord(const std::string& origin, + const std::string& destination, + const UniversalId::Type type, + const UniversalId::ArgumentType argumentType) = 0; + virtual const RecordBase& getRecord (const std::string& id) const = 0; virtual const RecordBase& getRecord (int index) const = 0; diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 281afb15f..75b88766a 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -6,81 +6,88 @@ #include "idtable.hpp" #include "idtable.hpp" -CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, - const QVariant& new_, QUndoCommand *parent) -: QUndoCommand (parent), mModel (model), mIndex (index), mNew (new_) +CSMWorld::ModifyCommand::ModifyCommand(QAbstractItemModel& model, const QModelIndex& index, + const QVariant& new_, QUndoCommand* parent) + : QUndoCommand(parent), mModel(model), mIndex(index), mNew(new_) { - mOld = mModel.data (mIndex, Qt::EditRole); + mOld = mModel.data(mIndex, Qt::EditRole); - setText ("Modify " + mModel.headerData (mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); + setText("Modify " + mModel.headerData(mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); } void CSMWorld::ModifyCommand::redo() { - mModel.setData (mIndex, mNew); + mModel.setData(mIndex, mNew); } void CSMWorld::ModifyCommand::undo() { - mModel.setData (mIndex, mOld); + mModel.setData(mIndex, mOld); } -CSMWorld::CloneCommand::CloneCommand (CSMWorld::IdTable& model, - const std::string& idOrigin, - const std::string& IdDestination, - CSMWorld::UniversalId::Type type, - QUndoCommand* parent) : - QUndoCommand(parent), - mModel(model), - mIdOrigin(idOrigin), +CSMWorld::CloneCommand::CloneCommand(CSMWorld::IdTable& model, + const std::string& idOrigin, + const std::string& IdDestination, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumentType, + QUndoCommand* parent) : + QUndoCommand(parent), + mModel(model), + mIdOrigin(idOrigin), mIdDestination(IdDestination), + mArgumentType(argumentType), mType(type) { - setText (("Clone record " + idOrigin + " to the " + IdDestination).c_str()); + setText(("Clone record " + idOrigin + " to the " + IdDestination).c_str()); } void CSMWorld::CloneCommand::redo() { - mModel.cloneRecord(mIdOrigin, mIdDestination, mType); + mModel.cloneRecord(mIdOrigin, mIdDestination, mArgumentType, mType); } - -CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent) -: QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None) +void CSMWorld::CloneCommand::undo() { - setText (("Create record " + id).c_str()); + mModel.removeRow(mModel.getModelIndex(mIdDestination, 0).row()); } -void CSMWorld::CreateCommand::addValue (int column, const QVariant& value) + +CSMWorld::CreateCommand::CreateCommand(IdTable& model, const std::string& id, QUndoCommand* parent) + : QUndoCommand(parent), mModel(model), mId(id), mType(UniversalId::Type_None) +{ + setText(("Create record " + id).c_str()); +} + +void CSMWorld::CreateCommand::addValue(int column, const QVariant& value) { mValues[column] = value; } -void CSMWorld::CreateCommand::setType (UniversalId::Type type) +void CSMWorld::CreateCommand::setType(UniversalId::Type type) { mType = type; } void CSMWorld::CreateCommand::redo() { - mModel.addRecord (mId, mType); + mModel.addRecord(mId, mType); - for (std::map::const_iterator iter (mValues.begin()); iter!=mValues.end(); ++iter) - mModel.setData (mModel.getModelIndex (mId, iter->first), iter->second); + for (std::map::const_iterator iter(mValues.begin()); iter != mValues.end(); ++iter) + mModel.setData(mModel.getModelIndex(mId, iter->first), iter->second); } void CSMWorld::CreateCommand::undo() { - mModel.removeRow (mModel.getModelIndex (mId, 0).row()); + mModel.removeRow(mModel.getModelIndex(mId, 0).row()); } -CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand *parent) -: QUndoCommand (parent), mModel (model), mId (id), mOld (0) +CSMWorld::RevertCommand::RevertCommand(IdTable& model, const std::string& id, QUndoCommand* parent) + : QUndoCommand(parent), mModel(model), mId(id), mOld(0) { - setText (("Revert record " + id).c_str()); + setText(("Revert record " + id).c_str()); - mOld = model.getRecord (id).clone(); + mOld = model.getRecord(id).clone(); } CSMWorld::RevertCommand::~RevertCommand() @@ -90,32 +97,32 @@ CSMWorld::RevertCommand::~RevertCommand() void CSMWorld::RevertCommand::redo() { - int column = mModel.findColumnIndex (Columns::ColumnId_Modification); + int column = mModel.findColumnIndex(Columns::ColumnId_Modification); - QModelIndex index = mModel.getModelIndex (mId, column); - RecordBase::State state = static_cast (mModel.data (index).toInt()); + QModelIndex index = mModel.getModelIndex(mId, column); + RecordBase::State state = static_cast(mModel.data(index).toInt()); - if (state==RecordBase::State_ModifiedOnly) + if (state == RecordBase::State_ModifiedOnly) { - mModel.removeRows (index.row(), 1); + mModel.removeRows(index.row(), 1); } else { - mModel.setData (index, static_cast (RecordBase::State_BaseOnly)); + mModel.setData(index, static_cast(RecordBase::State_BaseOnly)); } } void CSMWorld::RevertCommand::undo() { - mModel.setRecord (mId, *mOld); + mModel.setRecord(mId, *mOld); } -CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand *parent) -: QUndoCommand (parent), mModel (model), mId (id), mOld (0) +CSMWorld::DeleteCommand::DeleteCommand(IdTable& model, const std::string& id, QUndoCommand* parent) + : QUndoCommand(parent), mModel(model), mId(id), mOld(0) { - setText (("Delete record " + id).c_str()); + setText(("Delete record " + id).c_str()); - mOld = model.getRecord (id).clone(); + mOld = model.getRecord(id).clone(); } CSMWorld::DeleteCommand::~DeleteCommand() @@ -125,44 +132,45 @@ CSMWorld::DeleteCommand::~DeleteCommand() void CSMWorld::DeleteCommand::redo() { - int column = mModel.findColumnIndex (Columns::ColumnId_Modification); + int column = mModel.findColumnIndex(Columns::ColumnId_Modification); - QModelIndex index = mModel.getModelIndex (mId, column); - RecordBase::State state = static_cast (mModel.data (index).toInt()); + QModelIndex index = mModel.getModelIndex(mId, column); + RecordBase::State state = static_cast(mModel.data(index).toInt()); - if (state==RecordBase::State_ModifiedOnly) + if (state == RecordBase::State_ModifiedOnly) { - mModel.removeRows (index.row(), 1); + mModel.removeRows(index.row(), 1); } else { - mModel.setData (index, static_cast (RecordBase::State_Deleted)); + mModel.setData(index, static_cast(RecordBase::State_Deleted)); } } void CSMWorld::DeleteCommand::undo() { - mModel.setRecord (mId, *mOld); + mModel.setRecord(mId, *mOld); } -CSMWorld::ReorderRowsCommand::ReorderRowsCommand (IdTable& model, int baseIndex, - const std::vector& newOrder) -: mModel (model), mBaseIndex (baseIndex), mNewOrder (newOrder) +CSMWorld::ReorderRowsCommand::ReorderRowsCommand(IdTable& model, int baseIndex, + const std::vector& newOrder) + : mModel(model), mBaseIndex(baseIndex), mNewOrder(newOrder) {} void CSMWorld::ReorderRowsCommand::redo() { - mModel.reorderRows (mBaseIndex, mNewOrder); + mModel.reorderRows(mBaseIndex, mNewOrder); } void CSMWorld::ReorderRowsCommand::undo() { - int size = static_cast (mNewOrder.size()); - std::vector reverse (size); + int size = static_cast(mNewOrder.size()); + std::vector reverse(size); - for (int i=0; i mValues; public: CloneCommand (IdTable& model, const std::string& idOrigin, const std::string& IdDestination, - UniversalId::Type type, + const UniversalId::Type type, + const UniversalId::ArgumentType argumentType, QUndoCommand* parent = 0); virtual void redo(); -// virtual void undo(); + virtual void undo(); }; class CreateCommand : public QUndoCommand diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index b90bf1a9b..daa6bc51b 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -124,11 +124,12 @@ void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type endInsertRows(); } -void CSMWorld::IdTable::cloneRecord(const std::string& origin, +void CSMWorld::IdTable::cloneRecord(const std::string& origin, const std::string& destination, + CSMWorld::UniversalId::ArgumentType argumentType, CSMWorld::UniversalId::Type type) { - + mIdCollection->cloneRecord(origin, destination, type, argumentType); } diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index dfbc04134..ce47307e3 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -63,7 +63,10 @@ namespace CSMWorld void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None); ///< \param type Will be ignored, unless the collection supports multiple record types - void cloneRecord(const std::string& origin, const std::string& destination, UniversalId::Type type = UniversalId::Type_None); + void cloneRecord(const std::string& origin, + const std::string& destination, + UniversalId::ArgumentType argumentType, + UniversalId::Type type = UniversalId::Type_None); QModelIndex getModelIndex (const std::string& id, int column) const; diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 86a542c5c..896ee9010 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -449,6 +449,14 @@ void CSMWorld::RefIdCollection::replace (int index, const RecordBase& record) mData.getRecord (mData.globalToLocalIndex (index)).assign (record); } +void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin, + const std::string& destination, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumentType) +{ + +} + void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, UniversalId::Type type) { diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index 5ff4a70bf..4ad181004 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -69,6 +69,11 @@ namespace CSMWorld virtual void removeRows (int index, int count); + virtual void cloneRecord(const std::string& origin, + const std::string& destination, + const UniversalId::Type type, + const UniversalId::ArgumentType argumentType); + virtual void appendBlankRecord (const std::string& id, UniversalId::Type type); ///< \param type Will be ignored, unless the collection supports multiple record types diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index 58a5e21ad..5eaf3e178 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -25,7 +25,7 @@ namespace CSVWorld virtual void reset() = 0; - virtual void cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type) = 0; + virtual void cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType) = 0; virtual void setEditLock (bool locked) = 0; diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index f21a62d97..efdd9ecaf 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -59,7 +59,8 @@ const CSMWorld::UniversalId& CSVWorld::GenericCreator::getCollectionId() const CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id, bool relaxedIdRules) -: mData (data), mUndoStack (undoStack), mListId (id), mLocked (false), mCloneMode(false), mClonedType(CSMWorld::UniversalId::Type_None) +: mData (data), mUndoStack (undoStack), mListId (id), mLocked (false), mCloneMode(false), mClonedType(CSMWorld::UniversalId::Type_None), +mArgumentType(CSMWorld::UniversalId::ArgumentType_None) { mLayout = new QHBoxLayout; mLayout->setContentsMargins (0, 0, 0, 0); @@ -126,7 +127,12 @@ void CSVWorld::GenericCreator::create() { std::string id = getId(); std::auto_ptr command (new CSMWorld::CloneCommand ( - dynamic_cast (*mData.getTableModel(mListId)), mClonedId, id, mClonedType)); + dynamic_cast (*mData.getTableModel(mListId)), mClonedId, id, mClonedType, mArgumentType)); + + mUndoStack.push(command.release()); + + emit done(); + emit requestFocus(id); } else { std::string id = getId(); @@ -143,11 +149,14 @@ void CSVWorld::GenericCreator::create() } } -void CSVWorld::GenericCreator::cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type) +void CSVWorld::GenericCreator::cloneMode(const std::string& originid, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumentType) { mCloneMode = true; mClonedId = originid; mClonedType = type; + mArgumentType = argumentType; mId->setText(QString::fromStdString(mClonedId)); } diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index a1acb2e79..867595bb5 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -32,6 +32,7 @@ namespace CSVWorld bool mCloneMode; std::string mClonedId; CSMWorld::UniversalId::Type mClonedType; + CSMWorld::UniversalId::ArgumentType mArgumentType; protected: @@ -61,13 +62,14 @@ namespace CSVWorld virtual void reset(); - virtual void cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type); + virtual void cloneMode(const std::string& originid, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumentType); virtual std::string getErrors() const; ///< Return formatted error descriptions for the current state of the creator. if an empty /// string is returned, there is no error. - private slots: void textChanged (const QString& text); diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index b89f5180b..60a206d0c 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -158,10 +158,12 @@ void CSVWorld::TableBottomBox::createRequest() mCreating = true; } -void CSVWorld::TableBottomBox::cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type) +void CSVWorld::TableBottomBox::cloneRequest(const std::string& id, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumnetType) { mCreator->reset(); - mCreator->cloneMode(id, type); + mCreator->cloneMode(id, type, argumnetType); mLayout->setCurrentWidget(mCreator); setVisible (true); mCreating = true; diff --git a/apps/opencs/view/world/tablebottombox.hpp b/apps/opencs/view/world/tablebottombox.hpp index 58a6a5a2f..29afa2277 100644 --- a/apps/opencs/view/world/tablebottombox.hpp +++ b/apps/opencs/view/world/tablebottombox.hpp @@ -77,7 +77,7 @@ namespace CSVWorld /// \param modified Number of added and modified records void createRequest(); - void cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type); + void cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType); }; } diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 36200dbbc..7bb852cbe 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -50,8 +50,8 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); connect (mTable, SIGNAL (cloneRequest(int)), this, SLOT(cloneRequest(int))); - connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), - mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type))); + connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type, const CSMWorld::UniversalId::ArgumentType)), + mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type, const CSMWorld::UniversalId::ArgumentType))); } connect (mBottom, SIGNAL (requestFocus (const std::string&)), mTable, SLOT (requestFocus (const std::string&))); @@ -85,5 +85,5 @@ void CSVWorld::TableSubView::setStatusBar (bool show) void CSVWorld::TableSubView::cloneRequest(int row) { const CSMWorld::UniversalId& toClone(mTable->getUniversalId(row)); - emit cloneRequest(toClone.getId(), toClone.getType()); + emit cloneRequest(toClone.getId(), toClone.getType(), toClone.getArgumentType()); } diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 5b19110b6..01df204de 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -35,7 +35,9 @@ namespace CSVWorld virtual void setStatusBar (bool show); signals: - void cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type); + void cloneRequest(const std::string& id, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumentType); private slots: From a45339bbe6ecd68ab2d5704649582ad3e3cd06b1 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 20 Jan 2014 14:14:59 +0100 Subject: [PATCH 600/889] lower case in command, not in the collection --- apps/opencs/model/world/collection.hpp | 2 +- apps/opencs/model/world/commands.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 0b9b44fa9..091496926 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -212,7 +212,7 @@ namespace CSMWorld if (argumentType == UniversalId::ArgumentType_Id) { - copy.get().mId = Misc::StringUtils::lowerCase(destination); + copy.get().mId = destination; } insertRecord(copy, getAppendIndex(destination, type)); diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 75b88766a..f16117019 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -2,7 +2,7 @@ #include "commands.hpp" #include - +#include #include "idtable.hpp" #include "idtable.hpp" @@ -34,7 +34,7 @@ CSMWorld::CloneCommand::CloneCommand(CSMWorld::IdTable& model, QUndoCommand(parent), mModel(model), mIdOrigin(idOrigin), - mIdDestination(IdDestination), + mIdDestination(Misc::StringUtils::lowerCase(IdDestination)), mArgumentType(argumentType), mType(type) { From 38636fab9a60a442fce8f04de170827675194f6f Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 20 Jan 2014 14:29:47 +0100 Subject: [PATCH 601/889] added setId to adapter --- apps/opencs/model/world/commands.cpp | 3 +++ apps/opencs/model/world/refidadapter.hpp | 1 + apps/opencs/model/world/refidadapterimp.hpp | 8 ++++++++ 3 files changed, 12 insertions(+) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index f16117019..b7c88e872 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -45,6 +45,9 @@ CSMWorld::CloneCommand::CloneCommand(CSMWorld::IdTable& model, void CSMWorld::CloneCommand::redo() { mModel.cloneRecord(mIdOrigin, mIdDestination, mArgumentType, mType); + + for (std::map::const_iterator iter(mValues.begin()); iter != mValues.end(); ++iter) + mModel.setData(mModel.getModelIndex(mIdDestination, iter->first), iter->second); } void CSMWorld::CloneCommand::undo() diff --git a/apps/opencs/model/world/refidadapter.hpp b/apps/opencs/model/world/refidadapter.hpp index df0ceae70..0870a2d3e 100644 --- a/apps/opencs/model/world/refidadapter.hpp +++ b/apps/opencs/model/world/refidadapter.hpp @@ -31,6 +31,7 @@ namespace CSMWorld ///< If the data type does not match an exception is thrown. virtual std::string getId (const RecordBase& record) const = 0; + virtual void setId(RecordBase& record, const std::string& id) = 0; }; } diff --git a/apps/opencs/model/world/refidadapterimp.hpp b/apps/opencs/model/world/refidadapterimp.hpp index d5d84a8f7..bd509a86b 100644 --- a/apps/opencs/model/world/refidadapterimp.hpp +++ b/apps/opencs/model/world/refidadapterimp.hpp @@ -34,6 +34,8 @@ namespace CSMWorld BaseRefIdAdapter (UniversalId::Type type, const BaseColumns& base); virtual std::string getId (const RecordBase& record) const; + + virtual void setId (RecordBase& record, const std::string& id); virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) const; @@ -50,6 +52,12 @@ namespace CSMWorld : mType (type), mBase (base) {} + template + void BaseRefIdAdapter::setId (RecordBase& record, const std::string& id) + { + (dynamic_cast&> (record).get().mId) = id; + } + template std::string BaseRefIdAdapter::getId (const RecordBase& record) const { From 5a527157013f97bec71790f7bbbedc29e92779c5 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 20 Jan 2014 15:58:19 +0100 Subject: [PATCH 602/889] implemented check for deletion. But it seems flawed. --- apps/opencs/model/world/data.cpp | 21 +++++++++++++++++++++ apps/opencs/model/world/data.hpp | 2 ++ apps/opencs/view/world/genericcreator.cpp | 7 +++++-- apps/opencs/view/world/table.cpp | 3 ++- 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 04e35cdaa..8a7dc3971 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -657,6 +657,27 @@ int CSMWorld::Data::count (RecordBase::State state) const count (state, mReferenceables); } +bool CSMWorld::Data::recordDeleted(const std::string& id) const +{ + return + getGlobals().getRecord(id).isDeleted() || + getGmsts().getRecord(id).isDeleted() || + getSkills().getRecord(id).isDeleted() || + getClasses().getRecord(id).isDeleted() || + getFactions().getRecord(id).isDeleted() || + getRaces().getRecord(id).isDeleted() || + getSounds().getRecord(id).isDeleted() || + getScripts().getRecord(id).isDeleted() || + getRegions().getRecord(id).isDeleted() || + getBirthsigns().getRecord(id).isDeleted() || + getSpells().getRecord(id).isDeleted() || + getTopics().getRecord(id).isDeleted() || + getJournals().getRecord(id).isDeleted() || + getCells().getRecord(id).isDeleted() || + getReferenceables().getRecord(id).isDeleted(); +} + + void CSMWorld::Data::setDescription (const std::string& description) { mDescription = description; diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 152c3ac41..df51c936a 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -189,6 +189,8 @@ namespace CSMWorld void setAuthor (const std::string& author); std::string getAuthor() const; + + bool recordDeleted(const std::string& id) const; signals: diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index efdd9ecaf..16bd04ece 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -125,10 +125,13 @@ void CSVWorld::GenericCreator::create() { if (mCloneMode) { - std::string id = getId(); + const std::string id = getId(); + if (mData.recordDeleted(id)) + { + return; + } std::auto_ptr command (new CSMWorld::CloneCommand ( dynamic_cast (*mData.getTableModel(mListId)), mClonedId, id, mClonedType, mArgumentType)); - mUndoStack.push(command.release()); emit done(); diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 31a7d8b58..4577c40dc 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -308,9 +308,10 @@ void CSVWorld::Table::cloneRecord() if (!mEditLock) { QModelIndexList selectedRows = selectionModel()->selectedRows(); - if (selectedRows.size()==1) + { emit cloneRequest (selectedRows.begin()->row()); + } } } From d82f272e0546c9cb270dcc7f0951a7ead0489903 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 20 Jan 2014 16:17:49 +0100 Subject: [PATCH 603/889] Properly check if clone is deleted. --- apps/opencs/view/world/table.cpp | 2 +- apps/opencs/view/world/table.hpp | 2 +- apps/opencs/view/world/tablesubview.cpp | 11 +++++++---- apps/opencs/view/world/tablesubview.hpp | 7 ++++++- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 4577c40dc..02a93161c 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -310,7 +310,7 @@ void CSVWorld::Table::cloneRecord() QModelIndexList selectedRows = selectionModel()->selectedRows(); if (selectedRows.size()==1) { - emit cloneRequest (selectedRows.begin()->row()); + emit cloneRequest (selectedRows.begin()->row(), mModel); } } } diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index f4a5d3c9e..118bd3cf2 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -74,7 +74,7 @@ namespace CSVWorld /// \param modified Number of added and modified records void createRequest(); - void cloneRequest(int row); + void cloneRequest(int row, const CSMWorld::IdTable* table); private slots: diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 7bb852cbe..0cdc57f5f 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -6,7 +6,7 @@ #include "../../model/doc/document.hpp" #include "../filter/filterbox.hpp" - +#include "../../model/world/idtable.hpp" #include "table.hpp" #include "tablebottombox.hpp" #include "creator.hpp" @@ -49,7 +49,7 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D { connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); - connect (mTable, SIGNAL (cloneRequest(int)), this, SLOT(cloneRequest(int))); + connect (mTable, SIGNAL (cloneRequest(int, CSMWorld::IdTable*)), this, SLOT(cloneRequest(int, CSMWorld::IdTable*))); connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type, const CSMWorld::UniversalId::ArgumentType)), mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type, const CSMWorld::UniversalId::ArgumentType))); } @@ -82,8 +82,11 @@ void CSVWorld::TableSubView::setStatusBar (bool show) mBottom->setStatusBar (show); } -void CSVWorld::TableSubView::cloneRequest(int row) +void CSVWorld::TableSubView::cloneRequest(int row, const CSMWorld::IdTable* table) { const CSMWorld::UniversalId& toClone(mTable->getUniversalId(row)); - emit cloneRequest(toClone.getId(), toClone.getType(), toClone.getArgumentType()); + if (!(table->getRecord(toClone.getId()).isDeleted())) + { + emit cloneRequest(toClone.getId(), toClone.getType(), toClone.getArgumentType()); + } } diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 01df204de..650a098f6 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -5,6 +5,11 @@ class QModelIndex; +namespace CSMWorld +{ + class IdTable; +} + namespace CSMDoc { class Document; @@ -42,7 +47,7 @@ namespace CSVWorld private slots: void editRequest (int row); - void cloneRequest (int row); + void cloneRequest (int row, const CSMWorld::IdTable* table); }; } From 92ee252eef54b0e9072a39803087e81ccd7bff62 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 20 Jan 2014 16:23:34 +0100 Subject: [PATCH 604/889] Small correction. --- apps/opencs/model/world/data.cpp | 21 --------------------- apps/opencs/model/world/data.hpp | 2 -- apps/opencs/view/world/genericcreator.cpp | 7 ++----- apps/opencs/view/world/table.cpp | 1 + apps/opencs/view/world/table.hpp | 2 +- apps/opencs/view/world/tablesubview.cpp | 2 +- 6 files changed, 5 insertions(+), 30 deletions(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 8a7dc3971..04e35cdaa 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -657,27 +657,6 @@ int CSMWorld::Data::count (RecordBase::State state) const count (state, mReferenceables); } -bool CSMWorld::Data::recordDeleted(const std::string& id) const -{ - return - getGlobals().getRecord(id).isDeleted() || - getGmsts().getRecord(id).isDeleted() || - getSkills().getRecord(id).isDeleted() || - getClasses().getRecord(id).isDeleted() || - getFactions().getRecord(id).isDeleted() || - getRaces().getRecord(id).isDeleted() || - getSounds().getRecord(id).isDeleted() || - getScripts().getRecord(id).isDeleted() || - getRegions().getRecord(id).isDeleted() || - getBirthsigns().getRecord(id).isDeleted() || - getSpells().getRecord(id).isDeleted() || - getTopics().getRecord(id).isDeleted() || - getJournals().getRecord(id).isDeleted() || - getCells().getRecord(id).isDeleted() || - getReferenceables().getRecord(id).isDeleted(); -} - - void CSMWorld::Data::setDescription (const std::string& description) { mDescription = description; diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index df51c936a..152c3ac41 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -189,8 +189,6 @@ namespace CSMWorld void setAuthor (const std::string& author); std::string getAuthor() const; - - bool recordDeleted(const std::string& id) const; signals: diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 16bd04ece..efdd9ecaf 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -125,13 +125,10 @@ void CSVWorld::GenericCreator::create() { if (mCloneMode) { - const std::string id = getId(); - if (mData.recordDeleted(id)) - { - return; - } + std::string id = getId(); std::auto_ptr command (new CSMWorld::CloneCommand ( dynamic_cast (*mData.getTableModel(mListId)), mClonedId, id, mClonedType, mArgumentType)); + mUndoStack.push(command.release()); emit done(); diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 02a93161c..cc1f814dd 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -308,6 +308,7 @@ void CSVWorld::Table::cloneRecord() if (!mEditLock) { QModelIndexList selectedRows = selectionModel()->selectedRows(); + if (selectedRows.size()==1) { emit cloneRequest (selectedRows.begin()->row(), mModel); diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 118bd3cf2..af2d7877f 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -74,7 +74,7 @@ namespace CSVWorld /// \param modified Number of added and modified records void createRequest(); - void cloneRequest(int row, const CSMWorld::IdTable* table); + void cloneRequest(int row, const CSMWorld::IdTable*); private slots: diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 0cdc57f5f..765055610 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -49,7 +49,7 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D { connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); - connect (mTable, SIGNAL (cloneRequest(int, CSMWorld::IdTable*)), this, SLOT(cloneRequest(int, CSMWorld::IdTable*))); + connect (mTable, SIGNAL (cloneRequest(int, const CSMWorld::IdTable*)), this, SLOT(cloneRequest(int, const CSMWorld::IdTable*))); connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type, const CSMWorld::UniversalId::ArgumentType)), mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type, const CSMWorld::UniversalId::ArgumentType))); } From d0a52b7b242e114d39a5fd25720a62a972eebcbe Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 20 Jan 2014 18:28:06 +0100 Subject: [PATCH 605/889] mostly refreshing view. --- apps/opencs/model/world/idtable.cpp | 5 +++++ apps/opencs/model/world/refidcollection.cpp | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index daa6bc51b..d4008fb87 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -129,7 +129,12 @@ void CSMWorld::IdTable::cloneRecord(const std::string& origin, CSMWorld::UniversalId::ArgumentType argumentType, CSMWorld::UniversalId::Type type) { + int index = mIdCollection->getAppendIndex (destination); + beginInsertRows (QModelIndex(), index, index); mIdCollection->cloneRecord(origin, destination, type, argumentType); + endInsertRows(); + emit dataChanged (CSMWorld::IdTable::index (0, 0), + CSMWorld::IdTable::index (mIdCollection->getSize()-1, mIdCollection->getColumns()-1)); } diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 896ee9010..74f6f1359 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -454,7 +454,11 @@ void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType) { - + RecordBase *newRecord = mData.getRecord(mData.searchId(origin)).clone(); + newRecord->mState = RecordBase::State_ModifiedOnly; + mAdapters.find(type)->second->setId(*newRecord, destination); + mData.getAppendIndex(type); + //WIP } void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, From 7594bcf97aa0813d3517f2f53f2d1d633c22aee6 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 21 Jan 2014 08:27:29 +0100 Subject: [PATCH 606/889] Small refactoring. --- apps/opencs/view/world/genericcreator.cpp | 2 +- apps/opencs/view/world/table.cpp | 7 ++++--- apps/opencs/view/world/table.hpp | 2 +- apps/opencs/view/world/tablesubview.cpp | 10 +++------- apps/opencs/view/world/tablesubview.hpp | 8 ++++---- 5 files changed, 13 insertions(+), 16 deletions(-) diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index efdd9ecaf..163842ab8 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -149,7 +149,7 @@ void CSVWorld::GenericCreator::create() } } -void CSVWorld::GenericCreator::cloneMode(const std::string& originid, +void CSVWorld::GenericCreator::cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType) { diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index cc1f814dd..d343f9986 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -16,6 +16,7 @@ #include "recordstatusdelegate.hpp" #include "util.hpp" +#include void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) { @@ -308,10 +309,10 @@ void CSVWorld::Table::cloneRecord() if (!mEditLock) { QModelIndexList selectedRows = selectionModel()->selectedRows(); - - if (selectedRows.size()==1) + const CSMWorld::UniversalId& toClone = getUniversalId(selectedRows.begin()->row()); + if (selectedRows.size()==1 && !mModel->getRecord(toClone.getId()).isDeleted()) { - emit cloneRequest (selectedRows.begin()->row(), mModel); + emit cloneRequest (toClone); } } } diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index af2d7877f..d30083333 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -74,7 +74,7 @@ namespace CSVWorld /// \param modified Number of added and modified records void createRequest(); - void cloneRequest(int row, const CSMWorld::IdTable*); + void cloneRequest(const CSMWorld::UniversalId&); private slots: diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 765055610..3c71d1370 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -49,7 +49,7 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D { connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); - connect (mTable, SIGNAL (cloneRequest(int, const CSMWorld::IdTable*)), this, SLOT(cloneRequest(int, const CSMWorld::IdTable*))); + connect (mTable, SIGNAL (cloneRequest(const CSMWorld::UniversalId&)), this, SLOT(cloneRequest(const CSMWorld::UniversalId&))); connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type, const CSMWorld::UniversalId::ArgumentType)), mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type, const CSMWorld::UniversalId::ArgumentType))); } @@ -82,11 +82,7 @@ void CSVWorld::TableSubView::setStatusBar (bool show) mBottom->setStatusBar (show); } -void CSVWorld::TableSubView::cloneRequest(int row, const CSMWorld::IdTable* table) +void CSVWorld::TableSubView::cloneRequest(const CSMWorld::UniversalId& toClone) { - const CSMWorld::UniversalId& toClone(mTable->getUniversalId(row)); - if (!(table->getRecord(toClone.getId()).isDeleted())) - { - emit cloneRequest(toClone.getId(), toClone.getType(), toClone.getArgumentType()); - } + emit cloneRequest(toClone.getId(), toClone.getType(), toClone.getArgumentType()); } diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 650a098f6..b3a0a5162 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -40,14 +40,14 @@ namespace CSVWorld virtual void setStatusBar (bool show); signals: - void cloneRequest(const std::string& id, - const CSMWorld::UniversalId::Type type, - const CSMWorld::UniversalId::ArgumentType argumentType); + void cloneRequest(const std::string&, + const CSMWorld::UniversalId::Type, + const CSMWorld::UniversalId::ArgumentType); private slots: void editRequest (int row); - void cloneRequest (int row, const CSMWorld::IdTable* table); + void cloneRequest (const CSMWorld::UniversalId& toClone); }; } From 9b56b6585be31d6d8af9573d3e53b3aa4766cc2f Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 21 Jan 2014 09:43:02 +0100 Subject: [PATCH 607/889] Cloning works for referencables as well. --- apps/opencs/model/world/refidcollection.cpp | 3 ++- apps/opencs/model/world/refiddata.cpp | 17 ++++++++++++++++- apps/opencs/model/world/refiddata.hpp | 13 +++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 74f6f1359..75a1b681d 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -457,7 +457,8 @@ void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin, RecordBase *newRecord = mData.getRecord(mData.searchId(origin)).clone(); newRecord->mState = RecordBase::State_ModifiedOnly; mAdapters.find(type)->second->setId(*newRecord, destination); - mData.getAppendIndex(type); + mData.insertRecord(*newRecord, type, destination); + delete newRecord; //WIP } diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 8f59b0fe7..d4f08eb91 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -230,4 +230,19 @@ void CSMWorld::RefIdData::save (int index, ESM::ESMWriter& writer) const throw std::logic_error ("invalid local index type"); iter->second->save (localIndex.first, writer); -} \ No newline at end of file +} + + +void CSMWorld::RefIdData::insertRecord(CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id) +{ + std::map::iterator iter = + mRecordContainers.find (type); + + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); + + iter->second->insertRecord(record); + + mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), + LocalIndex (iter->second->getSize()-1, type))); +} diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 9595ab23b..a982f2f97 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -46,6 +46,8 @@ namespace CSMWorld virtual RecordBase& getRecord (int index)= 0; virtual void appendRecord (const std::string& id) = 0; + + virtual void insertRecord (RecordBase& record) = 0; virtual void load (int index, ESM::ESMReader& reader, bool base) = 0; @@ -68,6 +70,8 @@ namespace CSMWorld virtual RecordBase& getRecord (int index); virtual void appendRecord (const std::string& id); + + virtual void insertRecord (RecordBase& record); virtual void load (int index, ESM::ESMReader& reader, bool base); @@ -78,6 +82,13 @@ namespace CSMWorld virtual void save (int index, ESM::ESMWriter& writer) const; }; + template + void RefIdDataContainer::insertRecord(RecordBase& record) + { + Record& newRecord = dynamic_cast& >(record); + mContainer.push_back(newRecord); + } + template int RefIdDataContainer::getSize() const { @@ -200,6 +211,8 @@ namespace CSMWorld LocalIndex searchId (const std::string& id) const; void erase (int index, int count); + + void insertRecord(CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id); const RecordBase& getRecord (const LocalIndex& index) const; From bc0130f8d8451ff75ba73a622033ac0e82499cc6 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 21 Jan 2014 10:35:08 +0100 Subject: [PATCH 608/889] do not double check if record is deleted --- apps/opencs/model/world/collection.hpp | 5 ----- apps/opencs/view/world/genericcreator.cpp | 5 ++++- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 091496926..7cc283ce8 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -205,11 +205,6 @@ namespace CSMWorld const UniversalId::ArgumentType argumentType) { Record copy = getRecord(origin); - if (copy.isDeleted()) - { - return; - } - if (argumentType == UniversalId::ArgumentType_Id) { copy.get().mId = destination; diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 163842ab8..2fcce9186 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -158,5 +158,8 @@ void CSVWorld::GenericCreator::cloneMode(const std::string& originid, mClonedType = type; mArgumentType = argumentType; - mId->setText(QString::fromStdString(mClonedId)); + if (argumentType == CSMWorld::UniversalId::ArgumentType_Id) + { + mId->setText(QString::fromStdString(mClonedId)); + } } From 851a7d50144588f6cb2ceeb49a78e832324ae764 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 15:48:06 +0100 Subject: [PATCH 609/889] Feature #957: Handle area effects for "on touch" range --- apps/openmw/mwbase/world.hpp | 3 + apps/openmw/mwclass/creature.cpp | 20 +--- apps/openmw/mwclass/npc.cpp | 26 +---- apps/openmw/mwmechanics/spellcasting.cpp | 25 +++-- apps/openmw/mwmechanics/spellcasting.hpp | 5 +- apps/openmw/mwscript/miscextensions.cpp | 2 + apps/openmw/mwworld/actiontrap.cpp | 1 + apps/openmw/mwworld/worldimp.cpp | 128 ++++++++++++----------- apps/openmw/mwworld/worldimp.hpp | 3 + 9 files changed, 104 insertions(+), 109 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 3ce98c0bf..92a1a4143 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -466,6 +466,9 @@ namespace MWBase /// Spawn a blood effect for \a ptr at \a worldPosition virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition) = 0; + + virtual void explodeSpell (const Ogre::Vector3& origin, const MWWorld::Ptr& object, const ESM::EffectList& effects, + const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0; }; } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 4a244f6a9..b0caccc73 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -316,23 +316,9 @@ namespace MWClass enchantmentName); if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes) { - // Check if we have enough charges - const float enchantCost = enchantment->mData.mCost; - int eSkill = getSkill(ptr, ESM::Skill::Enchant); - const int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10)); - - if (weapon.getCellRef().mEnchantmentCharge == -1) - weapon.getCellRef().mEnchantmentCharge = enchantment->mData.mCharge; - if (weapon.getCellRef().mEnchantmentCharge < castCost) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}"); - } - else - { - weapon.getCellRef().mEnchantmentCharge -= castCost; - MWMechanics::CastSpell cast(ptr, victim); - cast.cast(weapon); - } + MWMechanics::CastSpell cast(ptr, victim); + cast.mHitPosition = hitPosition; + cast.cast(weapon); } } } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 7f75d910b..d7cd42489 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -576,28 +576,12 @@ namespace MWClass enchantmentName); if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes) { - // Check if we have enough charges - const float enchantCost = enchantment->mData.mCost; - int eSkill = stats.getSkill(ESM::Skill::Enchant).getModified(); - const int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10)); + MWMechanics::CastSpell cast(ptr, victim); + cast.mHitPosition = hitPosition; + bool success = cast.cast(weapon); - if (weapon.getCellRef().mEnchantmentCharge == -1) - weapon.getCellRef().mEnchantmentCharge = enchantment->mData.mCharge; - if (weapon.getCellRef().mEnchantmentCharge < castCost) - { - if (ptr.getRefData().getHandle() == "player") - MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}"); - } - else - { - weapon.getCellRef().mEnchantmentCharge -= castCost; - - MWMechanics::CastSpell cast(ptr, victim); - cast.cast(weapon); - - if (ptr.getRefData().getHandle() == "player") - skillUsageSucceeded (ptr, ESM::Skill::Enchant, 3); - } + if (ptr.getRefData().getHandle() == "player" && success) + skillUsageSucceeded (ptr, ESM::Skill::Enchant, 3); } } diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index f70050628..749a5d7b1 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -181,11 +181,12 @@ namespace MWMechanics : mCaster(caster) , mTarget(target) , mStack(false) + , mHitPosition(0,0,0) { } void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, - const ESM::EffectList &effects, ESM::RangeType range, bool reflected) + const ESM::EffectList &effects, ESM::RangeType range, bool reflected, bool exploded) { // If none of the effects need to apply, we can early-out bool found = false; @@ -375,11 +376,12 @@ namespace MWMechanics if (anim) anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, ""); } - - // TODO: For Area effects, launch a growing particle effect that applies the effect to more actors as it hits them. Best managed in World. } } + if (!exploded) + MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, mTarget, effects, caster, mId, mSourceName); + if (reflectedEffects.mList.size()) inflict(caster, target, reflectedEffects, range, true); @@ -521,12 +523,11 @@ namespace MWMechanics mStack = (enchantment->mData.mType == ESM::Enchantment::CastOnce); - if (enchantment->mData.mType == ESM::Enchantment::WhenUsed) + // Check if there's enough charge left + if (enchantment->mData.mType == ESM::Enchantment::WhenUsed || enchantment->mData.mType == ESM::Enchantment::WhenStrikes) { - // Check if there's enough charge left const float enchantCost = enchantment->mData.mCost; - MWMechanics::NpcStats &stats = MWWorld::Class::get(mCaster).getNpcStats(mCaster); - int eSkill = stats.getSkill(ESM::Skill::Enchant).getModified(); + int eSkill = mCaster.getClass().getSkill(mCaster, ESM::Skill::Enchant); const int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10)); if (item.getCellRef().mEnchantmentCharge == -1) @@ -539,10 +540,15 @@ namespace MWMechanics MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}"); return false; } - // Reduce charge item.getCellRef().mEnchantmentCharge -= castCost; } + + if (enchantment->mData.mType == ESM::Enchantment::WhenUsed) + { + if (mCaster.getRefData().getHandle() == "player") + mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 1); + } if (enchantment->mData.mType == ESM::Enchantment::CastOnce) item.getContainerStore()->remove(item, 1, mCaster); else if (enchantment->mData.mType != ESM::Enchantment::WhenStrikes) @@ -551,9 +557,6 @@ namespace MWMechanics MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item); // Set again to show the modified charge } - if (mCaster.getRefData().getHandle() == "player") - mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 1); - inflict(mCaster, mCaster, enchantment->mEffects, ESM::RT_Self); if (!mTarget.isEmpty()) diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index b58e7bb09..74dc490ea 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -3,6 +3,8 @@ #include "../mwworld/ptr.hpp" +#include + namespace MWMechanics { class EffectKey; @@ -36,6 +38,7 @@ namespace MWMechanics bool mStack; std::string mId; // ID of spell, potion, item etc std::string mSourceName; // Display name for spell, potion, etc + Ogre::Vector3 mHitPosition; // Used for spawning area orb public: CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target); @@ -49,7 +52,7 @@ namespace MWMechanics bool cast (const std::string& id); void inflict (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, - const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false); + const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false, bool exploded=false); void applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const MWMechanics::EffectKey& effect, float magnitude); }; diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 637159475..27730767f 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -745,6 +745,7 @@ namespace MWScript MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr (targetId, false); MWMechanics::CastSpell cast(ptr, target); + cast.mHitPosition = Ogre::Vector3(target.getRefData().getPosition().pos); cast.cast(spell); } }; @@ -761,6 +762,7 @@ namespace MWScript runtime.pop(); MWMechanics::CastSpell cast(ptr, ptr); + cast.mHitPosition = Ogre::Vector3(ptr.getRefData().getPosition().pos); cast.cast(spell); } }; diff --git a/apps/openmw/mwworld/actiontrap.cpp b/apps/openmw/mwworld/actiontrap.cpp index d723b9823..bcefb0181 100644 --- a/apps/openmw/mwworld/actiontrap.cpp +++ b/apps/openmw/mwworld/actiontrap.cpp @@ -8,6 +8,7 @@ namespace MWWorld void ActionTrap::executeImp(const Ptr &actor) { MWMechanics::CastSpell cast(mTrapSource, actor); + cast.mHitPosition = Ogre::Vector3(actor.getRefData().getPosition().pos); cast.cast(mSpellId); mTrapSource.getCellRef().mTrap = ""; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 419558387..57c85317f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2106,6 +2106,8 @@ namespace MWWorld std::string selectedSpell = stats.getSpells().getSelectedSpell(); MWMechanics::CastSpell cast(actor, target); + if (!target.isEmpty()) + cast.mHitPosition = Ogre::Vector3(target.getRefData().getPosition().pos); if (!selectedSpell.empty()) { @@ -2240,10 +2242,11 @@ namespace MWWorld else { MWMechanics::CastSpell cast(caster, obstacle); + cast.mHitPosition = pos; cast.mId = it->second.mId; cast.mSourceName = it->second.mSourceName; cast.mStack = it->second.mStack; - cast.inflict(obstacle, caster, it->second.mEffects, ESM::RT_Target); + cast.inflict(obstacle, caster, it->second.mEffects, ESM::RT_Target, false, true); } explode = true; @@ -2251,64 +2254,8 @@ namespace MWWorld if (explode) { - std::map > toApply; - for (std::vector::const_iterator effectIt = it->second.mEffects.mList.begin(); - effectIt != it->second.mEffects.mList.end(); ++effectIt) - { - const ESM::MagicEffect* effect = getStore().get().find(effectIt->mEffectID); - - if (effectIt->mArea <= 0) - continue; // Not an area effect - - // Spawn the explosion orb effect - const ESM::Static* areaStatic; - if (!effect->mCasting.empty()) - areaStatic = getStore().get().find (effect->mArea); - else - areaStatic = getStore().get().find ("VFX_DefaultArea"); - - mRendering->spawnEffect("meshes\\" + areaStatic->mModel, "", Ogre::Vector3(ptr.getRefData().getPosition().pos), effectIt->mArea); - - // Play explosion sound (make sure to use NoTrack, since we will delete the projectile now) - static const std::string schools[] = { - "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" - }; - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - if(!effect->mAreaSound.empty()) - sndMgr->playSound3D(ptr, effect->mAreaSound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); - else - sndMgr->playSound3D(ptr, schools[effect->mData.mSchool]+" area", 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); - - // Get the actors in range of the effect - std::vector objects; - MWBase::Environment::get().getMechanicsManager()->getObjectsInRange( - Ogre::Vector3(ptr.getRefData().getPosition().pos), feetToGameUnits(effectIt->mArea), objects); - - for (std::vector::iterator affected = objects.begin(); affected != objects.end(); ++affected) - toApply[*affected].push_back(*effectIt); - } - - // Now apply the appropriate effects to each actor in range - for (std::map >::iterator apply = toApply.begin(); apply != toApply.end(); ++apply) - { - MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle); - - // Vanilla-compatible behaviour of never applying the spell to the caster - // (could be changed by mods later) - if (apply->first == caster) - continue; - - if (caster.isEmpty()) - caster = apply->first; - - MWMechanics::CastSpell cast(caster, apply->first); - cast.mId = it->second.mId; - cast.mSourceName = it->second.mSourceName; - cast.mStack = it->second.mStack; - ESM::EffectList effects; - effects.mList = apply->second; - cast.inflict(apply->first, caster, effects, ESM::RT_Target); - } + MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle); + explodeSpell(Ogre::Vector3(ptr.getRefData().getPosition().pos), ptr, it->second.mEffects, caster, it->second.mId, it->second.mSourceName); deleteObject(ptr); mProjectiles.erase(it++); @@ -2707,4 +2654,67 @@ namespace MWWorld mRendering->spawnEffect(model, texture, worldPosition); } + + void World::explodeSpell(const Vector3 &origin, const MWWorld::Ptr& object, const ESM::EffectList &effects, const Ptr &caster, + const std::string& id, const std::string& sourceName) + { + std::map > toApply; + for (std::vector::const_iterator effectIt = effects.mList.begin(); + effectIt != effects.mList.end(); ++effectIt) + { + const ESM::MagicEffect* effect = getStore().get().find(effectIt->mEffectID); + + if (effectIt->mArea <= 0) + continue; // Not an area effect + + // Spawn the explosion orb effect + const ESM::Static* areaStatic; + if (!effect->mCasting.empty()) + areaStatic = getStore().get().find (effect->mArea); + else + areaStatic = getStore().get().find ("VFX_DefaultArea"); + + mRendering->spawnEffect("meshes\\" + areaStatic->mModel, "", origin, effectIt->mArea); + + // Play explosion sound (make sure to use NoTrack, since we will delete the projectile now) + static const std::string schools[] = { + "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" + }; + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + if(!effect->mAreaSound.empty()) + sndMgr->playSound3D(object, effect->mAreaSound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); + else + sndMgr->playSound3D(object, schools[effect->mData.mSchool]+" area", 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); + + // Get the actors in range of the effect + std::vector objects; + MWBase::Environment::get().getMechanicsManager()->getObjectsInRange( + origin, feetToGameUnits(effectIt->mArea), objects); + + for (std::vector::iterator affected = objects.begin(); affected != objects.end(); ++affected) + toApply[*affected].push_back(*effectIt); + } + + // Now apply the appropriate effects to each actor in range + for (std::map >::iterator apply = toApply.begin(); apply != toApply.end(); ++apply) + { + MWWorld::Ptr source = caster; + // Vanilla-compatible behaviour of never applying the spell to the caster + // (could be changed by mods later) + if (apply->first == caster) + continue; + + if (source.isEmpty()) + source = apply->first; + + MWMechanics::CastSpell cast(source, apply->first); + cast.mHitPosition = origin; + cast.mId = id; + cast.mSourceName = sourceName; + cast.mStack = false; + ESM::EffectList effects; + effects.mList = apply->second; + cast.inflict(apply->first, caster, effects, ESM::RT_Target, false, true); + } + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 38766e74f..8b1bd9538 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -552,6 +552,9 @@ namespace MWWorld /// Spawn a blood effect for \a ptr at \a worldPosition virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition); + + virtual void explodeSpell (const Ogre::Vector3& origin, const MWWorld::Ptr& object, const ESM::EffectList& effects, + const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName); }; } From 5de8c7fe293da2fc2fe54408ace81da3f388e2f5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 15:56:01 +0100 Subject: [PATCH 610/889] Feature #1130: Add auto-calculated NPC spells --- apps/openmw/mwclass/npc.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index d7cd42489..6b1be1b8f 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -141,7 +141,7 @@ namespace * * and by adding class, race, specialization bonus. */ - void autoCalculateSkills(const ESM::NPC* npc, MWMechanics::NpcStats& npcStats) + void autoCalculateSkills(const ESM::NPC* npc, MWMechanics::NpcStats& npcStats, const MWWorld::Ptr& ptr) { const ESM::Class *class_ = MWBase::Environment::get().getWorld()->getStore().get().find(npc->mClass); @@ -190,6 +190,18 @@ namespace majorMultiplier = 1.0f; break; } + if (class_->mData.mSkills[k][1] == skillIndex) + { + // Major skill -> add starting spells for this skill if existing + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + MWWorld::Store::iterator it = store.get().begin(); + for (; it != store.get().end(); ++it) + { + if (it->mData.mFlags & ESM::Spell::F_Autocalc + && MWMechanics::spellSchoolToSkill(MWMechanics::getSpellSchool(&*it, ptr)) == skillIndex) + npcStats.getSpells().add(it->mId); + } + } } // is this skill in the same Specialization as the class? @@ -302,7 +314,7 @@ namespace MWClass data->mNpcStats.setReputation(ref->mBase->mNpdt12.mReputation); autoCalculateAttributes(ref->mBase, data->mNpcStats); - autoCalculateSkills(ref->mBase, data->mNpcStats); + autoCalculateSkills(ref->mBase, data->mNpcStats, ptr); } if (data->mNpcStats.getFactionRanks().size()) From a4404054bbe79699d82d32227ff659e5c2e90069 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 16:02:39 +0100 Subject: [PATCH 611/889] Feature #1130: Add race power spells for NPCs as well, not just the player --- apps/openmw/mwclass/npc.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 6b1be1b8f..ef45fa28a 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -317,6 +317,14 @@ namespace MWClass autoCalculateSkills(ref->mBase, data->mNpcStats, ptr); } + // race powers + const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore().get().find(ref->mBase->mRace); + for (std::vector::const_iterator iter (race->mPowers.mList.begin()); + iter!=race->mPowers.mList.end(); ++iter) + { + data->mNpcStats.getSpells().add (*iter); + } + if (data->mNpcStats.getFactionRanks().size()) { static const int iAutoRepFacMod = MWBase::Environment::get().getWorld()->getStore().get() From 1617a4f7d94bd53be80be3d50323b9f972c5e076 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 17:31:08 +0100 Subject: [PATCH 612/889] Use fCombatDistance --- apps/openmw/mwclass/creature.cpp | 6 ++++-- apps/openmw/mwclass/npc.cpp | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index b0caccc73..20b37833a 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -225,8 +225,10 @@ namespace MWClass // TODO: where is the distance defined? float dist = 200.f; if (!weapon.isEmpty()) - dist = 100.f * weapon.get()->mBase->mData.mReach; - + { + const float fCombatDistance = gmst.find("fCombatDistance")->getFloat(); + dist = fCombatDistance * weapon.get()->mBase->mData.mReach; + } std::pair result = MWBase::Environment::get().getWorld()->getHitContact(ptr, dist); if (result.first.isEmpty()) return; // Didn't hit anything diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index ef45fa28a..4b955373c 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -467,10 +467,11 @@ namespace MWClass fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); getCreatureStats(ptr).setFatigue(fatigue); - - float dist = 100.0f * (!weapon.isEmpty() ? + const float fCombatDistance = gmst.find("fCombatDistance")->getFloat(); + float dist = fCombatDistance * (!weapon.isEmpty() ? weapon.get()->mBase->mData.mReach : gmst.find("fHandToHandReach")->getFloat()); + // TODO: Use second to work out the hit angle std::pair result = world->getHitContact(ptr, dist); MWWorld::Ptr victim = result.first; From 82a07af72cfc2829d0ac83195e60e82c6f0604bc Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 17:44:39 +0100 Subject: [PATCH 613/889] Fix typo (should be Strength, not Luck). Use GMSTs for strength damage. --- apps/openmw/mwclass/npc.cpp | 8 +++++++- apps/openmw/mwclass/npc.hpp | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4b955373c..96cb4832d 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -252,6 +252,8 @@ namespace MWClass fKnockDownMult = gmst.find("fKnockDownMult"); iKnockDownOddsMult = gmst.find("iKnockDownOddsMult"); iKnockDownOddsBase = gmst.find("iKnockDownOddsBase"); + fDamageStrengthBase = gmst.find("fDamageStrengthBase"); + fDamageStrengthMult = gmst.find("fDamageStrengthMult"); inited = true; } @@ -524,7 +526,8 @@ namespace MWClass if(attack) { damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength()); - damage *= 0.5f + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f); + damage *= fDamageStrengthBase->getFloat() + + (stats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult->getFloat() * 0.1); if(weaphashealth) { int weapmaxhealth = weapon.get()->mBase->mData.mHealth; @@ -1254,4 +1257,7 @@ namespace MWClass const ESM::GameSetting *Npc::fKnockDownMult; const ESM::GameSetting *Npc::iKnockDownOddsMult; const ESM::GameSetting *Npc::iKnockDownOddsBase; + const ESM::GameSetting *Npc::fDamageStrengthBase; + const ESM::GameSetting *Npc::fDamageStrengthMult; + } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index e658dde5f..157e3afd9 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -36,6 +36,8 @@ namespace MWClass static const ESM::GameSetting *fKnockDownMult; static const ESM::GameSetting *iKnockDownOddsMult; static const ESM::GameSetting *iKnockDownOddsBase; + static const ESM::GameSetting *fDamageStrengthBase; + static const ESM::GameSetting *fDamageStrengthMult; public: From 0f6089851759129daca3ce9995df6a170f7cf077 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 21 Jan 2014 14:13:13 +0100 Subject: [PATCH 614/889] adding missing cleanup for SoundManager --- apps/openmw/mwbase/soundmanager.hpp | 2 ++ apps/openmw/mwsound/soundmanagerimp.cpp | 9 +++++++++ apps/openmw/mwsound/soundmanagerimp.hpp | 2 ++ apps/openmw/mwstate/statemanagerimp.cpp | 2 ++ 4 files changed, 15 insertions(+) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index 4d764597c..1b3719e60 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -147,6 +147,8 @@ namespace MWBase virtual void update(float duration) = 0; virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up) = 0; + + virtual void clear() = 0; }; } diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 304c87191..95a4cd1dc 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -705,4 +705,13 @@ namespace MWSound { return bytes / framesToBytes(1, config, type); } + + void SoundManager::clear() + { + for (SoundMap::iterator iter (mActiveSounds.begin()); iter!=mActiveSounds.end(); ++iter) + iter->first->stop(); + + mActiveSounds.clear(); + stopMusic(); + } } diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index f62e62d50..bc8ef1db7 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -148,6 +148,8 @@ namespace MWSound virtual void update(float duration); virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up); + + virtual void clear(); }; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 2eb54a125..8571e93ff 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -15,6 +15,7 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/scriptmanager.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" @@ -27,6 +28,7 @@ void MWState::StateManager::cleanup() { if (mState!=State_NoGame) { + MWBase::Environment::get().getSoundManager()->clear(); MWBase::Environment::get().getDialogueManager()->clear(); MWBase::Environment::get().getJournal()->clear(); MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear(); From 9ebe66e693bcd38ddc6f2175e1a1e01f07095826 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 21 Jan 2014 14:50:58 +0100 Subject: [PATCH 615/889] improved cleanup; failed loads will now drop back into the main menu instead of crashing --- apps/openmw/mwgui/savegamedialog.cpp | 7 +++++++ apps/openmw/mwgui/windowmanagerimp.cpp | 5 +++++ apps/openmw/mwrender/renderingmanager.cpp | 9 +++++++-- apps/openmw/mwsound/soundmanagerimp.cpp | 10 ++++++++-- apps/openmw/mwworld/worldimp.cpp | 2 +- 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 04461fef9..552489bc4 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -8,6 +8,7 @@ #include "../mwbase/statemanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwstate/character.hpp" @@ -123,6 +124,12 @@ namespace MWGui } setVisible(false); + + if (MWBase::Environment::get().getStateManager()->getState()== + MWBase::StateManager::State_NoGame) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + } } void SaveGameDialog::onCharacterSelected(MyGUI::ComboBox *sender, size_t pos) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 9e57f5041..27d37eacf 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -14,6 +14,7 @@ #include #include "../mwbase/inputmanager.hpp" +#include "../mwbase/statemanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" @@ -709,6 +710,10 @@ namespace MWGui mToolTips->onFrame(frameDuration); + if (MWBase::Environment::get().getStateManager()->getState()== + MWBase::StateManager::State_NoGame) + return; + if (mDragAndDrop->mIsOnDragAndDrop) { assert(mDragAndDrop->mDraggedWidget); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index f97d7bebf..55ead476b 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -30,6 +30,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/inputmanager.hpp" // FIXME #include "../mwbase/windowmanager.hpp" // FIXME +#include "../mwbase/statemanager.hpp" #include "../mwmechanics/creaturestats.hpp" @@ -329,6 +330,12 @@ void RenderingManager::rebuildPtr(const MWWorld::Ptr &ptr) void RenderingManager::update (float duration, bool paused) { + mVideoPlayer->update (); + + if (MWBase::Environment::get().getStateManager()->getState()== + MWBase::StateManager::State_NoGame) + return; + MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayer().getPlayer(); @@ -365,8 +372,6 @@ void RenderingManager::update (float duration, bool paused) mOcclusionQuery->update(duration); - mVideoPlayer->update (); - mRendering.update(duration); Ogre::ControllerManager::getSingleton().setTimeFactor(paused ? 0.f : 1.f); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 95a4cd1dc..827ecd643 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/statemanager.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/player.hpp" @@ -607,8 +608,13 @@ namespace MWSound { if(!mOutput->isInitialized()) return; - updateSounds(duration); - updateRegionSound(duration); + + if (MWBase::Environment::get().getStateManager()->getState()!= + MWBase::StateManager::State_NoGame) + { + updateSounds(duration); + updateRegionSound(duration); + } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9704239d7..706701fa8 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1335,7 +1335,7 @@ namespace MWWorld updateWindowManager (); - if (mPlayer->getPlayer().getCell()->isExterior()) + if (!paused && mPlayer->getPlayer().getCell()->isExterior()) { ESM::Position pos = mPlayer->getPlayer().getRefData().getPosition(); mPlayer->setLastKnownExteriorPosition(Ogre::Vector3(pos.pos)); From b3b51992ef8ac49e292787704cdf7ab466f1fbb4 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 21 Jan 2014 21:37:21 +0100 Subject: [PATCH 616/889] copying references. --- apps/opencs/model/world/collection.hpp | 6 +++--- apps/opencs/model/world/commands.cpp | 1 - apps/opencs/model/world/refcollection.cpp | 15 +++++++++++++++ apps/opencs/model/world/refcollection.hpp | 6 ++++++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 7cc283ce8..8ecad816b 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -200,9 +200,9 @@ namespace CSMWorld template void Collection::cloneRecord(const std::string& origin, - const std::string& destination, - const UniversalId::Type type, - const UniversalId::ArgumentType argumentType) + const std::string& destination, + const UniversalId::Type type, + const UniversalId::ArgumentType argumentType) { Record copy = getRecord(origin); if (argumentType == UniversalId::ArgumentType_Id) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index b7c88e872..762e4809c 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -4,7 +4,6 @@ #include #include #include "idtable.hpp" -#include "idtable.hpp" CSMWorld::ModifyCommand::ModifyCommand(QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, QUndoCommand* parent) diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 696aeefaa..2ecd3b663 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -5,6 +5,8 @@ #include "ref.hpp" #include "cell.hpp" +#include "universalid.hpp" +#include "record.hpp" void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base) { @@ -34,4 +36,17 @@ std::string CSMWorld::RefCollection::getNewId() std::ostringstream stream; stream << "ref#" << mNextId++; return stream.str(); +} + +void CSMWorld::RefCollection::cloneRecord(const std::string& origin, + const std::string& destination, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumentType) +{ + Record originRecord = getRecord(origin); + Record *copy = dynamic_cast* >(originRecord.clone()); + copy->mState = CSMWorld::RecordBase::State_ModifiedOnly; + copy->get().mId = getNewId(); + insertRecord(*copy, getAppendIndex(destination, type)); + delete copy; } \ No newline at end of file diff --git a/apps/opencs/model/world/refcollection.hpp b/apps/opencs/model/world/refcollection.hpp index b5f8c8064..dcfd2036c 100644 --- a/apps/opencs/model/world/refcollection.hpp +++ b/apps/opencs/model/world/refcollection.hpp @@ -8,6 +8,7 @@ namespace CSMWorld { struct Cell; + struct UniversalId; /// \brief References in cells class RefCollection : public Collection @@ -25,6 +26,11 @@ namespace CSMWorld ///< Load a sequence of references. std::string getNewId(); + + void cloneRecord(const std::string& origin, + const std::string& destination, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumentType); }; } From 16f5f5862de0921bc213ae366e1d93939f223944 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 21 Jan 2014 01:01:21 +0100 Subject: [PATCH 617/889] Feature #956: Implement blocking melee attacks --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwclass/creature.cpp | 72 ++++++++++---- apps/openmw/mwclass/creature.hpp | 2 + apps/openmw/mwclass/npc.cpp | 31 +++++- apps/openmw/mwclass/npc.hpp | 2 + apps/openmw/mwmechanics/character.cpp | 9 ++ apps/openmw/mwmechanics/character.hpp | 3 +- apps/openmw/mwmechanics/combat.cpp | 111 ++++++++++++++++++++++ apps/openmw/mwmechanics/combat.hpp | 14 +++ apps/openmw/mwmechanics/creaturestats.cpp | 12 ++- apps/openmw/mwmechanics/creaturestats.hpp | 3 + apps/openmw/mwrender/animation.cpp | 3 + apps/openmw/mwworld/class.cpp | 5 + apps/openmw/mwworld/class.hpp | 4 + 14 files changed, 247 insertions(+), 26 deletions(-) create mode 100644 apps/openmw/mwmechanics/combat.cpp create mode 100644 apps/openmw/mwmechanics/combat.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 3b533b416..41cc320ad 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -74,7 +74,7 @@ add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting - disease pickpocket levelledlist + disease pickpocket levelledlist combat ) add_openmw_dir (mwbase diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 20b37833a..d0a913760 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -30,6 +30,7 @@ #include "../mwworld/inventorystore.hpp" #include "../mwmechanics/npcstats.hpp" +#include "../mwmechanics/combat.hpp" namespace { @@ -325,8 +326,11 @@ namespace MWClass } } - // TODO: do not do this if the attack is blocked - MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); + if (!weapon.isEmpty() && MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage)) + damage = 0; + + if (damage > 0) + MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); victim.getClass().onHit(victim, damage, true, MWWorld::Ptr(), ptr, true); } @@ -355,31 +359,57 @@ namespace MWClass ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); } - // Check for knockdown - float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat(); - float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() - * iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt(); - int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] - if (ishealth && agilityTerm <= damage && knockdownTerm <= roll) + if (damage > 0.f) { - getCreatureStats(ptr).setKnockedDown(true); + // Check for knockdown + float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat(); + float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() + * iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt(); + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (ishealth && agilityTerm <= damage && knockdownTerm <= roll) + { + getCreatureStats(ptr).setKnockedDown(true); - } - else - getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? + } + else + getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? - if(ishealth) - { - if(damage > 0.0f) + if(ishealth) + { MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); - float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; - setActorHealth(ptr, health, attacker); + float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; + setActorHealth(ptr, health, attacker); + } + else + { + MWMechanics::DynamicStat fatigue(getCreatureStats(ptr).getFatigue()); + fatigue.setCurrent(fatigue.getCurrent() - damage, true); + getCreatureStats(ptr).setFatigue(fatigue); + } } - else + } + + void Creature::block(const MWWorld::Ptr &ptr) const + { + MWWorld::InventoryStore& inv = getInventoryStore(ptr); + MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if (shield == inv.end()) + return; + + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + switch(shield->getClass().getEquipmentSkill(*shield)) { - MWMechanics::DynamicStat fatigue(getCreatureStats(ptr).getFatigue()); - fatigue.setCurrent(fatigue.getCurrent() - damage, true); - getCreatureStats(ptr).setFatigue(fatigue); + case ESM::Skill::LightArmor: + sndMgr->playSound3D(ptr, "Light Armor Hit", 1.0f, 1.0f); + break; + case ESM::Skill::MediumArmor: + sndMgr->playSound3D(ptr, "Medium Armor Hit", 1.0f, 1.0f); + break; + case ESM::Skill::HeavyArmor: + sndMgr->playSound3D(ptr, "Heavy Armor Hit", 1.0f, 1.0f); + break; + default: + return; } } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index cfa06ed62..ca8abc040 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -56,6 +56,8 @@ namespace MWClass virtual void hit(const MWWorld::Ptr& ptr, int type) const; + virtual void block(const MWWorld::Ptr &ptr) const; + virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 96cb4832d..f4ccd3c5f 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -20,6 +20,7 @@ #include "../mwmechanics/movement.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/disease.hpp" +#include "../mwmechanics/combat.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" @@ -609,8 +610,10 @@ namespace MWClass } } - // TODO: do not do this if the attack is blocked - if (healthdmg) + if (!weapon.isEmpty() && MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage)) + damage = 0; + + if (healthdmg && damage > 0) MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); othercls.onHit(victim, damage, healthdmg, weapon, ptr, true); @@ -751,6 +754,30 @@ namespace MWClass } } + void Npc::block(const MWWorld::Ptr &ptr) const + { + MWWorld::InventoryStore& inv = getInventoryStore(ptr); + MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if (shield == inv.end()) + return; + + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + switch(shield->getClass().getEquipmentSkill(*shield)) + { + case ESM::Skill::LightArmor: + sndMgr->playSound3D(ptr, "Light Armor Hit", 1.0f, 1.0f); + break; + case ESM::Skill::MediumArmor: + sndMgr->playSound3D(ptr, "Medium Armor Hit", 1.0f, 1.0f); + break; + case ESM::Skill::HeavyArmor: + sndMgr->playSound3D(ptr, "Heavy Armor Hit", 1.0f, 1.0f); + break; + default: + return; + } + } + void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const { MWMechanics::CreatureStats &crstats = getCreatureStats(ptr); diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 157e3afd9..2bd91d198 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -79,6 +79,8 @@ namespace MWClass virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; + virtual void block(const MWWorld::Ptr &ptr) const; + virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const; virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 3f29cbb7c..2f5b0ca6c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -153,6 +153,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat { bool recovery = mPtr.getClass().getCreatureStats(mPtr).getHitRecovery(); bool knockdown = mPtr.getClass().getCreatureStats(mPtr).getKnockedDown(); + bool block = mPtr.getClass().getCreatureStats(mPtr).getBlock(); if(mHitState == CharState_None) { if(knockdown) @@ -167,6 +168,12 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat mCurrentHit = chooseRandomGroup("hit"); mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } + else if (block) + { + mHitState = CharState_Block; + mCurrentHit = "shield"; + mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "block start", "block stop", 0.0f, 0); + } } else if(!mAnimation->isPlaying(mCurrentHit)) { @@ -175,6 +182,8 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(false); if (recovery) mPtr.getClass().getCreatureStats(mPtr).setHitRecovery(false); + if (block) + mPtr.getClass().getCreatureStats(mPtr).setBlock(false); mHitState = CharState_None; } } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index ea12d9b94..915de93eb 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -92,7 +92,8 @@ enum CharacterState { CharState_SwimDeath, CharState_Hit, - CharState_KnockDown + CharState_KnockDown, + CharState_Block }; enum WeaponType { diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp new file mode 100644 index 000000000..d54736857 --- /dev/null +++ b/apps/openmw/mwmechanics/combat.cpp @@ -0,0 +1,111 @@ +#include "combat.hpp" + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/movement.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/inventorystore.hpp" + +namespace +{ + +Ogre::Radian signedAngle(Ogre::Vector3 v1, Ogre::Vector3 v2, Ogre::Vector3 n) +{ + return Ogre::Math::ATan2( + n.dotProduct( v1.crossProduct(v2) ), + v1.dotProduct(v2) + ); +} + +} + +namespace MWMechanics +{ + + bool blockMeleeAttack(const MWWorld::Ptr &attacker, const MWWorld::Ptr &blocker, const MWWorld::Ptr &weapon, float damage) + { + if (!blocker.getClass().hasInventoryStore(blocker)) + return false; + + if (blocker.getClass().getCreatureStats(blocker).getKnockedDown() + || blocker.getClass().getCreatureStats(blocker).getHitRecovery()) + return false; + + MWWorld::InventoryStore& inv = blocker.getClass().getInventoryStore(blocker); + MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if (shield == inv.end() || shield->getTypeName() != typeid(ESM::Armor).name()) + return false; + + Ogre::Degree angle = signedAngle (Ogre::Vector3(attacker.getRefData().getPosition().pos) - Ogre::Vector3(blocker.getRefData().getPosition().pos), + blocker.getRefData().getBaseNode()->getOrientation().yAxis(), Ogre::Vector3(0,0,1)); + + const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); + if (angle.valueDegrees() < gmst.find("fCombatBlockLeftAngle")->getFloat()) + return false; + if (angle.valueDegrees() > gmst.find("fCombatBlockRightAngle")->getFloat()) + return false; + + MWMechanics::CreatureStats& blockerStats = blocker.getClass().getCreatureStats(blocker); + if (blockerStats.getDrawState() == DrawState_Spell) + return false; + + MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker); + + float blockTerm = blocker.getClass().getSkill(blocker, ESM::Skill::Block) + 0.2 * blockerStats.getAttribute(ESM::Attribute::Agility).getModified() + + 0.1 * blockerStats.getAttribute(ESM::Attribute::Luck).getModified(); + float enemySwing = attackerStats.getAttackStrength(); + float swingTerm = enemySwing * gmst.find("fSwingBlockMult")->getFloat() + gmst.find("fSwingBlockBase")->getFloat(); + + float blockerTerm = blockTerm * swingTerm; + if (blocker.getClass().getMovementSettings(blocker).mPosition[1] <= 0) + blockerTerm *= gmst.find("fBlockStillBonus")->getFloat(); + blockerTerm *= blockerStats.getFatigueTerm(); + + float attackerSkill = attacker.getClass().getSkill(attacker, weapon.getClass().getEquipmentSkill(weapon)); + float attackerTerm = attackerSkill + 0.2 * attackerStats.getAttribute(ESM::Attribute::Agility).getModified() + + 0.1 * attackerStats.getAttribute(ESM::Attribute::Luck).getModified(); + attackerTerm *= attackerStats.getFatigueTerm(); + + int x = int(blockerTerm - attackerTerm); + int iBlockMaxChance = gmst.find("iBlockMaxChance")->getInt(); + int iBlockMinChance = gmst.find("iBlockMinChance")->getInt(); + x = std::min(iBlockMaxChance, std::max(iBlockMinChance, x)); + + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (roll < x) + { + // Reduce shield durability by incoming damage + if (shield->getCellRef().mCharge == -1) + shield->getCellRef().mCharge = shield->getClass().getItemMaxHealth(*shield); + shield->getCellRef().mCharge -= std::min(shield->getCellRef().mCharge, int(damage)); + if (!shield->getCellRef().mCharge) + inv.unequipItem(*shield, blocker); + + // Reduce blocker fatigue + const float fFatigueBlockBase = gmst.find("fFatigueBlockBase")->getFloat(); + const float fFatigueBlockMult = gmst.find("fFatigueBlockMult")->getFloat(); + const float fWeaponFatigueBlockMult = gmst.find("fWeaponFatigueBlockMult")->getFloat(); + MWMechanics::DynamicStat fatigue = blockerStats.getFatigue(); + float normalizedEncumbrance = blocker.getClass().getEncumbrance(blocker) / blocker.getClass().getCapacity(blocker); + normalizedEncumbrance = std::min(1.f, normalizedEncumbrance); + float fatigueLoss = fFatigueBlockBase + normalizedEncumbrance * fFatigueBlockMult; + fatigueLoss += weapon.getClass().getWeight(weapon) * attackerStats.getAttackStrength() * fWeaponFatigueBlockMult; + fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); + blockerStats.setFatigue(fatigue); + + blockerStats.setBlock(true); + + if (blocker.getClass().isNpc()) + blocker.getClass().skillUsageSucceeded(blocker, ESM::Skill::Block, 0); + + return true; + } + return false; + } + +} diff --git a/apps/openmw/mwmechanics/combat.hpp b/apps/openmw/mwmechanics/combat.hpp new file mode 100644 index 000000000..d666905f2 --- /dev/null +++ b/apps/openmw/mwmechanics/combat.hpp @@ -0,0 +1,14 @@ +#ifndef OPENMW_MECHANICS_COMBAT_H +#define OPENMW_MECHANICS_COMBAT_H + +#include "../mwworld/ptr.hpp" + +namespace MWMechanics +{ + +/// @return can we block the attack? +bool blockMeleeAttack (const MWWorld::Ptr& attacker, const MWWorld::Ptr& blocker, const MWWorld::Ptr& weapon, float damage); + +} + +#endif diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 62bae8ca8..8f84a7979 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -15,7 +15,7 @@ namespace MWMechanics mAttacked (false), mHostile (false), mAttackingOrSpell(false), mAttackType(AT_Chop), mIsWerewolf(false), - mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false), + mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false), mBlock(false), mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f) { for (int i=0; i<4; ++i) @@ -427,6 +427,16 @@ namespace MWMechanics return mHitRecovery; } + void CreatureStats::setBlock(bool value) + { + mBlock = value; + } + + bool CreatureStats::getBlock() const + { + return mBlock; + } + bool CreatureStats::getMovementFlag (Flag flag) const { return mMovementFlags & flag; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 6893f385e..0501eb286 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -39,6 +39,7 @@ namespace MWMechanics bool mAttackingOrSpell; bool mKnockdown; bool mHitRecovery; + bool mBlock; unsigned int mMovementFlags; float mAttackStrength; // Note only some creatures attack with weapons @@ -204,6 +205,8 @@ namespace MWMechanics bool getKnockedDown() const; void setHitRecovery(bool value); bool getHitRecovery() const; + void setBlock(bool value); + bool getBlock() const; enum Flag { diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index a7623efea..cd2d9a47a 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -679,6 +679,9 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co else if (groupname == "spellcast" && evt.substr(evt.size()-7, 7) == "release") MWBase::Environment::get().getWorld()->castSpell(mPtr); + + else if (groupname == "shield" && evt.compare(off, len, "block hit") == 0) + mPtr.getClass().block(mPtr); } void Animation::changeGroups(const std::string &groupname, int groups) diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index c7b0d2e1f..9771ffde3 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -92,6 +92,11 @@ namespace MWWorld throw std::runtime_error("class cannot hit"); } + void Class::block(const Ptr &ptr) const + { + throw std::runtime_error("class cannot block"); + } + void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker, bool successful) const { throw std::runtime_error("class cannot be hit"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 823538cf8..0dee8b292 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -128,6 +128,10 @@ namespace MWWorld /// actor responsible for the attack, and \a successful specifies if the hit is /// successful or not. + virtual void block (const Ptr& ptr) const; + ///< Play the appropriate sound for a blocked attack, depending on the currently equipped shield + /// (default implementation: throw an exception) + virtual void setActorHealth(const Ptr& ptr, float health, const Ptr& attacker=Ptr()) const; ///< Sets a new current health value for the actor, optionally specifying the object causing /// the change. Use this instead of using CreatureStats directly as this will make sure the From f89c400305a8f0563e4ba5156a0977f142c8492f Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 22 Jan 2014 00:02:43 +0100 Subject: [PATCH 618/889] Don't complain about greetings with no sound --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 3 ++- components/esm/loadinfo.hpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 263c101e5..7fbebb9d7 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -586,7 +586,8 @@ namespace MWDialogue MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); if(winMgr->getSubtitlesEnabled()) winMgr->messageBox(info->mResponse); - sndMgr->say(actor, info->mSound); + if (!info->mSound.empty()) + sndMgr->say(actor, info->mSound); } } diff --git a/components/esm/loadinfo.hpp b/components/esm/loadinfo.hpp index 737494f6c..0c0d662a8 100644 --- a/components/esm/loadinfo.hpp +++ b/components/esm/loadinfo.hpp @@ -70,7 +70,7 @@ struct DialInfo // Sound and text associated with this item std::string mSound, mResponse; - // Result script (uncomiled) to run whenever this dialog item is + // Result script (uncompiled) to run whenever this dialog item is // selected std::string mResultScript; From 194413c9558d4fa0d9238d0e57054c26c015cff2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 22 Jan 2014 12:09:44 +0100 Subject: [PATCH 619/889] Feature #1119: Implement Resistance/Weakness to normal weapons magic effect. Handle fWereWolfSilverWeaponDamageMult. --- apps/openmw/mwclass/creature.cpp | 3 +++ apps/openmw/mwclass/npc.cpp | 3 +++ apps/openmw/mwmechanics/combat.cpp | 28 +++++++++++++++++++++++++++- apps/openmw/mwmechanics/combat.hpp | 2 ++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index d0a913760..c23b9e23a 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -359,6 +359,9 @@ namespace MWClass ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); } + if (damage > 0.0f && !object.isEmpty()) + MWMechanics::resistNormalWeapon(ptr, attacker, object, damage); + if (damage > 0.f) { // Check for knockdown diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index f4ccd3c5f..8c6e89544 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -652,6 +652,9 @@ namespace MWClass if (!attacker.isEmpty()) MWMechanics::diseaseContact(ptr, attacker); + if (damage > 0.0f && !object.isEmpty()) + MWMechanics::resistNormalWeapon(ptr, attacker, object, damage); + if(damage > 0.0f) { // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index d54736857..204264106 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -5,12 +5,14 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/movement.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwbase/windowmanager.hpp" + namespace { @@ -108,4 +110,28 @@ namespace MWMechanics return false; } + void resistNormalWeapon(const MWWorld::Ptr &actor, const MWWorld::Ptr& attacker, const MWWorld::Ptr &weapon, float &damage) + { + MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); + float resistance = std::min(100.f, stats.getMagicEffects().get(ESM::MagicEffect::ResistNormalWeapons).mMagnitude + - stats.getMagicEffects().get(ESM::MagicEffect::WeaknessToNormalWeapons).mMagnitude); + + float multiplier = 0; + if (resistance >= 0) + multiplier = 1 - resistance / 100.f; + else + multiplier = -(resistance-100) / 100.f; + + if (!(weapon.get()->mBase->mData.mFlags & ESM::Weapon::Silver + || weapon.get()->mBase->mData.mFlags & ESM::Weapon::Magical)) + damage *= multiplier; + + if (weapon.get()->mBase->mData.mFlags & ESM::Weapon::Silver + & actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) + damage *= MWBase::Environment::get().getWorld()->getStore().get().find("fWereWolfSilverWeaponDamageMult")->getFloat(); + + if (damage == 0 && attacker.getRefData().getHandle() == "player") + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResistsWeapons}"); + } + } diff --git a/apps/openmw/mwmechanics/combat.hpp b/apps/openmw/mwmechanics/combat.hpp index d666905f2..7f2415697 100644 --- a/apps/openmw/mwmechanics/combat.hpp +++ b/apps/openmw/mwmechanics/combat.hpp @@ -9,6 +9,8 @@ namespace MWMechanics /// @return can we block the attack? bool blockMeleeAttack (const MWWorld::Ptr& attacker, const MWWorld::Ptr& blocker, const MWWorld::Ptr& weapon, float damage); +void resistNormalWeapon (const MWWorld::Ptr& actor, const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon, float& damage); + } #endif From 264226dfe3e28054b4afe1ca3827211b1b85e898 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 22 Jan 2014 12:33:47 +0100 Subject: [PATCH 620/889] Handle iWereWolfBounty --- apps/openmw/mwmechanics/npcstats.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 4fff6eac8..e41ce2078 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -267,12 +267,16 @@ bool MWMechanics::NpcStats::hasBeenUsed (const std::string& id) const int MWMechanics::NpcStats::getBounty() const { - return mBounty; + if (mIsWerewolf) + return MWBase::Environment::get().getWorld()->getStore().get().find("iWereWolfBounty")->getInt(); + else + return mBounty; } void MWMechanics::NpcStats::setBounty (int bounty) { - mBounty = bounty; + if (!mIsWerewolf) + mBounty = bounty; } int MWMechanics::NpcStats::getFactionReputation (const std::string& faction) const From f27701cbe896503fc308f9320d4d0e510e9f6fb0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 22 Jan 2014 13:04:36 +0100 Subject: [PATCH 621/889] Detect selling stolen items or enchanting with stolen items --- apps/openmw/mwgui/enchantingdialog.cpp | 24 ++++++++++++++++++++++++ apps/openmw/mwgui/tradewindow.cpp | 19 +++++++++++++++++++ apps/openmw/mwmechanics/enchanting.hpp | 2 ++ 3 files changed, 45 insertions(+) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index d9d2a2ea8..92205c3e9 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -5,6 +5,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" @@ -296,6 +298,28 @@ namespace MWGui return; } + // check if the player is attempting to use a soulstone or item that was stolen from this actor + if (mPtr != player) + { + for (int i=0; i<2; ++i) + { + MWWorld::Ptr item = (i == 0) ? mEnchanting.getOldItem() : mEnchanting.getGem(); + if (Misc::StringUtils::ciEqual(item.getCellRef().mOwner, mPtr.getCellRef().mRefID)) + { + std::string msg = MWBase::Environment::get().getWorld()->getStore().get().find("sNotifyMessage49")->getString(); + if (msg.find("%s") != std::string::npos) + msg.replace(msg.find("%s"), 2, item.getClass().getName(item)); + MWBase::Environment::get().getWindowManager()->messageBox(msg); + MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); + MWBase::Environment::get().getMechanicsManager()->reportCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft, + item.getClass().getValue(item)); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue); + return; + } + } + } + int result = mEnchanting.create(); if(result==1) diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index d0c10bd30..92ba9470d 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -271,6 +271,25 @@ namespace MWGui return; } + // check if the player is attempting to sell back an item stolen from this actor + for (std::vector::iterator it = merchantBought.begin(); it != merchantBought.end(); ++it) + { + if (Misc::StringUtils::ciEqual(it->mBase.getCellRef().mOwner, mPtr.getCellRef().mRefID)) + { + std::string msg = gmst.find("sNotifyMessage49")->getString(); + if (msg.find("%s") != std::string::npos) + msg.replace(msg.find("%s"), 2, it->mBase.getClass().getName(it->mBase)); + MWBase::Environment::get().getWindowManager()->messageBox(msg); + MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); + MWBase::Environment::get().getMechanicsManager()->reportCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft, + it->mBase.getClass().getValue(it->mBase) + * it->mCount); + onCancelButtonClicked(mCancelButton); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); + return; + } + } + if(mCurrentBalance > mCurrentMerchantOffer) { //if npc is a creature: reject (no haggle) diff --git a/apps/openmw/mwmechanics/enchanting.hpp b/apps/openmw/mwmechanics/enchanting.hpp index 988ce41fc..ae0b25a4a 100644 --- a/apps/openmw/mwmechanics/enchanting.hpp +++ b/apps/openmw/mwmechanics/enchanting.hpp @@ -28,6 +28,8 @@ namespace MWMechanics void setEnchanter(MWWorld::Ptr enchanter); void setSelfEnchanting(bool selfEnchanting); void setOldItem(MWWorld::Ptr oldItem); + MWWorld::Ptr getOldItem() { return mOldItemPtr; } + MWWorld::Ptr getGem() { return mSoulGemPtr; } void setNewItemName(const std::string& s); void setEffect(ESM::EffectList effectList); void setSoulGem(MWWorld::Ptr soulGem); From 4c94289b1f5b7d8e1bda849e7c47f4b440f2080e Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 22 Jan 2014 13:30:07 +0100 Subject: [PATCH 622/889] Fix PC Health Percent function --- apps/openmw/mwdialogue/filter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index f132d13a3..bd64dcf9c 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -216,7 +216,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c float ratio = MWWorld::Class::get (player).getCreatureStats (player).getHealth().getCurrent() / MWWorld::Class::get (player).getCreatureStats (player).getHealth().getModified(); - return select.selectCompare (ratio); + return select.selectCompare (static_cast(ratio*100)); } case SelectWrapper::Function_PcDynamicStat: From ea21d8fec327bf448ef06e2eb1680e5d4c81ed18 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 22 Jan 2014 13:30:45 +0100 Subject: [PATCH 623/889] Fix CreatureTargetted function --- apps/openmw/mwdialogue/filter.cpp | 2 +- apps/openmw/mwmechanics/aicombat.cpp | 4 ++-- apps/openmw/mwmechanics/aicombat.hpp | 2 +- apps/openmw/mwmechanics/aisequence.cpp | 7 +++---- apps/openmw/mwmechanics/aisequence.hpp | 5 ++--- apps/openmw/mwmechanics/creaturestats.cpp | 4 +++- apps/openmw/mwscript/aiextensions.cpp | 12 +++--------- 7 files changed, 15 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index bd64dcf9c..731fafbf6 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -536,7 +536,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co case SelectWrapper::Function_CreatureTargetted: - return MWWorld::Class::get (mActor).getCreatureStats (mActor).getCreatureTargetted(); + return mActor.getClass().getCreatureStats (mActor).getCreatureTargetted(); case SelectWrapper::Function_Werewolf: diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index c01c4096b..283c7f042 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -360,9 +360,9 @@ namespace MWMechanics return 1; } - const std::string &AiCombat::getTargetId() const + MWWorld::Ptr AiCombat::getTarget() const { - return mTarget.getRefData().getHandle(); + return mTarget; } AiCombat *MWMechanics::AiCombat::clone() const diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 27f7f5d95..a24183c65 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -25,7 +25,7 @@ namespace MWMechanics virtual unsigned int getPriority() const; - const std::string &getTargetId() const; + MWWorld::Ptr getTarget() const; private: PathFinder mPathFinder; diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 73caa6ca7..3902b9186 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -55,13 +55,12 @@ int MWMechanics::AiSequence::getTypeId() const return mPackages.front()->getTypeId(); } -bool MWMechanics::AiSequence::getCombatTarget(std::string &targetActorId) const +MWWorld::Ptr MWMechanics::AiSequence::getCombatTarget() const { if (getTypeId() != AiPackage::TypeIdCombat) - return false; + return MWWorld::Ptr(); const AiCombat *combat = static_cast(mPackages.front()); - targetActorId = combat->getTargetId(); - return true; + return combat->getTarget(); } void MWMechanics::AiSequence::stopCombat() diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index d65c31616..075c43ff7 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -36,9 +36,8 @@ namespace MWMechanics int getTypeId() const; ///< @see enum AiPackage::TypeId - bool getCombatTarget (std::string &targetActorId) const; - ///< Return true and assign target if combat package is currently - /// active, return false otherwise + MWWorld::Ptr getCombatTarget () const; + ///< Return the combat target if a combat package is active, or an empty Ptr otherwise void stopCombat(); ///< Removes all combat packages until first non-combat or stack empty. diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 8f84a7979..ec300f6f0 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -348,7 +348,9 @@ namespace MWMechanics bool CreatureStats::getCreatureTargetted() const { - return false; + if (mAiSequence.getCombatTarget().isEmpty()) + return false; + return mAiSequence.getCombatTarget().getTypeName() == typeid(ESM::Creature).name(); } float CreatureStats::getEvasion() const diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 759d0ba94..75c635103 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -412,16 +412,10 @@ namespace MWScript std::string testedTargetId = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - const MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); - std::string currentTargetId; + const MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); - bool targetsAreEqual = false; - if (creatureStats.getAiSequence().getCombatTarget (currentTargetId)) - { - if (currentTargetId == testedTargetId) - targetsAreEqual = true; - } - runtime.push(int(targetsAreEqual)); + MWWorld::Ptr target = creatureStats.getAiSequence().getCombatTarget(); + runtime.push(Misc::StringUtils::ciEqual(target.getCellRef().mRefID, testedTargetId)); } }; From 2e6e0fd0a05363bba558aaf0b5d9bc9b2001e9dc Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 22 Jan 2014 14:45:36 +0100 Subject: [PATCH 624/889] Fix GetPcCell bug --- apps/openmw/mwscript/cellextensions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index 5de95d1d4..1834c5651 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -115,6 +115,7 @@ namespace MWScript current = region->mName; } + Misc::StringUtils::toLower(current); bool match = current.length()>=name.length() && current.substr (0, name.length())==name; From cf378ec31ef28a0a967b02dc19266fd1d91a9d9e Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 22 Jan 2014 15:25:03 +0100 Subject: [PATCH 625/889] Support optional volume and pitch arguments for soundgen events (e.g. moan 0.5 1.0) as required for some actors --- apps/openmw/mwrender/animation.cpp | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index cd2d9a47a..e8d215f5a 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -616,6 +616,13 @@ bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const s return true; } +void split(const std::string &s, char delim, std::vector &elems) { + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } +} void Animation::handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key) { @@ -630,14 +637,29 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co } if(evt.compare(0, 10, "soundgen: ") == 0) { - std::string sound = MWWorld::Class::get(mPtr).getSoundIdFromSndGen(mPtr, evt.substr(10)); + std::string soundgen = evt.substr(10); + + // The event can optionally contain volume and pitch modifiers + float volume=1.f, pitch=1.f; + if (soundgen.find(" ") != std::string::npos) + { + std::vector tokens; + split(soundgen, ' ', tokens); + soundgen = tokens[0]; + if (tokens.size() >= 2) + volume = Ogre::StringConverter::parseReal(tokens[1]); + if (tokens.size() >= 3) + pitch = Ogre::StringConverter::parseReal(tokens[2]); + } + + std::string sound = mPtr.getClass().getSoundIdFromSndGen(mPtr, soundgen); if(!sound.empty()) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); MWBase::SoundManager::PlayType type = MWBase::SoundManager::Play_TypeSfx; if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0) type = MWBase::SoundManager::Play_TypeFoot; - sndMgr->playSound3D(mPtr, sound, 1.0f, 1.0f, type); + sndMgr->playSound3D(mPtr, sound, volume, pitch, type); } return; } From 43bc223e683e9059b6b89653f91d10f8ac0a9978 Mon Sep 17 00:00:00 2001 From: pvdk Date: Wed, 22 Jan 2014 16:57:18 +0100 Subject: [PATCH 626/889] Added version info retrieval from git tags --- CMakeLists.txt | 26 +++- cmake/GetGitRevisionDescription.cmake | 159 +++++++++++++++++++++++ cmake/GetGitRevisionDescription.cmake.in | 38 ++++++ 3 files changed, 217 insertions(+), 6 deletions(-) create mode 100644 cmake/GetGitRevisionDescription.cmake create mode 100644 cmake/GetGitRevisionDescription.cmake.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 8eb8b44c2..9bd2cdddc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,15 +14,29 @@ endif (APPLE) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) -include (OpenMWMacros) +include(OpenMWMacros) # Version -set (OPENMW_VERSION_MAJOR 0) -set (OPENMW_VERSION_MINOR 27) -set (OPENMW_VERSION_RELEASE 0) +include(GetGitRevisionDescription) -set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") +get_git_tag_revision(taghash --tags --max-count=1) +get_git_head_revision(refspec commithash) +git_describe(version --tags ${taghash}) + +string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" match "${version}") +if (match) + string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" OPENMW_VERSION_MAJOR "${version}") + string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_MINOR "${version}") + string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_RELEASE "${version}") + + set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") + set(OPENMW_VERSION_COMMIT "${commithash}") + + message(STATUS "Configuring OpenMW ${OPENMW_VERSION}...") +else (match) + message(FATAL_ERROR "Failed to get valid version information from Git") +endif (match) # doxygen main page @@ -319,7 +333,7 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg configure_file(${OpenMW_SOURCE_DIR}/files/opencs.cfg "${OpenMW_BINARY_DIR}/opencs.cfg") - + configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters "${OpenMW_BINARY_DIR}/resources/defaultfilters" COPYONLY) diff --git a/cmake/GetGitRevisionDescription.cmake b/cmake/GetGitRevisionDescription.cmake new file mode 100644 index 000000000..f70f64261 --- /dev/null +++ b/cmake/GetGitRevisionDescription.cmake @@ -0,0 +1,159 @@ +# - Returns a version string from Git +# +# These functions force a re-configure on each git commit so that you can +# trust the values of the variables in your build system. +# +# get_git_head_revision( [ ...]) +# +# Returns the refspec and sha hash of the current head revision +# +# git_describe( [ ...]) +# +# Returns the results of git describe on the source tree, and adjusting +# the output so that it tests false if an error occurs. +# +# git_get_exact_tag( [ ...]) +# +# Returns the results of git describe --exact-match on the source tree, +# and adjusting the output so that it tests false if there was no exact +# matching tag. +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +if(__get_git_revision_description) + return() +endif() +set(__get_git_revision_description YES) + +# We must run the following at "include" time, not at function call time, +# to find the path to this module rather than the path to a calling list file +get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) + +function(get_git_head_revision _refspecvar _hashvar) + set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories + set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") + get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) + if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) + # We have reached the root directory, we are not in git + set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) + set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) + return() + endif() + + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + endwhile() + + # check if this is a submodule + if(NOT IS_DIRECTORY ${GIT_DIR}) + file(READ ${GIT_DIR} submodule) + string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) + get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) + get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) + endif() + + set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") + + if(NOT EXISTS "${GIT_DATA}") + file(MAKE_DIRECTORY "${GIT_DATA}") + endif() + + if(NOT EXISTS "${GIT_DIR}/HEAD") + return() + endif() + + set(HEAD_FILE "${GIT_DATA}/HEAD") + configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) + + configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" + "${GIT_DATA}/grabRef.cmake" @ONLY) + include("${GIT_DATA}/grabRef.cmake") + + set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) + set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) +endfunction() + +function(git_describe _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + + #get_git_head_revision(refspec hash) + + if(NOT GIT_FOUND) + set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) + return() + endif() + + #if(NOT hash) + # set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) + # return() + #endif() + + # TODO sanitize + #if((${ARGN}" MATCHES "&&") OR + # (ARGN MATCHES "||") OR + # (ARGN MATCHES "\\;")) + # message("Please report the following error to the project!") + # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") + #endif() + + #message(STATUS "Arguments to execute_process: ${ARGN}") + + execute_process(COMMAND + "${GIT_EXECUTABLE}" + describe + #${hash} + ${ARGN} + WORKING_DIRECTORY + "${CMAKE_SOURCE_DIR}" + RESULT_VARIABLE + res + OUTPUT_VARIABLE + out + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} "${out}" PARENT_SCOPE) +endfunction() + +function(get_git_tag_revision _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + + execute_process(COMMAND + "${GIT_EXECUTABLE}" + rev-list + ${ARGN} + WORKING_DIRECTORY + "${CMAKE_SOURCE_DIR}" + RESULT_VARIABLE + res + OUTPUT_VARIABLE + out + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} "${out}" PARENT_SCOPE) +endfunction() + + diff --git a/cmake/GetGitRevisionDescription.cmake.in b/cmake/GetGitRevisionDescription.cmake.in new file mode 100644 index 000000000..888ce13aa --- /dev/null +++ b/cmake/GetGitRevisionDescription.cmake.in @@ -0,0 +1,38 @@ +# +# Internal file for GetGitRevisionDescription.cmake +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +set(HEAD_HASH) + +file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) + +string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) +if(HEAD_CONTENTS MATCHES "ref") + # named branch + string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") + if(EXISTS "@GIT_DIR@/${HEAD_REF}") + configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) + elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}") + configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) + set(HEAD_HASH "${HEAD_REF}") + endif() +else() + # detached HEAD + configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) +endif() + +if(NOT HEAD_HASH) + file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) + string(STRIP "${HEAD_HASH}" HEAD_HASH) +endif() From c95b8bcb391f32ba92458c14b27d20f90a6e8397 Mon Sep 17 00:00:00 2001 From: pvdk Date: Wed, 22 Jan 2014 17:33:55 +0100 Subject: [PATCH 627/889] Moved the generated version header stuff into components --- apps/openmw/CMakeLists.txt | 5 ----- apps/openmw/config.hpp.cmake | 9 --------- apps/openmw/main.cpp | 3 +-- components/CMakeLists.txt | 12 ++++++++++-- 4 files changed, 11 insertions(+), 18 deletions(-) delete mode 100644 apps/openmw/config.hpp.cmake diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 3b533b416..ff0ff65d2 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -1,7 +1,3 @@ - -# config file -configure_file ("${CMAKE_CURRENT_SOURCE_DIR}/config.hpp.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/config.hpp") - # local files set(GAME main.cpp @@ -12,7 +8,6 @@ if(NOT WIN32) endif() set(GAME_HEADER engine.hpp - config.hpp ) source_group(game FILES ${GAME} ${GAME_HEADER}) diff --git a/apps/openmw/config.hpp.cmake b/apps/openmw/config.hpp.cmake deleted file mode 100644 index 848fbe0eb..000000000 --- a/apps/openmw/config.hpp.cmake +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef CONFIG_H -#define CONFIG_H - -#define OPENMW_VERSION_MAJOR @OPENMW_VERSION_MAJOR@ -#define OPENMW_VERSION_MINOR @OPENMW_VERSION_MINOR@ -#define OPENMW_VERSION_RELEASE @OPENMW_VERSION_RELEASE@ -#define OPENMW_VERSION "@OPENMW_VERSION@" - -#endif diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 3129e6bd3..260a35a9b 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include @@ -30,8 +31,6 @@ extern int is_debugger_attached(void); #include #endif -#include "config.hpp" - #include /** * Workaround for problems with whitespaces in paths in older versions of Boost library diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index a037fd5fa..9c37d2e62 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -1,5 +1,9 @@ project (Components) set (CMAKE_BUILD_TYPE DEBUG) + +# Version file +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version/version.hpp.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/version/version.hpp") + # source files add_component_dir (settings @@ -75,8 +79,12 @@ add_component_dir (loadinglistener ) add_component_dir (ogreinit - ogreinit ogreplugin - ) + ogreinit ogreplugin + ) + +add_component_dir (version + version + ) set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui ) From fa186f5ec1430c6b75ffbcf2bf0848b6498e90fe Mon Sep 17 00:00:00 2001 From: pvdk Date: Wed, 22 Jan 2014 17:51:10 +0100 Subject: [PATCH 628/889] Added version information to the launcher --- apps/launcher/maindialog.cpp | 9 ++++++++ files/ui/mainwindow.ui | 42 +++++++++++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 9b3c4e1b0..4aa8a4816 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -1,5 +1,8 @@ #include "maindialog.hpp" +#include + +#include #include #include #include @@ -67,6 +70,12 @@ Launcher::MainDialog::MainDialog(QWidget *parent) // Remove what's this? button setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); + // Add version information to bottom of the window + QString revision(OPENMW_VERSION_COMMIT); + revision = revision.left(10); + + versionLabel->setText(tr("OpenMW %0 revision %1").arg(OPENMW_VERSION, revision)); + createIcons(); } diff --git a/files/ui/mainwindow.ui b/files/ui/mainwindow.ui index a1dfb172b..5f2be05a2 100644 --- a/files/ui/mainwindow.ui +++ b/files/ui/mainwindow.ui @@ -2,6 +2,14 @@ MainWindow + + + 0 + 0 + 575 + 535 + + 575 @@ -56,11 +64,35 @@ - - - QDialogButtonBox::Close - - + + + + + OpenMW version + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QDialogButtonBox::Close + + + + From c354cd52be15d92940e97dcaee9f7d3de0a86024 Mon Sep 17 00:00:00 2001 From: pvdk Date: Wed, 22 Jan 2014 18:37:49 +0100 Subject: [PATCH 629/889] Added compile date and time to the version info in the launcher --- apps/launcher/maindialog.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 4aa8a4816..42d3d1754 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -3,6 +3,8 @@ #include #include +#include +#include #include #include #include @@ -74,7 +76,12 @@ Launcher::MainDialog::MainDialog(QWidget *parent) QString revision(OPENMW_VERSION_COMMIT); revision = revision.left(10); + QDate date(QDate::fromString(__DATE__, QLatin1String("MMM dd yyyy"))); + QTime time(QTime::fromString(__TIME__, QLatin1String("hh:m:ss"))); + versionLabel->setText(tr("OpenMW %0 revision %1").arg(OPENMW_VERSION, revision)); + versionLabel->setToolTip(tr("Compiled on %0 %1").arg(date.toString(Qt::SystemLocaleShortDate), + time.toString(Qt::SystemLocaleShortDate))); createIcons(); } From d92ded3bcd37af3a5bfd56b05e3f3e58903efe1e Mon Sep 17 00:00:00 2001 From: pvdk Date: Wed, 22 Jan 2014 19:30:41 +0100 Subject: [PATCH 630/889] Forgot adding the version header CMake file --- components/version/version.hpp.cmake | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 components/version/version.hpp.cmake diff --git a/components/version/version.hpp.cmake b/components/version/version.hpp.cmake new file mode 100644 index 000000000..4adb0d8c2 --- /dev/null +++ b/components/version/version.hpp.cmake @@ -0,0 +1,12 @@ +#ifndef VERSION_HPP +#define VERSION_HPP + +#define OPENMW_VERSION_MAJOR @OPENMW_VERSION_MAJOR@ +#define OPENMW_VERSION_MINOR @OPENMW_VERSION_MINOR@ +#define OPENMW_VERSION_RELEASE @OPENMW_VERSION_RELEASE@ +#define OPENMW_VERSION "@OPENMW_VERSION@" + +#define OPENMW_VERSION_COMMIT "@OPENMW_VERSION_COMMIT@" + +#endif // VERSION_HPP + From 4d9d31b25e9ad56bd35580b35134118e88ea3053 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 23 Jan 2014 09:40:32 +0100 Subject: [PATCH 631/889] refrences cloning does not work --- apps/opencs/model/world/collection.hpp | 1 + apps/opencs/model/world/data.cpp | 1 + apps/opencs/model/world/idtable.cpp | 2 -- apps/opencs/model/world/refcollection.cpp | 10 ++++------ apps/opencs/view/world/genericcreator.cpp | 7 +------ apps/opencs/view/world/referencecreator.cpp | 2 +- 6 files changed, 8 insertions(+), 15 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 8ecad816b..74019dda7 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -205,6 +205,7 @@ namespace CSMWorld const UniversalId::ArgumentType argumentType) { Record copy = getRecord(origin); + copy.mState = RecordBase::State_ModifiedOnly; if (argumentType == UniversalId::ArgumentType_Id) { copy.get().mId = destination; diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 04e35cdaa..69fb4ab1e 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -194,6 +194,7 @@ CSMWorld::Data::Data() : mRefs (mCells) mRefs.addColumn (new StringIdColumn (true)); mRefs.addColumn (new RecordStateColumn); + mRefs.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Reference)); mRefs.addColumn (new CellColumn); mRefs.addColumn (new IdColumn); mRefs.addColumn (new PosColumn (&CellRef::mPos, 0, false)); diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index d4008fb87..f131f7087 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -133,8 +133,6 @@ void CSMWorld::IdTable::cloneRecord(const std::string& origin, beginInsertRows (QModelIndex(), index, index); mIdCollection->cloneRecord(origin, destination, type, argumentType); endInsertRows(); - emit dataChanged (CSMWorld::IdTable::index (0, 0), - CSMWorld::IdTable::index (mIdCollection->getSize()-1, mIdCollection->getColumns()-1)); } diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 2ecd3b663..6b0081258 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -43,10 +43,8 @@ void CSMWorld::RefCollection::cloneRecord(const std::string& origin, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType) { - Record originRecord = getRecord(origin); - Record *copy = dynamic_cast* >(originRecord.clone()); - copy->mState = CSMWorld::RecordBase::State_ModifiedOnly; - copy->get().mId = getNewId(); - insertRecord(*copy, getAppendIndex(destination, type)); - delete copy; + Record clone(getRecord(origin)); + clone.mState = CSMWorld::RecordBase::State_ModifiedOnly; + clone.get().mId = destination; + insertRecord(clone, getAppendIndex(destination, type), type); } \ No newline at end of file diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 2fcce9186..ab387393b 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -157,9 +157,4 @@ void CSVWorld::GenericCreator::cloneMode(const std::string& originid, mClonedId = originid; mClonedType = type; mArgumentType = argumentType; - - if (argumentType == CSMWorld::UniversalId::ArgumentType_Id) - { - mId->setText(QString::fromStdString(mClonedId)); - } -} +} \ No newline at end of file diff --git a/apps/opencs/view/world/referencecreator.cpp b/apps/opencs/view/world/referencecreator.cpp index aef25a81d..06b3eb76d 100644 --- a/apps/opencs/view/world/referencecreator.cpp +++ b/apps/opencs/view/world/referencecreator.cpp @@ -40,9 +40,9 @@ CSVWorld::ReferenceCreator::ReferenceCreator (CSMWorld::Data& data, QUndoStack& void CSVWorld::ReferenceCreator::reset() { + GenericCreator::reset(); mCell->setText (""); mId = getData().getReferences().getNewId(); - GenericCreator::reset(); } std::string CSVWorld::ReferenceCreator::getErrors() const From 22cb4784b5079bb8d3d453bdb076dfc4b438d1d4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 23 Jan 2014 11:29:40 +0100 Subject: [PATCH 632/889] store cell state in saved game files (no references yet) --- apps/openmw/mwstate/statemanagerimp.cpp | 127 +++++++++++++----------- apps/openmw/mwstate/statemanagerimp.hpp | 2 +- apps/openmw/mwworld/cells.cpp | 96 ++++++++++++++++++ apps/openmw/mwworld/cells.hpp | 17 +++- apps/openmw/mwworld/cellstore.cpp | 21 ++++ apps/openmw/mwworld/cellstore.hpp | 10 +- apps/openmw/mwworld/worldimp.cpp | 8 +- components/CMakeLists.txt | 2 +- components/esm/cellstate.cpp | 17 ++++ components/esm/cellstate.hpp | 25 +++++ components/esm/defs.hpp | 3 +- 11 files changed, 261 insertions(+), 67 deletions(-) create mode 100644 components/esm/cellstate.cpp create mode 100644 components/esm/cellstate.hpp diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 8571e93ff..7020678d0 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -24,9 +24,9 @@ #include "../mwscript/globalscripts.hpp" -void MWState::StateManager::cleanup() +void MWState::StateManager::cleanup (bool force) { - if (mState!=State_NoGame) + if (mState!=State_NoGame || force) { MWBase::Environment::get().getSoundManager()->clear(); MWBase::Environment::get().getDialogueManager()->clear(); @@ -184,77 +184,86 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot void MWState::StateManager::loadGame (const Character *character, const Slot *slot) { - cleanup(); - - mTimePlayed = slot->mProfile.mTimePlayed; - - ESM::ESMReader reader; - reader.open (slot->mPath.string()); - - while (reader.hasMoreRecs()) + try { - ESM::NAME n = reader.getRecName(); - reader.getRecHeader(); + cleanup(); - switch (n.val) + mTimePlayed = slot->mProfile.mTimePlayed; + + ESM::ESMReader reader; + reader.open (slot->mPath.string()); + + while (reader.hasMoreRecs()) { - case ESM::REC_SAVE: + ESM::NAME n = reader.getRecName(); + reader.getRecHeader(); - // don't need to read that here - reader.skipRecord(); - break; + switch (n.val) + { + case ESM::REC_SAVE: - case ESM::REC_JOUR: - case ESM::REC_QUES: + // don't need to read that here + reader.skipRecord(); + break; - MWBase::Environment::get().getJournal()->readRecord (reader, n.val); - break; + case ESM::REC_JOUR: + case ESM::REC_QUES: - case ESM::REC_ALCH: - case ESM::REC_ARMO: - case ESM::REC_BOOK: - case ESM::REC_CLAS: - case ESM::REC_CLOT: - case ESM::REC_ENCH: - case ESM::REC_NPC_: - case ESM::REC_SPEL: - case ESM::REC_WEAP: - case ESM::REC_GLOB: - case ESM::REC_PLAY: + MWBase::Environment::get().getJournal()->readRecord (reader, n.val); + break; - MWBase::Environment::get().getWorld()->readRecord (reader, n.val); - break; + case ESM::REC_ALCH: + case ESM::REC_ARMO: + case ESM::REC_BOOK: + case ESM::REC_CLAS: + case ESM::REC_CLOT: + case ESM::REC_ENCH: + case ESM::REC_NPC_: + case ESM::REC_SPEL: + case ESM::REC_WEAP: + case ESM::REC_GLOB: + case ESM::REC_PLAY: + case ESM::REC_CSTA: - case ESM::REC_GSCR: + MWBase::Environment::get().getWorld()->readRecord (reader, n.val); + break; - MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val); - break; + case ESM::REC_GSCR: - default: + MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val); + break; - // ignore invalid records - /// \todo log error - reader.skipRecord(); + default: + + // ignore invalid records + /// \todo log error + reader.skipRecord(); + } } + + mCharacterManager.setCurrentCharacter(character); + + mState = State_Running; + + Settings::Manager::setString ("character", "Saves", + slot->mPath.parent_path().filename().string()); + + MWBase::Environment::get().getWorld()->setupPlayer(); + MWBase::Environment::get().getWorld()->renderPlayer(); + MWBase::Environment::get().getWindowManager()->updatePlayer(); + MWBase::Environment::get().getMechanicsManager()->playerLoaded(); + + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + + ESM::CellId cellId = ptr.getCell()->mCell->getCellId(); + + MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition()); + } + catch (const std::exception& e) + { + std::cerr << "failed to load saved game: " << e.what() << std::endl; + cleanup (true); } - - mCharacterManager.setCurrentCharacter(character); - - mState = State_Running; - - Settings::Manager::setString ("character", "Saves", - slot->mPath.parent_path().filename().string()); - - MWBase::Environment::get().getWorld()->setupPlayer(); - MWBase::Environment::get().getWorld()->renderPlayer(); - MWBase::Environment::get().getWindowManager()->updatePlayer(); - MWBase::Environment::get().getMechanicsManager()->playerLoaded(); - - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - - ESM::CellId cellId = ptr.getCell()->mCell->getCellId(); - - MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition()); } MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index a2abcfd1b..d6bb7575d 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -19,7 +19,7 @@ namespace MWState private: - void cleanup(); + void cleanup (bool force = false); public: diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index c844b689e..ead12567f 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -1,5 +1,10 @@ #include "cells.hpp" +#include +#include +#include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -59,6 +64,30 @@ MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, CellStore& return ptr; } +void MWWorld::Cells::writeCell (ESM::ESMWriter& writer, const CellStore& cell) const +{ + ESM::CellState cellState; + + cell.saveState (cellState); + + writer.startRecord (ESM::REC_CSTA); + cellState.mId.save (writer); + cellState.save (writer); + /// \todo write references + writer.endRecord (ESM::REC_CSTA); +} + +bool MWWorld::Cells::hasState (const CellStore& cellStore) const +{ + if (cellStore.mState==CellStore::State_Loaded) + return true; + + if (cellStore.mCell->mData.mFlags & ESM::Cell::Interior) + return cellStore.mCell->mData.mFlags & ESM::Cell::HasWater; + else + return false; +} + MWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector& reader) : mStore (store), mReader (reader), mIdCache (40, std::pair ("", (CellStore*)0)), /// \todo make cache size configurable @@ -121,6 +150,14 @@ MWWorld::CellStore *MWWorld::Cells::getInterior (const std::string& name) return &result->second; } +MWWorld::CellStore *MWWorld::Cells::getCell (const ESM::CellId& id) +{ + if (id.mPaged) + return getExterior (id.mIndex.mX, id.mIndex.mY); + + return getInterior (id.mWorldspace); +} + MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, CellStore& cell, bool searchInContainers) { @@ -271,3 +308,62 @@ void MWWorld::Cells::getExteriorPtrs(const std::string &name, std::vector::const_iterator iter (mInteriors.begin()); + iter!=mInteriors.end(); ++iter) + if (hasState (iter->second)) + ++count; + + for (std::map, CellStore>::const_iterator iter (mExteriors.begin()); + iter!=mExteriors.end(); ++iter) + if (hasState (iter->second)) + ++count; + + return count; +} + +void MWWorld::Cells::write (ESM::ESMWriter& writer) const +{ + for (std::map, CellStore>::const_iterator iter (mExteriors.begin()); + iter!=mExteriors.end(); ++iter) + if (hasState (iter->second)) + writeCell (writer, iter->second); + + for (std::map::const_iterator iter (mInteriors.begin()); + iter!=mInteriors.end(); ++iter) + if (hasState (iter->second)) + writeCell (writer, iter->second); +} + +bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, int32_t type) +{ + if (type==ESM::REC_CSTA) + { + ESM::CellState state; + state.mId.load (reader); + + CellStore *cellStore = 0; + + try + { + cellStore = getCell (state.mId); + } + catch (...) + { + // silently drop cells that don't exist anymore + /// \todo log + } + + state.load (reader); + cellStore->loadState (state); + reader.skipRecord(); + + return true; + } + + return false; +} \ No newline at end of file diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 31de2f60e..27e107646 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -10,6 +10,8 @@ namespace ESM { class ESMReader; + class ESMWriter; + struct CellId; } namespace MWWorld @@ -33,18 +35,23 @@ namespace MWWorld Ptr getPtrAndCache (const std::string& name, CellStore& cellStore); + void writeCell (ESM::ESMWriter& writer, const CellStore& cell) const; + + bool hasState (const CellStore& cellStore) const; + ///< Check if cell has state that needs to be included in a saved game file. + public: void clear(); Cells (const MWWorld::ESMStore& store, std::vector& reader); - ///< \todo pass the dynamic part of the ESMStore isntead (once it is written) of the whole - /// world CellStore *getExterior (int x, int y); CellStore *getInterior (const std::string& name); + CellStore *getCell (const ESM::CellId& id); + Ptr getPtr (const std::string& name, CellStore& cellStore, bool searchInContainers = false); ///< \param searchInContainers Only affect loaded cells. /// @note name must be lower case @@ -56,6 +63,12 @@ namespace MWWorld /// @note Due to the current implementation of getPtr this only supports one Ptr per cell. /// @note name must be lower case void getExteriorPtrs (const std::string& name, std::vector& out); + + int countSavedGameRecords() const; + + void write (ESM::ESMWriter& writer) const; + + bool readRecord (ESM::ESMReader& reader, int32_t type); }; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index cffa0537a..3cbf85e8e 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -2,6 +2,9 @@ #include +#include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -230,4 +233,22 @@ namespace MWWorld << "WARNING: Ignoring reference '" << ref.mRefID << "' of unhandled type\n"; } } + + void CellStore::loadState (const ESM::CellState& state) + { + if (mCell->mData.mFlags & ESM::Cell::Interior && mCell->mData.mFlags & ESM::Cell::HasWater) + mWaterLevel = state.mWaterLevel; + + mWaterLevel = state.mWaterLevel; + } + + void CellStore::saveState (ESM::CellState& state) const + { + state.mId = mCell->getCellId(); + + if (mCell->mData.mFlags & ESM::Cell::Interior && mCell->mData.mFlags & ESM::Cell::HasWater) + state.mWaterLevel = mWaterLevel; + + state.mWaterLevel = mWaterLevel; + } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 1da219a9e..64843e7a4 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -7,6 +7,11 @@ #include "livecellref.hpp" #include "esmstore.hpp" +namespace ESM +{ + struct CellState; +} + namespace MWWorld { @@ -133,6 +138,10 @@ namespace MWWorld Ptr searchInContainer (const std::string& id); + void loadState (const ESM::CellState& state); + + void saveState (ESM::CellState& state) const; + private: template @@ -158,7 +167,6 @@ namespace MWWorld ///< Make case-adjustments to \a ref and insert it into the respective container. /// /// Invalid \a ref objects are silently dropped. - }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 706701fa8..f8321d74e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -304,13 +304,16 @@ namespace MWWorld { return mStore.countSavedGameRecords() - +mGlobalVariables.countSavedGameRecords(); + +mGlobalVariables.countSavedGameRecords() + +1 // player record + +mCells.countSavedGameRecords(); } void World::write (ESM::ESMWriter& writer) const { mStore.write (writer); mGlobalVariables.write (writer); + mCells.write (writer); mPlayer->write (writer); } @@ -318,7 +321,8 @@ namespace MWWorld { if (!mStore.readRecord (reader, type) && !mGlobalVariables.readRecord (reader, type) && - !mPlayer->readRecord (reader, type)) + !mPlayer->readRecord (reader, type) && + !mCells.readRecord (reader, type)) { throw std::runtime_error ("unknown record in saved game"); } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 4c0bff59d..d9ab8129d 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -40,7 +40,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame journalentry queststate locals globalscript player objectstate cellid + savedgame journalentry queststate locals globalscript player objectstate cellid cellstate ) add_component_dir (misc diff --git a/components/esm/cellstate.cpp b/components/esm/cellstate.cpp new file mode 100644 index 000000000..1f7e8197e --- /dev/null +++ b/components/esm/cellstate.cpp @@ -0,0 +1,17 @@ + +#include "cellstate.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::CellState::load (ESMReader &esm) +{ + mWaterLevel = 0; + esm.getHNOT (mWaterLevel, "WLVL"); +} + +void ESM::CellState::save (ESMWriter &esm) const +{ + if (!mId.mPaged) + esm.writeHNT ("WLVL", mWaterLevel); +} \ No newline at end of file diff --git a/components/esm/cellstate.hpp b/components/esm/cellstate.hpp new file mode 100644 index 000000000..cd0db3067 --- /dev/null +++ b/components/esm/cellstate.hpp @@ -0,0 +1,25 @@ +#ifndef OPENMW_ESM_CELLSTATE_H +#define OPENMW_ESM_CELLSTATE_H + +#include "cellid.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + /// \note Does not include references + struct CellState + { + CellId mId; + + float mWaterLevel; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif \ No newline at end of file diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 2b956d216..1ca6e88fc 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -88,7 +88,8 @@ enum RecNameInts REC_JOUR = 0x524f55a4, REC_QUES = 0x53455551, REC_GSCR = 0x52435347, - REC_PLAY = 0x504c4159, + REC_PLAY = 0x59414c50, + REC_CSTA = 0x41545343, // format 1 REC_FILT = 0x544C4946 From 358cc3c62febd25a28f7e81156e0da3177153d6e Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 23 Jan 2014 12:24:06 +0100 Subject: [PATCH 633/889] Closes #1060: Fix incorrect message box size --- apps/openmw/mwgui/messagebox.cpp | 33 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 644b8f66a..74231c008 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -287,24 +287,6 @@ namespace MWGui else { mainWidgetSize.width = textSize.width + 3*textPadding; } - mainWidgetSize.height = textSize.height + 2*textPadding + textButtonPadding + buttonHeight * buttons.size() + buttonMainPadding; - - mMainWidget->setSize(mainWidgetSize); - - MyGUI::IntCoord absCoord; - absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2; - absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2; - - mMainWidget->setCoord(absCoord); - mMainWidget->setSize(mainWidgetSize); - - - MyGUI::IntCoord messageWidgetCoord; - messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; - messageWidgetCoord.top = textPadding; - mMessageWidget->setCoord(messageWidgetCoord); - - mMessageWidget->setSize(textSize); MyGUI::IntCoord buttonCord; MyGUI::IntSize buttonSize(0, buttonHeight); @@ -326,6 +308,21 @@ namespace MWGui top += buttonSize.height + 2*buttonTopPadding; } + mainWidgetSize.height = top + buttonMainPadding; + mMainWidget->setSize(mainWidgetSize); + + MyGUI::IntPoint absPos; + absPos.left = (gameWindowSize.width - mainWidgetSize.width)/2; + absPos.top = (gameWindowSize.height - mainWidgetSize.height)/2; + + mMainWidget->setPosition(absPos); + + MyGUI::IntCoord messageWidgetCoord; + messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; + messageWidgetCoord.top = textPadding; + messageWidgetCoord.width = textSize.width; + messageWidgetCoord.height = textSize.height; + mMessageWidget->setCoord(messageWidgetCoord); } // Set key focus to "Ok" button From dd7d80ffbc6e5b7eca0cef25d18c1d71fcc83742 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 23 Jan 2014 12:51:25 +0100 Subject: [PATCH 634/889] removed a redundant field from object state --- components/esm/objectstate.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp index bbbc4798f..c599bb973 100644 --- a/components/esm/objectstate.hpp +++ b/components/esm/objectstate.hpp @@ -17,8 +17,6 @@ namespace ESM ///< \brief Save state for objects, that do not use custom data struct ObjectState { - std::string mId; - CellRef mRef; unsigned char mHasLocals; From 419e3a7d30f223088d16dfdc1a838b59b5cf6121 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 23 Jan 2014 12:51:42 +0100 Subject: [PATCH 635/889] write references in cells to saved game file --- apps/openmw/mwworld/cells.cpp | 2 +- apps/openmw/mwworld/cellstore.cpp | 50 +++++++++++++++++++++++++++++++ apps/openmw/mwworld/cellstore.hpp | 2 ++ components/esm/defs.hpp | 1 + 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index ead12567f..77ea3856c 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -73,7 +73,7 @@ void MWWorld::Cells::writeCell (ESM::ESMWriter& writer, const CellStore& cell) c writer.startRecord (ESM::REC_CSTA); cellState.mId.save (writer); cellState.save (writer); - /// \todo write references + cell.writeReferences (writer); writer.endRecord (ESM::REC_CSTA); } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 3cbf85e8e..b8cd15f53 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -4,6 +4,8 @@ #include #include +#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -32,6 +34,30 @@ namespace return MWWorld::Ptr(); } + + template + void writeReferenceCollection (ESM::ESMWriter& writer, + const MWWorld::CellRefList& collection) + { + if (!collection.mList.empty()) + { + // section header + writer.writeHNT ("CSEC", collection.mList.front().mBase->sRecordId); + + // references + for (typename MWWorld::CellRefList::List::const_iterator + iter (collection.mList.begin()); + iter!=collection.mList.end(); ++iter) + { + RecordType state; + iter->save (state); + + writer.startRecord (ESM::REC_OBJE); + state.save (writer); + writer.endRecord (ESM::REC_OBJE); + } + } + } } namespace MWWorld @@ -251,4 +277,28 @@ namespace MWWorld state.mWaterLevel = mWaterLevel; } + + void CellStore::writeReferences (ESM::ESMWriter& writer) const + { + writeReferenceCollection (writer, mActivators); + writeReferenceCollection (writer, mPotions); + writeReferenceCollection (writer, mAppas); + writeReferenceCollection (writer, mArmors); + writeReferenceCollection (writer, mBooks); + writeReferenceCollection (writer, mClothes); + writeReferenceCollection (writer, mContainers); + writeReferenceCollection (writer, mCreatures); + writeReferenceCollection (writer, mDoors); + writeReferenceCollection (writer, mIngreds); + writeReferenceCollection (writer, mCreatureLists); + writeReferenceCollection (writer, mItemLists); + writeReferenceCollection (writer, mLights); + writeReferenceCollection (writer, mLockpicks); + writeReferenceCollection (writer, mMiscItems); + writeReferenceCollection (writer, mNpcs); + writeReferenceCollection (writer, mProbes); + writeReferenceCollection (writer, mRepairs); + writeReferenceCollection (writer, mStatics); + writeReferenceCollection (writer, mWeapons); + } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 64843e7a4..feebfe90a 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -142,6 +142,8 @@ namespace MWWorld void saveState (ESM::CellState& state) const; + void writeReferences (ESM::ESMWriter& writer) const; + private: template diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 1ca6e88fc..74d987df8 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -90,6 +90,7 @@ enum RecNameInts REC_GSCR = 0x52435347, REC_PLAY = 0x59414c50, REC_CSTA = 0x41545343, + REC_OBJE = 0x454a424f, // format 1 REC_FILT = 0x544C4946 From 460089c0aa1079764de73ff27f160ce0c337c845 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 23 Jan 2014 12:53:55 +0100 Subject: [PATCH 636/889] ignore deleted references that did not came from a content file --- apps/openmw/mwworld/cellstore.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index b8cd15f53..aea4e5b6a 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -49,6 +49,9 @@ namespace iter (collection.mList.begin()); iter!=collection.mList.end(); ++iter) { + if (iter->mData.getCount()==0 && iter->mRef.mRefNum.mContentFile==-1) + continue; // deleted file that did not came from a content file -> ignore + RecordType state; iter->save (state); From 849ee54399486406b2961c58a92efe857b1a5732 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 23 Jan 2014 12:54:37 +0100 Subject: [PATCH 637/889] Feature #764: Show quest names in quest book if present --- apps/openmw/mwgui/journalviewmodel.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index 79a77070a..ba6552225 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -206,10 +206,12 @@ struct JournalViewModelImpl : JournalViewModel if (active_only && i->second.isFinished ()) continue; - /// \todo quest.getName() is broken? returns empty string - //const MWDialogue::Quest& quest = i->second; - - visitor (reinterpret_cast (&i->second), toUtf8Span (i->first)); + const MWDialogue::Quest& quest = i->second; + // Unfortunately Morrowind.esm has no quest names, since the quest book was added with tribunal. + if (quest.getName().empty()) + visitor (reinterpret_cast (&i->second), toUtf8Span (i->first)); + else + visitor (reinterpret_cast (&i->second), toUtf8Span (quest.getName())); } } From 70264eae3b64811c90ece2dbebb256f70f9af41f Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 23 Jan 2014 13:08:56 +0100 Subject: [PATCH 638/889] Feature #764: Quest completetion status in quest book --- apps/openmw/mwdialogue/quest.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwdialogue/quest.cpp b/apps/openmw/mwdialogue/quest.cpp index 5e2739be1..520331bc1 100644 --- a/apps/openmw/mwdialogue/quest.cpp +++ b/apps/openmw/mwdialogue/quest.cpp @@ -39,21 +39,24 @@ namespace MWDialogue const ESM::Dialogue *dialogue = MWBase::Environment::get().getWorld()->getStore().get().find (mTopic); + bool found=false; for (std::vector::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) if (iter->mData.mDisposition==index && iter->mQuestStatus!=ESM::DialInfo::QS_Name) { - mIndex = index; - if (iter->mQuestStatus==ESM::DialInfo::QS_Finished) mFinished = true; else if (iter->mQuestStatus==ESM::DialInfo::QS_Restart) mFinished = false; - return; + found = true; + // Don't return here. Quest status may actually be in a different info record, since we don't merge these (yet?) } - throw std::runtime_error ("unknown journal index for topic " + mTopic); + if (found) + mIndex = index; + else + throw std::runtime_error ("unknown journal index for topic " + mTopic); } bool Quest::isFinished() const From 19bef4fce8cf490be89c40057ab498fe30d0394f Mon Sep 17 00:00:00 2001 From: pvdk Date: Thu, 23 Jan 2014 13:18:05 +0100 Subject: [PATCH 639/889] Distinguish between release and development builds --- CMakeLists.txt | 23 ++++++++++++----------- apps/launcher/maindialog.cpp | 12 +++++++++--- components/version/version.hpp.cmake | 3 ++- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bd2cdddc..01ac9b23f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,23 +20,24 @@ include(OpenMWMacros) include(GetGitRevisionDescription) -get_git_tag_revision(taghash --tags --max-count=1) -get_git_head_revision(refspec commithash) -git_describe(version --tags ${taghash}) +get_git_tag_revision(TAGHASH --tags --max-count=1) +get_git_head_revision(REFSPEC COMMITHASH) +git_describe(VERSION --tags ${TAGHASH}) -string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" match "${version}") -if (match) - string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" OPENMW_VERSION_MAJOR "${version}") - string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_MINOR "${version}") - string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_RELEASE "${version}") +string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" MATCH "${VERSION}") +if (MATCH) + string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" OPENMW_VERSION_MAJOR "${VERSION}") + string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_MINOR "${VERSION}") + string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_RELEASE "${VERSION}") set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") - set(OPENMW_VERSION_COMMIT "${commithash}") + set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}") + set(OPENMW_VERSION_TAGHASH "${TAGHASH}") message(STATUS "Configuring OpenMW ${OPENMW_VERSION}...") -else (match) +else (MATCH) message(FATAL_ERROR "Failed to get valid version information from Git") -endif (match) +endif (MATCH) # doxygen main page diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 42d3d1754..67665adf0 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -73,13 +73,19 @@ Launcher::MainDialog::MainDialog(QWidget *parent) setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); // Add version information to bottom of the window - QString revision(OPENMW_VERSION_COMMIT); - revision = revision.left(10); + QString revision(OPENMW_VERSION_COMMITHASH); + QString tag(OPENMW_VERSION_TAGHASH); + if (revision == tag) { + versionLabel->setText(tr("OpenMW %0 release").arg(OPENMW_VERSION)); + } else { + versionLabel->setText(tr("OpenMW unstable, revision %0").arg(revision.left(10))); + } + + // Add the compile date and time QDate date(QDate::fromString(__DATE__, QLatin1String("MMM dd yyyy"))); QTime time(QTime::fromString(__TIME__, QLatin1String("hh:m:ss"))); - versionLabel->setText(tr("OpenMW %0 revision %1").arg(OPENMW_VERSION, revision)); versionLabel->setToolTip(tr("Compiled on %0 %1").arg(date.toString(Qt::SystemLocaleShortDate), time.toString(Qt::SystemLocaleShortDate))); diff --git a/components/version/version.hpp.cmake b/components/version/version.hpp.cmake index 4adb0d8c2..4cdfa32f0 100644 --- a/components/version/version.hpp.cmake +++ b/components/version/version.hpp.cmake @@ -6,7 +6,8 @@ #define OPENMW_VERSION_RELEASE @OPENMW_VERSION_RELEASE@ #define OPENMW_VERSION "@OPENMW_VERSION@" -#define OPENMW_VERSION_COMMIT "@OPENMW_VERSION_COMMIT@" +#define OPENMW_VERSION_COMMITHASH "@OPENMW_VERSION_COMMITHASH@" +#define OPENMW_VERSION_TAGHASH "@OPENMW_VERSION_TAGHASH@" #endif // VERSION_HPP From 1b1ecafdd8c68672dd51898ca9f8ccc3878b4f0b Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 23 Jan 2014 15:13:37 +0100 Subject: [PATCH 640/889] introduced missing columns to data --- apps/opencs/model/world/collection.hpp | 7 ++----- apps/opencs/model/world/data.cpp | 5 +++++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 74019dda7..f97b5f529 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -204,12 +204,9 @@ namespace CSMWorld const UniversalId::Type type, const UniversalId::ArgumentType argumentType) { - Record copy = getRecord(origin); + Record copy(getRecord(origin)); copy.mState = RecordBase::State_ModifiedOnly; - if (argumentType == UniversalId::ArgumentType_Id) - { - copy.get().mId = destination; - } + copy.get().mId = destination; insertRecord(copy, getAppendIndex(destination, type)); } diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 69fb4ab1e..a52821036 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -154,14 +154,17 @@ CSMWorld::Data::Data() : mRefs (mCells) mTopics.addColumn (new StringIdColumn); mTopics.addColumn (new RecordStateColumn); + mTopics.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Topic)); mTopics.addColumn (new DialogueTypeColumn); mJournals.addColumn (new StringIdColumn); mJournals.addColumn (new RecordStateColumn); + mJournals.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Journal)); mJournals.addColumn (new DialogueTypeColumn (true)); mTopicInfos.addColumn (new StringIdColumn (true)); mTopicInfos.addColumn (new RecordStateColumn); + mTopicInfos.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Journal)); mTopicInfos.addColumn (new TopicColumn (false)); mTopicInfos.addColumn (new ActorColumn); mTopicInfos.addColumn (new RaceColumn); @@ -178,6 +181,7 @@ CSMWorld::Data::Data() : mRefs (mCells) mJournalInfos.addColumn (new StringIdColumn (true)); mJournalInfos.addColumn (new RecordStateColumn); + mJournalInfos.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Journal)); mJournalInfos.addColumn (new TopicColumn (true)); mJournalInfos.addColumn (new QuestStatusTypeColumn); mJournalInfos.addColumn (new QuestIndexColumn); @@ -225,6 +229,7 @@ CSMWorld::Data::Data() : mRefs (mCells) mFilters.addColumn (new StringIdColumn); mFilters.addColumn (new RecordStateColumn); + mFilters.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Filter)); mFilters.addColumn (new FilterColumn); mFilters.addColumn (new DescriptionColumn); mFilters.addColumn (new ScopeColumn); From dda7ddb6f8628b30632833ef2f486b8678aa4351 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 23 Jan 2014 16:00:44 +0100 Subject: [PATCH 641/889] Disable not needed referencable creator widget when in cloning mode. --- apps/opencs/view/world/creator.hpp | 2 ++ apps/opencs/view/world/genericcreator.cpp | 6 +++++- apps/opencs/view/world/genericcreator.hpp | 4 +++- apps/opencs/view/world/referenceablecreator.cpp | 8 +++++++- apps/opencs/view/world/referenceablecreator.hpp | 1 + apps/opencs/view/world/tablebottombox.cpp | 2 ++ 6 files changed, 20 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index 5eaf3e178..5174232bc 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -28,6 +28,8 @@ namespace CSVWorld virtual void cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType) = 0; virtual void setEditLock (bool locked) = 0; + + virtual void toggleWidgets(bool active = true) = 0; signals: diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index ab387393b..2a93f0e0f 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -157,4 +157,8 @@ void CSVWorld::GenericCreator::cloneMode(const std::string& originid, mClonedId = originid; mClonedType = type; mArgumentType = argumentType; -} \ No newline at end of file +} + +void CSVWorld::GenericCreator::toggleWidgets(bool active) +{ +} diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 867595bb5..e4813594b 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -48,7 +48,7 @@ namespace CSVWorld virtual std::string getId() const; virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; - + CSMWorld::Data& getData() const; const CSMWorld::UniversalId& getCollectionId() const; @@ -61,6 +61,8 @@ namespace CSVWorld virtual void setEditLock (bool locked); virtual void reset(); + + virtual void toggleWidgets (bool active = true); virtual void cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type, diff --git a/apps/opencs/view/world/referenceablecreator.cpp b/apps/opencs/view/world/referenceablecreator.cpp index 718fe9ca7..7a5fca853 100644 --- a/apps/opencs/view/world/referenceablecreator.cpp +++ b/apps/opencs/view/world/referenceablecreator.cpp @@ -40,4 +40,10 @@ void CSVWorld::ReferenceableCreator::reset() { mType->setCurrentIndex (0); GenericCreator::reset(); -} \ No newline at end of file +} + +void CSVWorld::ReferenceableCreator::toggleWidgets(bool active) +{ + CSVWorld::GenericCreator::toggleWidgets(active); + mType->setEnabled(active); +} diff --git a/apps/opencs/view/world/referenceablecreator.hpp b/apps/opencs/view/world/referenceablecreator.hpp index 06e0e582b..88545575e 100644 --- a/apps/opencs/view/world/referenceablecreator.hpp +++ b/apps/opencs/view/world/referenceablecreator.hpp @@ -23,6 +23,7 @@ namespace CSVWorld const CSMWorld::UniversalId& id); virtual void reset(); + virtual void toggleWidgets(bool active = true); }; } diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index 60a206d0c..ef20cf1d4 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -153,6 +153,7 @@ void CSVWorld::TableBottomBox::tableSizeChanged (int size, int deleted, int modi void CSVWorld::TableBottomBox::createRequest() { mCreator->reset(); + mCreator->toggleWidgets(true); mLayout->setCurrentWidget (mCreator); setVisible (true); mCreating = true; @@ -163,6 +164,7 @@ void CSVWorld::TableBottomBox::cloneRequest(const std::string& id, const CSMWorld::UniversalId::ArgumentType argumnetType) { mCreator->reset(); + mCreator->toggleWidgets(false); mCreator->cloneMode(id, type, argumnetType); mLayout->setCurrentWidget(mCreator); setVisible (true); From c87d9ff38d67d698f60d0f438b45b99c546a869a Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 23 Jan 2014 16:17:04 +0100 Subject: [PATCH 642/889] Disable reference creator widget when in the clone mode. --- apps/opencs/view/world/genericcreator.hpp | 6 ++++-- apps/opencs/view/world/referencecreator.cpp | 21 +++++++++++++++++++-- apps/opencs/view/world/referencecreator.hpp | 5 +++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index e4813594b..4870ba46c 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -29,11 +29,13 @@ namespace CSVWorld std::string mErrors; QHBoxLayout *mLayout; bool mLocked; - bool mCloneMode; std::string mClonedId; CSMWorld::UniversalId::Type mClonedType; CSMWorld::UniversalId::ArgumentType mArgumentType; - + + protected: + bool mCloneMode; + protected: void update(); diff --git a/apps/opencs/view/world/referencecreator.cpp b/apps/opencs/view/world/referencecreator.cpp index 06b3eb76d..6991ecf36 100644 --- a/apps/opencs/view/world/referencecreator.cpp +++ b/apps/opencs/view/world/referencecreator.cpp @@ -49,8 +49,13 @@ std::string CSVWorld::ReferenceCreator::getErrors() const { std::string errors = GenericCreator::getErrors(); + if (mCloneMode) + { + return errors; + } + std::string cell = mCell->text().toUtf8().constData(); - + if (cell.empty()) { if (!errors.empty()) @@ -72,4 +77,16 @@ std::string CSVWorld::ReferenceCreator::getErrors() const void CSVWorld::ReferenceCreator::cellChanged() { update(); -} \ No newline at end of file +} + +void CSVWorld::ReferenceCreator::toggleWidgets(bool active) +{ + CSVWorld::GenericCreator::toggleWidgets(active); + mCell->setEnabled(active); +} + +void CSVWorld::ReferenceCreator::cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType) +{ + CSVWorld::GenericCreator::cloneMode(originid, type, argumentType); + cellChanged(); //otherwise ok button will remain disabled +} diff --git a/apps/opencs/view/world/referencecreator.hpp b/apps/opencs/view/world/referencecreator.hpp index 27f81564f..2f0897026 100644 --- a/apps/opencs/view/world/referencecreator.hpp +++ b/apps/opencs/view/world/referencecreator.hpp @@ -25,7 +25,12 @@ namespace CSVWorld ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id); + virtual void cloneMode(const std::string& originid, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumentType); + virtual void reset(); + virtual void toggleWidgets(bool active = true); virtual std::string getErrors() const; ///< Return formatted error descriptions for the current state of the creator. if an empty From f390c7f4b0279cf64981e78dca3bfefe6316cf5b Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 23 Jan 2014 16:24:03 +0100 Subject: [PATCH 643/889] Disable widgets in the cell creator. --- apps/opencs/view/world/cellcreator.cpp | 8 +++++++- apps/opencs/view/world/cellcreator.hpp | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/world/cellcreator.cpp b/apps/opencs/view/world/cellcreator.cpp index 74fb06870..43af16c82 100644 --- a/apps/opencs/view/world/cellcreator.cpp +++ b/apps/opencs/view/world/cellcreator.cpp @@ -78,4 +78,10 @@ void CSVWorld::CellCreator::setType (int index) void CSVWorld::CellCreator::valueChanged (int index) { update(); -} \ No newline at end of file +} + +void CSVWorld::CellCreator::toggleWidgets(bool active) +{ + CSVWorld::GenericCreator::toggleWidgets(active); + mType->setEnabled(active); +} diff --git a/apps/opencs/view/world/cellcreator.hpp b/apps/opencs/view/world/cellcreator.hpp index a5473e2c9..4e8705cb0 100644 --- a/apps/opencs/view/world/cellcreator.hpp +++ b/apps/opencs/view/world/cellcreator.hpp @@ -28,6 +28,8 @@ namespace CSVWorld CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id); virtual void reset(); + + virtual void toggleWidgets(bool active = true); private slots: From 87e83a92f84e011ad9be71c3dfb993e1c80e80c5 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Thu, 23 Jan 2014 23:14:20 +0200 Subject: [PATCH 644/889] refactoring of setting an attack type --- apps/openmw/mwinput/inputmanagerimp.cpp | 14 ---- apps/openmw/mwmechanics/aicombat.cpp | 38 +++++----- apps/openmw/mwmechanics/character.cpp | 92 ++++++++++++++++------- apps/openmw/mwmechanics/character.hpp | 3 +- apps/openmw/mwmechanics/creaturestats.hpp | 6 +- apps/openmw/mwmechanics/pathfinding.cpp | 6 ++ apps/openmw/mwmechanics/pathfinding.hpp | 2 + 7 files changed, 96 insertions(+), 65 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index f0feba89f..2ad667d3f 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -159,20 +159,6 @@ namespace MWInput if (action == A_Use) { MWWorld::Class::get(mPlayer->getPlayer()).getCreatureStats(mPlayer->getPlayer()).setAttackingOrSpell(currentValue); - if (currentValue == 1) - { - int type = MWMechanics::CreatureStats::AT_Chop; - bool forward = (mInputBinder->getChannel(A_MoveForward)->getValue() > 0 - || mInputBinder->getChannel(A_MoveBackward)->getValue() > 0); - bool side = (mInputBinder->getChannel(A_MoveLeft)->getValue() > 0 - || mInputBinder->getChannel(A_MoveRight)->getValue() > 0); - if (side && !forward) - type = MWMechanics::CreatureStats::AT_Slash; - if (forward && !side) - type = MWMechanics::CreatureStats::AT_Thrust; - - MWWorld::Class::get(mPlayer->getPlayer()).getCreatureStats(mPlayer->getPlayer()).setAttackType(type); - } } if (currentValue == 1) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index f12a10f9b..8f2408587 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -28,7 +28,6 @@ namespace return -1.0; } - void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement); //chooses an attack depending on probability to avoid uniformity void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement); } @@ -57,9 +56,6 @@ namespace MWMechanics return true; //Update every frame - if(mReadyToAttack) - determineAttackType(actor, mMovement); - if(mCombatMove) { mTimerCombatMove -= duration; @@ -155,7 +151,7 @@ namespace MWMechanics else //is creature { weaptype = WeapType_HandToHand; //doesn't matter, should only reflect if it is melee or distant weapon - weapRange = 100; //TODO: use true attack range (the same problem in Creature::hit) + weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit) } //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); @@ -167,7 +163,9 @@ namespace MWMechanics float rangeMelee; float rangeCloseUp; bool distantCombat = false; - if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_ThowWeapon) // || WeapType_Spell_OnTarget + int attackType = actor.getClass().getCreatureStats(actor).getAttackType(); + if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_ThowWeapon + || attackType==MWMechanics::CreatureStats::AT_Target ) { rangeMelee = 1000; // TODO: should depend on archer skill rangeCloseUp = 0; //doesn't needed when attacking from distance @@ -251,7 +249,11 @@ namespace MWMechanics //delete visited path node mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); - zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + //try shortcut + if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) + zAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); + else + zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); // TODO: use movement settings instead of rotating directly MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); @@ -375,24 +377,12 @@ namespace MWMechanics namespace { -void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) -{ - if (movement.mPosition[0] && !movement.mPosition[1]) //sideway - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Slash); - else if (movement.mPosition[1]) //forward - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Thrust); - else - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Chop); -} void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) { - //the more damage attackType deals the more probability it has - if (weapon == NULL) { - //hand-to-hand and creatures' attacks handled here - //hand-to-hand deals equal damage + //hand-to-hand and creatures' attacks deal equal damage for each type float roll = static_cast(rand())/RAND_MAX; if(roll <= 0.333f) //side punch { @@ -401,10 +391,15 @@ void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement } else if(roll <= 0.666f) //forward punch movement.mPosition[1] = 1; + else + { + movement.mPosition[1] = movement.mPosition[0] = 0; + } return; } + //the more damage attackType deals the more probability it has int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; @@ -419,7 +414,8 @@ void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement } else if(roll <= (static_cast(slash) + static_cast(thrust))/total) movement.mPosition[1] = 1; - //else chop + else + movement.mPosition[1] = movement.mPosition[0] = 0; } } \ No newline at end of file diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 967f7413a..05c4e1f2a 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -490,18 +490,7 @@ bool CharacterController::updateCreatureState() { MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); - switch (stats.getAttackType()) - { - case CreatureStats::AT_Chop: - mCurrentWeapon = "attack1"; - break; - case CreatureStats::AT_Slash: - mCurrentWeapon = "attack2"; - break; - case CreatureStats::AT_Thrust: - mCurrentWeapon = "attack3"; - break; - } + determineAttackType(); mAnimation->play(mCurrentWeapon, Priority_Weapon, MWRender::Animation::Group_All, true, @@ -517,7 +506,7 @@ bool CharacterController::updateCreatureState() return false; } -bool CharacterController::updateNpcState(bool inwater, bool isrunning) +bool CharacterController::updateNpcState() { const MWWorld::Class &cls = MWWorld::Class::get(mPtr); NpcStats &stats = cls.getNpcStats(mPtr); @@ -586,7 +575,9 @@ bool CharacterController::updateNpcState(bool inwater, bool isrunning) if(isWerewolf) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - if(isrunning && !inwater && mWeaponType == WeapType_None) + if(cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run) + && !MWBase::Environment::get().getWorld()->isSwimming(mPtr) + && mWeaponType == WeapType_None) { if(!sndMgr->getSoundPlaying(mPtr, "WolfRun")) sndMgr->playSound3D(mPtr, "WolfRun", 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, @@ -653,12 +644,7 @@ bool CharacterController::updateNpcState(bool inwater, bool isrunning) mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Left Hand", effect->mParticle); mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Right Hand", effect->mParticle); - switch(effectentry.mRange) - { - case 0: mAttackType = "self"; break; - case 1: mAttackType = "touch"; break; - case 2: mAttackType = "target"; break; - } + determineAttackType(effectentry.mRange); mAnimation->play(mCurrentWeapon, Priority_Weapon, MWRender::Animation::Group_UpperBody, true, @@ -715,13 +701,8 @@ bool CharacterController::updateNpcState(bool inwater, bool isrunning) int attackType = stats.getAttackType(); if(isWeapon && Settings::Manager::getBool("best attack", "Game")) attackType = getBestAttack(weapon->get()->mBase); - - if (attackType == MWMechanics::CreatureStats::AT_Chop) - mAttackType = "chop"; - else if (attackType == MWMechanics::CreatureStats::AT_Slash) - mAttackType = "slash"; else - mAttackType = "thrust"; + determineAttackType(); } mAnimation->play(mCurrentWeapon, Priority_Weapon, @@ -902,7 +883,8 @@ void CharacterController::update(float duration) bool isrunning = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run); bool sneak = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak); bool flying = world->isFlying(mPtr); - Ogre::Vector3 vec = cls.getMovementVector(mPtr); + //Ogre::Vector3 vec = cls.getMovementVector(mPtr); + Ogre::Vector3 vec(cls.getMovementSettings(mPtr).mPosition); vec.normalise(); if(mHitState != CharState_None && mJumpState == JumpState_None) vec = Ogre::Vector3(0.0f); @@ -1129,7 +1111,7 @@ void CharacterController::update(float duration) } if(cls.isNpc()) - forcestateupdate = updateNpcState(inwater, isrunning) || forcestateupdate; + forcestateupdate = updateNpcState() || forcestateupdate; else forcestateupdate = updateCreatureState() || forcestateupdate; @@ -1149,6 +1131,7 @@ void CharacterController::update(float duration) } movement = vec; + cls.getMovementSettings(mPtr).mPosition[0] = cls.getMovementSettings(mPtr).mPosition[1] = cls.getMovementSettings(mPtr).mPosition[2] = 0; } else if(cls.getCreatureStats(mPtr).isDead()) { @@ -1319,4 +1302,57 @@ void CharacterController::updateVisibility() mAnimation->setAlpha(alpha); } +void CharacterController::determineAttackType(int spellRange) +{ + if(spellRange == -1) + { + float * move = mPtr.getClass().getMovementSettings(mPtr).mPosition; + + if (move[0] && !move[1]) //sideway + { + mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Slash); + if(mPtr.getClass().isNpc()) + mAttackType = "slash"; + else + mCurrentWeapon = "attack2"; + } + else if (move[1]) //forward + { + mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Thrust); + if(mPtr.getClass().isNpc()) + mAttackType = "thrust"; + else + mCurrentWeapon = "attack3"; + } + else + { + mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Chop); + if(mPtr.getClass().isNpc()) + mAttackType = "chop"; + else + mCurrentWeapon = "attack1"; + } + + } + else + { + switch(spellRange) + { + case 0: + mAttackType = "self"; + mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Self); + break; + case 1: + mAttackType = "touch"; + mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Touch); + break; + case 2: + mAttackType = "target"; + mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Target); + break; + } + } + } + +} \ No newline at end of file diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 01985f38e..37fc42f1e 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -165,12 +165,13 @@ class CharacterController float mSecondsOfRunning; std::string mAttackType; // slash, chop or thrust + void determineAttackType(int spellRange = -1); void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false); void clearAnimQueue(); - bool updateNpcState(bool inwater, bool isrunning); + bool updateNpcState(); bool updateCreatureState(); void updateVisibility(); diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 308883fc5..f9b52abc0 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -118,7 +118,11 @@ namespace MWMechanics { AT_Chop, AT_Slash, - AT_Thrust + AT_Thrust, + + AT_Self, + AT_Touch, + AT_Target, }; void setAttackType(int attackType) { mAttackType = attackType; } int getAttackType() { return mAttackType; } diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index fd44fcc4c..b9c8c5c4f 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -201,6 +201,12 @@ namespace MWMechanics return Ogre::Radian(Ogre::Math::ACos(directionY / directionResult) * sgn(Ogre::Math::ASin(directionX / directionResult))).valueDegrees(); } + float PathFinder::getDistToNext(float x, float y, float z) + { + ESM::Pathgrid::Point nextPoint = *mPath.begin(); + return distance(nextPoint, x, y, z); + } + bool PathFinder::checkWaypoint(float x, float y, float z) { if(mPath.empty()) diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index de58f5db8..6f836dac7 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -22,6 +22,8 @@ namespace MWMechanics ///< \Returns true if a way point was reached float getZAngleToNext(float x, float y) const; + float getDistToNext(float x, float y, float z); + bool isPathConstructed() const { return mIsPathConstructed; From 01be9386d6521f2878daa7a6b89a319820baaddb Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 24 Jan 2014 11:22:20 +0100 Subject: [PATCH 645/889] Id to the coordinates with the boost and explicit specialisations of template member functions. --- apps/opencs/model/world/collection.hpp | 27 ++++++++++++++++++++++- apps/opencs/view/world/cellcreator.cpp | 14 +++++++++++- apps/opencs/view/world/cellcreator.hpp | 5 +++++ apps/opencs/view/world/tablebottombox.cpp | 2 +- 4 files changed, 45 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index f97b5f529..0a8dc5a9d 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -7,10 +7,13 @@ #include #include #include +#include +#include #include #include +#include #include "columnbase.hpp" @@ -50,6 +53,8 @@ namespace CSMWorld // not implemented Collection (const Collection&); Collection& operator= (const Collection&); + + void idToCoordiantes(Record& record, const std::string& destination) {} //dose nothing protected: @@ -150,7 +155,24 @@ namespace CSMWorld void setRecord (int index, const Record& record); ///< \attention This function must not change the ID. }; - + + template<> + class Collection > : public CollectionBase //explicit specialisation + { + void idToCoordiantes(Record& record, const std::string& destination) + { + if (record.get().isExterior()) + { + unsigned separator = destination.find(' '); + assert(separator != std::string::npos); + std::string xPos(destination.substr(0, separator)); + std::string yPos(destination.substr(separator+1, destination.size())); + record.get().mData.mX = boost::lexical_cast(xPos); + record.get().mData.mY = boost::lexical_cast(yPos); + } + } + }; + template const std::map& Collection::getIdMap() const { @@ -208,6 +230,9 @@ namespace CSMWorld copy.mState = RecordBase::State_ModifiedOnly; copy.get().mId = destination; + //the below function has explicit specialization for cells, and does nothing fo other records + idToCoordiantes(copy, destination); + insertRecord(copy, getAppendIndex(destination, type)); } diff --git a/apps/opencs/view/world/cellcreator.cpp b/apps/opencs/view/world/cellcreator.cpp index 43af16c82..6298e3ba5 100644 --- a/apps/opencs/view/world/cellcreator.cpp +++ b/apps/opencs/view/world/cellcreator.cpp @@ -22,7 +22,7 @@ std::string CSVWorld::CellCreator::getId() const CSVWorld::CellCreator::CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id) -: GenericCreator (data, undoStack, id) +: GenericCreator (data, undoStack, id), mCloningExterior(false) { mY = new QSpinBox (this); mY->setVisible (false); @@ -80,6 +80,18 @@ void CSVWorld::CellCreator::valueChanged (int index) update(); } +void CSVWorld::CellCreator::cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType) +{ + CSVWorld::GenericCreator::cloneMode(originid, type, argumentType); + if (*(originid.begin()) == '#') //if originid points to the exterior cell + { + setType(1); //enable x and y controls + } else { + setType(0); + } +} + + void CSVWorld::CellCreator::toggleWidgets(bool active) { CSVWorld::GenericCreator::toggleWidgets(active); diff --git a/apps/opencs/view/world/cellcreator.hpp b/apps/opencs/view/world/cellcreator.hpp index 4e8705cb0..521abd173 100644 --- a/apps/opencs/view/world/cellcreator.hpp +++ b/apps/opencs/view/world/cellcreator.hpp @@ -19,6 +19,7 @@ namespace CSVWorld QLabel *mYLabel; QSpinBox *mY; + bool mCloningExterior; protected: virtual std::string getId() const; @@ -30,6 +31,10 @@ namespace CSVWorld virtual void reset(); virtual void toggleWidgets(bool active = true); + + virtual void cloneMode(const std::string& originid, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumentType); private slots: diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index ef20cf1d4..a6801b6fc 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -164,9 +164,9 @@ void CSVWorld::TableBottomBox::cloneRequest(const std::string& id, const CSMWorld::UniversalId::ArgumentType argumnetType) { mCreator->reset(); - mCreator->toggleWidgets(false); mCreator->cloneMode(id, type, argumnetType); mLayout->setCurrentWidget(mCreator); + mCreator->toggleWidgets(false); setVisible (true); mCreating = true; } From ac4c904e62c5172d9847a2141ef1d7b4aa0f01e9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 24 Jan 2014 13:22:30 +0100 Subject: [PATCH 646/889] Fix projectile bug --- apps/openmw/mwworld/worldimp.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 57c85317f..0394a2edd 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2199,6 +2199,7 @@ namespace MWWorld { if (!mWorldScene->isCellActive(*it->first.getCell())) { + deleteObject(it->first); mProjectiles.erase(it++); continue; } From 9b9c39af7af02a96db04b34c9c08bb8f1f8bf179 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 24 Jan 2014 13:22:39 +0100 Subject: [PATCH 647/889] Fix being able to activate magic bolts mid-flight --- apps/openmw/engine.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 5d04b985f..f18c8a8b8 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -509,6 +509,9 @@ void OMW::Engine::activate() if (ptr.isEmpty()) return; + if (ptr.getClass().getName(ptr) == "") // objects without name presented to user can never be activated + return; + MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr); boost::shared_ptr action = From 786ed6ca5b462f3e28f6db2a647f6ca2fba361d3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Jan 2014 20:56:24 +0100 Subject: [PATCH 648/889] Include some required Ogre headers explicitely --- apps/opencs/view/render/scenewidget.cpp | 1 + apps/openmw/mwrender/characterpreview.cpp | 1 + apps/openmw/mwrender/occlusionquery.cpp | 1 + apps/openmw/mwrender/shadows.cpp | 1 + apps/openmw/mwrender/water.cpp | 1 + components/nifogre/ogrenifloader.cpp | 4 +++- libs/openengine/ogre/renderer.cpp | 1 + 7 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index c8b37e9bb..620586bd2 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace CSVRender { diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 643225515..b1ec2d5ff 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include diff --git a/apps/openmw/mwrender/occlusionquery.cpp b/apps/openmw/mwrender/occlusionquery.cpp index a69511acd..246103471 100644 --- a/apps/openmw/mwrender/occlusionquery.cpp +++ b/apps/openmw/mwrender/occlusionquery.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "renderconst.hpp" diff --git a/apps/openmw/mwrender/shadows.cpp b/apps/openmw/mwrender/shadows.cpp index 21bbe51b6..9ebb0ab08 100644 --- a/apps/openmw/mwrender/shadows.cpp +++ b/apps/openmw/mwrender/shadows.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 0a4db30e9..9e3105168 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "sky.hpp" #include "renderingmanager.hpp" diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 63e905766..b97d1dbe9 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -36,6 +35,9 @@ #include #include #include +#include +#include +#include #include diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 9e5ec5414..c86697497 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include From 295aed3533592f3bc09d688c923a38a634668817 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 24 Jan 2014 17:49:16 +0100 Subject: [PATCH 649/889] Implement savegame screenshots --- apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwgui/savegamedialog.cpp | 28 ++++++++++++++++++ apps/openmw/mwrender/renderingmanager.cpp | 35 +++++++++++++++++++++++ apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwstate/statemanagerimp.cpp | 9 ++++++ apps/openmw/mwworld/worldimp.cpp | 5 ++++ apps/openmw/mwworld/worldimp.hpp | 1 + components/esm/savedgame.cpp | 8 ++++++ components/esm/savedgame.hpp | 3 +- 9 files changed, 89 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index eaf411d20..99346bc6f 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -412,6 +412,7 @@ namespace MWBase virtual void playVideo(const std::string& name, bool allowSkipping) = 0; virtual void stopVideo() = 0; virtual void frameStarted (float dt, bool paused) = 0; + virtual void screenshot (Ogre::Image& image, int w, int h) = 0; /// Find default position inside exterior cell specified by name /// \return false if exterior with given name not exists, true otherwise diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 552489bc4..91993b0be 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -1,6 +1,9 @@ #include "savegamedialog.hpp" #include "widgets.hpp" +#include +#include + #include #include @@ -166,6 +169,7 @@ namespace MWGui if (pos == MyGUI::ITEM_NONE) { mInfoText->setCaption(""); + mScreenshot->setImageTexture(""); return; } @@ -199,5 +203,29 @@ namespace MWGui << " " << hour << " " << (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); mInfoText->setCaptionWithReplacing(text.str()); + + // Decode screenshot + std::vector data = slot->mProfile.mScreenshot; // MemoryDataStream doesn't work with const data :( + Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size())); + Ogre::Image image; + image.load(stream, "jpg"); + + const std::string textureName = "@savegame_screenshot"; + Ogre::TexturePtr texture; + texture = Ogre::TextureManager::getSingleton().getByName(textureName); + mScreenshot->setImageTexture(""); + if (texture.isNull()) + { + texture = Ogre::TextureManager::getSingleton().createManual(textureName, + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, + image.getWidth(), image.getHeight(), 0, Ogre::PF_BYTE_RGBA, Ogre::TU_DYNAMIC_WRITE_ONLY); + } + texture->unload(); + texture->setWidth(image.getWidth()); + texture->setHeight(image.getHeight()); + texture->loadImage(image); + + mScreenshot->setImageTexture(textureName); } } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 55ead476b..84cc0ac23 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -289,6 +289,9 @@ void RenderingManager::rotateObject(const MWWorld::Ptr &ptr) void RenderingManager::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur) { + Ogre::Image im; + im.encode(".jpg"); + Ogre::SceneNode *child = mRendering.getScene()->getSceneNode(old.getRefData().getHandle()); @@ -966,6 +969,38 @@ Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr) return anim; } +void RenderingManager::screenshot(Image &image, int w, int h) +{ + // Create a temporary render target. We do not use the RenderWindow since we want a specific size. + // Also, the GUI should not be visible (and it is only rendered on the RenderWindow's primary viewport) + const std::string tempName = "@temp"; + Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().createManual(tempName, + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, w, h, 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET); + + float oldAspect = mRendering.getCamera()->getAspectRatio(); + + mRendering.getCamera()->setAspectRatio(w / static_cast(h)); + + Ogre::RenderTarget* rt = texture->getBuffer()->getRenderTarget(); + Ogre::Viewport* vp = rt->addViewport(mRendering.getCamera()); + vp->setBackgroundColour(mRendering.getViewport()->getBackgroundColour()); + vp->setOverlaysEnabled(false); + vp->setVisibilityMask(mRendering.getViewport()->getVisibilityMask()); + rt->update(); + + Ogre::PixelFormat pf = rt->suggestPixelFormat(); + + std::vector data; + data.resize(w * h * Ogre::PixelUtil::getNumElemBytes(pf)); + + Ogre::PixelBox pb(w, h, 1, pf, &data[0]); + rt->copyContentsToMemory(pb); + + image.loadDynamicImage(&data[0], w, h, pf); + + Ogre::TextureManager::getSingleton().remove(tempName); + mRendering.getCamera()->setAspectRatio(oldAspect); +} void RenderingManager::playVideo(const std::string& name, bool allowSkipping) { diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 5631c9470..f62ca8b3c 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -213,6 +213,7 @@ public: void playVideo(const std::string& name, bool allowSkipping); void stopVideo(); void frameStarted(float dt, bool paused); + void screenshot(Ogre::Image& image, int w, int h); protected: virtual void windowResized(int x, int y); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 7020678d0..6251c49c4 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -8,6 +8,8 @@ #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" @@ -151,6 +153,13 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot profile.mTimePlayed = mTimePlayed; profile.mDescription = description; + int screenshotW = 259*2, screenshotH = 133*2; // *2 to get some nice antialiasing + Ogre::Image screenshot; + world.screenshot(screenshot, screenshotW, screenshotH); + Ogre::DataStreamPtr encoded = screenshot.encode("jpg"); + profile.mScreenshot.resize(encoded->size()); + encoded->read(&profile.mScreenshot[0], encoded->size()); + if (!slot) slot = mCharacterManager.getCurrentCharacter()->createSlot (profile); else diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f8321d74e..46ed31217 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1810,6 +1810,11 @@ namespace MWWorld mRendering->frameStarted(dt, paused); } + void World::screenshot(Ogre::Image &image, int w, int h) + { + mRendering->screenshot(image, w, h); + } + void World::activateDoor(const MWWorld::Ptr& door) { if (mDoorStates.find(door) != mDoorStates.end()) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 58a6111c5..0e0bb2814 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -504,6 +504,7 @@ namespace MWWorld virtual void playVideo(const std::string& name, bool allowSkipping); virtual void stopVideo(); virtual void frameStarted (float dt, bool paused); + virtual void screenshot (Ogre::Image& image, int w, int h); /// Find center of exterior cell above land surface /// \return false if exterior with given name not exists, true otherwise diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index 55b17289c..d6887f170 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -19,6 +19,11 @@ void ESM::SavedGame::load (ESMReader &esm) while (esm.isNextSub ("DEPE")) mContentFiles.push_back (esm.getHString()); + + esm.getSubNameIs("SCRN"); + esm.getSubHeader(); + mScreenshot.resize(esm.getSubSize()); + esm.getExact(&mScreenshot[0], mScreenshot.size()); } void ESM::SavedGame::save (ESMWriter &esm) const @@ -35,4 +40,7 @@ void ESM::SavedGame::save (ESMWriter &esm) const iter!=mContentFiles.end(); ++iter) esm.writeHNString ("DEPE", *iter); + esm.startSubRecord("SCRN"); + esm.write(&mScreenshot[0], mScreenshot.size()); + esm.endRecord("SCRN"); } diff --git a/components/esm/savedgame.hpp b/components/esm/savedgame.hpp index 6c11d318f..9c7bf551d 100644 --- a/components/esm/savedgame.hpp +++ b/components/esm/savedgame.hpp @@ -31,8 +31,7 @@ namespace ESM TimeStamp mInGameTime; double mTimePlayed; std::string mDescription; - - /// \todo add field for screenshot + std::vector mScreenshot; // raw jpg-encoded data void load (ESMReader &esm); void save (ESMWriter &esm) const; From 5ca59467603edd5f8723a72c47786e63e0012b81 Mon Sep 17 00:00:00 2001 From: gus Date: Fri, 24 Jan 2014 19:13:23 +0100 Subject: [PATCH 650/889] WIP --- apps/openmw/mwmechanics/aicombat.cpp | 3 ++- apps/openmw/mwmechanics/aifollow.cpp | 34 +++++++++++++++------------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index b9dc2f7db..0eff943d4 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -97,7 +97,8 @@ namespace MWMechanics zAngle = Ogre::Radian( Ogre::Math::ACos(directionY / directionResult) * sgn(Ogre::Math::ASin(directionX / directionResult)) ).valueDegrees(); // TODO: use movement settings instead of rotating directly - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + //MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + MWWorld::Class::get(actor).getMovementSettings(actor).mRotation[2] = 10*(Ogre::Degree(zAngle).valueRadians()-pos.rot[2]); mPathFinder.clearPath(); diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 898f91e7c..21a629d90 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -4,6 +4,8 @@ #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" #include "movement.hpp" + +#include MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) : mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), mTimer(0), mStuckTimer(0) @@ -53,6 +55,15 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) dest.mY = target.getRefData().getPosition().pos[1]; dest.mZ = target.getRefData().getPosition().pos[2]; + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; + start.mZ = pos.pos[2]; + + if(mPathFinder.getPath().empty()) + mPathFinder.buildPath(start, dest, actor.getCell(), true); + + if(mTimer > 0.25) { if(!mPathFinder.getPath().empty()) @@ -63,10 +74,8 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) +(dest.mY - lastPos.mY)*(dest.mY - lastPos.mY) +(dest.mZ - lastPos.mZ)*(dest.mZ - lastPos.mZ) > 100*100) - mPathFinder.addPointToPath(dest); + mPathFinder.addPointToPath(dest); } - else - mPathFinder.addPointToPath(dest); mTimer = 0; } @@ -75,27 +84,20 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) { if((mStuckPos.pos[0] - pos.pos[0])*(mStuckPos.pos[0] - pos.pos[0]) +(mStuckPos.pos[1] - pos.pos[1])*(mStuckPos.pos[1] - pos.pos[1]) - +(mStuckPos.pos[2] - pos.pos[2])*(mStuckPos.pos[2] - pos.pos[2]) - < 100) //NPC is stuck - { - ESM::Pathgrid::Point start; - start.mX = pos.pos[0]; - start.mY = pos.pos[1]; - start.mZ = pos.pos[2]; - + +(mStuckPos.pos[2] - pos.pos[2])*(mStuckPos.pos[2] - pos.pos[2]) < 100) //NPC is stuck mPathFinder.buildPath(start, dest, actor.getCell(), true); - } + mStuckTimer = 0; mStuckPos = pos; } - if(mPathFinder.getPath().empty()) - mPathFinder.addPointToPath(dest); - if(!mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2])) { float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + //MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + MWWorld::Class::get(actor).getMovementSettings(actor).mRotation[2] = 10*(Ogre::Degree(zAngle).valueRadians()-pos.rot[2]); + //std::cout << Ogre::Degree(zAngle).valueDegrees()-Ogre::Radian(actor.getRefData().getPosition().rot[2]).valueDegrees() << " "<< pos.rot[2] << " " << zAngle << "\n"; + //MWWorld::Class::get(actor).get } if((dest.mX - pos.pos[0])*(dest.mX - pos.pos[0])+(dest.mY - pos.pos[1])*(dest.mY - pos.pos[1])+(dest.mZ - pos.pos[2])*(dest.mZ - pos.pos[2]) From 032c542396758e6f71bccdd968476ab8dfa30652 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 24 Jan 2014 20:34:33 +0100 Subject: [PATCH 651/889] improving the cell cloning. --- apps/opencs/view/world/cellcreator.cpp | 9 +++++++-- apps/opencs/view/world/cellcreator.hpp | 1 - 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/world/cellcreator.cpp b/apps/opencs/view/world/cellcreator.cpp index 6298e3ba5..ebf88122b 100644 --- a/apps/opencs/view/world/cellcreator.cpp +++ b/apps/opencs/view/world/cellcreator.cpp @@ -22,7 +22,7 @@ std::string CSVWorld::CellCreator::getId() const CSVWorld::CellCreator::CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id) -: GenericCreator (data, undoStack, id), mCloningExterior(false) +: GenericCreator (data, undoStack, id) { mY = new QSpinBox (this); mY->setVisible (false); @@ -61,6 +61,7 @@ void CSVWorld::CellCreator::reset() mX->setValue (0); mY->setValue (0); mType->setCurrentIndex (0); + setType(0); GenericCreator::reset(); } @@ -80,14 +81,18 @@ void CSVWorld::CellCreator::valueChanged (int index) update(); } -void CSVWorld::CellCreator::cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType) +void CSVWorld::CellCreator::cloneMode(const std::string& originid, + const CSMWorld::UniversalId::Type type, + const CSMWorld::UniversalId::ArgumentType argumentType) { CSVWorld::GenericCreator::cloneMode(originid, type, argumentType); if (*(originid.begin()) == '#') //if originid points to the exterior cell { setType(1); //enable x and y controls + mType->setCurrentIndex(1); } else { setType(0); + mType->setCurrentIndex(0); } } diff --git a/apps/opencs/view/world/cellcreator.hpp b/apps/opencs/view/world/cellcreator.hpp index 521abd173..3130c1328 100644 --- a/apps/opencs/view/world/cellcreator.hpp +++ b/apps/opencs/view/world/cellcreator.hpp @@ -19,7 +19,6 @@ namespace CSVWorld QLabel *mYLabel; QSpinBox *mY; - bool mCloningExterior; protected: virtual std::string getId() const; From a1ac99463fff5440235d01e041111d053b2efff6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 24 Jan 2014 22:52:04 +0100 Subject: [PATCH 652/889] Fix an uninitialized value --- components/terrain/quadtreenode.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 02225cb02..7fc452fbf 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -433,6 +433,7 @@ void QuadTreeNode::prepareForCompositeMap(Ogre::TRect area) LayerInfo info; info.mDiffuseMap = "textures\\_land_default.dds"; info.mParallax = false; + info.mSpecular = false; layer.push_back(info); matGen.setLayerList(layer); makeQuad(sceneMgr, area.left, area.top, area.right, area.bottom, matGen.generateForCompositeMapRTT(Ogre::MaterialPtr())); From f09328ca845306f2a033157ee924ba7d91b5ef48 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 13:34:56 +0100 Subject: [PATCH 653/889] Clear global map overlay when starting/loading a game --- apps/openmw/mwbase/windowmanager.hpp | 3 +++ apps/openmw/mwgui/mapwindow.cpp | 19 +++++++++++++---- apps/openmw/mwgui/mapwindow.hpp | 3 +++ apps/openmw/mwgui/windowmanagerimp.cpp | 5 +++++ apps/openmw/mwgui/windowmanagerimp.hpp | 3 +++ apps/openmw/mwrender/globalmap.cpp | 25 +++++++++++------------ apps/openmw/mwrender/globalmap.hpp | 3 +++ apps/openmw/mwrender/renderingmanager.cpp | 3 --- apps/openmw/mwstate/statemanagerimp.cpp | 6 ++++-- apps/openmw/mwworld/worldimp.cpp | 1 + 10 files changed, 49 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index c39de4400..4d47e7eb7 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -285,6 +285,9 @@ namespace MWBase /// Should the cursor be visible? virtual bool getCursorVisible() = 0; + + /// Clear all savegame-specific data + virtual void clear() = 0; }; } diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index ba6114262..c09b4fea0 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -434,7 +434,7 @@ namespace MWGui static int _counter=0; - MyGUI::Button* markerWidget = mGlobalMapImage->createWidget("ButtonImage", + MyGUI::Button* markerWidget = mGlobalMapOverlay->createWidget("ButtonImage", widgetCoord, MyGUI::Align::Default, "Door" + boost::lexical_cast(_counter)); markerWidget->setImageResource("DoorMarker"); markerWidget->setUserString("ToolTipType", "Layout"); @@ -499,10 +499,11 @@ namespace MWGui mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); - for (unsigned int i=0; igetChildCount (); ++i) + // force markers to foreground + for (unsigned int i=0; igetChildCount (); ++i) { - if (mGlobalMapImage->getChildAt (i)->getName().substr(0,4) == "Door") - mGlobalMapImage->getChildAt (i)->castType()->setImageResource("DoorMarker"); + if (mGlobalMapOverlay->getChildAt (i)->getName().substr(0,4) == "Door") + mGlobalMapOverlay->getChildAt (i)->castType()->setImageResource("DoorMarker"); } globalMapUpdatePlayer(); @@ -573,4 +574,14 @@ namespace MWGui mGlobalMap->setViewOffset(viewoffs); } + void MapWindow::clear() + { + mGlobalMapRender->clear(); + + while (mEventBoxGlobal->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mEventBoxGlobal->getChildAt(0)); + while (mGlobalMapOverlay->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mGlobalMapOverlay->getChildAt(0)); + } + } diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 7df2105dc..dec27199a 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -92,6 +92,9 @@ namespace MWGui virtual void open(); + /// Clear all savegame-specific data + void clear(); + private: void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index e11d1afcc..8e49d6614 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1381,4 +1381,9 @@ namespace MWGui Settings::Manager::setFloat(setting + " h", "Windows", h); } + void WindowManager::clear() + { + mMap->clear(); + } + } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 9838a667f..68dc947af 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -280,6 +280,9 @@ namespace MWGui virtual bool getCursorVisible(); + /// Clear all savegame-specific data + virtual void clear(); + private: bool mConsoleOnlyScripts; diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 120a83fae..522bbb321 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -170,21 +170,10 @@ namespace MWRender tex->load(); - - mOverlayTexture = Ogre::TextureManager::getSingleton().createManual("GlobalMapOverlay", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mWidth, mHeight, 0, Ogre::PF_A8B8G8R8, Ogre::TU_DYNAMIC_WRITE_ONLY); - - std::vector buffer; - buffer.resize(mWidth * mHeight); - - // initialize to (0, 0, 0, 0) - for (int p=0; pgetBuffer()->lock(Ogre::HardwareBuffer::HBL_DISCARD), &buffer[0], mWidth*mHeight*4); - mOverlayTexture->getBuffer()->unlock(); + clear(); loadingListener->loadingOff(); } @@ -227,9 +216,19 @@ namespace MWRender if (!localMapTexture.isNull()) { - mOverlayTexture->getBuffer()->blit(localMapTexture->getBuffer(), Ogre::Image::Box(0,0,512,512), Ogre::Image::Box(originX,originY,originX+24,originY+24)); } } + + void GlobalMap::clear() + { + std::vector buffer; + // initialize to (0,0,0,0) + buffer.resize(mWidth * mHeight, 0); + + Ogre::PixelBox pb(mWidth, mHeight, 1, Ogre::PF_A8B8G8R8, &buffer[0]); + + mOverlayTexture->getBuffer()->blitFromMemory(pb); + } } diff --git a/apps/openmw/mwrender/globalmap.hpp b/apps/openmw/mwrender/globalmap.hpp index aad9adcc4..20f40de99 100644 --- a/apps/openmw/mwrender/globalmap.hpp +++ b/apps/openmw/mwrender/globalmap.hpp @@ -31,6 +31,9 @@ namespace MWRender void exploreCell (int cellX, int cellY); + /// Clears the overlay + void clear(); + private: std::string mCacheDir; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 56096b2f5..f9dd4e566 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -290,9 +290,6 @@ void RenderingManager::rotateObject(const MWWorld::Ptr &ptr) void RenderingManager::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur) { - Ogre::Image im; - im.encode(".jpg"); - Ogre::SceneNode *child = mRendering.getScene()->getSceneNode(old.getRefData().getHandle()); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 6251c49c4..e94f790c7 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -35,6 +35,7 @@ void MWState::StateManager::cleanup (bool force) MWBase::Environment::get().getJournal()->clear(); MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear(); MWBase::Environment::get().getWorld()->clear(); + MWBase::Environment::get().getWindowManager()->clear(); mState = State_NoGame; mCharacterManager.clearCurrentCharacter(); @@ -73,8 +74,8 @@ void MWState::StateManager::askLoadRecent() { MWState::Slot lastSave = *getCurrentCharacter()->begin(); std::vector buttons; - buttons.push_back("Yes"); - buttons.push_back("No"); + buttons.push_back("#{sYes}"); + buttons.push_back("#{sNo}"); std::string tag("%s"); std::string message = MWBase::Environment::get().getWindowManager()->getGameSettingString("sLoadLastSaveMsg", tag); size_t pos = message.find(tag); @@ -257,6 +258,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl Settings::Manager::setString ("character", "Saves", slot->mPath.parent_path().filename().string()); + MWBase::Environment::get().getWindowManager()->setNewGame(false); MWBase::Environment::get().getWorld()->setupPlayer(); MWBase::Environment::get().getWorld()->renderPlayer(); MWBase::Environment::get().getWindowManager()->updatePlayer(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9d07d0119..e5138eaea 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -248,6 +248,7 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->updatePlayer(); + // FIXME: this will add cell 0,0 as visible on the global map ESM::Position pos; const int cellSize = 8192; pos.pos[0] = cellSize/2; From 9c0ed6955041285dfc9df39be6e06634a70c3239 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 15:10:25 +0100 Subject: [PATCH 654/889] Fix CellRef loading issue causing a startup script failure when TR_Mainland.esm is loaded. --- components/esm/cellref.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index b9f630290..19427af0c 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -18,6 +18,11 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) mRefID = esm.getHNString ("NAME"); + // Again, UNAM sometimes appears after NAME and sometimes later. + // Or perhaps this UNAM means something different? + mReferenceBlocked = -1; + esm.getHNOT (mReferenceBlocked, "UNAM"); + mScale = 1.0; esm.getHNOT (mScale, "XSCL"); @@ -54,7 +59,6 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) mKey = esm.getHNOString ("KNAM"); mTrap = esm.getHNOString ("TNAM"); - mReferenceBlocked = -1; mFltv = 0; esm.getHNOT (mReferenceBlocked, "UNAM"); esm.getHNOT (mFltv, "FLTV"); @@ -162,4 +166,4 @@ void ESM::CellRef::blank() bool ESM::operator== (const CellRef::RefNum& left, const CellRef::RefNum& right) { return left.mIndex==right.mIndex && left.mContentFile==right.mContentFile; -} \ No newline at end of file +} From 28185e20174b994b28cbc52cb636219781eff654 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 15:54:24 +0100 Subject: [PATCH 655/889] Death/godmode fixes: Revive player *after* character update, since there might be fall damage. --- apps/openmw/engine.cpp | 12 ++++---- apps/openmw/mwmechanics/actors.cpp | 40 ++++++++++++++------------- apps/openmw/mwmechanics/character.cpp | 1 - 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index b2c0c1968..66dea4f1d 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -89,6 +89,8 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) if (mUseSound) MWBase::Environment::get().getSoundManager()->update(frametime); + bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode(); + if (MWBase::Environment::get().getStateManager()->getState()== MWBase::StateManager::State_Running) { @@ -105,7 +107,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) if (changed) // keep change flag for another frame, if cell changed happened in local script MWBase::Environment::get().getWorld()->markCellAsUnchanged(); - if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) + if (!paused) MWBase::Environment::get().getWorld()->advanceTime( frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); @@ -116,18 +118,18 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) // update actors MWBase::Environment::get().getMechanicsManager()->update(frametime, - MWBase::Environment::get().getWindowManager()->isGuiMode()); + paused); if (MWBase::Environment::get().getStateManager()->getState()== MWBase::StateManager::State_Running) { - MWWorld::Ptr player = mEnvironment.getWorld()->getPlayer().getPlayer(); - if(MWWorld::Class::get(player).getCreatureStats(player).isDead()) + MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr(); + if(!paused && player.getClass().getCreatureStats(player).isDead()) MWBase::Environment::get().getStateManager()->endGame(); } // update world - MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode()); + MWBase::Environment::get().getWorld()->update(frametime, paused); // update GUI Ogre::RenderWindow* window = mOgre->getWindow(); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 51e91a954..34cfe16fa 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -771,6 +771,24 @@ namespace MWMechanics void Actors::update (float duration, bool paused) { + if(!paused) + { + // Note: we need to do this before any of the animations are updated. + // Reaching the text keys may trigger Hit / Spellcast (and as such, particles), + // so updating VFX immediately after that would just remove the particle effects instantly. + // There needs to be a magic effect update in between. + for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + iter->second->updateContinuousVfx(); + + for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + { + if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects().get( + ESM::MagicEffect::Paralyze).mMagnitude > 0) + iter->second->skipAnim(); + iter->second->update(duration); + } + } + if (!paused) { for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++) @@ -804,7 +822,6 @@ namespace MWMechanics stat.setModified(1, 0); stats.setHealth(stat); } - stats.resurrect(); continue; } @@ -818,6 +835,9 @@ namespace MWMechanics spells.purge(iter->first.getRefData().getHandle()); } + // FIXME: see http://bugs.openmw.org/issues/869 + MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false); + if (iter->second->kill()) { ++mDeathCount[cls.getId(iter->first)]; @@ -839,24 +859,6 @@ namespace MWMechanics } } } - - if(!paused) - { - // Note: we need to do this before any of the animations are updated. - // Reaching the text keys may trigger Hit / Spellcast (and as such, particles), - // so updating VFX immediately after that would just remove the particle effects instantly. - // There needs to be a magic effect update in between. - for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) - iter->second->updateContinuousVfx(); - - for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) - { - if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects().get( - ESM::MagicEffect::Paralyze).mMagnitude > 0) - iter->second->skipAnim(); - iter->second->update(duration); - } - } } void Actors::restoreDynamicStats(bool sleep) { diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index ee0d07731..40e6cde69 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1153,7 +1153,6 @@ void CharacterController::update(float duration) } else if(cls.getCreatureStats(mPtr).isDead()) { - MWBase::Environment::get().getWorld()->enableActorCollision(mPtr, false); world->queueMovement(mPtr, Ogre::Vector3(0.0f)); } From bdb03926c29d837bb23ce436a998e10b277a9426 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 16:13:45 +0100 Subject: [PATCH 656/889] Fix crash when loading another game after dying --- apps/openmw/mwworld/worldimp.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e5138eaea..076fcfdb6 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1783,6 +1783,12 @@ namespace MWWorld void World::renderPlayer() { mRendering->renderPlayer(mPlayer->getPlayer()); + + // At this point the Animation object is live, and the CharacterController associated with it must be created. + // It has to be done at this point: resetCamera below does animation->setViewMode -> CharacterController::forceStateUpdate + // so we should make sure not to use a "stale" controller for that. + MWBase::Environment::get().getMechanicsManager()->add(mPlayer->getPlayer()); + mPhysics->addActor(mPlayer->getPlayer()); mRendering->resetCamera(); } From 79a9c4e048a839156ff084251bac3b8bdcca2dbe Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 16:33:39 +0100 Subject: [PATCH 657/889] Clear mShared before populating it. Fixes an issue with duplicate records (e.g. dialogue keywords) after loading a savegame. --- apps/openmw/mwworld/store.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index df957408d..7bd00d6bf 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -201,6 +201,7 @@ namespace MWWorld void setUp() { //std::sort(mStatic.begin(), mStatic.end(), RecordCmp()); + mShared.clear(); mShared.reserve(mStatic.size()); typename std::map::iterator it = mStatic.begin(); for (; it != mStatic.end(); ++it) { From e62bf8fca9900a7c747d03ffa2afd6a3f99e8504 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 16:41:26 +0100 Subject: [PATCH 658/889] Remove some garbage --- apps/openmw/mwgui/journalbooks.cpp | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/apps/openmw/mwgui/journalbooks.cpp b/apps/openmw/mwgui/journalbooks.cpp index dbea10e77..8caea770e 100644 --- a/apps/openmw/mwgui/journalbooks.cpp +++ b/apps/openmw/mwgui/journalbooks.cpp @@ -196,34 +196,6 @@ book JournalBooks::createEmptyJournalBook () typesetter->lineBreak (); typesetter->write (body, to_utf8_span ("You should have gone though the starting quest and got an initial quest.")); - BookTypesetter::Style* big = typesetter->createStyle ("", MyGUI::Colour::Black); - BookTypesetter::Style* test = typesetter->createStyle ("MonoFont", MyGUI::Colour::Blue); - - typesetter->sectionBreak (20); - typesetter->write (body, to_utf8_span ( - "The layout engine doesn't currently support aligning fonts to " - "their baseline within a single line so the following text looks " - "funny. In order to properly implement it, a stupidly simple " - "change is needed in MyGUI to report the where the baseline is for " - "a particular font" - )); - - typesetter->sectionBreak (20); - typesetter->write (big, to_utf8_span ("big text g")); - typesetter->write (body, to_utf8_span (" проверяем только в дебаге")); - typesetter->write (body, to_utf8_span (" normal g")); - typesetter->write (big, to_utf8_span (" done g")); - - typesetter->sectionBreak (20); - typesetter->write (test, to_utf8_span ( - "int main (int argc,\n" - " char ** argv)\n" - "{\n" - " print (\"hello world!\\n\");\n" - " return 0;\n" - "}\n" - )); - return typesetter->complete (); } From e0de76a6f714100df72135476cc9738bc6babf87 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 18:20:17 +0100 Subject: [PATCH 659/889] Save/load global map --- apps/openmw/mwbase/windowmanager.hpp | 5 ++ apps/openmw/mwgui/mapwindow.cpp | 17 +++++ apps/openmw/mwgui/mapwindow.hpp | 9 +++ apps/openmw/mwgui/windowmanagerimp.cpp | 10 +++ apps/openmw/mwgui/windowmanagerimp.hpp | 3 + apps/openmw/mwrender/globalmap.cpp | 99 ++++++++++++++++++++++++- apps/openmw/mwrender/globalmap.hpp | 10 ++- apps/openmw/mwstate/statemanagerimp.cpp | 7 ++ components/CMakeLists.txt | 2 +- components/esm/defs.hpp | 1 + components/esm/globalmap.cpp | 26 +++++++ components/esm/globalmap.hpp | 34 +++++++++ 12 files changed, 219 insertions(+), 4 deletions(-) create mode 100644 components/esm/globalmap.cpp create mode 100644 components/esm/globalmap.hpp diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 4d47e7eb7..fa0fe888b 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -33,6 +33,8 @@ namespace OEngine namespace ESM { struct Class; + class ESMReader; + class ESMWriter; } namespace MWWorld @@ -288,6 +290,9 @@ namespace MWBase /// Clear all savegame-specific data virtual void clear() = 0; + + virtual void write (ESM::ESMWriter& writer) = 0; + virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0; }; } diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index c09b4fea0..9ed3bf80f 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -584,4 +584,21 @@ namespace MWGui MyGUI::Gui::getInstance().destroyWidget(mGlobalMapOverlay->getChildAt(0)); } + void MapWindow::write(ESM::ESMWriter &writer) + { + mGlobalMapRender->write(writer); + } + + void MapWindow::readRecord(ESM::ESMReader &reader, int32_t type) + { + std::vector > exploredCells; + mGlobalMapRender->readRecord(reader, type, exploredCells); + + for (std::vector >::iterator it = exploredCells.begin(); it != exploredCells.end(); ++it) + { + const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getStore().get().search(it->first, it->second); + if (cell && !cell->mName.empty()) + addVisitedLocation(cell->mName, it->first, it->second); + } + } } diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index dec27199a..6ace7dc0f 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -8,6 +8,12 @@ namespace MWRender class GlobalMap; } +namespace ESM +{ + class ESMReader; + class ESMWriter; +} + namespace Loading { class Listener; @@ -95,6 +101,9 @@ namespace MWGui /// Clear all savegame-specific data void clear(); + void write (ESM::ESMWriter& writer); + void readRecord (ESM::ESMReader& reader, int32_t type); + private: void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 8e49d6614..66bd805af 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1386,4 +1386,14 @@ namespace MWGui mMap->clear(); } + void WindowManager::write(ESM::ESMWriter &writer) + { + mMap->write(writer); + } + + void WindowManager::readRecord(ESM::ESMReader &reader, int32_t type) + { + mMap->readRecord(reader, type); + } + } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 68dc947af..bc440d818 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -283,6 +283,9 @@ namespace MWGui /// Clear all savegame-specific data virtual void clear(); + virtual void write (ESM::ESMWriter& writer); + virtual void readRecord (ESM::ESMReader& reader, int32_t type); + private: bool mConsoleOnlyScripts; diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 522bbb321..6fbcfdc6b 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -12,6 +12,8 @@ #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -60,8 +62,6 @@ namespace MWRender loadingListener->setProgressRange((mMaxX-mMinX+1) * (mMaxY-mMinY+1)); loadingListener->setProgress(0); - mExploredBuffer.resize((mMaxX-mMinX+1) * (mMaxY-mMinY+1) * 4); - //if (!boost::filesystem::exists(mCacheDir + "/GlobalMap.png")) if (1) { @@ -231,4 +231,99 @@ namespace MWRender mOverlayTexture->getBuffer()->blitFromMemory(pb); } + + void GlobalMap::write(ESM::ESMWriter &writer) + { + ESM::GlobalMap map; + map.mBounds.mMinX = mMinX; + map.mBounds.mMaxX = mMaxX; + map.mBounds.mMinY = mMinY; + map.mBounds.mMaxY = mMaxY; + + Ogre::Image image; + mOverlayTexture->convertToImage(image); + Ogre::DataStreamPtr encoded = image.encode("png"); + map.mImageData.resize(encoded->size()); + encoded->read(&map.mImageData[0], encoded->size()); + + writer.startRecord(ESM::REC_GMAP); + map.save(writer); + writer.endRecord(ESM::REC_GMAP); + } + + void GlobalMap::readRecord(ESM::ESMReader &reader, int32_t type, std::vector >& exploredCells) + { + if (type == ESM::REC_GMAP) + { + ESM::GlobalMap map; + map.load(reader); + + const ESM::GlobalMap::Bounds& bounds = map.mBounds; + + if (bounds.mMaxX-bounds.mMinX <= 0) + return; + if (bounds.mMaxY-bounds.mMinY <= 0) + return; + + Ogre::Image image; + Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&map.mImageData[0], map.mImageData.size())); + image.load(stream, "png"); + + int xLength = (bounds.mMaxX-bounds.mMinX+1); + int yLength = (bounds.mMaxY-bounds.mMinY+1); + + // Size of one cell in image space + int cellImageSizeSrc = image.getWidth() / xLength; + if (int(image.getHeight() / yLength) != cellImageSizeSrc) + throw std::runtime_error("cell size must be quadratic"); + + // Determine which cells were explored by reading the image data + for (int x=0; x < xLength; ++x) + { + for (int y=0; y < yLength; ++y) + { + unsigned int imageX = (x) * cellImageSizeSrc; + // NB y + 1, because we want the top left corner, not bottom left where the origin of the cell is + unsigned int imageY = (yLength - (y + 1)) * cellImageSizeSrc; + + assert(imageX < image.getWidth()); + assert(imageY < image.getWidth()); + + if (image.getColourAt(imageX, imageY, 0).a > 0) + exploredCells.push_back(std::make_pair(x+bounds.mMinX,y+bounds.mMinY)); + } + } + + // If cell bounds of the currently loaded content and the loaded savegame do not match, + // we need to resize source/dest boxes to accommodate + // This means nonexisting cells will be dropped silently + + int cellImageSizeDst = 24; + + int leftDiff = (mMinX - bounds.mMinX); + int topDiff = (bounds.mMaxY - mMaxY); + int rightDiff = (bounds.mMaxX - mMaxX); + int bottomDiff = (mMinY - bounds.mMinY); + Ogre::Image::Box srcBox ( std::max(0, leftDiff * cellImageSizeSrc), + std::max(0, topDiff * cellImageSizeSrc), + std::min(image.getWidth(), image.getWidth() - rightDiff * cellImageSizeSrc), + std::min(image.getHeight(), image.getHeight() - bottomDiff * cellImageSizeSrc)); + + Ogre::Image::Box destBox ( std::max(0, -leftDiff * cellImageSizeDst), + std::max(0, -topDiff * cellImageSizeDst), + std::min(mOverlayTexture->getWidth(), mOverlayTexture->getWidth() + rightDiff * cellImageSizeDst), + std::min(mOverlayTexture->getHeight(), mOverlayTexture->getHeight() + bottomDiff * cellImageSizeDst)); + + // Looks like there is no interface for blitting from memory with src/dst boxes. + // So we create a temporary texture for blitting. + Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton().createManual("@temp", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, image.getWidth(), + image.getHeight(), 0, Ogre::PF_A8B8G8R8); + tex->loadImage(image); + + mOverlayTexture->getBuffer()->blit(tex->getBuffer(), srcBox, destBox); + + Ogre::TextureManager::getSingleton().remove("@temp"); + } + } } diff --git a/apps/openmw/mwrender/globalmap.hpp b/apps/openmw/mwrender/globalmap.hpp index 20f40de99..5fe878cd4 100644 --- a/apps/openmw/mwrender/globalmap.hpp +++ b/apps/openmw/mwrender/globalmap.hpp @@ -10,6 +10,12 @@ namespace Loading class Listener; } +namespace ESM +{ + class ESMWriter; + class ESMReader; +} + namespace MWRender { @@ -34,13 +40,15 @@ namespace MWRender /// Clears the overlay void clear(); + void write (ESM::ESMWriter& writer); + void readRecord (ESM::ESMReader& reader, int32_t type, std::vector >& exploredCells); + private: std::string mCacheDir; std::vector< std::pair > mExploredCells; Ogre::TexturePtr mOverlayTexture; - std::vector mExploredBuffer; int mWidth; int mHeight; diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index e94f790c7..a396d78c5 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -174,6 +174,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot +MWBase::Environment::get().getJournal()->countSavedGameRecords() +MWBase::Environment::get().getWorld()->countSavedGameRecords() +MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords() + + 1 // global map ); writer.save (stream); @@ -185,6 +186,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot MWBase::Environment::get().getJournal()->write (writer); MWBase::Environment::get().getWorld()->write (writer); MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer); + MWBase::Environment::get().getWindowManager()->write(writer); writer.close(); @@ -243,6 +245,11 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val); break; + case ESM::REC_GMAP: + + MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val); + break; + default: // ignore invalid records diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index d9ab8129d..d73bcaf74 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -40,7 +40,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame journalentry queststate locals globalscript player objectstate cellid cellstate + savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap ) add_component_dir (misc diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 74d987df8..40ef7ecb6 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -91,6 +91,7 @@ enum RecNameInts REC_PLAY = 0x59414c50, REC_CSTA = 0x41545343, REC_OBJE = 0x454a424f, + REC_GMAP = 0x50414d47, // format 1 REC_FILT = 0x544C4946 diff --git a/components/esm/globalmap.cpp b/components/esm/globalmap.cpp new file mode 100644 index 000000000..1fa5f907e --- /dev/null +++ b/components/esm/globalmap.cpp @@ -0,0 +1,26 @@ +#include "globalmap.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" +#include "defs.hpp" + +unsigned int ESM::GlobalMap::sRecordId = ESM::REC_GMAP; + +void ESM::GlobalMap::load (ESMReader &esm) +{ + esm.getHNT(mBounds, "BNDS"); + + esm.getSubNameIs("DATA"); + esm.getSubHeader(); + mImageData.resize(esm.getSubSize()); + esm.getExact(&mImageData[0], mImageData.size()); +} + +void ESM::GlobalMap::save (ESMWriter &esm) const +{ + esm.writeHNT("BNDS", mBounds); + + esm.startSubRecord("DATA"); + esm.write(&mImageData[0], mImageData.size()); + esm.endRecord("DATA"); +} diff --git a/components/esm/globalmap.hpp b/components/esm/globalmap.hpp new file mode 100644 index 000000000..5d036c736 --- /dev/null +++ b/components/esm/globalmap.hpp @@ -0,0 +1,34 @@ +#ifndef OPENMW_COMPONENTS_ESM_GLOBALMAP_H +#define OPENMW_COMPONENTS_ESM_GLOBALMAP_H + +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + ///< \brief An image containing the explored areas on the global map. + struct GlobalMap + { + static unsigned int sRecordId; + + // The minimum and maximum cell coordinates + struct Bounds + { + int mMinX, mMaxX, mMinY, mMaxY; + }; + + Bounds mBounds; + + std::vector mImageData; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; + +} + +#endif From a1fbd1fcc8ada98cfb1c8c0583cdfe3669c87cd6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 00:14:49 +0100 Subject: [PATCH 660/889] Revert "Merge remote-tracking branch 'mrcheko/master'" This reverts commit df7c139e2f614f8a951aae1b4889b920af0013fe, reversing changes made to fec26342cd57269bc0801e5d901e00419fbc7cec. --- apps/openmw/mwmechanics/aicombat.cpp | 27 ++++++++++--------------- apps/openmw/mwmechanics/aicombat.hpp | 2 +- apps/openmw/mwmechanics/pathfinding.cpp | 17 ---------------- apps/openmw/mwmechanics/pathfinding.hpp | 5 ----- 4 files changed, 12 insertions(+), 39 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 283c7f042..f24618510 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -1,5 +1,4 @@ #include "aicombat.hpp" -#include "aifollow.hpp" #include "movement.hpp" @@ -39,7 +38,7 @@ namespace MWMechanics mTimerAttack(0), mTimerReact(0), mTimerCombatMove(0), - mFollowTarget(false), + mCloseUp(false), mReadyToAttack(false), mStrike(false), mCombatMove(false), @@ -183,7 +182,7 @@ namespace MWMechanics Ogre::Vector3 vDir = vDest - vStart; float distBetween = vDir.length(); - if(distBetween < rangeMelee || (distBetween <= rangeCloseUp && mFollowTarget) ) + if(distBetween < rangeMelee || (distBetween <= rangeCloseUp && mCloseUp) ) { //Melee and Close-up combat vDir.z = 0; @@ -193,10 +192,13 @@ namespace MWMechanics // TODO: use movement settings instead of rotating directly MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + if(mPathFinder.isPathConstructed()) + mPathFinder.clearPath(); + //MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; //bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget); - if (mFollowTarget && distBetween > rangeMelee) + if (mCloseUp && distBetween > rangeMelee) { //Close-up combat: just run up on target mMovement.mPosition[1] = 1; @@ -212,7 +214,7 @@ namespace MWMechanics mTimerCombatMove = 0.1f + 0.1f * static_cast(rand())/RAND_MAX; mCombatMove = true; } - else if(actor.getClass().isNpc() && (!distantCombat || (distantCombat && rangeMelee/5))) + else if(!distantCombat || (distantCombat && rangeMelee/5)) { //apply sideway movement (kind of dodging) with some probability if(static_cast(rand())/RAND_MAX < 0.25) @@ -230,19 +232,13 @@ namespace MWMechanics mReadyToAttack = true; //only once got in melee combat, actor is allowed to use close-up shortcutting - mFollowTarget = true; + mCloseUp = true; } } else { - //target is at far distance: build path to target OR follow target (if previously actor had reached it once) - - /* - //apply when AIFOLLOW package implementation will be existent - if(mFollowTarget) - actor.getClass().getCreatureStats(actor).getAiSequence().stack(AiFollow(mTarget));*/ - - mFollowTarget = false; + //target is at far distance: build & follow the path + mCloseUp = false; buildNewPath(actor); @@ -344,8 +340,7 @@ namespace MWMechanics //maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path, //not the actual path length. Here we should know if the new path is actually more effective. //if(pathFinder2.getPathSize() < mPathFinder.getPathSize()) - newPathFinder.syncStart(mPathFinder.getPath()); - mPathFinder = newPathFinder; + mPathFinder = newPathFinder; } } } diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index a24183c65..ab9b9a821 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -37,7 +37,7 @@ namespace MWMechanics float mTimerCombatMove; bool mReadyToAttack, mStrike; - bool mFollowTarget; + bool mCloseUp; bool mCombatMove; MWMechanics::Movement mMovement; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index fd44fcc4c..c8bc9b49c 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -4,7 +4,6 @@ #include "../mwbase/environment.hpp" #include "OgreMath.h" -#include "OgreVector3.h" #include #include @@ -234,21 +233,5 @@ namespace MWMechanics return false; } - - void PathFinder::syncStart(const std::list &path) - { - std::list::const_iterator oldStart = path.begin(); - std::list::iterator iter = ++mPath.begin(); - - if( (*iter).mX == oldStart->mX - && (*iter).mY == oldStart->mY - && (*iter).mZ == oldStart->mZ - && (*iter).mAutogenerated == oldStart->mAutogenerated - && (*iter).mConnectionNum == oldStart->mConnectionNum ) - { - mPath.pop_front(); - } - - } } diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index de58f5db8..916df850b 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -37,11 +37,6 @@ namespace MWMechanics return mPath; } - //When first point of newly created path is the nearest to actor point, then - //the cituation can occure when this point is undesirable (if the 2nd point of new path == the 1st point of old path) - //This functions deletes that point. - void syncStart(const std::list &path); - private: std::list mPath; bool mIsPathConstructed; From b1066de81de13f493ce2b94207bcbbf7e7d0d722 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 00:16:56 +0100 Subject: [PATCH 661/889] Revert "Merge remote-tracking branch 'mrcheko/master'" This reverts commit 4e360136b10061e29fd60492544aad59850513e7, reversing changes made to 047bbe43b2ca08a63c8e18d2b4af52e0dbefb37b. Conflicts: apps/openmw/mwmechanics/aicombat.cpp --- apps/openmw/mwmechanics/actors.cpp | 3 +- apps/openmw/mwmechanics/aicombat.cpp | 426 +++++----------------- apps/openmw/mwmechanics/aicombat.hpp | 26 +- apps/openmw/mwmechanics/aisequence.cpp | 7 +- apps/openmw/mwmechanics/aisequence.hpp | 5 +- apps/openmw/mwmechanics/character.cpp | 4 +- apps/openmw/mwmechanics/character.hpp | 10 +- apps/openmw/mwmechanics/creaturestats.cpp | 11 +- apps/openmw/mwscript/aiextensions.cpp | 23 +- 9 files changed, 135 insertions(+), 380 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 35783bd9f..272b9a0d0 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -13,7 +13,6 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/actionequip.hpp" -#include "../mwworld/player.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -209,7 +208,7 @@ namespace MWMechanics && LOS ) { - creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayer().getPlayer())); + creatureStats.getAiSequence().stack(AiCombat("player")); creatureStats.setHostile(true); } } diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index f24618510..700cea2f3 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -4,18 +4,15 @@ #include "../mwworld/class.hpp" #include "../mwworld/timestamp.hpp" - +#include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.hpp" -#include "character.hpp" -#include "../mwworld/inventorystore.hpp" - #include "creaturestats.hpp" +#include "npcstats.hpp" #include -#include namespace { @@ -25,80 +22,107 @@ namespace return 1.0; return -1.0; } - - void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement); - //chooses an attack depending on probability to avoid uniformity - void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement); } namespace MWMechanics { - AiCombat::AiCombat(const MWWorld::Ptr& actor) : - mTarget(actor), - mTimerAttack(0), - mTimerReact(0), - mTimerCombatMove(0), - mCloseUp(false), - mReadyToAttack(false), - mStrike(false), - mCombatMove(false), - mMovement() + + AiCombat::AiCombat(const std::string &targetId) + :mTargetId(targetId),mTimer(0),mTimer2(0) { } bool AiCombat::execute (const MWWorld::Ptr& actor,float duration) { - //General description - if(!actor.getClass().getCreatureStats(actor).isHostile()) - return true; - if (mTarget.getClass().getCreatureStats(mTarget).isDead()) + if(!MWWorld::Class::get(actor).getCreatureStats(actor).isHostile()) return true; + + const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mTargetId, false); + + if (target.getClass().getCreatureStats(target).isDead()) return true; - //Update every frame - if(mReadyToAttack) - determineAttackType(actor, mMovement); + if(MWWorld::Class::get(actor).getCreatureStats(actor).getHealth().getCurrent() <= 0) return true; - if(mCombatMove) + actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); + + if (actor.getClass().hasInventoryStore(actor)) { - mTimerCombatMove -= duration; - if( mTimerCombatMove <= 0) + MWMechanics::DrawState_ state = actor.getClass().getCreatureStats(actor).getDrawState(); + if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) + actor.getClass().getCreatureStats(actor).setDrawState(MWMechanics::DrawState_Weapon); + //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true); + } + + ESM::Position pos = actor.getRefData().getPosition(); + const ESM::Pathgrid *pathgrid = + MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); + + float xCell = 0; + float yCell = 0; + + if (actor.getCell()->mCell->isExterior()) + { + xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; + yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; + } + + ESM::Pathgrid::Point dest; + dest.mX = target.getRefData().getPosition().pos[0]; + dest.mY = target.getRefData().getPosition().pos[1]; + dest.mZ = target.getRefData().getPosition().pos[2]; + + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; + start.mZ = pos.pos[2]; + + mTimer2 = mTimer2 + duration; + + if(!mPathFinder.isPathConstructed()) + mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, true); + else + { + mPathFinder2.buildPath(start, dest, pathgrid, xCell, yCell, true); + ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); + if((mTimer2 > 0.25)&&(mPathFinder2.getPathSize() < mPathFinder.getPathSize() || + (dest.mX - lastPt.mX)*(dest.mX - lastPt.mX)+(dest.mY - lastPt.mY)*(dest.mY - lastPt.mY)+(dest.mZ - lastPt.mZ)*(dest.mZ - lastPt.mZ) > 200*200)) { - mTimerCombatMove = 0; - mMovement.mPosition[1] = mMovement.mPosition[0] = 0; - mCombatMove = false; + mTimer2 = 0; + mPathFinder = mPathFinder2; } } - actor.getClass().getMovementSettings(actor) = mMovement; - - //actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mReadyToAttack); - mTimerAttack -= duration; - actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mStrike); - float tReaction = 0.25f; - if(mTimerReact < tReaction) + mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); + + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + + // TODO: use movement settings instead of rotating directly + MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + + + float range = 100; + MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); + if((dest.mX - start.mX)*(dest.mX - start.mX)+(dest.mY - start.mY)*(dest.mY - start.mY)+(dest.mZ - start.mZ)*(dest.mZ - start.mZ) + < range*range) { - mTimerReact += duration; - return false; - } + float directionX = dest.mX - start.mX; + float directionY = dest.mY - start.mY; + float directionResult = sqrt(directionX * directionX + directionY * directionY); - //Update with period = tReaction + zAngle = Ogre::Radian( Ogre::Math::ACos(directionY / directionResult) * sgn(Ogre::Math::ASin(directionX / directionResult)) ).valueDegrees(); + // TODO: use movement settings instead of rotating directly + MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); - mTimerReact = 0; + mPathFinder.clearPath(); - //actual attacking logic - //TODO: Some skills affect period of strikes.For berserk-like style period ~ 0.25f - float attackPeriod = 1.0f; - if(mReadyToAttack) - { - if(mTimerAttack <= -attackPeriod) + if(mTimer == 0) + { + MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); + //mTimer = mTimer + duration; + } + if( mTimer > 1) { - //TODO: should depend on time between 'start' to 'min attack' - //for better controlling of NPCs' attack strength. - //Also it seems that this time is different for slash/thrust/chop - mTimerAttack = 0.35f * static_cast(rand())/RAND_MAX; - mStrike = true; - - //say a provoking combat phrase if (actor.getClass().isNpc()) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); @@ -109,242 +133,21 @@ namespace MWMechanics MWBase::Environment::get().getDialogueManager()->say(actor, "attack"); } } - } - else if (mTimerAttack <= 0) - mStrike = false; - } - else - { - mTimerAttack = -attackPeriod; - mStrike = false; - } - - const MWWorld::Class &cls = actor.getClass(); - const ESM::Weapon *weapon = NULL; - MWMechanics::WeaponType weaptype; - float weapRange, weapSpeed = 1.0f; - actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); - - if(actor.getClass().hasInventoryStore(actor)) - { - MWMechanics::DrawState_ state = actor.getClass().getCreatureStats(actor).getDrawState(); - if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) - actor.getClass().getCreatureStats(actor).setDrawState(MWMechanics::DrawState_Weapon); - - //Get weapon speed and range - MWWorld::ContainerStoreIterator weaponSlot = - MWMechanics::getActiveWeapon(cls.getCreatureStats(actor), cls.getInventoryStore(actor), &weaptype); - if (weaptype == WeapType_HandToHand) - { - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); - weapRange = gmst.find("fHandToHandReach")->getFloat(); + MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true); + mTimer = 0; } else { - weapon = weaponSlot->get()->mBase; - weapRange = weapon->mData.mReach; - weapSpeed = weapon->mData.mSpeed; + mTimer = mTimer + duration; } - weapRange *= 100.0f; + + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(!MWWorld::Class::get(actor).getCreatureStats(actor).getAttackingOrSpell()); } - else //is creature - { - weaptype = WeapType_HandToHand; //doesn't matter, should only reflect if it is melee or distant weapon - weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit) - } - - //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); - - ESM::Position pos = actor.getRefData().getPosition(); - - float zAngle; - - float rangeMelee; - float rangeCloseUp; - bool distantCombat = false; - if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_ThowWeapon) // || WeapType_Spell_OnTarget - { - rangeMelee = 1000; // TODO: should depend on archer skill - rangeCloseUp = 0; //doesn't needed when attacking from distance - distantCombat = true; - } - else - { - rangeMelee = weapRange; - rangeCloseUp = 300; - } - - Ogre::Vector3 vStart(pos.pos[0], pos.pos[1], pos.pos[2]); - ESM::Position targetPos = mTarget.getRefData().getPosition(); - Ogre::Vector3 vDest(targetPos.pos[0], targetPos.pos[1], targetPos.pos[2]); - Ogre::Vector3 vDir = vDest - vStart; - float distBetween = vDir.length(); - - if(distBetween < rangeMelee || (distBetween <= rangeCloseUp && mCloseUp) ) - { - //Melee and Close-up combat - vDir.z = 0; - float dirLen = vDir.length(); - zAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / dirLen) * sgn(Ogre::Math::ASin(vDir.x / dirLen)) ).valueDegrees(); - - // TODO: use movement settings instead of rotating directly - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); - - if(mPathFinder.isPathConstructed()) - mPathFinder.clearPath(); - - //MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; - - //bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget); - if (mCloseUp && distBetween > rangeMelee) - { - //Close-up combat: just run up on target - mMovement.mPosition[1] = 1; - } - else - { - //Melee: stop running and attack - mMovement.mPosition[1] = 0; - chooseBestAttack(weapon, mMovement); - - if(mMovement.mPosition[0] != 0 || mMovement.mPosition[1]) - { - mTimerCombatMove = 0.1f + 0.1f * static_cast(rand())/RAND_MAX; - mCombatMove = true; - } - else if(!distantCombat || (distantCombat && rangeMelee/5)) - { - //apply sideway movement (kind of dodging) with some probability - if(static_cast(rand())/RAND_MAX < 0.25) - { - mMovement.mPosition[0] = static_cast(rand())/RAND_MAX < 0.5? 1: -1; - mTimerCombatMove = 0.05f + 0.15f * static_cast(rand())/RAND_MAX; - mCombatMove = true; - } - } - - if(distantCombat && distBetween < rangeMelee/4) - { - mMovement.mPosition[1] = -1; - } - - mReadyToAttack = true; - //only once got in melee combat, actor is allowed to use close-up shortcutting - mCloseUp = true; - } - } - else - { - //target is at far distance: build & follow the path - mCloseUp = false; - - buildNewPath(actor); - - //delete visited path node - mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); - - zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - - // TODO: use movement settings instead of rotating directly - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); - //MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; - mMovement.mPosition[1] = 1; - mReadyToAttack = false; - } - - if(distBetween > rangeMelee) - { - //special run attack; it shouldn't affect melee combat tactics - if(actor.getClass().getMovementSettings(actor).mPosition[1] == 1) - { - //check if actor can overcome the distance = distToTarget - attackerWeapRange - //less than in time of playing weapon anim from 'start' to 'hit' tags (t_swing) - //then start attacking - float speed1 = cls.getSpeed(actor); - float speed2 = mTarget.getClass().getSpeed(mTarget); - if(mTarget.getClass().getMovementSettings(mTarget).mPosition[0] == 0 - && mTarget.getClass().getMovementSettings(mTarget).mPosition[1] == 0) - speed2 = 0; - - float s1 = distBetween - weapRange; - float t = s1/speed1; - float s2 = speed2 * t; - float t_swing = 0.17f/weapSpeed;//0.17 should be the time of playing weapon anim from 'start' to 'hit' tags - if (t + s2/speed1 <= t_swing) - { - mReadyToAttack = true; - if(mTimerAttack <= -attackPeriod) - { - mTimerAttack = 0.3f*static_cast(rand())/RAND_MAX; - mStrike = true; - } - } - } - } - return false; } - void AiCombat::buildNewPath(const MWWorld::Ptr& actor) - { - //Construct path to target - ESM::Pathgrid::Point dest; - dest.mX = mTarget.getRefData().getPosition().pos[0]; - dest.mY = mTarget.getRefData().getPosition().pos[1]; - dest.mZ = mTarget.getRefData().getPosition().pos[2]; - Ogre::Vector3 newPathTarget = Ogre::Vector3(dest.mX, dest.mY, dest.mZ); - - ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); - Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); - float dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); - - float targetPosThreshold; - bool isOutside = actor.getCell()->mCell->isExterior(); - if (isOutside) - targetPosThreshold = 300; - else - targetPosThreshold = 100; - - if(dist > targetPosThreshold) - { - //construct new path only if target has moved away more than on - ESM::Position pos = actor.getRefData().getPosition(); - - ESM::Pathgrid::Point start; - start.mX = pos.pos[0]; - start.mY = pos.pos[1]; - start.mZ = pos.pos[2]; - - const ESM::Pathgrid *pathgrid = - MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); - - float xCell = 0; - float yCell = 0; - - if (actor.getCell()->mCell->isExterior()) - { - xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; - yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; - } - - if(!mPathFinder.isPathConstructed()) - mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, isOutside); - else - { - PathFinder newPathFinder; - newPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, isOutside); - - //TO EXPLORE: - //maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path, - //not the actual path length. Here we should know if the new path is actually more effective. - //if(pathFinder2.getPathSize() < mPathFinder.getPathSize()) - mPathFinder = newPathFinder; - } - } - } - int AiCombat::getTypeId() const { return TypeIdCombat; @@ -355,64 +158,15 @@ namespace MWMechanics return 1; } - MWWorld::Ptr AiCombat::getTarget() const + const std::string &AiCombat::getTargetId() const { - return mTarget; + return mTargetId; } + AiCombat *MWMechanics::AiCombat::clone() const { return new AiCombat(*this); } } -namespace -{ -void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) -{ - if (movement.mPosition[0] && !movement.mPosition[1]) //sideway - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Slash); - else if (movement.mPosition[1]) //forward - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Thrust); - else - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Chop); -} - -void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) -{ - //the more damage attackType deals the more probability it has - - if (weapon == NULL) - { - //hand-to-hand and creatures' attacks handled here - //hand-to-hand deals equal damage - float roll = static_cast(rand())/RAND_MAX; - if(roll <= 0.333f) //side punch - { - movement.mPosition[0] = (static_cast(rand())/RAND_MAX < 0.5f)? 1: -1; - movement.mPosition[1] = 0; - } - else if(roll <= 0.666f) //forward punch - movement.mPosition[1] = 1; - - return; - } - - int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; - int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; - int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; - - float total = slash + chop + thrust; - - float roll = static_cast(rand())/RAND_MAX; - if(roll <= static_cast(slash)/total) - { - movement.mPosition[0] = (static_cast(rand())/RAND_MAX < 0.5f)? 1: -1; - movement.mPosition[1] = 0; - } - else if(roll <= (static_cast(slash) + static_cast(thrust))/total) - movement.mPosition[1] = 1; - //else chop -} - -} diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index ab9b9a821..82efc043b 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -7,14 +7,12 @@ #include "movement.hpp" -#include "../mwbase/world.hpp" - namespace MWMechanics { class AiCombat : public AiPackage { public: - AiCombat(const MWWorld::Ptr& actor); + AiCombat(const std::string &targetId); virtual AiCombat *clone() const; @@ -25,25 +23,15 @@ namespace MWMechanics virtual unsigned int getPriority() const; - MWWorld::Ptr getTarget() const; + const std::string &getTargetId() const; private: + std::string mTargetId; + PathFinder mPathFinder; - //controls duration of the actual strike - float mTimerAttack; - float mTimerReact; - //controls duration of the sideway & forward moves - //when mCombatMove is true - float mTimerCombatMove; - - bool mReadyToAttack, mStrike; - bool mCloseUp; - bool mCombatMove; - - MWMechanics::Movement mMovement; - MWWorld::Ptr mTarget; - - void buildNewPath(const MWWorld::Ptr& actor); + PathFinder mPathFinder2; + float mTimer; + float mTimer2; }; } diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 3902b9186..73caa6ca7 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -55,12 +55,13 @@ int MWMechanics::AiSequence::getTypeId() const return mPackages.front()->getTypeId(); } -MWWorld::Ptr MWMechanics::AiSequence::getCombatTarget() const +bool MWMechanics::AiSequence::getCombatTarget(std::string &targetActorId) const { if (getTypeId() != AiPackage::TypeIdCombat) - return MWWorld::Ptr(); + return false; const AiCombat *combat = static_cast(mPackages.front()); - return combat->getTarget(); + targetActorId = combat->getTargetId(); + return true; } void MWMechanics::AiSequence::stopCombat() diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 075c43ff7..d65c31616 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -36,8 +36,9 @@ namespace MWMechanics int getTypeId() const; ///< @see enum AiPackage::TypeId - MWWorld::Ptr getCombatTarget () const; - ///< Return the combat target if a combat package is active, or an empty Ptr otherwise + bool getCombatTarget (std::string &targetActorId) const; + ///< Return true and assign target if combat package is currently + /// active, return false otherwise void stopCombat(); ///< Removes all combat packages until first non-combat or stack empty. diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 2f5b0ca6c..64df7becc 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -310,7 +310,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat } -void getWeaponGroup(WeaponType weaptype, std::string &group) +void CharacterController::getWeaponGroup(WeaponType weaptype, std::string &group) { const WeaponInfo *info = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(weaptype)); if(info != sWeaponTypeListEnd) @@ -318,7 +318,7 @@ void getWeaponGroup(WeaponType weaptype, std::string &group) } -MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype) +MWWorld::ContainerStoreIterator CharacterController::getActiveWeapon(CreatureStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype) { if(stats.getDrawState() == DrawState_Spell) { diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 915de93eb..112ea18ce 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -169,6 +169,12 @@ class CharacterController void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false); + static void getWeaponGroup(WeaponType weaptype, std::string &group); + + static MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, + MWWorld::InventoryStore &inv, + WeaponType *weaptype); + void clearAnimQueue(); bool updateWeaponState(bool inwater, bool isrunning); @@ -205,10 +211,6 @@ public: void forceStateUpdate(); }; - void getWeaponGroup(WeaponType weaptype, std::string &group); - MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, - MWWorld::InventoryStore &inv, - WeaponType *weaptype); } #endif /* GAME_MWMECHANICS_CHARACTER_HPP */ diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index ec300f6f0..c6522f08f 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -348,9 +348,14 @@ namespace MWMechanics bool CreatureStats::getCreatureTargetted() const { - if (mAiSequence.getCombatTarget().isEmpty()) - return false; - return mAiSequence.getCombatTarget().getTypeName() == typeid(ESM::Creature).name(); + std::string target; + if (mAiSequence.getCombatTarget(target)) + { + MWWorld::Ptr targetPtr; + targetPtr = MWBase::Environment::get().getWorld()->getPtr(target, true); + return targetPtr.getTypeName() == typeid(ESM::Creature).name(); + } + return false; } float CreatureStats::getEvasion() const diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 75c635103..09b1ed447 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -412,10 +412,16 @@ namespace MWScript std::string testedTargetId = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - const MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); + const MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); + std::string currentTargetId; - MWWorld::Ptr target = creatureStats.getAiSequence().getCombatTarget(); - runtime.push(Misc::StringUtils::ciEqual(target.getCellRef().mRefID, testedTargetId)); + bool targetsAreEqual = false; + if (creatureStats.getAiSequence().getCombatTarget (currentTargetId)) + { + if (currentTargetId == testedTargetId) + targetsAreEqual = true; + } + runtime.push(int(targetsAreEqual)); } }; @@ -426,14 +432,13 @@ namespace MWScript virtual void execute (Interpreter::Runtime &runtime) { MWWorld::Ptr actor = R()(runtime); - std::string targetID = runtime.getStringLiteral (runtime[0].mInteger); + std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); - - creatureStats.setHostile(true); - creatureStats.getAiSequence().stack( - MWMechanics::AiCombat(MWBase::Environment::get().getWorld()->getPtr(targetID, true) )); + MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); + creatureStats.getAiSequence().stack(MWMechanics::AiCombat(actorID)); + if (actorID == "player") + creatureStats.setHostile(true); } }; From eba6c9a8fd9b8083ce52b3b2a02689e411c0176f Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 02:49:01 +0100 Subject: [PATCH 662/889] Fix massive console spam regarding the scrib's idle3 animation --- apps/openmw/mwrender/animation.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index e8d215f5a..c0e4eebda 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -584,7 +584,11 @@ bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const s const std::string stoptag = groupname+": "+stop; NifOgre::TextKeyMap::const_iterator stopkey(groupstart); - while(stopkey != keys.end() && stopkey->second != stoptag) + while(stopkey != keys.end() + // We have to ignore extra garbage at the end. + // The Scrib's idle3 animation has "Idle3: Stop." instead of "Idle3: Stop". + // Why, just why? :( + && (stopkey->second.size() < stoptag.size() || stopkey->second.substr(0,stoptag.size()) != stoptag)) stopkey++; if(stopkey == keys.end()) return false; From 7cb47aa6350ad49ba7e4954463427a9308c2171c Mon Sep 17 00:00:00 2001 From: gus Date: Sun, 26 Jan 2014 21:26:19 +0100 Subject: [PATCH 663/889] new implementation of pathfinding. Works, but need clean up --- apps/openmw/mwmechanics/aifollow.cpp | 5 -- apps/openmw/mwmechanics/pathfinding.cpp | 110 +++++++++++++++++++++++- 2 files changed, 108 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index be57adadc..10bff8356 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -104,11 +104,6 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) return false; } - int MWMechanics::AiFollow::getTypeId() const -{ - return 3; -} - std::string MWMechanics::AiFollow::getFollowedActor() { return mActorId; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index f8908fbd9..1b42b11a4 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -6,6 +6,7 @@ #include "OgreMath.h" #include +#include namespace { @@ -132,6 +133,111 @@ namespace } } +namespace +{ + struct Edge + { + int destination; + float cost; + }; + struct Node + { + int label; + std::vector edges; + int parent;//used in pathfinding + }; + + std::list reconstructPath(const std::vector& graph,const ESM::Pathgrid* pathgrid, int lastNode,float xCell, float yCell) + { + std::list path; + while(graph[lastNode].parent != -1) + { + //std::cout << "not empty" << xCell; + ESM::Pathgrid::Point pt = pathgrid->mPoints[lastNode]; + pt.mX += xCell; + pt.mY += yCell; + path.push_front(pt); + lastNode = graph[lastNode].parent; + } + return path; + } + + std::list buildPath2(const ESM::Pathgrid* pathgrid,int start,int goal,float xCell = 0, float yCell = 0) + { + std::vector graph; + for(unsigned int i = 0; i < pathgrid->mPoints.size(); i++) + { + Node node; + node.label = i; + node.parent = -1; + graph.push_back(node); + } + for(unsigned int i = 0; i < pathgrid->mEdges.size(); i++) + { + Edge edge; + edge.destination = pathgrid->mEdges[i].mV1; + edge.cost = distance(pathgrid->mPoints[pathgrid->mEdges[i].mV0],pathgrid->mPoints[pathgrid->mEdges[i].mV1]); + graph[pathgrid->mEdges[i].mV0].edges.push_back(edge); + edge.destination = pathgrid->mEdges[i].mV0; + graph[pathgrid->mEdges[i].mV1].edges.push_back(edge); + } + + std::vector g_score(pathgrid->mPoints.size(),-1.); + std::vector f_score(pathgrid->mPoints.size(),-1.); + + g_score[start] = 0; + f_score[start] = distance(pathgrid->mPoints[start],pathgrid->mPoints[goal]); + + std::list openset; + std::list closedset; + openset.push_back(start); + + int current = -1; + + while(!openset.empty()) + { + current = openset.front(); + openset.pop_front(); + + if(current == goal) break; + + closedset.push_back(current); + + for(int j = 0;jmPoints[dest],pathgrid->mPoints[goal]); + if(!isInOpenSet) + { + std::list::iterator it = openset.begin(); + for(it = openset.begin();it!= openset.end();it++) + { + if(g_score[*it]>g_score[dest]) + break; + } + openset.insert(it,dest); + } + } + } + } + + } + return reconstructPath(graph,pathgrid,current,xCell,yCell); + + } + +} + namespace MWMechanics { PathFinder::PathFinder() @@ -183,9 +289,9 @@ namespace MWMechanics if(startNode != -1 && endNode != -1) { - if(!mIsGraphConstructed) buildPathgridGraph(pathGrid, xCell, yCell); + //if(!mIsGraphConstructed) buildPathgridGraph(pathGrid, xCell, yCell); - mPath = findPath(startNode, endNode, mGraph); + mPath = buildPath2(pathGrid,startNode,endNode,xCell,yCell);//findPath(startNode, endNode, mGraph); if(!mPath.empty()) { From 764011dd1b8a5856fc1a83a892d2fd4754f656e7 Mon Sep 17 00:00:00 2001 From: gus Date: Sun, 26 Jan 2014 21:53:55 +0100 Subject: [PATCH 664/889] clean up --- apps/openmw/mwmechanics/pathfinding.cpp | 201 +++++++++++++----------- apps/openmw/mwmechanics/pathfinding.hpp | 29 +++- 2 files changed, 129 insertions(+), 101 deletions(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 1b42b11a4..dcf47ca33 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -5,30 +5,10 @@ #include "OgreMath.h" -#include #include namespace { - struct found_path {}; - - typedef boost::adjacency_list< boost::vecS, boost::vecS, boost::undirectedS, - boost::property, boost::property > - PathGridGraph; - typedef boost::property_map::type WeightMap; - typedef PathGridGraph::vertex_descriptor PointID; - typedef PathGridGraph::edge_descriptor PointConnectionID; - - class goalVisited : public boost::default_dijkstra_visitor - { - public: - goalVisited(PointID goal) {mGoal = goal;}; - void examine_vertex(PointID u, const PathGridGraph g) {if(u == mGoal) throw found_path();}; - - private: - PointID mGoal; - }; - float distanceZCorrected(ESM::Pathgrid::Point point, float x, float y, float z) { x -= point.mX; @@ -80,74 +60,7 @@ namespace return closestIndex; } - PathGridGraph buildGraph(const ESM::Pathgrid* pathgrid, float xCell = 0, float yCell = 0) - { - PathGridGraph graph; - - for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++) - { - PointID pID = boost::add_vertex(graph); - graph[pID].mX = pathgrid->mPoints[counter].mX + xCell; - graph[pID].mY = pathgrid->mPoints[counter].mY + yCell; - graph[pID].mZ = pathgrid->mPoints[counter].mZ; - } - - for(unsigned int counterTwo = 0; counterTwo < pathgrid->mEdges.size(); counterTwo++) - { - PointID u = pathgrid->mEdges[counterTwo].mV0; - PointID v = pathgrid->mEdges[counterTwo].mV1; - - PointConnectionID edge; - bool done; - boost::tie(edge, done) = boost::add_edge(u, v, graph); - WeightMap weightmap = boost::get(boost::edge_weight, graph); - weightmap[edge] = distance(graph[u], graph[v]); - } - - return graph; - } - - std::list findPath(PointID start, PointID end,const PathGridGraph& graph) - { - std::vector p(boost::num_vertices(graph)); - std::vector d(boost::num_vertices(graph)); - std::list shortest_path; - - try - { - boost::dijkstra_shortest_paths(graph, start, - boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(goalVisited(end))); - } - - catch(found_path& fg) - { - for(PointID v = end; ; v = p[v]) - { - shortest_path.push_front(graph[v]); - if(p[v] == v) - break; - } - } - - return shortest_path; - } -} - -namespace -{ - struct Edge - { - int destination; - float cost; - }; - struct Node - { - int label; - std::vector edges; - int parent;//used in pathfinding - }; - - std::list reconstructPath(const std::vector& graph,const ESM::Pathgrid* pathgrid, int lastNode,float xCell, float yCell) + /*std::list reconstructPath(const std::vector& graph,const ESM::Pathgrid* pathgrid, int lastNode,float xCell, float yCell) { std::list path; while(graph[lastNode].parent != -1) @@ -160,9 +73,11 @@ namespace lastNode = graph[lastNode].parent; } return path; - } + }*/ - std::list buildPath2(const ESM::Pathgrid* pathgrid,int start,int goal,float xCell = 0, float yCell = 0) + + + /*std::list buildPath2(const ESM::Pathgrid* pathgrid,int start,int goal,float xCell = 0, float yCell = 0) { std::vector graph; for(unsigned int i = 0; i < pathgrid->mPoints.size(); i++) @@ -234,7 +149,7 @@ namespace } return reconstructPath(graph,pathgrid,current,xCell,yCell); - } + }*/ } @@ -252,12 +167,108 @@ namespace MWMechanics mIsPathConstructed = false; } - void PathFinder::buildPathgridGraph(const ESM::Pathgrid* pathGrid,float xCell, float yCell) + void PathFinder::buildPathgridGraph(const ESM::Pathgrid* pathGrid) { - mGraph = buildGraph(pathGrid, xCell, yCell); + mGraph.clear(); + mGScore.resize(pathGrid->mPoints.size(),-1); + mFScore.resize(pathGrid->mPoints.size(),-1); + Node defaultNode; + defaultNode.label = -1; + defaultNode.parent = -1; + mGraph.resize(pathGrid->mPoints.size(),defaultNode); + for(unsigned int i = 0; i < pathGrid->mPoints.size(); i++) + { + Node node; + node.label = i; + node.parent = -1; + mGraph[i] = node; + } + for(unsigned int i = 0; i < pathGrid->mEdges.size(); i++) + { + Edge edge; + edge.destination = pathGrid->mEdges[i].mV1; + edge.cost = distance(pathGrid->mPoints[pathGrid->mEdges[i].mV0],pathGrid->mPoints[pathGrid->mEdges[i].mV1]); + mGraph[pathGrid->mEdges[i].mV0].edges.push_back(edge); + edge.destination = pathGrid->mEdges[i].mV0; + mGraph[pathGrid->mEdges[i].mV1].edges.push_back(edge); + } mIsGraphConstructed = true; } + void PathFinder::cleanUpAStar() + { + for(int i=0;i PathFinder::aStarSearch(const ESM::Pathgrid* pathGrid,int start,int goal,float xCell, float yCell) + { + cleanUpAStar(); + mGScore[start] = 0; + mFScore[start] = distance(pathGrid->mPoints[start],pathGrid->mPoints[goal]); + + std::list openset; + std::list closedset; + openset.push_back(start); + + int current = -1; + + while(!openset.empty()) + { + current = openset.front(); + openset.pop_front(); + + if(current == goal) break; + + closedset.push_back(current); + + for(int j = 0;jmPoints[dest],pathGrid->mPoints[goal]); + if(!isInOpenSet) + { + std::list::iterator it = openset.begin(); + for(it = openset.begin();it!= openset.end();it++) + { + if(mGScore[*it]>mGScore[dest]) + break; + } + openset.insert(it,dest); + } + } + } + } + + } + + std::list path; + while(mGraph[current].parent != -1) + { + //std::cout << "not empty" << xCell; + ESM::Pathgrid::Point pt = pathGrid->mPoints[current]; + pt.mX += xCell; + pt.mY += yCell; + path.push_front(pt); + current = mGraph[current].parent; + } + return path; + } + void PathFinder::buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, const MWWorld::CellStore* cell, bool allowShortcuts) { @@ -289,9 +300,9 @@ namespace MWMechanics if(startNode != -1 && endNode != -1) { - //if(!mIsGraphConstructed) buildPathgridGraph(pathGrid, xCell, yCell); + if(!mIsGraphConstructed) buildPathgridGraph(pathGrid); - mPath = buildPath2(pathGrid,startNode,endNode,xCell,yCell);//findPath(startNode, endNode, mGraph); + mPath = aStarSearch(pathGrid,startNode,endNode,xCell,yCell);//findPath(startNode, endNode, mGraph); if(!mPath.empty()) { diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index 687031491..5bc9d7493 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -19,7 +19,7 @@ namespace MWMechanics void clearPath(); - void buildPathgridGraph(const ESM::Pathgrid* pathGrid,float xCell = 0, float yCell = 0); + void buildPathgridGraph(const ESM::Pathgrid* pathGrid); void buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, const MWWorld::CellStore* cell, bool allowShortcuts = true); @@ -51,13 +51,30 @@ namespace MWMechanics } private: - std::list mPath; + + struct Edge + { + int destination; + float cost; + }; + struct Node + { + int label; + std::vector edges; + int parent;//used in pathfinding + }; + + std::vector mGScore; + std::vector mFScore; + + std::list aStarSearch(const ESM::Pathgrid* pathGrid,int start,int goal,float xCell = 0, float yCell = 0); + void cleanUpAStar(); + + std::vector mGraph; bool mIsPathConstructed; - typedef boost::adjacency_list< boost::vecS, boost::vecS, boost::undirectedS, - boost::property, boost::property > - PathGridGraph; - PathGridGraph mGraph; + + std::list mPath; bool mIsGraphConstructed; const MWWorld::CellStore* mCell; }; From f5d589388ccbb6ec02b22dd6d9d4a8a41627a65e Mon Sep 17 00:00:00 2001 From: gus Date: Sun, 26 Jan 2014 22:06:54 +0100 Subject: [PATCH 665/889] bug fix --- apps/openmw/mwmechanics/pathfinding.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index dcf47ca33..668b4f2b1 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -266,6 +266,15 @@ namespace MWMechanics path.push_front(pt); current = mGraph[current].parent; } + + if(path.empty()) + { + ESM::Pathgrid::Point pt = pathGrid->mPoints[goal]; + pt.mX += xCell; + pt.mY += yCell; + path.push_front(pt); + } + return path; } From 45b3aa3d934b644436843e9ff305ab5aa1d4f3dd Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sun, 26 Jan 2014 23:32:57 +0200 Subject: [PATCH 666/889] corrected things connected to determining attack type --- apps/openmw/mwmechanics/aicombat.cpp | 4 +- apps/openmw/mwmechanics/character.cpp | 74 +++++++++-------------- apps/openmw/mwmechanics/character.hpp | 2 +- apps/openmw/mwmechanics/creaturestats.hpp | 6 +- 4 files changed, 31 insertions(+), 55 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 8f2408587..5ae2a0ca1 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -163,9 +163,7 @@ namespace MWMechanics float rangeMelee; float rangeCloseUp; bool distantCombat = false; - int attackType = actor.getClass().getCreatureStats(actor).getAttackType(); - if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_ThowWeapon - || attackType==MWMechanics::CreatureStats::AT_Target ) + if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_ThowWeapon) { rangeMelee = 1000; // TODO: should depend on archer skill rangeCloseUp = 0; //doesn't needed when attacking from distance diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 05c4e1f2a..954eb18b3 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -644,7 +644,12 @@ bool CharacterController::updateNpcState() mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Left Hand", effect->mParticle); mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Right Hand", effect->mParticle); - determineAttackType(effectentry.mRange); + switch(effectentry.mRange) + { + case 0: mAttackType = "self"; break; + case 1: mAttackType = "touch"; break; + case 2: mAttackType = "target"; break; + } mAnimation->play(mCurrentWeapon, Priority_Weapon, MWRender::Animation::Group_UpperBody, true, @@ -698,7 +703,7 @@ bool CharacterController::updateNpcState() mAttackType = "shoot"; else { - int attackType = stats.getAttackType(); + int attackType; if(isWeapon && Settings::Manager::getBool("best attack", "Game")) attackType = getBestAttack(weapon->get()->mBase); else @@ -1302,57 +1307,34 @@ void CharacterController::updateVisibility() mAnimation->setAlpha(alpha); } -void CharacterController::determineAttackType(int spellRange) +void CharacterController::determineAttackType() { - if(spellRange == -1) + float * move = mPtr.getClass().getMovementSettings(mPtr).mPosition; + + if (move[0] && !move[1]) //sideway { - float * move = mPtr.getClass().getMovementSettings(mPtr).mPosition; - - if (move[0] && !move[1]) //sideway - { - mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Slash); - if(mPtr.getClass().isNpc()) - mAttackType = "slash"; - else - mCurrentWeapon = "attack2"; - } - else if (move[1]) //forward - { - mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Thrust); - if(mPtr.getClass().isNpc()) - mAttackType = "thrust"; - else - mCurrentWeapon = "attack3"; - } + mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Slash); + if(mPtr.getClass().isNpc()) + mAttackType = "slash"; else - { - mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Chop); - if(mPtr.getClass().isNpc()) - mAttackType = "chop"; - else - mCurrentWeapon = "attack1"; - } - + mCurrentWeapon = "attack2"; + } + else if (move[1]) //forward + { + mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Thrust); + if(mPtr.getClass().isNpc()) + mAttackType = "thrust"; + else + mCurrentWeapon = "attack3"; } else { - switch(spellRange) - { - case 0: - mAttackType = "self"; - mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Self); - break; - case 1: - mAttackType = "touch"; - mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Touch); - break; - case 2: - mAttackType = "target"; - mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Target); - break; - } + mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Chop); + if(mPtr.getClass().isNpc()) + mAttackType = "chop"; + else + mCurrentWeapon = "attack1"; } - } } \ No newline at end of file diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 37fc42f1e..6950854c8 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -165,7 +165,7 @@ class CharacterController float mSecondsOfRunning; std::string mAttackType; // slash, chop or thrust - void determineAttackType(int spellRange = -1); + void determineAttackType(); void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false); diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index f9b52abc0..308883fc5 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -118,11 +118,7 @@ namespace MWMechanics { AT_Chop, AT_Slash, - AT_Thrust, - - AT_Self, - AT_Touch, - AT_Target, + AT_Thrust }; void setAttackType(int attackType) { mAttackType = attackType; } int getAttackType() { return mAttackType; } From 62c2259c871de8826b83fd239bb4a229f8cc7db3 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 10:10:23 +0100 Subject: [PATCH 667/889] removing coordinates handling --- apps/opencs/model/world/collection.hpp | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 0a8dc5a9d..d91260a5f 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -8,7 +8,6 @@ #include #include #include -#include #include @@ -54,8 +53,6 @@ namespace CSMWorld Collection (const Collection&); Collection& operator= (const Collection&); - void idToCoordiantes(Record& record, const std::string& destination) {} //dose nothing - protected: const std::map& getIdMap() const; @@ -156,23 +153,6 @@ namespace CSMWorld ///< \attention This function must not change the ID. }; - template<> - class Collection > : public CollectionBase //explicit specialisation - { - void idToCoordiantes(Record& record, const std::string& destination) - { - if (record.get().isExterior()) - { - unsigned separator = destination.find(' '); - assert(separator != std::string::npos); - std::string xPos(destination.substr(0, separator)); - std::string yPos(destination.substr(separator+1, destination.size())); - record.get().mData.mX = boost::lexical_cast(xPos); - record.get().mData.mY = boost::lexical_cast(yPos); - } - } - }; - template const std::map& Collection::getIdMap() const { @@ -230,9 +210,6 @@ namespace CSMWorld copy.mState = RecordBase::State_ModifiedOnly; copy.get().mId = destination; - //the below function has explicit specialization for cells, and does nothing fo other records - idToCoordiantes(copy, destination); - insertRecord(copy, getAppendIndex(destination, type)); } From be8b978a89b182ada92eb46ae94ac0970c8038c4 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 12:23:03 +0100 Subject: [PATCH 668/889] corrected mistake in data --- apps/opencs/model/world/commands.cpp | 1 - apps/opencs/model/world/data.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 762e4809c..789e65a21 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -1,4 +1,3 @@ - #include "commands.hpp" #include diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index a52821036..8d53c4e53 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -164,7 +164,7 @@ CSMWorld::Data::Data() : mRefs (mCells) mTopicInfos.addColumn (new StringIdColumn (true)); mTopicInfos.addColumn (new RecordStateColumn); - mTopicInfos.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Journal)); + mTopicInfos.addColumn (new FixedRecordTypeColumn (UniversalId::Type_TopicInfo)); mTopicInfos.addColumn (new TopicColumn (false)); mTopicInfos.addColumn (new ActorColumn); mTopicInfos.addColumn (new RaceColumn); From aa6d1ff4c3b4fa5d490c0a2468212ef9412bf928 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 13:08:14 +0100 Subject: [PATCH 669/889] removed needless argument for cloning --- apps/opencs/model/world/collection.hpp | 6 ++---- apps/opencs/model/world/collectionbase.hpp | 3 +-- apps/opencs/model/world/commands.cpp | 4 +--- apps/opencs/model/world/commands.hpp | 2 -- apps/opencs/model/world/idtable.cpp | 3 +-- apps/opencs/model/world/idtable.hpp | 1 - apps/opencs/model/world/refidcollection.cpp | 4 +--- apps/opencs/model/world/refidcollection.hpp | 3 +-- apps/opencs/view/world/cellcreator.cpp | 5 ++--- apps/opencs/view/world/cellcreator.hpp | 3 +-- apps/opencs/view/world/creator.hpp | 3 ++- apps/opencs/view/world/genericcreator.cpp | 6 ++---- apps/opencs/view/world/genericcreator.hpp | 3 +-- apps/opencs/view/world/referencecreator.cpp | 5 +++-- apps/opencs/view/world/referencecreator.hpp | 3 +-- apps/opencs/view/world/tablebottombox.cpp | 5 ++--- apps/opencs/view/world/tablebottombox.hpp | 3 ++- apps/opencs/view/world/tablesubview.cpp | 10 ++++++---- apps/opencs/view/world/tablesubview.hpp | 3 +-- 19 files changed, 30 insertions(+), 45 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index d91260a5f..4bb10c1d8 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -102,8 +102,7 @@ namespace CSMWorld virtual void cloneRecord(const std::string& origin, const std::string& destination, - const UniversalId::Type type, - const UniversalId::ArgumentType argumentType); + const UniversalId::Type type); virtual int searchId (const std::string& id) const; ////< Search record with \a id. @@ -203,8 +202,7 @@ namespace CSMWorld template void Collection::cloneRecord(const std::string& origin, const std::string& destination, - const UniversalId::Type type, - const UniversalId::ArgumentType argumentType) + const UniversalId::Type type) { Record copy(getRecord(origin)); copy.mState = RecordBase::State_ModifiedOnly; diff --git a/apps/opencs/model/world/collectionbase.hpp b/apps/opencs/model/world/collectionbase.hpp index d32847490..2473ae741 100644 --- a/apps/opencs/model/world/collectionbase.hpp +++ b/apps/opencs/model/world/collectionbase.hpp @@ -76,8 +76,7 @@ namespace CSMWorld virtual void cloneRecord(const std::string& origin, const std::string& destination, - const UniversalId::Type type, - const UniversalId::ArgumentType argumentType) = 0; + const UniversalId::Type type) = 0; virtual const RecordBase& getRecord (const std::string& id) const = 0; diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 789e65a21..5eb2e9414 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -27,13 +27,11 @@ CSMWorld::CloneCommand::CloneCommand(CSMWorld::IdTable& model, const std::string& idOrigin, const std::string& IdDestination, const CSMWorld::UniversalId::Type type, - const CSMWorld::UniversalId::ArgumentType argumentType, QUndoCommand* parent) : QUndoCommand(parent), mModel(model), mIdOrigin(idOrigin), mIdDestination(Misc::StringUtils::lowerCase(IdDestination)), - mArgumentType(argumentType), mType(type) { setText(("Clone record " + idOrigin + " to the " + IdDestination).c_str()); @@ -42,7 +40,7 @@ CSMWorld::CloneCommand::CloneCommand(CSMWorld::IdTable& model, void CSMWorld::CloneCommand::redo() { - mModel.cloneRecord(mIdOrigin, mIdDestination, mArgumentType, mType); + mModel.cloneRecord(mIdOrigin, mIdDestination, mType); for (std::map::const_iterator iter(mValues.begin()); iter != mValues.end(); ++iter) mModel.setData(mModel.getModelIndex(mIdDestination, iter->first), iter->second); diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index 04eb0afbf..0dedea012 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -45,7 +45,6 @@ namespace CSMWorld std::string mIdOrigin; std::string mIdDestination; UniversalId::Type mType; - UniversalId::ArgumentType mArgumentType; std::map mValues; public: @@ -53,7 +52,6 @@ namespace CSMWorld CloneCommand (IdTable& model, const std::string& idOrigin, const std::string& IdDestination, const UniversalId::Type type, - const UniversalId::ArgumentType argumentType, QUndoCommand* parent = 0); virtual void redo(); diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index f131f7087..bea307aa2 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -126,12 +126,11 @@ void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type void CSMWorld::IdTable::cloneRecord(const std::string& origin, const std::string& destination, - CSMWorld::UniversalId::ArgumentType argumentType, CSMWorld::UniversalId::Type type) { int index = mIdCollection->getAppendIndex (destination); beginInsertRows (QModelIndex(), index, index); - mIdCollection->cloneRecord(origin, destination, type, argumentType); + mIdCollection->cloneRecord(origin, destination, type); endInsertRows(); } diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index ce47307e3..2d620004c 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -65,7 +65,6 @@ namespace CSMWorld void cloneRecord(const std::string& origin, const std::string& destination, - UniversalId::ArgumentType argumentType, UniversalId::Type type = UniversalId::Type_None); QModelIndex getModelIndex (const std::string& id, int column) const; diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 75a1b681d..5e8d38117 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -451,15 +451,13 @@ void CSMWorld::RefIdCollection::replace (int index, const RecordBase& record) void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin, const std::string& destination, - const CSMWorld::UniversalId::Type type, - const CSMWorld::UniversalId::ArgumentType argumentType) + const CSMWorld::UniversalId::Type type) { RecordBase *newRecord = mData.getRecord(mData.searchId(origin)).clone(); newRecord->mState = RecordBase::State_ModifiedOnly; mAdapters.find(type)->second->setId(*newRecord, destination); mData.insertRecord(*newRecord, type, destination); delete newRecord; - //WIP } void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index 4ad181004..ebbf2100d 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -71,8 +71,7 @@ namespace CSMWorld virtual void cloneRecord(const std::string& origin, const std::string& destination, - const UniversalId::Type type, - const UniversalId::ArgumentType argumentType); + const UniversalId::Type type); virtual void appendBlankRecord (const std::string& id, UniversalId::Type type); ///< \param type Will be ignored, unless the collection supports multiple record types diff --git a/apps/opencs/view/world/cellcreator.cpp b/apps/opencs/view/world/cellcreator.cpp index ebf88122b..e65501e5f 100644 --- a/apps/opencs/view/world/cellcreator.cpp +++ b/apps/opencs/view/world/cellcreator.cpp @@ -82,10 +82,9 @@ void CSVWorld::CellCreator::valueChanged (int index) } void CSVWorld::CellCreator::cloneMode(const std::string& originid, - const CSMWorld::UniversalId::Type type, - const CSMWorld::UniversalId::ArgumentType argumentType) + const CSMWorld::UniversalId::Type type) { - CSVWorld::GenericCreator::cloneMode(originid, type, argumentType); + CSVWorld::GenericCreator::cloneMode(originid, type); if (*(originid.begin()) == '#') //if originid points to the exterior cell { setType(1); //enable x and y controls diff --git a/apps/opencs/view/world/cellcreator.hpp b/apps/opencs/view/world/cellcreator.hpp index 3130c1328..472be43b9 100644 --- a/apps/opencs/view/world/cellcreator.hpp +++ b/apps/opencs/view/world/cellcreator.hpp @@ -32,8 +32,7 @@ namespace CSVWorld virtual void toggleWidgets(bool active = true); virtual void cloneMode(const std::string& originid, - const CSMWorld::UniversalId::Type type, - const CSMWorld::UniversalId::ArgumentType argumentType); + const CSMWorld::UniversalId::Type type); private slots: diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index 5174232bc..9be619915 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -25,7 +25,8 @@ namespace CSVWorld virtual void reset() = 0; - virtual void cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType) = 0; + virtual void cloneMode(const std::string& originid, + const CSMWorld::UniversalId::Type type) = 0; virtual void setEditLock (bool locked) = 0; diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 2a93f0e0f..1f74b9954 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -127,7 +127,7 @@ void CSVWorld::GenericCreator::create() { std::string id = getId(); std::auto_ptr command (new CSMWorld::CloneCommand ( - dynamic_cast (*mData.getTableModel(mListId)), mClonedId, id, mClonedType, mArgumentType)); + dynamic_cast (*mData.getTableModel(mListId)), mClonedId, id, mClonedType)); mUndoStack.push(command.release()); @@ -150,13 +150,11 @@ void CSVWorld::GenericCreator::create() } void CSVWorld::GenericCreator::cloneMode(const std::string& originid, - const CSMWorld::UniversalId::Type type, - const CSMWorld::UniversalId::ArgumentType argumentType) + const CSMWorld::UniversalId::Type type) { mCloneMode = true; mClonedId = originid; mClonedType = type; - mArgumentType = argumentType; } void CSVWorld::GenericCreator::toggleWidgets(bool active) diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 4870ba46c..06b110b11 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -67,8 +67,7 @@ namespace CSVWorld virtual void toggleWidgets (bool active = true); virtual void cloneMode(const std::string& originid, - const CSMWorld::UniversalId::Type type, - const CSMWorld::UniversalId::ArgumentType argumentType); + const CSMWorld::UniversalId::Type type); virtual std::string getErrors() const; ///< Return formatted error descriptions for the current state of the creator. if an empty diff --git a/apps/opencs/view/world/referencecreator.cpp b/apps/opencs/view/world/referencecreator.cpp index 6991ecf36..069300f42 100644 --- a/apps/opencs/view/world/referencecreator.cpp +++ b/apps/opencs/view/world/referencecreator.cpp @@ -85,8 +85,9 @@ void CSVWorld::ReferenceCreator::toggleWidgets(bool active) mCell->setEnabled(active); } -void CSVWorld::ReferenceCreator::cloneMode(const std::string& originid, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType) +void CSVWorld::ReferenceCreator::cloneMode(const std::string& originid, + const CSMWorld::UniversalId::Type type) { - CSVWorld::GenericCreator::cloneMode(originid, type, argumentType); + CSVWorld::GenericCreator::cloneMode(originid, type); cellChanged(); //otherwise ok button will remain disabled } diff --git a/apps/opencs/view/world/referencecreator.hpp b/apps/opencs/view/world/referencecreator.hpp index 2f0897026..96c1bec27 100644 --- a/apps/opencs/view/world/referencecreator.hpp +++ b/apps/opencs/view/world/referencecreator.hpp @@ -26,8 +26,7 @@ namespace CSVWorld const CSMWorld::UniversalId& id); virtual void cloneMode(const std::string& originid, - const CSMWorld::UniversalId::Type type, - const CSMWorld::UniversalId::ArgumentType argumentType); + const CSMWorld::UniversalId::Type type); virtual void reset(); virtual void toggleWidgets(bool active = true); diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index a6801b6fc..239c7410f 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -160,11 +160,10 @@ void CSVWorld::TableBottomBox::createRequest() } void CSVWorld::TableBottomBox::cloneRequest(const std::string& id, - const CSMWorld::UniversalId::Type type, - const CSMWorld::UniversalId::ArgumentType argumnetType) + const CSMWorld::UniversalId::Type type) { mCreator->reset(); - mCreator->cloneMode(id, type, argumnetType); + mCreator->cloneMode(id, type); mLayout->setCurrentWidget(mCreator); mCreator->toggleWidgets(false); setVisible (true); diff --git a/apps/opencs/view/world/tablebottombox.hpp b/apps/opencs/view/world/tablebottombox.hpp index 29afa2277..8f0d85163 100644 --- a/apps/opencs/view/world/tablebottombox.hpp +++ b/apps/opencs/view/world/tablebottombox.hpp @@ -77,7 +77,8 @@ namespace CSVWorld /// \param modified Number of added and modified records void createRequest(); - void cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type, const CSMWorld::UniversalId::ArgumentType argumentType); + void cloneRequest(const std::string& id, + const CSMWorld::UniversalId::Type type); }; } diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 3c71d1370..e4dbb64ed 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -49,9 +49,11 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D { connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); - connect (mTable, SIGNAL (cloneRequest(const CSMWorld::UniversalId&)), this, SLOT(cloneRequest(const CSMWorld::UniversalId&))); - connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type, const CSMWorld::UniversalId::ArgumentType)), - mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type, const CSMWorld::UniversalId::ArgumentType))); + connect (mTable, SIGNAL (cloneRequest(const CSMWorld::UniversalId&)), this, + SLOT(cloneRequest(const CSMWorld::UniversalId&))); + + connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), + mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type))); } connect (mBottom, SIGNAL (requestFocus (const std::string&)), mTable, SLOT (requestFocus (const std::string&))); @@ -84,5 +86,5 @@ void CSVWorld::TableSubView::setStatusBar (bool show) void CSVWorld::TableSubView::cloneRequest(const CSMWorld::UniversalId& toClone) { - emit cloneRequest(toClone.getId(), toClone.getType(), toClone.getArgumentType()); + emit cloneRequest(toClone.getId(), toClone.getType()); } diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index b3a0a5162..9d2d005a8 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -41,8 +41,7 @@ namespace CSVWorld signals: void cloneRequest(const std::string&, - const CSMWorld::UniversalId::Type, - const CSMWorld::UniversalId::ArgumentType); + const CSMWorld::UniversalId::Type); private slots: From 71d63647546db1bf0e2b8adb189cbacb1f6ba1d7 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 13:13:39 +0100 Subject: [PATCH 670/889] Chaninging variables name to follow our policy. --- apps/opencs/view/world/cellcreator.cpp | 6 +++--- apps/opencs/view/world/cellcreator.hpp | 2 +- apps/opencs/view/world/creator.hpp | 2 +- apps/opencs/view/world/genericcreator.cpp | 4 ++-- apps/opencs/view/world/genericcreator.hpp | 2 +- apps/opencs/view/world/referencecreator.cpp | 4 ++-- apps/opencs/view/world/referencecreator.hpp | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/opencs/view/world/cellcreator.cpp b/apps/opencs/view/world/cellcreator.cpp index e65501e5f..cdeee5655 100644 --- a/apps/opencs/view/world/cellcreator.cpp +++ b/apps/opencs/view/world/cellcreator.cpp @@ -81,11 +81,11 @@ void CSVWorld::CellCreator::valueChanged (int index) update(); } -void CSVWorld::CellCreator::cloneMode(const std::string& originid, +void CSVWorld::CellCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) { - CSVWorld::GenericCreator::cloneMode(originid, type); - if (*(originid.begin()) == '#') //if originid points to the exterior cell + CSVWorld::GenericCreator::cloneMode(originId, type); + if (*(originId.begin()) == '#') //if originid points to the exterior cell { setType(1); //enable x and y controls mType->setCurrentIndex(1); diff --git a/apps/opencs/view/world/cellcreator.hpp b/apps/opencs/view/world/cellcreator.hpp index 472be43b9..74bf5f76b 100644 --- a/apps/opencs/view/world/cellcreator.hpp +++ b/apps/opencs/view/world/cellcreator.hpp @@ -31,7 +31,7 @@ namespace CSVWorld virtual void toggleWidgets(bool active = true); - virtual void cloneMode(const std::string& originid, + virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type); private slots: diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index 9be619915..2d5e014e2 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -25,7 +25,7 @@ namespace CSVWorld virtual void reset() = 0; - virtual void cloneMode(const std::string& originid, + virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) = 0; virtual void setEditLock (bool locked) = 0; diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 1f74b9954..9fb0b4b21 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -149,11 +149,11 @@ void CSVWorld::GenericCreator::create() } } -void CSVWorld::GenericCreator::cloneMode(const std::string& originid, +void CSVWorld::GenericCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) { mCloneMode = true; - mClonedId = originid; + mClonedId = originId; mClonedType = type; } diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 06b110b11..98dd12ac2 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -66,7 +66,7 @@ namespace CSVWorld virtual void toggleWidgets (bool active = true); - virtual void cloneMode(const std::string& originid, + virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type); virtual std::string getErrors() const; diff --git a/apps/opencs/view/world/referencecreator.cpp b/apps/opencs/view/world/referencecreator.cpp index 069300f42..384b59fc4 100644 --- a/apps/opencs/view/world/referencecreator.cpp +++ b/apps/opencs/view/world/referencecreator.cpp @@ -85,9 +85,9 @@ void CSVWorld::ReferenceCreator::toggleWidgets(bool active) mCell->setEnabled(active); } -void CSVWorld::ReferenceCreator::cloneMode(const std::string& originid, +void CSVWorld::ReferenceCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) { - CSVWorld::GenericCreator::cloneMode(originid, type); + CSVWorld::GenericCreator::cloneMode(originId, type); cellChanged(); //otherwise ok button will remain disabled } diff --git a/apps/opencs/view/world/referencecreator.hpp b/apps/opencs/view/world/referencecreator.hpp index 96c1bec27..73b0899e5 100644 --- a/apps/opencs/view/world/referencecreator.hpp +++ b/apps/opencs/view/world/referencecreator.hpp @@ -25,7 +25,7 @@ namespace CSVWorld ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id); - virtual void cloneMode(const std::string& originid, + virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type); virtual void reset(); From 29c3a288e3525ca29cef275e66536dded60a3683 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 27 Jan 2014 13:27:42 +0100 Subject: [PATCH 671/889] load and save of reference in cells (without CustomData state) --- apps/openmw/engine.cpp | 9 +- apps/openmw/mwbase/world.hpp | 4 +- apps/openmw/mwstate/statemanagerimp.cpp | 40 +++++- apps/openmw/mwstate/statemanagerimp.hpp | 4 + apps/openmw/mwworld/cells.cpp | 9 +- apps/openmw/mwworld/cells.hpp | 3 +- apps/openmw/mwworld/cellstore.cpp | 163 +++++++++++++++++++++++- apps/openmw/mwworld/cellstore.hpp | 2 + apps/openmw/mwworld/worldimp.cpp | 5 +- apps/openmw/mwworld/worldimp.hpp | 3 +- components/esm/defs.hpp | 1 - 11 files changed, 222 insertions(+), 21 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 66dea4f1d..08f9cdb5c 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -127,7 +127,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) if(!paused && player.getClass().getCreatureStats(player).isDead()) MWBase::Environment::get().getStateManager()->endGame(); } - + // update world MWBase::Environment::get().getWorld()->update(frametime, paused); @@ -282,12 +282,7 @@ void OMW::Engine::setCell (const std::string& cellName) void OMW::Engine::addContentFile(const std::string& file) { - if (file.find_last_of(".") == std::string::npos) - { - throw std::runtime_error("Missing extension in content file!"); - } - - mContentFiles.push_back(file); + mContentFiles.push_back(file); } void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 64104f2ba..cbf363581 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -2,6 +2,7 @@ #define GAME_MWBASE_WORLD_H #include +#include #include @@ -102,7 +103,8 @@ namespace MWBase virtual void write (ESM::ESMWriter& writer) const = 0; - virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0; + virtual void readRecord (ESM::ESMReader& reader, int32_t type, + const std::map& contentFileMap) = 0; virtual OEngine::Render::Fader* getFader() = 0; ///< \todo remove this function. Rendering details should not be exposed. diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index a396d78c5..fb2d10bbf 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include #include @@ -43,6 +45,31 @@ void MWState::StateManager::cleanup (bool force) } } +std::map MWState::StateManager::buildContentFileIndexMap (const ESM::ESMReader& reader) + const +{ + const std::vector& current = + MWBase::Environment::get().getWorld()->getContentFiles(); + + const std::vector& prev = reader.getGameFiles(); + + std::map map; + + for (int iPrev = 0; iPrev (prev.size()); ++iPrev) + { + std::string id = Misc::StringUtils::lowerCase (prev[iPrev].name); + + for (int iCurrent = 0; iCurrent (current.size()); ++iCurrent) + if (id==Misc::StringUtils::lowerCase (current[iCurrent])) + { + map.insert (std::make_pair (iPrev, iCurrent)); + break; + } + } + + return map; +} + MWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::string& game) : mQuitRequest (false), mAskLoadRecent(false), mState (State_NoGame), mCharacterManager (saves, game), mTimePlayed (0) { @@ -167,7 +194,16 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile); std::ofstream stream (slot->mPath.string().c_str()); + ESM::ESMWriter writer; + + const std::vector& current = + MWBase::Environment::get().getWorld()->getContentFiles(); + + for (std::vector::const_iterator iter (current.begin()); iter!=current.end(); + ++iter) + writer.addMaster (*iter, 0); // not using the size information anyway -> use value of 0 + writer.setFormat (ESM::Header::CurrentFormat); writer.setRecordCount ( 1 // saved game header @@ -205,6 +241,8 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl ESM::ESMReader reader; reader.open (slot->mPath.string()); + std::map contentFileMap = buildContentFileIndexMap (reader); + while (reader.hasMoreRecs()) { ESM::NAME n = reader.getRecName(); @@ -237,7 +275,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl case ESM::REC_PLAY: case ESM::REC_CSTA: - MWBase::Environment::get().getWorld()->readRecord (reader, n.val); + MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap); break; case ESM::REC_GSCR: diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index d6bb7575d..46ade236b 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -1,6 +1,8 @@ #ifndef GAME_STATE_STATEMANAGER_H #define GAME_STATE_STATEMANAGER_H +#include + #include "../mwbase/statemanager.hpp" #include @@ -21,6 +23,8 @@ namespace MWState void cleanup (bool force = false); + std::map buildContentFileIndexMap (const ESM::ESMReader& reader) const; + public: StateManager (const boost::filesystem::path& saves, const std::string& game); diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 26ec14a6d..965c9fc5d 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -352,7 +352,8 @@ void MWWorld::Cells::write (ESM::ESMWriter& writer) const writeCell (writer, iter->second); } -bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, int32_t type) +bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, int32_t type, + const std::map& contentFileMap) { if (type==ESM::REC_CSTA) { @@ -373,7 +374,11 @@ bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, int32_t type) state.load (reader); cellStore->loadState (state); - reader.skipRecord(); + + if (cellStore->mState!=CellStore::State_Loaded) + cellStore->load (mStore, mReader); + + cellStore->readReferences (reader, contentFileMap); return true; } diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 1b0fe6724..7ee8a3f6c 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -73,7 +73,8 @@ namespace MWWorld void write (ESM::ESMWriter& writer) const; - bool readRecord (ESM::ESMReader& reader, int32_t type); + bool readRecord (ESM::ESMReader& reader, int32_t type, + const std::map& contentFileMap); }; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index aea4e5b6a..88c241e1a 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -41,9 +41,6 @@ namespace { if (!collection.mList.empty()) { - // section header - writer.writeHNT ("CSEC", collection.mList.front().mBase->sRecordId); - // references for (typename MWWorld::CellRefList::List::const_iterator iter (collection.mList.begin()); @@ -55,12 +52,51 @@ namespace RecordType state; iter->save (state); - writer.startRecord (ESM::REC_OBJE); + writer.writeHNT ("OBJE", collection.mList.front().mBase->sRecordId); state.save (writer); - writer.endRecord (ESM::REC_OBJE); } } } + + template + void readReferenceCollection (ESM::ESMReader& reader, + MWWorld::CellRefList& collection, const std::map& contentFileMap) + { + const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); + + RecordType state; + state.load (reader); + + std::map::const_iterator iter = + contentFileMap.find (state.mRef.mRefNum.mContentFile); + + if (iter==contentFileMap.end()) + return; // content file has been removed -> skip + + state.mRef.mRefNum.mContentFile = iter->second; + + if (!MWWorld::LiveCellRef::checkState (state)) + return; // not valid anymore with current content files -> skip + + const T *record = esmStore.get().search (state.mRef.mRefID); + + if (!record) + return; + + for (typename MWWorld::CellRefList::List::iterator iter (collection.mList.begin()); + iter!=collection.mList.end(); ++iter) + if (iter->mRef.mRefNum==state.mRef.mRefNum) + { + // overwrite existing reference + iter->load (state); + return; + } + + // new reference + MWWorld::LiveCellRef ref (record); + ref.load (state); + collection.mList.push_back (ref); + } } namespace MWWorld @@ -304,4 +340,121 @@ namespace MWWorld writeReferenceCollection (writer, mStatics); writeReferenceCollection (writer, mWeapons); } + + void CellStore::readReferences (ESM::ESMReader& reader, + const std::map& contentFileMap) + { + while (reader.isNextSub ("OBJE")) + { + unsigned int id = 0; + reader.getHT (id); + + switch (id) + { + case ESM::REC_ACTI: + + readReferenceCollection (reader, mActivators, contentFileMap); + break; + + case ESM::REC_ALCH: + + readReferenceCollection (reader, mPotions, contentFileMap); + break; + + case ESM::REC_APPA: + + readReferenceCollection (reader, mAppas, contentFileMap); + break; + + case ESM::REC_ARMO: + + readReferenceCollection (reader, mArmors, contentFileMap); + break; + + case ESM::REC_BOOK: + + readReferenceCollection (reader, mBooks, contentFileMap); + break; + + case ESM::REC_CLOT: + + readReferenceCollection (reader, mClothes, contentFileMap); + break; + + case ESM::REC_CONT: + + readReferenceCollection (reader, mContainers, contentFileMap); + break; + + case ESM::REC_CREA: + + readReferenceCollection (reader, mCreatures, contentFileMap); + break; + + case ESM::REC_DOOR: + + readReferenceCollection (reader, mDoors, contentFileMap); + break; + + case ESM::REC_INGR: + + readReferenceCollection (reader, mIngreds, contentFileMap); + break; + + case ESM::REC_LEVC: + + readReferenceCollection (reader, mCreatureLists, contentFileMap); + break; + + case ESM::REC_LEVI: + + readReferenceCollection (reader, mItemLists, contentFileMap); + break; + + case ESM::REC_LIGH: + + readReferenceCollection (reader, mLights, contentFileMap); + break; + + case ESM::REC_LOCK: + + readReferenceCollection (reader, mLockpicks, contentFileMap); + break; + + case ESM::REC_MISC: + + readReferenceCollection (reader, mMiscItems, contentFileMap); + break; + + case ESM::REC_NPC_: + + readReferenceCollection (reader, mNpcs, contentFileMap); + break; + + case ESM::REC_PROB: + + readReferenceCollection (reader, mProbes, contentFileMap); + break; + + case ESM::REC_REPA: + + readReferenceCollection (reader, mRepairs, contentFileMap); + break; + + case ESM::REC_STAT: + + readReferenceCollection (reader, mStatics, contentFileMap); + break; + + case ESM::REC_WEAP: + + readReferenceCollection (reader, mWeapons, contentFileMap); + break; + + default: + + throw std::runtime_error ("unknown type in cell reference section"); + } + } + } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index feebfe90a..a4f219013 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -144,6 +144,8 @@ namespace MWWorld void writeReferences (ESM::ESMWriter& writer) const; + void readReferences (ESM::ESMReader& reader, const std::map& contentFileMap); + private: template diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 076fcfdb6..26cb34977 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -324,12 +324,13 @@ namespace MWWorld mPlayer->write (writer); } - void World::readRecord (ESM::ESMReader& reader, int32_t type) + void World::readRecord (ESM::ESMReader& reader, int32_t type, + const std::map& contentFileMap) { if (!mStore.readRecord (reader, type) && !mGlobalVariables.readRecord (reader, type) && !mPlayer->readRecord (reader, type) && - !mCells.readRecord (reader, type)) + !mCells.readRecord (reader, type, contentFileMap)) { throw std::runtime_error ("unknown record in saved game"); } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 0fb4ed1ea..b99509322 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -179,7 +179,8 @@ namespace MWWorld virtual void write (ESM::ESMWriter& writer) const; - virtual void readRecord (ESM::ESMReader& reader, int32_t type); + virtual void readRecord (ESM::ESMReader& reader, int32_t type, + const std::map& contentFileMap); virtual OEngine::Render::Fader* getFader(); ///< \todo remove this function. Rendering details should not be exposed. diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 40ef7ecb6..1b0125e78 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -90,7 +90,6 @@ enum RecNameInts REC_GSCR = 0x52435347, REC_PLAY = 0x59414c50, REC_CSTA = 0x41545343, - REC_OBJE = 0x454a424f, REC_GMAP = 0x50414d47, // format 1 From bb62efc3d844171b1b77b517424871fb3f6de73b Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 13:36:01 +0100 Subject: [PATCH 672/889] Removed pointless includes. --- apps/opencs/model/world/commands.cpp | 1 + apps/opencs/view/world/genericcreator.cpp | 1 - apps/opencs/view/world/table.cpp | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 5eb2e9414..789c5d9b8 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -1,3 +1,4 @@ + #include "commands.hpp" #include diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 9fb0b4b21..c1faa741d 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -2,7 +2,6 @@ #include "genericcreator.hpp" #include -#include #include #include diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index d343f9986..f166171fe 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -16,7 +16,6 @@ #include "recordstatusdelegate.hpp" #include "util.hpp" -#include void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) { From 6a0b5defd78e2e8e38a05c2c16c68c6ba37403d7 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 13:41:37 +0100 Subject: [PATCH 673/889] removed needless member value. --- apps/opencs/model/world/commands.cpp | 3 +-- apps/opencs/view/world/genericcreator.cpp | 12 +++++++++--- apps/opencs/view/world/genericcreator.hpp | 1 - 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 789c5d9b8..d3ef7128f 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -1,9 +1,8 @@ #include "commands.hpp" - #include -#include #include "idtable.hpp" +#include CSMWorld::ModifyCommand::ModifyCommand(QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, QUndoCommand* parent) diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index c1faa741d..447c07424 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -57,9 +57,15 @@ const CSMWorld::UniversalId& CSVWorld::GenericCreator::getCollectionId() const } CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, - const CSMWorld::UniversalId& id, bool relaxedIdRules) -: mData (data), mUndoStack (undoStack), mListId (id), mLocked (false), mCloneMode(false), mClonedType(CSMWorld::UniversalId::Type_None), -mArgumentType(CSMWorld::UniversalId::ArgumentType_None) + const CSMWorld::UniversalId& id, bool relaxedIdRules): + + mData (data), + mUndoStack (undoStack), + mListId (id), + mLocked (false), + mCloneMode(false), + mClonedType(CSMWorld::UniversalId::Type_None) + { mLayout = new QHBoxLayout; mLayout->setContentsMargins (0, 0, 0, 0); diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 98dd12ac2..0f426e1a7 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -31,7 +31,6 @@ namespace CSVWorld bool mLocked; std::string mClonedId; CSMWorld::UniversalId::Type mClonedType; - CSMWorld::UniversalId::ArgumentType mArgumentType; protected: bool mCloneMode; From 2899f04a3f11ab4821143e69ccbd9807c54b0302 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 14:43:12 +0100 Subject: [PATCH 674/889] reformatting --- apps/opencs/model/world/collection.hpp | 3 ++- apps/opencs/model/world/commands.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 4bb10c1d8..290558928 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -204,7 +204,8 @@ namespace CSMWorld const std::string& destination, const UniversalId::Type type) { - Record copy(getRecord(origin)); + Record copy; + copy = getRecord(origin); copy.mState = RecordBase::State_ModifiedOnly; copy.get().mId = destination; diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index d3ef7128f..5ca169fcd 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -1,7 +1,8 @@ #include "commands.hpp" -#include + #include "idtable.hpp" +#include #include CSMWorld::ModifyCommand::ModifyCommand(QAbstractItemModel& model, const QModelIndex& index, From 4d6fb31610cd65dcf9d7d07d0dfea4118fd299e5 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 14:45:07 +0100 Subject: [PATCH 675/889] reformating --- apps/opencs/model/world/commands.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 5ca169fcd..3b70f6940 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -1,8 +1,10 @@ #include "commands.hpp" -#include "idtable.hpp" #include + +#include "idtable.hpp" + #include CSMWorld::ModifyCommand::ModifyCommand(QAbstractItemModel& model, const QModelIndex& index, @@ -38,7 +40,6 @@ CSMWorld::CloneCommand::CloneCommand(CSMWorld::IdTable& model, setText(("Clone record " + idOrigin + " to the " + IdDestination).c_str()); } - void CSMWorld::CloneCommand::redo() { mModel.cloneRecord(mIdOrigin, mIdDestination, mType); From 316debb8277d6e4b851dc8a85be6efb1c0d248bc Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 14:46:58 +0100 Subject: [PATCH 676/889] reformating --- apps/opencs/model/world/commands.cpp | 56 ++++++++++++++-------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 3b70f6940..9159b1f16 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -26,34 +26,6 @@ void CSMWorld::ModifyCommand::undo() mModel.setData(mIndex, mOld); } -CSMWorld::CloneCommand::CloneCommand(CSMWorld::IdTable& model, - const std::string& idOrigin, - const std::string& IdDestination, - const CSMWorld::UniversalId::Type type, - QUndoCommand* parent) : - QUndoCommand(parent), - mModel(model), - mIdOrigin(idOrigin), - mIdDestination(Misc::StringUtils::lowerCase(IdDestination)), - mType(type) -{ - setText(("Clone record " + idOrigin + " to the " + IdDestination).c_str()); -} - -void CSMWorld::CloneCommand::redo() -{ - mModel.cloneRecord(mIdOrigin, mIdDestination, mType); - - for (std::map::const_iterator iter(mValues.begin()); iter != mValues.end(); ++iter) - mModel.setData(mModel.getModelIndex(mIdDestination, iter->first), iter->second); -} - -void CSMWorld::CloneCommand::undo() -{ - mModel.removeRow(mModel.getModelIndex(mIdDestination, 0).row()); -} - - CSMWorld::CreateCommand::CreateCommand(IdTable& model, const std::string& id, QUndoCommand* parent) : QUndoCommand(parent), mModel(model), mId(id), mType(UniversalId::Type_None) { @@ -174,4 +146,32 @@ void CSMWorld::ReorderRowsCommand::undo() mModel.reorderRows(mBaseIndex, reverse); } + +CSMWorld::CloneCommand::CloneCommand(CSMWorld::IdTable& model, + const std::string& idOrigin, + const std::string& IdDestination, + const CSMWorld::UniversalId::Type type, + QUndoCommand* parent) : + QUndoCommand(parent), + mModel(model), + mIdOrigin(idOrigin), + mIdDestination(Misc::StringUtils::lowerCase(IdDestination)), + mType(type) +{ + setText(("Clone record " + idOrigin + " to the " + IdDestination).c_str()); +} + +void CSMWorld::CloneCommand::redo() +{ + mModel.cloneRecord(mIdOrigin, mIdDestination, mType); + + for (std::map::const_iterator iter(mValues.begin()); iter != mValues.end(); ++iter) + mModel.setData(mModel.getModelIndex(mIdDestination, iter->first), iter->second); +} + +void CSMWorld::CloneCommand::undo() +{ + mModel.removeRow(mModel.getModelIndex(mIdDestination, 0).row()); +} + // kate: indent-mode cstyle; indent-width 4; replace-tabs on; From 2b71568bb65b9e7f80cb4d43c813dbeb50bb0cb6 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 14:50:36 +0100 Subject: [PATCH 677/889] still reformating --- apps/opencs/model/world/commands.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 9159b1f16..9a9e246f5 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -3,6 +3,7 @@ #include + #include "idtable.hpp" #include From 52176d6435baee3b092fe21e72be85dcdbe089cf Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 14:51:57 +0100 Subject: [PATCH 678/889] reforfucking --- apps/opencs/model/world/commands.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 9a9e246f5..80a751ac6 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -3,9 +3,7 @@ #include - #include "idtable.hpp" - #include CSMWorld::ModifyCommand::ModifyCommand(QAbstractItemModel& model, const QModelIndex& index, From c91ae86084df5f86af4734ec7617e5d09dc180dd Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 14:53:39 +0100 Subject: [PATCH 679/889] is there something that can generate diff from two git branches? Using github for this is kinda annoying. --- apps/opencs/model/world/commands.cpp | 118 +++++++++++++-------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 80a751ac6..4b36b1b63 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -6,60 +6,60 @@ #include "idtable.hpp" #include -CSMWorld::ModifyCommand::ModifyCommand(QAbstractItemModel& model, const QModelIndex& index, - const QVariant& new_, QUndoCommand* parent) - : QUndoCommand(parent), mModel(model), mIndex(index), mNew(new_) +CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, + const QVariant& new_, QUndoCommand* parent) + : QUndoCommand (parent), mModel (model), mIndex (index), mNew (new_) { - mOld = mModel.data(mIndex, Qt::EditRole); + mOld = mModel.data (mIndex, Qt::EditRole); - setText("Modify " + mModel.headerData(mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); + setText ("Modify " + mModel.headerData (mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); } void CSMWorld::ModifyCommand::redo() { - mModel.setData(mIndex, mNew); + mModel.setData (mIndex, mNew); } void CSMWorld::ModifyCommand::undo() { - mModel.setData(mIndex, mOld); + mModel.setData (mIndex, mOld); } -CSMWorld::CreateCommand::CreateCommand(IdTable& model, const std::string& id, QUndoCommand* parent) - : QUndoCommand(parent), mModel(model), mId(id), mType(UniversalId::Type_None) +CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand* parent) + : QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None) { - setText(("Create record " + id).c_str()); + setText ( ("Create record " + id).c_str()); } -void CSMWorld::CreateCommand::addValue(int column, const QVariant& value) +void CSMWorld::CreateCommand::addValue (int column, const QVariant& value) { mValues[column] = value; } -void CSMWorld::CreateCommand::setType(UniversalId::Type type) +void CSMWorld::CreateCommand::setType (UniversalId::Type type) { mType = type; } void CSMWorld::CreateCommand::redo() { - mModel.addRecord(mId, mType); + mModel.addRecord (mId, mType); - for (std::map::const_iterator iter(mValues.begin()); iter != mValues.end(); ++iter) - mModel.setData(mModel.getModelIndex(mId, iter->first), iter->second); + for (std::map::const_iterator iter (mValues.begin()); iter != mValues.end(); ++iter) + mModel.setData (mModel.getModelIndex (mId, iter->first), iter->second); } void CSMWorld::CreateCommand::undo() { - mModel.removeRow(mModel.getModelIndex(mId, 0).row()); + mModel.removeRow (mModel.getModelIndex (mId, 0).row()); } -CSMWorld::RevertCommand::RevertCommand(IdTable& model, const std::string& id, QUndoCommand* parent) - : QUndoCommand(parent), mModel(model), mId(id), mOld(0) +CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand* parent) + : QUndoCommand (parent), mModel (model), mId (id), mOld (0) { - setText(("Revert record " + id).c_str()); + setText ( ("Revert record " + id).c_str()); - mOld = model.getRecord(id).clone(); + mOld = model.getRecord (id).clone(); } CSMWorld::RevertCommand::~RevertCommand() @@ -69,32 +69,32 @@ CSMWorld::RevertCommand::~RevertCommand() void CSMWorld::RevertCommand::redo() { - int column = mModel.findColumnIndex(Columns::ColumnId_Modification); + int column = mModel.findColumnIndex (Columns::ColumnId_Modification); - QModelIndex index = mModel.getModelIndex(mId, column); - RecordBase::State state = static_cast(mModel.data(index).toInt()); + QModelIndex index = mModel.getModelIndex (mId, column); + RecordBase::State state = static_cast (mModel.data (index).toInt()); if (state == RecordBase::State_ModifiedOnly) { - mModel.removeRows(index.row(), 1); + mModel.removeRows (index.row(), 1); } else { - mModel.setData(index, static_cast(RecordBase::State_BaseOnly)); + mModel.setData (index, static_cast (RecordBase::State_BaseOnly)); } } void CSMWorld::RevertCommand::undo() { - mModel.setRecord(mId, *mOld); + mModel.setRecord (mId, *mOld); } -CSMWorld::DeleteCommand::DeleteCommand(IdTable& model, const std::string& id, QUndoCommand* parent) - : QUndoCommand(parent), mModel(model), mId(id), mOld(0) +CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand* parent) + : QUndoCommand (parent), mModel (model), mId (id), mOld (0) { - setText(("Delete record " + id).c_str()); + setText ( ("Delete record " + id).c_str()); - mOld = model.getRecord(id).clone(); + mOld = model.getRecord (id).clone(); } CSMWorld::DeleteCommand::~DeleteCommand() @@ -104,73 +104,73 @@ CSMWorld::DeleteCommand::~DeleteCommand() void CSMWorld::DeleteCommand::redo() { - int column = mModel.findColumnIndex(Columns::ColumnId_Modification); + int column = mModel.findColumnIndex (Columns::ColumnId_Modification); - QModelIndex index = mModel.getModelIndex(mId, column); - RecordBase::State state = static_cast(mModel.data(index).toInt()); + QModelIndex index = mModel.getModelIndex (mId, column); + RecordBase::State state = static_cast (mModel.data (index).toInt()); if (state == RecordBase::State_ModifiedOnly) { - mModel.removeRows(index.row(), 1); + mModel.removeRows (index.row(), 1); } else { - mModel.setData(index, static_cast(RecordBase::State_Deleted)); + mModel.setData (index, static_cast (RecordBase::State_Deleted)); } } void CSMWorld::DeleteCommand::undo() { - mModel.setRecord(mId, *mOld); + mModel.setRecord (mId, *mOld); } -CSMWorld::ReorderRowsCommand::ReorderRowsCommand(IdTable& model, int baseIndex, +CSMWorld::ReorderRowsCommand::ReorderRowsCommand (IdTable& model, int baseIndex, const std::vector& newOrder) - : mModel(model), mBaseIndex(baseIndex), mNewOrder(newOrder) + : mModel (model), mBaseIndex (baseIndex), mNewOrder (newOrder) {} void CSMWorld::ReorderRowsCommand::redo() { - mModel.reorderRows(mBaseIndex, mNewOrder); + mModel.reorderRows (mBaseIndex, mNewOrder); } void CSMWorld::ReorderRowsCommand::undo() { - int size = static_cast(mNewOrder.size()); - std::vector reverse(size); + int size = static_cast (mNewOrder.size()); + std::vector reverse (size); for (int i = 0; i < size; ++i) - reverse.at(mNewOrder[i]) = i; + reverse.at (mNewOrder[i]) = i; - mModel.reorderRows(mBaseIndex, reverse); + mModel.reorderRows (mBaseIndex, reverse); } -CSMWorld::CloneCommand::CloneCommand(CSMWorld::IdTable& model, - const std::string& idOrigin, - const std::string& IdDestination, - const CSMWorld::UniversalId::Type type, - QUndoCommand* parent) : - QUndoCommand(parent), - mModel(model), - mIdOrigin(idOrigin), - mIdDestination(Misc::StringUtils::lowerCase(IdDestination)), - mType(type) +CSMWorld::CloneCommand::CloneCommand (CSMWorld::IdTable& model, + const std::string& idOrigin, + const std::string& IdDestination, + const CSMWorld::UniversalId::Type type, + QUndoCommand* parent) : + QUndoCommand (parent), + mModel (model), + mIdOrigin (idOrigin), + mIdDestination (Misc::StringUtils::lowerCase (IdDestination)), + mType (type) { - setText(("Clone record " + idOrigin + " to the " + IdDestination).c_str()); + setText ( ("Clone record " + idOrigin + " to the " + IdDestination).c_str()); } void CSMWorld::CloneCommand::redo() { - mModel.cloneRecord(mIdOrigin, mIdDestination, mType); - - for (std::map::const_iterator iter(mValues.begin()); iter != mValues.end(); ++iter) - mModel.setData(mModel.getModelIndex(mIdDestination, iter->first), iter->second); + mModel.cloneRecord (mIdOrigin, mIdDestination, mType); + + for (std::map::const_iterator iter (mValues.begin()); iter != mValues.end(); ++iter) + mModel.setData (mModel.getModelIndex (mIdDestination, iter->first), iter->second); } void CSMWorld::CloneCommand::undo() { - mModel.removeRow(mModel.getModelIndex(mIdDestination, 0).row()); + mModel.removeRow (mModel.getModelIndex (mIdDestination, 0).row()); } // kate: indent-mode cstyle; indent-width 4; replace-tabs on; From 51115fa5be719a1ad02e03ee7d4cc6b78f665382 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 14:56:57 +0100 Subject: [PATCH 680/889] spaces around operators --- apps/opencs/model/world/commands.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 4b36b1b63..1bb1cf2b2 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -170,7 +170,7 @@ void CSMWorld::CloneCommand::redo() void CSMWorld::CloneCommand::undo() { - mModel.removeRow (mModel.getModelIndex (mIdDestination, 0).row()); + mModel.removeRow (mModel.getModelIndex (mIdDestination, 0).row()); //should be enough } // kate: indent-mode cstyle; indent-width 4; replace-tabs on; From 84e07c95b1d375d0b03d68c6fa1076d39f5e5bf7 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 15:02:29 +0100 Subject: [PATCH 681/889] formatting --- apps/opencs/model/world/commands.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 1bb1cf2b2..a544baee3 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -8,7 +8,7 @@ CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, QUndoCommand* parent) - : QUndoCommand (parent), mModel (model), mIndex (index), mNew (new_) +: QUndoCommand (parent), mModel (model), mIndex (index), mNew (new_) { mOld = mModel.data (mIndex, Qt::EditRole); @@ -26,9 +26,9 @@ void CSMWorld::ModifyCommand::undo() } CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand* parent) - : QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None) +: QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None) { - setText ( ("Create record " + id).c_str()); + setText (("Create record " + id).c_str()); } void CSMWorld::CreateCommand::addValue (int column, const QVariant& value) @@ -45,7 +45,7 @@ void CSMWorld::CreateCommand::redo() { mModel.addRecord (mId, mType); - for (std::map::const_iterator iter (mValues.begin()); iter != mValues.end(); ++iter) + for (std::map::const_iterator iter (mValues.begin()); iter!=mValues.end(); ++iter) mModel.setData (mModel.getModelIndex (mId, iter->first), iter->second); } @@ -55,9 +55,9 @@ void CSMWorld::CreateCommand::undo() } CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand* parent) - : QUndoCommand (parent), mModel (model), mId (id), mOld (0) +: QUndoCommand (parent), mModel (model), mId (id), mOld (0) { - setText ( ("Revert record " + id).c_str()); + setText (("Revert record " + id).c_str()); mOld = model.getRecord (id).clone(); } @@ -74,7 +74,7 @@ void CSMWorld::RevertCommand::redo() QModelIndex index = mModel.getModelIndex (mId, column); RecordBase::State state = static_cast (mModel.data (index).toInt()); - if (state == RecordBase::State_ModifiedOnly) + if (state==RecordBase::State_ModifiedOnly) { mModel.removeRows (index.row(), 1); } @@ -90,9 +90,9 @@ void CSMWorld::RevertCommand::undo() } CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand* parent) - : QUndoCommand (parent), mModel (model), mId (id), mOld (0) +: QUndoCommand (parent), mModel (model), mId (id), mOld (0) { - setText ( ("Delete record " + id).c_str()); + setText (("Delete record " + id).c_str()); mOld = model.getRecord (id).clone(); } @@ -109,7 +109,7 @@ void CSMWorld::DeleteCommand::redo() QModelIndex index = mModel.getModelIndex (mId, column); RecordBase::State state = static_cast (mModel.data (index).toInt()); - if (state == RecordBase::State_ModifiedOnly) + if (state==RecordBase::State_ModifiedOnly) { mModel.removeRows (index.row(), 1); } @@ -127,7 +127,7 @@ void CSMWorld::DeleteCommand::undo() CSMWorld::ReorderRowsCommand::ReorderRowsCommand (IdTable& model, int baseIndex, const std::vector& newOrder) - : mModel (model), mBaseIndex (baseIndex), mNewOrder (newOrder) +: mModel (model), mBaseIndex (baseIndex), mNewOrder (newOrder) {} void CSMWorld::ReorderRowsCommand::redo() @@ -140,7 +140,7 @@ void CSMWorld::ReorderRowsCommand::undo() int size = static_cast (mNewOrder.size()); std::vector reverse (size); - for (int i = 0; i < size; ++i) + for (int i=0; i < size; ++i) reverse.at (mNewOrder[i]) = i; mModel.reorderRows (mBaseIndex, reverse); From 9c579dbd6c4b4fea3dd72bf9e446d6d74d00bfb1 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 15:03:29 +0100 Subject: [PATCH 682/889] ok, that should be enough --- apps/opencs/model/world/commands.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index a544baee3..5526d7418 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -140,7 +140,7 @@ void CSMWorld::ReorderRowsCommand::undo() int size = static_cast (mNewOrder.size()); std::vector reverse (size); - for (int i=0; i < size; ++i) + for (int i=0; i< size; ++i) reverse.at (mNewOrder[i]) = i; mModel.reorderRows (mBaseIndex, reverse); From ed0ba906cfbfd72a7b41b3dbd48db12b9cc05023 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 15:05:29 +0100 Subject: [PATCH 683/889] removed needless include --- apps/opencs/view/world/tablesubview.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index e4dbb64ed..5495c708f 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -6,7 +6,6 @@ #include "../../model/doc/document.hpp" #include "../filter/filterbox.hpp" -#include "../../model/world/idtable.hpp" #include "table.hpp" #include "tablebottombox.hpp" #include "creator.hpp" From 846276f747c7ff62712e8224ba5c442b2b508c21 Mon Sep 17 00:00:00 2001 From: gus Date: Mon, 27 Jan 2014 15:14:24 +0100 Subject: [PATCH 684/889] clean up --- apps/openmw/mwmechanics/actors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index ed3c343a5..38b11c0ac 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -927,7 +927,7 @@ namespace MWMechanics const MWWorld::Class &cls = MWWorld::Class::get(iter->first); CreatureStats &stats = cls.getCreatureStats(iter->first); - if(stats.getAiSequence().getTypeId() == 3) + if(stats.getAiSequence().getTypeId() == AiPackage::TypeIdFollow) { MWMechanics::AiFollow* package = static_cast(stats.getAiSequence().getActivePackage()); if(package->getFollowedActor() == actor.getCellRef().mRefID) From d0b07de7efd4b77c105112cc495d1040b33261af Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 16:59:18 +0100 Subject: [PATCH 685/889] Corrected bug mentioned by zini. --- apps/opencs/model/world/collection.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 290558928..64b950e60 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -205,7 +205,7 @@ namespace CSMWorld const UniversalId::Type type) { Record copy; - copy = getRecord(origin); + copy.mModified = getRecord(origin).get(); copy.mState = RecordBase::State_ModifiedOnly; copy.get().mId = destination; From c82db915f17922d03b68a33de3a85b4e427b0c01 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 19:32:51 +0100 Subject: [PATCH 686/889] Removed needless includes --- apps/opencs/model/world/collection.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 64b950e60..9fc97d580 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -7,12 +7,10 @@ #include #include #include -#include #include #include -#include #include "columnbase.hpp" From 62ea0bb06685aefff34329c19486222714a94934 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 19:40:05 +0100 Subject: [PATCH 687/889] Cleared whitespaces. --- apps/opencs/model/world/collection.hpp | 12 ++++++------ apps/opencs/model/world/collectionbase.hpp | 2 +- apps/opencs/model/world/commands.hpp | 4 ++-- apps/opencs/model/world/idtable.hpp | 2 +- apps/opencs/model/world/refidcollection.hpp | 2 +- apps/opencs/view/world/cellcreator.hpp | 4 ++-- apps/opencs/view/world/creator.hpp | 4 ++-- apps/opencs/view/world/genericcreator.cpp | 6 +++--- apps/opencs/view/world/genericcreator.hpp | 6 +++--- apps/opencs/view/world/referencecreator.cpp | 4 ++-- apps/opencs/view/world/referencecreator.hpp | 2 +- apps/opencs/view/world/table.cpp | 2 +- apps/opencs/view/world/tablesubview.cpp | 6 +++--- apps/opencs/view/world/tablesubview.hpp | 4 ++-- 14 files changed, 30 insertions(+), 30 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 9fc97d580..d342e88a4 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -50,7 +50,7 @@ namespace CSMWorld // not implemented Collection (const Collection&); Collection& operator= (const Collection&); - + protected: const std::map& getIdMap() const; @@ -97,11 +97,11 @@ namespace CSMWorld virtual void appendBlankRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None); ///< \param type Will be ignored, unless the collection supports multiple record types - + virtual void cloneRecord(const std::string& origin, const std::string& destination, const UniversalId::Type type); - + virtual int searchId (const std::string& id) const; ////< Search record with \a id. /// \return index of record (if found) or -1 (not found) @@ -149,7 +149,7 @@ namespace CSMWorld void setRecord (int index, const Record& record); ///< \attention This function must not change the ID. }; - + template const std::map& Collection::getIdMap() const { @@ -206,10 +206,10 @@ namespace CSMWorld copy.mModified = getRecord(origin).get(); copy.mState = RecordBase::State_ModifiedOnly; copy.get().mId = destination; - + insertRecord(copy, getAppendIndex(destination, type)); } - + template Collection::Collection() {} diff --git a/apps/opencs/model/world/collectionbase.hpp b/apps/opencs/model/world/collectionbase.hpp index 2473ae741..408a89d92 100644 --- a/apps/opencs/model/world/collectionbase.hpp +++ b/apps/opencs/model/world/collectionbase.hpp @@ -77,7 +77,7 @@ namespace CSMWorld virtual void cloneRecord(const std::string& origin, const std::string& destination, const UniversalId::Type type) = 0; - + virtual const RecordBase& getRecord (const std::string& id) const = 0; virtual const RecordBase& getRecord (int index) const = 0; diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index 0dedea012..ec6350658 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -53,12 +53,12 @@ namespace CSMWorld const std::string& IdDestination, const UniversalId::Type type, QUndoCommand* parent = 0); - + virtual void redo(); virtual void undo(); }; - + class CreateCommand : public QUndoCommand { IdTable& mModel; diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index 2d620004c..74923867d 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -66,7 +66,7 @@ namespace CSMWorld void cloneRecord(const std::string& origin, const std::string& destination, UniversalId::Type type = UniversalId::Type_None); - + QModelIndex getModelIndex (const std::string& id, int column) const; void setRecord (const std::string& id, const RecordBase& record); diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index ebbf2100d..5e973b447 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -72,7 +72,7 @@ namespace CSMWorld virtual void cloneRecord(const std::string& origin, const std::string& destination, const UniversalId::Type type); - + virtual void appendBlankRecord (const std::string& id, UniversalId::Type type); ///< \param type Will be ignored, unless the collection supports multiple record types diff --git a/apps/opencs/view/world/cellcreator.hpp b/apps/opencs/view/world/cellcreator.hpp index 74bf5f76b..db9fbf8a3 100644 --- a/apps/opencs/view/world/cellcreator.hpp +++ b/apps/opencs/view/world/cellcreator.hpp @@ -28,9 +28,9 @@ namespace CSVWorld CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id); virtual void reset(); - + virtual void toggleWidgets(bool active = true); - + virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type); diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index 2d5e014e2..88da70330 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -24,12 +24,12 @@ namespace CSVWorld virtual ~Creator(); virtual void reset() = 0; - + virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) = 0; virtual void setEditLock (bool locked) = 0; - + virtual void toggleWidgets(bool active = true) = 0; signals: diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 447c07424..cd7a5fa18 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -57,15 +57,15 @@ const CSMWorld::UniversalId& CSVWorld::GenericCreator::getCollectionId() const } CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, - const CSMWorld::UniversalId& id, bool relaxedIdRules): - + const CSMWorld::UniversalId& id, bool relaxedIdRules): + mData (data), mUndoStack (undoStack), mListId (id), mLocked (false), mCloneMode(false), mClonedType(CSMWorld::UniversalId::Type_None) - + { mLayout = new QHBoxLayout; mLayout->setContentsMargins (0, 0, 0, 0); diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 0f426e1a7..6cc39cf23 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -31,10 +31,10 @@ namespace CSVWorld bool mLocked; std::string mClonedId; CSMWorld::UniversalId::Type mClonedType; - + protected: bool mCloneMode; - + protected: void update(); @@ -49,7 +49,7 @@ namespace CSVWorld virtual std::string getId() const; virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; - + CSMWorld::Data& getData() const; const CSMWorld::UniversalId& getCollectionId() const; diff --git a/apps/opencs/view/world/referencecreator.cpp b/apps/opencs/view/world/referencecreator.cpp index 384b59fc4..6b8e02da0 100644 --- a/apps/opencs/view/world/referencecreator.cpp +++ b/apps/opencs/view/world/referencecreator.cpp @@ -53,9 +53,9 @@ std::string CSVWorld::ReferenceCreator::getErrors() const { return errors; } - + std::string cell = mCell->text().toUtf8().constData(); - + if (cell.empty()) { if (!errors.empty()) diff --git a/apps/opencs/view/world/referencecreator.hpp b/apps/opencs/view/world/referencecreator.hpp index 73b0899e5..12fb12dd9 100644 --- a/apps/opencs/view/world/referencecreator.hpp +++ b/apps/opencs/view/world/referencecreator.hpp @@ -27,7 +27,7 @@ namespace CSVWorld virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type); - + virtual void reset(); virtual void toggleWidgets(bool active = true); diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index f166171fe..8f6fc46a8 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -203,7 +203,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q mCreateAction = new QAction (tr ("Add Record"), this); connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest())); addAction (mCreateAction); - + mCloneAction = new QAction (tr ("Clone Record"), this); connect(mCloneAction, SIGNAL (triggered()), this, SLOT (cloneRecord())); addAction(mCloneAction); diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 5495c708f..981faaf59 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -47,11 +47,11 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D if (mBottom->canCreateAndDelete()) { connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); - + connect (mTable, SIGNAL (cloneRequest(const CSMWorld::UniversalId&)), this, SLOT(cloneRequest(const CSMWorld::UniversalId&))); - - connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), + + connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type))); } connect (mBottom, SIGNAL (requestFocus (const std::string&)), diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 9d2d005a8..d728dc2f3 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -38,9 +38,9 @@ namespace CSVWorld virtual void updateEditorSetting (const QString& key, const QString& value); virtual void setStatusBar (bool show); - + signals: - void cloneRequest(const std::string&, + void cloneRequest(const std::string&, const CSMWorld::UniversalId::Type); private slots: From d3000ce0990a05f89fd212b138107edfb10b20ba Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 27 Jan 2014 19:47:54 +0100 Subject: [PATCH 688/889] whitespaces removed again. --- apps/opencs/view/world/genericcreator.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 6cc39cf23..714853f98 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -62,12 +62,12 @@ namespace CSVWorld virtual void setEditLock (bool locked); virtual void reset(); - + virtual void toggleWidgets (bool active = true); virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type); - + virtual std::string getErrors() const; ///< Return formatted error descriptions for the current state of the creator. if an empty /// string is returned, there is no error. From e50e94af0b639634760cc8feda3f0728e3781019 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Mon, 27 Jan 2014 23:05:17 +0200 Subject: [PATCH 689/889] remove CreatureStats::mAttackType, set/getAttackType() --- apps/openmw/mwmechanics/character.cpp | 33 +++++++++-------------- apps/openmw/mwmechanics/creaturestats.cpp | 2 +- apps/openmw/mwmechanics/creaturestats.hpp | 12 --------- apps/openmw/mwrender/animation.cpp | 12 ++++----- components/esm/loadweap.hpp | 7 +++++ 5 files changed, 27 insertions(+), 39 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 4acdb4462..a54f2365d 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -39,17 +39,17 @@ namespace { -int getBestAttack (const ESM::Weapon* weapon) +std::string getBestAttack (const ESM::Weapon* weapon) { int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; if (slash >= chop && slash >= thrust) - return MWMechanics::CreatureStats::AT_Slash; + return "slash"; else if (chop >= slash && chop >= thrust) - return MWMechanics::CreatureStats::AT_Chop; + return "chop"; else - return MWMechanics::CreatureStats::AT_Thrust; + return "thrust"; } } @@ -691,9 +691,8 @@ bool CharacterController::updateWeaponState() mAttackType = "shoot"; else { - int attackType; if(isWeapon && Settings::Manager::getBool("best attack", "Game")) - attackType = getBestAttack(weapon->get()->mBase); + mAttackType = getBestAttack(weapon->get()->mBase); else determineAttackType(); } @@ -1299,27 +1298,21 @@ void CharacterController::determineAttackType() { float * move = mPtr.getClass().getMovementSettings(mPtr).mPosition; - if (move[0] && !move[1]) //sideway + if(mPtr.getClass().hasInventoryStore(mPtr)) { - mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Slash); - if(mPtr.getClass().hasInventoryStore(mPtr)) + if (move[0] && !move[1]) //sideway mAttackType = "slash"; - else - mCurrentWeapon = "attack2"; - } - else if (move[1]) //forward - { - mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Thrust); - if(mPtr.getClass().hasInventoryStore(mPtr)) + else if (move[1]) //forward mAttackType = "thrust"; else - mCurrentWeapon = "attack3"; + mAttackType = "chop"; } else { - mPtr.getClass().getCreatureStats(mPtr).setAttackType(MWMechanics::CreatureStats::AT_Chop); - if(mPtr.getClass().hasInventoryStore(mPtr)) - mAttackType = "chop"; + if (move[0] && !move[1]) //sideway + mCurrentWeapon = "attack2"; + else if (move[1]) //forward + mCurrentWeapon = "attack3"; else mCurrentWeapon = "attack1"; } diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index c6522f08f..30db59311 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -13,7 +13,7 @@ namespace MWMechanics : mLevel (0), mLevelHealthBonus(0.f), mDead (false), mDied (false), mFriendlyHits (0), mTalkedTo (false), mAlarmed (false), mAttacked (false), mHostile (false), - mAttackingOrSpell(false), mAttackType(AT_Chop), + mAttackingOrSpell(false), mIsWerewolf(false), mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false), mBlock(false), mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f) diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 0501eb286..67d925a19 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -45,14 +45,11 @@ namespace MWMechanics float mFallHeight; - int mAttackType; - std::string mLastHitObject; // The last object to hit this actor // Do we need to recalculate stats derived from attributes or other factors? bool mRecalcDynamicStats; - std::map mUsedPowers; protected: bool mIsWerewolf; @@ -125,15 +122,6 @@ namespace MWMechanics void setAttackingOrSpell(bool attackingOrSpell); - enum AttackType - { - AT_Chop, - AT_Slash, - AT_Thrust - }; - void setAttackType(int attackType) { mAttackType = attackType; } - int getAttackType() { return mAttackType; } - void setLevel(int level); enum AiSetting diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index c0e4eebda..3f45ce769 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -686,19 +686,19 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co else if(evt.compare(off, len, "unequip detach") == 0) showWeapons(false); else if(evt.compare(off, len, "chop hit") == 0) - mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Chop); + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Chop); else if(evt.compare(off, len, "slash hit") == 0) - mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Slash); + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Slash); else if(evt.compare(off, len, "thrust hit") == 0) - mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Thrust); + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Thrust); else if(evt.compare(off, len, "hit") == 0) { if (groupname == "attack1") - mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Chop); + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Chop); else if (groupname == "attack2") - mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Slash); + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Slash); else if (groupname == "attack3") - mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Thrust); + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Thrust); else mPtr.getClass().hit(mPtr); } diff --git a/components/esm/loadweap.hpp b/components/esm/loadweap.hpp index fde716b91..14ddb4708 100644 --- a/components/esm/loadweap.hpp +++ b/components/esm/loadweap.hpp @@ -35,6 +35,13 @@ struct Weapon Bolt = 13 }; + enum AttackType + { + AT_Chop, + AT_Slash, + AT_Thrust + }; + enum Flags { Magical = 0x01, From 2bd98a69ab487062ec9d54efccde89ce9b8e6929 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 28 Jan 2014 11:16:48 +0100 Subject: [PATCH 690/889] Corrections, according to the comments. Thanks for the review. :-) --- apps/opencs/model/world/commands.cpp | 6 ++---- apps/opencs/model/world/refidcollection.cpp | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 5526d7418..b60ffeb29 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -170,7 +170,5 @@ void CSMWorld::CloneCommand::redo() void CSMWorld::CloneCommand::undo() { - mModel.removeRow (mModel.getModelIndex (mIdDestination, 0).row()); //should be enough -} - -// kate: indent-mode cstyle; indent-width 4; replace-tabs on; + mModel.removeRow (mModel.getModelIndex (mIdDestination, 0).row()); +} \ No newline at end of file diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 5e8d38117..5d310208a 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -2,6 +2,7 @@ #include "refidcollection.hpp" #include +#include #include @@ -453,11 +454,10 @@ void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin, const std::string& destination, const CSMWorld::UniversalId::Type type) { - RecordBase *newRecord = mData.getRecord(mData.searchId(origin)).clone(); + std::auto_ptr newRecord(mData.getRecord(mData.searchId(origin)).clone()); newRecord->mState = RecordBase::State_ModifiedOnly; mAdapters.find(type)->second->setId(*newRecord, destination); mData.insertRecord(*newRecord, type, destination); - delete newRecord; } void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, From a473c3f6193b311ae84cf963034f144e20795c72 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 28 Jan 2014 12:36:10 +0100 Subject: [PATCH 691/889] some cleanup --- apps/openmw/mwmechanics/aitravel.cpp | 8 -------- apps/openmw/mwmechanics/pathfinding.cpp | 8 ++++---- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index ba75cb418..9a1b98d8f 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -66,14 +66,6 @@ namespace MWMechanics { cellX = cell->mData.mX; cellY = cell->mData.mY; - float xCell = 0; - float yCell = 0; - - if(cell->isExterior()) - { - xCell = cell->mData.mX * ESM::Land::REAL_SIZE; - yCell = cell->mData.mY * ESM::Land::REAL_SIZE; - } ESM::Pathgrid::Point dest; dest.mX = mX; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 668b4f2b1..1ead89040 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -117,7 +117,7 @@ namespace if(current == goal) break; closedset.push_back(current); - + for(int j = 0;j (mGraph.size());i++) { mGraph[i].parent = -1; mGScore[i] = -1; @@ -225,8 +225,8 @@ namespace MWMechanics if(current == goal) break; closedset.push_back(current); - - for(int j = 0;j (mGraph[current].edges.size());j++) { //int next = mGraph[current].edges[j].destination if(std::find(closedset.begin(),closedset.end(),mGraph[current].edges[j].destination) == closedset.end()) From a623f038504232f152b04a79d7bd790f0565f1db Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 28 Jan 2014 13:49:59 +0100 Subject: [PATCH 692/889] reduced object state for objects in containers --- components/esm/cellref.cpp | 31 +++++++++++++++++-------------- components/esm/cellref.hpp | 2 +- components/esm/objectstate.cpp | 14 ++++++++------ components/esm/objectstate.hpp | 2 +- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 19427af0c..00b15f4a3 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -75,7 +75,7 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) esm.getHT (mNam0); } -void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum) const +void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) const { if (wideRefNum) esm.writeHNT ("FRMR", mRefNum, 8); @@ -107,29 +107,32 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum) const esm.writeHNT("NAM9", mGoldValue); } - if (mTeleport) + if (mTeleport && !inInventory) { esm.writeHNT("DODT", mDoorDest); esm.writeHNOCString("DNAM", mDestCell); } - if (mLockLevel != -1) { + if (mLockLevel != -1 && !inInventory) esm.writeHNT("FLTV", mLockLevel); - } - esm.writeHNOCString("KNAM", mKey); - esm.writeHNOCString("TNAM", mTrap); - if (mReferenceBlocked != -1) { + if (!inInventory) + esm.writeHNOCString ("KNAM", mKey); + + if (!inInventory) + esm.writeHNOCString ("TNAM", mTrap); + + if (mReferenceBlocked != -1) esm.writeHNT("UNAM", mReferenceBlocked); - } - if (mFltv != 0) { - esm.writeHNT("FLTV", mFltv); - } - esm.writeHNT("DATA", mPos, 24); - if (mNam0 != 0) { + if (mFltv != 0 && !inInventory) + esm.writeHNT("FLTV", mFltv); + + if (!inInventory) + esm.writeHNT("DATA", mPos, 24); + + if (mNam0 != 0 && !inInventory) esm.writeHNT("NAM0", mNam0); - } } void ESM::CellRef::blank() diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 0cd6a3673..16f6603a2 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -89,7 +89,7 @@ namespace ESM void load (ESMReader& esm, bool wideRefNum = false); - void save(ESMWriter &esm, bool wideRefNum = false) const; + void save (ESMWriter &esm, bool wideRefNum = false, bool inInventory = false) const; void blank(); }; diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index 56289acae..21585e09d 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -25,9 +25,9 @@ void ESM::ObjectState::load (ESMReader &esm) esm.getHNT (mLocalRotation, "LROT", 12); } -void ESM::ObjectState::save (ESMWriter &esm) const +void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const { - mRef.save (esm, true); + mRef.save (esm, true, inInventory); if (mHasLocals) { @@ -35,13 +35,15 @@ void ESM::ObjectState::save (ESMWriter &esm) const mLocals.save (esm); } - if (!mEnabled) + if (!mEnabled && !inInventory) esm.writeHNT ("ENAB", mEnabled); if (mCount!=1) esm.writeHNT ("COUN", mCount); - esm.writeHNT ("POS_", mPosition, 24); - - esm.writeHNT ("LROT", mLocalRotation, 12); + if (!inInventory) + { + esm.writeHNT ("POS_", mPosition, 24); + esm.writeHNT ("LROT", mLocalRotation, 12); + } } \ No newline at end of file diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp index c599bb973..34226ea91 100644 --- a/components/esm/objectstate.hpp +++ b/components/esm/objectstate.hpp @@ -27,7 +27,7 @@ namespace ESM float mLocalRotation[3]; void load (ESMReader &esm); - void save (ESMWriter &esm) const; + void save (ESMWriter &esm, bool inInventory = false) const; }; } From e0537a3253022246d473bc179a3d9a6629562b14 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 28 Jan 2014 13:53:24 +0100 Subject: [PATCH 693/889] made object state polymorphic --- components/esm/objectstate.cpp | 4 +++- components/esm/objectstate.hpp | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index 21585e09d..6aa820599 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -46,4 +46,6 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const esm.writeHNT ("POS_", mPosition, 24); esm.writeHNT ("LROT", mLocalRotation, 12); } -} \ No newline at end of file +} + +ESM::ObjectState::~ObjectState() {} \ No newline at end of file diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp index 34226ea91..9c9ca5f2e 100644 --- a/components/esm/objectstate.hpp +++ b/components/esm/objectstate.hpp @@ -26,8 +26,10 @@ namespace ESM ESM::Position mPosition; float mLocalRotation[3]; - void load (ESMReader &esm); - void save (ESMWriter &esm, bool inInventory = false) const; + virtual void load (ESMReader &esm); + virtual void save (ESMWriter &esm, bool inInventory = false) const; + + virtual ~ObjectState(); }; } From 5ee1dc6be83d0859fc5b48d3ba73f2066b7c9422 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Tue, 28 Jan 2014 21:07:26 +0200 Subject: [PATCH 694/889] attacktypes error fix --- apps/openmw/mwclass/creature.cpp | 6 +++--- apps/openmw/mwclass/npc.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index c23b9e23a..812cd16f7 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -282,11 +282,11 @@ namespace MWClass { const bool weaphashealth = get(weapon).hasItemHealth(weapon); const unsigned char *attack = NULL; - if(type == MWMechanics::CreatureStats::AT_Chop) + if(type == ESM::Weapon::AT_Chop) attack = weapon.get()->mBase->mData.mChop; - else if(type == MWMechanics::CreatureStats::AT_Slash) + else if(type == ESM::Weapon::AT_Slash) attack = weapon.get()->mBase->mData.mSlash; - else if(type == MWMechanics::CreatureStats::AT_Thrust) + else if(type == ESM::Weapon::AT_Thrust) attack = weapon.get()->mBase->mData.mThrust; if(attack) { diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 8c6e89544..4ee02d73e 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -518,11 +518,11 @@ namespace MWClass { const bool weaphashealth = get(weapon).hasItemHealth(weapon); const unsigned char *attack = NULL; - if(type == MWMechanics::CreatureStats::AT_Chop) + if(type == ESM::Weapon::AT_Chop) attack = weapon.get()->mBase->mData.mChop; - else if(type == MWMechanics::CreatureStats::AT_Slash) + else if(type == ESM::Weapon::AT_Slash) attack = weapon.get()->mBase->mData.mSlash; - else if(type == MWMechanics::CreatureStats::AT_Thrust) + else if(type == ESM::Weapon::AT_Thrust) attack = weapon.get()->mBase->mData.mThrust; if(attack) { From 365ae155326af422ef471fa6f82aea25ef3c7856 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 14:47:01 +0100 Subject: [PATCH 695/889] Drag&Drop: auto-hide windows that can't be drop targets on mouseover --- apps/openmw/mwgui/mapwindow.cpp | 5 ++-- apps/openmw/mwgui/mapwindow.hpp | 6 +++-- apps/openmw/mwgui/spellwindow.cpp | 3 ++- apps/openmw/mwgui/spellwindow.hpp | 6 +++-- apps/openmw/mwgui/statswindow.cpp | 7 ++++-- apps/openmw/mwgui/statswindow.hpp | 6 ++--- apps/openmw/mwgui/windowbase.cpp | 34 ++++++++++++++++++++++++++ apps/openmw/mwgui/windowbase.hpp | 16 ++++++++++++ apps/openmw/mwgui/windowmanagerimp.cpp | 10 +++++--- 9 files changed, 77 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index ba6114262..d5b243ca5 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -373,8 +373,9 @@ namespace MWGui // ------------------------------------------------------------------------------------------ - MapWindow::MapWindow(const std::string& cacheDir) - : MWGui::WindowPinnableBase("openmw_map_window.layout") + MapWindow::MapWindow(DragAndDrop* drag, const std::string& cacheDir) + : WindowPinnableBase("openmw_map_window.layout") + , NoDrop(drag, mMainWidget) , mGlobal(false) , mGlobalMap(0) , mGlobalMapRender(0) diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 7df2105dc..88c53f06f 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -75,10 +75,10 @@ namespace MWGui float mLastDirectionY; }; - class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase + class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase, public NoDrop { public: - MapWindow(const std::string& cacheDir); + MapWindow(DragAndDrop* drag, const std::string& cacheDir); virtual ~MapWindow(); void setCellName(const std::string& cellName); @@ -92,6 +92,8 @@ namespace MWGui virtual void open(); + void onFrame(float dt) { NoDrop::onFrame(dt); } + private: void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 07076159c..6b261a799 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -42,8 +42,9 @@ namespace namespace MWGui { - SpellWindow::SpellWindow() + SpellWindow::SpellWindow(DragAndDrop* drag) : WindowPinnableBase("openmw_spell_window.layout") + , NoDrop(drag, mMainWidget) , mHeight(0) , mWidth(0) { diff --git a/apps/openmw/mwgui/spellwindow.hpp b/apps/openmw/mwgui/spellwindow.hpp index 521e73d76..38a761931 100644 --- a/apps/openmw/mwgui/spellwindow.hpp +++ b/apps/openmw/mwgui/spellwindow.hpp @@ -7,14 +7,16 @@ namespace MWGui { class SpellIcons; - class SpellWindow : public WindowPinnableBase + class SpellWindow : public WindowPinnableBase, public NoDrop { public: - SpellWindow(); + SpellWindow(DragAndDrop* drag); virtual ~SpellWindow(); void updateSpells(); + void onFrame(float dt) { NoDrop::onFrame(dt); } + protected: MyGUI::ScrollView* mSpellView; MyGUI::Widget* mEffectBox; diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 40eb2d3b1..abaca165e 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -18,8 +18,9 @@ namespace MWGui const int StatsWindow::sLineHeight = 18; - StatsWindow::StatsWindow () + StatsWindow::StatsWindow (DragAndDrop* drag) : WindowPinnableBase("openmw_stats_window.layout") + , NoDrop(drag, mMainWidget) , mSkillView(NULL) , mMajorSkills() , mMinorSkills() @@ -219,11 +220,13 @@ namespace MWGui updateSkillArea(); } - void StatsWindow::onFrame () + void StatsWindow::onFrame (float dt) { if (!mMainWidget->getVisible()) return; + NoDrop::onFrame(dt); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWMechanics::NpcStats &PCstats = MWWorld::Class::get(player).getNpcStats(player); diff --git a/apps/openmw/mwgui/statswindow.hpp b/apps/openmw/mwgui/statswindow.hpp index 28d96ca90..d90c16be9 100644 --- a/apps/openmw/mwgui/statswindow.hpp +++ b/apps/openmw/mwgui/statswindow.hpp @@ -10,17 +10,17 @@ namespace MWGui { class WindowManager; - class StatsWindow : public WindowPinnableBase + class StatsWindow : public WindowPinnableBase, public NoDrop { public: typedef std::map FactionList; typedef std::vector SkillList; - StatsWindow(); + StatsWindow(DragAndDrop* drag); /// automatically updates all the data in the stats window, but only if it has changed. - void onFrame(); + void onFrame(float dt); void setBar(const std::string& name, const std::string& tname, int val, int max); void setPlayerName(const std::string& playerName); diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index cc74579ab..87b26b814 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -1,6 +1,7 @@ #include "windowbase.hpp" #include "../mwbase/windowmanager.hpp" +#include "container.hpp" using namespace MWGui; @@ -50,3 +51,36 @@ void WindowModal::close() { MyGUI::InputManager::getInstance ().removeWidgetModal (mMainWidget); } + +NoDrop::NoDrop(DragAndDrop *drag, MyGUI::Widget *widget) + : mDrag(drag), mWidget(widget), mTransparent(false) +{ +} + +void NoDrop::onFrame(float dt) +{ + MyGUI::IntPoint mousePos = MyGUI::InputManager::getInstance().getMousePosition(); + + if (mDrag->mIsOnDragAndDrop) + { + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getMouseFocusWidget(); + while (focus && focus != mWidget) + focus = focus->getParent(); + + if (focus == mWidget) + mTransparent = true; + } + if (!mWidget->getAbsoluteCoord().inside(mousePos)) + mTransparent = false; + + if (mTransparent) + { + mWidget->setNeedMouseFocus(false); // Allow click-through + mWidget->setAlpha(std::max(0.13f, mWidget->getAlpha() - dt*5)); + } + else + { + mWidget->setNeedMouseFocus(true); + mWidget->setAlpha(std::min(1.0f, mWidget->getAlpha() + dt*5)); + } +} diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index 2c014baf0..48de9ea87 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -11,6 +11,7 @@ namespace MWBase namespace MWGui { class WindowManager; + class DragAndDrop; class WindowBase: public OEngine::GUI::Layout { @@ -42,6 +43,21 @@ namespace MWGui virtual void open(); virtual void close(); }; + + /// A window that cannot be the target of a drag&drop action. + /// When hovered with a drag item, the window will become transparent and allow click-through. + class NoDrop + { + public: + NoDrop(DragAndDrop* drag, MyGUI::Widget* widget); + + void onFrame(float dt); + + private: + MyGUI::Widget* mWidget; + DragAndDrop* mDrag; + bool mTransparent; + }; } #endif diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index cda146e8c..1c48e1d9b 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -200,9 +200,9 @@ namespace MWGui mRecharge = new Recharge(); mMenu = new MainMenu(w,h); - mMap = new MapWindow(""); + mMap = new MapWindow(mDragAndDrop, ""); trackWindow(mMap, "map"); - mStatsWindow = new StatsWindow(); + mStatsWindow = new StatsWindow(mDragAndDrop); trackWindow(mStatsWindow, "stats"); mConsole = new Console(w,h, mConsoleOnlyScripts); trackWindow(mConsole, "console"); @@ -227,7 +227,7 @@ namespace MWGui mConfirmationDialog = new ConfirmationDialog(); mAlchemyWindow = new AlchemyWindow(); trackWindow(mAlchemyWindow, "alchemy"); - mSpellWindow = new SpellWindow(); + mSpellWindow = new SpellWindow(mDragAndDrop); trackWindow(mSpellWindow, "spells"); mQuickKeysMenu = new QuickKeysMenu(); mLevelupDialog = new LevelupDialog(); @@ -709,7 +709,9 @@ namespace MWGui mInventoryWindow->onFrame(); - mStatsWindow->onFrame(); + mStatsWindow->onFrame(frameDuration); + mMap->onFrame(frameDuration); + mSpellWindow->onFrame(frameDuration); mWaitDialog->onFrame(frameDuration); From ba67bf45f837e7296c9e55c47237f669ba233140 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 20:15:22 +0100 Subject: [PATCH 696/889] Fix an issue with InventoryStore copy constructor. Don't copy the iterator directly - mContainer will be wrong and comparisons against end() will always fail. This caused an exception when looting a creature that had moved cells. --- apps/openmw/mwworld/inventorystore.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index ea43314e6..5da871d9d 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -34,6 +34,13 @@ void MWWorld::InventoryStore::copySlots (const InventoryStore& store) mSlots.push_back (slot); } + + // some const-trickery, required because of a flaw in the handling of MW-references and the + // resulting workarounds + std::size_t distance = std::distance (const_cast (store).begin(), const_cast (store).mSelectedEnchantItem); + ContainerStoreIterator slot = begin(); + std::advance (slot, distance); + mSelectedEnchantItem = slot; } void MWWorld::InventoryStore::initSlots (TSlots& slots_) @@ -54,18 +61,19 @@ MWWorld::InventoryStore::InventoryStore() MWWorld::InventoryStore::InventoryStore (const InventoryStore& store) : ContainerStore (store) , mSelectedEnchantItem(end()) - , mListener(NULL) - , mUpdatesEnabled(true) { mMagicEffects = store.mMagicEffects; mFirstAutoEquip = store.mFirstAutoEquip; - mSelectedEnchantItem = store.mSelectedEnchantItem; + mListener = store.mListener; + mUpdatesEnabled = store.mUpdatesEnabled; + mPermanentMagicEffectMagnitudes = store.mPermanentMagicEffectMagnitudes; copySlots (store); } MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStore& store) { + mListener = store.mListener; mMagicEffects = store.mMagicEffects; mFirstAutoEquip = store.mFirstAutoEquip; mPermanentMagicEffectMagnitudes = store.mPermanentMagicEffectMagnitudes; From cc40cec39577fb79e163436deaee2b277beb577d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 21:08:11 +0100 Subject: [PATCH 697/889] Move levelup to NpcStats The code came from back in the days where NpcStats did not derive from CreatureStats. --- apps/openmw/mwgui/charactercreation.cpp | 7 +++-- apps/openmw/mwgui/levelupdialog.cpp | 6 ++--- apps/openmw/mwmechanics/creaturestats.cpp | 31 +---------------------- apps/openmw/mwmechanics/creaturestats.hpp | 11 +------- apps/openmw/mwmechanics/npcstats.cpp | 22 ++++++++++++++++ apps/openmw/mwmechanics/npcstats.hpp | 6 +++++ 6 files changed, 35 insertions(+), 48 deletions(-) diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 1a3226074..5526bd26d 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -10,7 +10,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/npcstats.hpp" #include "../mwworld/class.hpp" #include "../mwworld/fallback.hpp" @@ -47,9 +47,8 @@ namespace void updatePlayerHealth() { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats(player); - - creatureStats.updateHealth(); + MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats(player); + npcStats.updateHealth(); } } diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index e55d9d8ca..f56d80883 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -155,7 +155,6 @@ namespace MWGui void LevelupDialog::onOkButtonClicked (MyGUI::Widget* sender) { MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); - MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats (player); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); if (mSpentAttributes.size() < 3) @@ -165,15 +164,14 @@ namespace MWGui // increase attributes for (int i=0; i<3; ++i) { - MWMechanics::AttributeValue attribute = creatureStats.getAttribute(mSpentAttributes[i]); + MWMechanics::AttributeValue attribute = pcStats.getAttribute(mSpentAttributes[i]); attribute.setBase (attribute.getBase () + pcStats.getLevelupAttributeMultiplier (mSpentAttributes[i])); if (attribute.getBase() >= 100) attribute.setBase(100); - creatureStats.setAttribute(mSpentAttributes[i], attribute); + pcStats.setAttribute(mSpentAttributes[i], attribute); } - creatureStats.levelUp(); pcStats.levelUp (); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Levelup); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 30db59311..c862c0ab4 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -10,7 +10,7 @@ namespace MWMechanics { CreatureStats::CreatureStats() - : mLevel (0), mLevelHealthBonus(0.f), mDead (false), mDied (false), mFriendlyHits (0), + : mLevel (0), mDead (false), mDied (false), mFriendlyHits (0), mTalkedTo (false), mAlarmed (false), mAttacked (false), mHostile (false), mAttackingOrSpell(false), @@ -22,35 +22,6 @@ namespace MWMechanics mAiSettings[i] = 0; } - float CreatureStats::getLevelHealthBonus () const - { - return mLevelHealthBonus; - } - - void CreatureStats::levelUp() - { - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); - - const int endurance = getAttribute(ESM::Attribute::Endurance).getBase(); - - // "When you gain a level, in addition to increasing three primary attributes, your Health - // will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level, - // the Health increase is calculated from the increased Endurance" - mLevelHealthBonus += endurance * gmst.find("fLevelUpHealthEndMult")->getFloat(); - updateHealth(); - - mLevel++; - } - - void CreatureStats::updateHealth() - { - const int endurance = getAttribute(ESM::Attribute::Endurance).getBase(); - const int strength = getAttribute(ESM::Attribute::Strength).getBase(); - - setHealth(static_cast (0.5 * (strength + endurance)) + mLevelHealthBonus); - } - const AiSequence& CreatureStats::getAiSequence() const { return mAiSequence; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 67d925a19..bb9583301 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -22,13 +22,11 @@ namespace MWMechanics DrawState_ mDrawState; AttributeValue mAttributes[8]; DynamicStat mDynamic[3]; // health, magicka, fatigue - int mLevel; Spells mSpells; ActiveSpells mActiveSpells; MagicEffects mMagicEffects; Stat mAiSettings[4]; AiSequence mAiSequence; - float mLevelHealthBonus; bool mDead; bool mDied; int mFriendlyHits; @@ -54,6 +52,7 @@ namespace MWMechanics protected: bool mIsWerewolf; AttributeValue mWerewolfAttributes[8]; + int mLevel; public: CreatureStats(); @@ -142,14 +141,6 @@ namespace MWMechanics float getFatigueTerm() const; ///< Return effective fatigue - float getLevelHealthBonus() const; - - void levelUp(); - - void updateHealth(); - ///< Calculate health based on endurance and strength. - /// Called at character creation and at level up. - bool isDead() const; bool hasDied() const; diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index e41ce2078..df59328c2 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -30,6 +30,7 @@ MWMechanics::NpcStats::NpcStats() , mProfit(0) , mTimeToStartDrowning(20.0) , mLastDrowningHit(0) +, mLevelHealthBonus(0) { mSkillIncreases.resize (ESM::Attribute::Length, 0); } @@ -237,6 +238,27 @@ void MWMechanics::NpcStats::levelUp() mLevelProgress -= 10; for (int i=0; i &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + + const int endurance = getAttribute(ESM::Attribute::Endurance).getBase(); + + // "When you gain a level, in addition to increasing three primary attributes, your Health + // will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level, + // the Health increase is calculated from the increased Endurance" + mLevelHealthBonus += endurance * gmst.find("fLevelUpHealthEndMult")->getFloat(); + updateHealth(); + + setLevel(getLevel()+1); +} + +void MWMechanics::NpcStats::updateHealth() +{ + const int endurance = getAttribute(ESM::Attribute::Endurance).getBase(); + const int strength = getAttribute(ESM::Attribute::Strength).getBase(); + + setHealth(static_cast (0.5 * (strength + endurance)) + mLevelHealthBonus); } int MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 8cdeeea5d..d7db999e4 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -51,6 +51,8 @@ namespace MWMechanics /// time since last hit from drowning float mLastDrowningHit; + float mLevelHealthBonus; + public: NpcStats(); @@ -98,6 +100,10 @@ namespace MWMechanics void levelUp(); + void updateHealth(); + ///< Calculate health based on endurance and strength. + /// Called at character creation and at level up. + void flagAsUsed (const std::string& id); bool hasBeenUsed (const std::string& id) const; From 82146e7f8d865f931c388f39a4129d0bec189d90 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 21:50:40 +0100 Subject: [PATCH 698/889] Use GMSTs for levelup --- apps/openmw/mwgui/statswindow.cpp | 5 ++- apps/openmw/mwgui/waitdialog.cpp | 4 ++- apps/openmw/mwmechanics/npcstats.cpp | 53 ++++++++++++++++------------ files/mygui/openmw_tooltips.layout | 3 -- 4 files changed, 38 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index abaca165e..3d4c741a3 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -234,9 +234,12 @@ namespace MWGui MyGUI::Widget* levelWidget; for (int i=0; i<2; ++i) { + int max = MWBase::Environment::get().getWorld()->getStore().get().find("iLevelUpTotal")->getInt(); getWidget(levelWidget, i==0 ? "Level_str" : "LevelText"); levelWidget->setUserString("RangePosition_LevelProgress", boost::lexical_cast(PCstats.getLevelProgress())); - levelWidget->setUserString("Caption_LevelProgressText", boost::lexical_cast(PCstats.getLevelProgress()) + "/10"); + levelWidget->setUserString("Range_LevelProgress", boost::lexical_cast(max)); + levelWidget->setUserString("Caption_LevelProgressText", boost::lexical_cast(PCstats.getLevelProgress()) + "/" + + boost::lexical_cast(max)); } setFactions(PCstats.getFactionRanks()); diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 0ead54d9d..484462043 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -271,7 +271,9 @@ namespace MWGui const MWMechanics::NpcStats &pcstats = MWWorld::Class::get(player).getNpcStats(player); // trigger levelup if possible - if (mSleeping && pcstats.getLevelProgress () >= 10) + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + if (mSleeping && pcstats.getLevelProgress () >= gmst.find("iLevelUpTotal")->getInt()) { MWBase::Environment::get().getWindowManager()->pushGuiMode (GM_Levelup); } diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index df59328c2..e642ffc5a 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -190,22 +190,31 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas base += 1; - // if this is a major or minor skill of the class, increase level progress - bool levelProgress = false; - for (int i=0; i<2; ++i) - for (int j=0; j<5; ++j) + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + + // is this a minor or major skill? + int increase = gmst.find("iLevelupMiscMultAttriubte")->getInt(); // Note: GMST has a typo + for (int k=0; k<5; ++k) + { + if (class_.mData.mSkills[k][0] == skillIndex) { - int skill = class_.mData.mSkills[j][i]; - if (skill == skillIndex) - levelProgress = true; + mLevelProgress += gmst.find("iLevelUpMinorMult")->getInt(); + increase = gmst.find("iLevelUpMajorMultAttribute")->getInt(); } + } + for (int k=0; k<5; ++k) + { + if (class_.mData.mSkills[k][1] == skillIndex) + { + mLevelProgress += gmst.find("iLevelUpMajorMult")->getInt(); + increase = gmst.find("iLevelUpMinorMultAttribute")->getInt(); + } + } - mLevelProgress += levelProgress; - - // check the attribute this skill belongs to const ESM::Skill* skill = MWBase::Environment::get().getWorld ()->getStore ().get().find(skillIndex); - ++mSkillIncreases[skill->mData.mAttribute]; + mSkillIncreases[skill->mData.mAttribute] += increase; // Play sound & skill progress notification /// \todo check if character is the player, if levelling is ever implemented for NPCs @@ -217,7 +226,7 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas % static_cast (base); MWBase::Environment::get().getWindowManager ()->messageBox(message.str()); - if (mLevelProgress >= 10) + if (mLevelProgress >= gmst.find("iLevelUpTotal")->getInt()) { // levelup is possible now MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}"); @@ -263,18 +272,18 @@ void MWMechanics::NpcStats::updateHealth() int MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const { - // Source: http://www.uesp.net/wiki/Morrowind:Level#How_to_Level_Up int num = mSkillIncreases[attribute]; - if (num <= 1) + + if (num == 0) return 1; - else if (num <= 4) - return 2; - else if (num <= 7) - return 3; - else if (num <= 9) - return 4; - else - return 5; + + num = std::min(10, num); + + // iLevelUp01Mult - iLevelUp10Mult + std::stringstream gmst; + gmst << "iLevelUp" << std::setfill('0') << std::setw(2) << num << "Mult"; + + return MWBase::Environment::get().getWorld()->getStore().get().find(gmst.str())->getInt(); } void MWMechanics::NpcStats::flagAsUsed (const std::string& id) diff --git a/files/mygui/openmw_tooltips.layout b/files/mygui/openmw_tooltips.layout index 624c133f2..3f4fec59f 100644 --- a/files/mygui/openmw_tooltips.layout +++ b/files/mygui/openmw_tooltips.layout @@ -184,11 +184,8 @@ - - - From b8db151da75457de35c412269ca390245315ddb6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 22:00:36 +0100 Subject: [PATCH 699/889] Add missing skill increases for armor hits --- apps/openmw/mwclass/npc.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4ee02d73e..9e54f2856 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -726,6 +726,9 @@ namespace MWClass if (armorref.mCharge == 0) inv.unequipItem(armor, ptr); + if (ptr.getRefData().getHandle() == "player") + skillUsageSucceeded(ptr, get(armor).getEquipmentSkill(armor), 0); + switch(get(armor).getEquipmentSkill(armor)) { case ESM::Skill::LightArmor: @@ -739,6 +742,8 @@ namespace MWClass break; } } + else if(ptr.getRefData().getHandle() == "player") + skillUsageSucceeded(ptr, ESM::Skill::Unarmored, 0); } } From ea7e0abdc23b778b43e917e3f590792bdbb7b91d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 22:07:20 +0100 Subject: [PATCH 700/889] Move WhenStrikes skill success to a more appropriate place --- apps/openmw/mwclass/npc.cpp | 5 +---- apps/openmw/mwmechanics/spellcasting.cpp | 3 +++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 9e54f2856..512a5279c 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -603,10 +603,7 @@ namespace MWClass { MWMechanics::CastSpell cast(ptr, victim); cast.mHitPosition = hitPosition; - bool success = cast.cast(weapon); - - if (ptr.getRefData().getHandle() == "player" && success) - skillUsageSucceeded (ptr, ESM::Skill::Enchant, 3); + cast.cast(weapon); } } diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 749a5d7b1..0dec49f13 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -554,7 +554,10 @@ namespace MWMechanics else if (enchantment->mData.mType != ESM::Enchantment::WhenStrikes) { if (mCaster.getRefData().getHandle() == "player") + { MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item); // Set again to show the modified charge + mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 3); + } } inflict(mCaster, mCaster, enchantment->mEffects, ESM::RT_Self); From 292d692b40102dd5c28cdbfebea1176176bb2129 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 23:32:46 +0100 Subject: [PATCH 701/889] Loosen window size restrictions --- files/mygui/openmw_inventory_window.layout | 2 +- files/mygui/openmw_map_window.layout | 2 +- files/mygui/openmw_spell_window.layout | 2 +- files/mygui/openmw_stats_window.layout | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/files/mygui/openmw_inventory_window.layout b/files/mygui/openmw_inventory_window.layout index ecccd995b..ba6bf820e 100644 --- a/files/mygui/openmw_inventory_window.layout +++ b/files/mygui/openmw_inventory_window.layout @@ -2,7 +2,7 @@ - + diff --git a/files/mygui/openmw_map_window.layout b/files/mygui/openmw_map_window.layout index 232f31b75..6e0efce7e 100644 --- a/files/mygui/openmw_map_window.layout +++ b/files/mygui/openmw_map_window.layout @@ -2,7 +2,7 @@ - + diff --git a/files/mygui/openmw_spell_window.layout b/files/mygui/openmw_spell_window.layout index ab924da6d..ec655ace8 100644 --- a/files/mygui/openmw_spell_window.layout +++ b/files/mygui/openmw_spell_window.layout @@ -2,7 +2,7 @@ - + diff --git a/files/mygui/openmw_stats_window.layout b/files/mygui/openmw_stats_window.layout index 36c28c450..efec0ab37 100644 --- a/files/mygui/openmw_stats_window.layout +++ b/files/mygui/openmw_stats_window.layout @@ -2,7 +2,7 @@ - + From 190512156d02a15f65e6f54801582c753eda98fa Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Jan 2014 01:58:04 +0100 Subject: [PATCH 702/889] Use some more GMSTs for enchanting --- apps/openmw/mwclass/armor.cpp | 4 ++-- apps/openmw/mwclass/armor.hpp | 2 +- apps/openmw/mwclass/book.cpp | 4 ++-- apps/openmw/mwclass/book.hpp | 2 +- apps/openmw/mwclass/clothing.cpp | 4 ++-- apps/openmw/mwclass/clothing.hpp | 2 +- apps/openmw/mwclass/weapon.cpp | 4 ++-- apps/openmw/mwclass/weapon.hpp | 2 +- apps/openmw/mwmechanics/enchanting.cpp | 25 ++++++++++++------------- apps/openmw/mwworld/class.cpp | 2 +- apps/openmw/mwworld/class.hpp | 2 +- 11 files changed, 26 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 83bda25d1..e3974f243 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -366,12 +366,12 @@ namespace MWClass return MWWorld::Ptr(&cell.mArmors.insert(*ref), &cell); } - float Armor::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + int Armor::getEnchantmentPoints (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - return ref->mBase->mData.mEnchant/10.f; + return ref->mBase->mData.mEnchant; } bool Armor::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index d8d09d5bb..17cfca453 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -79,7 +79,7 @@ namespace MWClass virtual std::string getModel(const MWWorld::Ptr &ptr) const; - virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const; virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 429d91259..0e6506514 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -189,12 +189,12 @@ namespace MWClass return MWWorld::Ptr(&cell.mBooks.insert(*ref), &cell); } - float Book::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + int Book::getEnchantmentPoints (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - return ref->mBase->mData.mEnchant/10.f; + return ref->mBase->mData.mEnchant; } bool Book::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/book.hpp b/apps/openmw/mwclass/book.hpp index 7fb8a9507..79b823fa9 100644 --- a/apps/openmw/mwclass/book.hpp +++ b/apps/openmw/mwclass/book.hpp @@ -58,7 +58,7 @@ namespace MWClass virtual std::string getModel(const MWWorld::Ptr &ptr) const; - virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const; virtual float getWeight (const MWWorld::Ptr& ptr) const; diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index a135585eb..ab98d05ae 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -279,12 +279,12 @@ namespace MWClass return MWWorld::Ptr(&cell.mClothes.insert(*ref), &cell); } - float Clothing::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + int Clothing::getEnchantmentPoints (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - return ref->mBase->mData.mEnchant/10.f; + return ref->mBase->mData.mEnchant; } bool Clothing::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index e2e1188a1..a73b2c190 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -71,7 +71,7 @@ namespace MWClass virtual std::string getModel(const MWWorld::Ptr &ptr) const; - virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const; virtual float getWeight (const MWWorld::Ptr& ptr) const; diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 82935673c..af0234cd5 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -429,12 +429,12 @@ namespace MWClass return MWWorld::Ptr(&cell.mWeapons.insert(*ref), &cell); } - float Weapon::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + int Weapon::getEnchantmentPoints (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - return ref->mBase->mData.mEnchant/10.f; + return ref->mBase->mData.mEnchant; } bool Weapon::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index 181c637db..db44cd2b7 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -84,7 +84,7 @@ namespace MWClass virtual float getWeight (const MWWorld::Ptr& ptr) const; - virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 6c765aa41..87337cdd7 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -250,7 +250,10 @@ namespace MWMechanics { if (itemEmpty()) return 0; - return MWWorld::Class::get(mOldItemPtr).getEnchantmentPoints(mOldItemPtr); + + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + + return mOldItemPtr.getClass().getEnchantmentPoints(mOldItemPtr) * store.get().find("fEnchantmentMult")->getFloat(); } bool Enchanting::soulEmpty() const { @@ -274,22 +277,18 @@ namespace MWMechanics float Enchanting::getEnchantChance() const { - /* - Formula from http://www.uesp.net/wiki/Morrowind:Enchant - */ - const CreatureStats& creatureStats = MWWorld::Class::get (mEnchanter).getCreatureStats (mEnchanter); const NpcStats& npcStats = MWWorld::Class::get (mEnchanter).getNpcStats (mEnchanter); float chance1 = (npcStats.getSkill (ESM::Skill::Enchant).getModified() + - (0.25 * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified()) - + (0.125 * creatureStats.getAttribute (ESM::Attribute::Luck).getModified())); + (0.25 * npcStats.getAttribute (ESM::Attribute::Intelligence).getModified()) + + (0.125 * npcStats.getAttribute (ESM::Attribute::Luck).getModified())); + + const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); + + float chance2 = 7.5 / (gmst.find("fEnchantmentChanceMult")->getFloat() * ((mCastStyle == ESM::Enchantment::ConstantEffect) ? + gmst.find("fEnchantmentConstantChanceMult")->getFloat() : 1 )) + * getEnchantPoints(); - float chance2 = 2.5 * getEnchantPoints(); - if(mCastStyle==ESM::Enchantment::ConstantEffect) - { - float constantChance = MWBase::Environment::get().getWorld()->getStore().get().find ("fEnchantmentConstantChanceMult")->getFloat(); - chance2 /= constantChance; - } return (chance1-chance2); } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 9771ffde3..12676b43c 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -167,7 +167,7 @@ namespace MWWorld return 0; } - float Class::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + int Class::getEnchantmentPoints (const MWWorld::Ptr& ptr) const { throw std::runtime_error ("class does not support enchanting"); } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 0dee8b292..ad292df5c 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -259,7 +259,7 @@ namespace MWWorld ///< @return the enchantment ID if the object is enchanted, otherwise an empty string /// (default implementation: return empty string) - virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const; ///< @return the number of enchantment points available for possible enchanting virtual void adjustScale(const MWWorld::Ptr& ptr,float& scale) const; From a8a09762ce536b49b0d5ee4b701cac0b32fa2d46 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Jan 2014 19:03:20 +0100 Subject: [PATCH 703/889] Don't crash when deleting or disabling a moving door --- apps/openmw/mwworld/worldimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0394a2edd..340c2fa58 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1153,7 +1153,7 @@ namespace MWWorld std::map::iterator it = mDoorStates.begin(); while (it != mDoorStates.end()) { - if (!mWorldScene->isCellActive(*it->first.getCell())) + if (!mWorldScene->isCellActive(*it->first.getCell()) || !it->first.getRefData().getBaseNode()) mDoorStates.erase(it++); else { From edff88542be04be60c02e0fc575eb782276b8875 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Jan 2014 20:12:37 +0100 Subject: [PATCH 704/889] Allow double click / Enter key for accepting race/class/birthsign --- apps/openmw/mwgui/birth.cpp | 11 +++++++++-- apps/openmw/mwgui/birth.hpp | 1 + apps/openmw/mwgui/class.cpp | 11 +++++++++-- apps/openmw/mwgui/class.hpp | 1 + apps/openmw/mwgui/race.cpp | 11 +++++++++-- apps/openmw/mwgui/race.hpp | 1 + 6 files changed, 30 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 9c8e07f3e..7f58309ba 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -33,8 +33,7 @@ namespace MWGui getWidget(mBirthList, "BirthsignList"); mBirthList->setScrollVisible(true); - mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); - mBirthList->eventListMouseItemActivate += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); + mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onAccept); mBirthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); MyGUI::Button* backButton; @@ -97,6 +96,14 @@ namespace MWGui eventDone(this); } + void BirthDialog::onAccept(MyGUI::ListBox *_sender, size_t _index) + { + onSelectBirth(_sender, _index); + if(mBirthList->getIndexSelected() == MyGUI::ITEM_NONE) + return; + eventDone(this); + } + void BirthDialog::onBackClicked(MyGUI::Widget* _sender) { eventBack(); diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index cc958ddca..20a64c78c 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -38,6 +38,7 @@ namespace MWGui protected: void onSelectBirth(MyGUI::ListBox* _sender, size_t _index); + void onAccept(MyGUI::ListBox* _sender, size_t index); void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 1e1aebd95..1c8cc7840 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -82,8 +82,7 @@ namespace MWGui getWidget(mClassList, "ClassList"); mClassList->setScrollVisible(true); - mClassList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); - mClassList->eventListMouseItemActivate += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); + mClassList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onAccept); mClassList->eventListChangePosition += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); getWidget(mClassImage, "ClassImage"); @@ -152,6 +151,14 @@ namespace MWGui eventBack(); } + void PickClassDialog::onAccept(MyGUI::ListBox* _sender, size_t _index) + { + onSelectClass(_sender, _index); + if(mClassList->getIndexSelected() == MyGUI::ITEM_NONE) + return; + eventDone(this); + } + void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index) { if (_index == MyGUI::ITEM_NONE) diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index e74370a4c..f78f7541b 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -111,6 +111,7 @@ namespace MWGui protected: void onSelectClass(MyGUI::ListBox* _sender, size_t _index); + void onAccept(MyGUI::ListBox* _sender, size_t _index); void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 299b34b51..3dff1b7e4 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -70,8 +70,7 @@ namespace MWGui setText("RaceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu5", "Race")); getWidget(mRaceList, "RaceList"); mRaceList->setScrollVisible(true); - mRaceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); - mRaceList->eventListMouseItemActivate += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); + mRaceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onAccept); mRaceList->eventListChangePosition += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); setText("SkillsT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sBonusSkillTitle", "Skill Bonus")); @@ -241,6 +240,14 @@ namespace MWGui updateSpellPowers(); } + void RaceDialog::onAccept(MyGUI::ListBox *_sender, size_t _index) + { + onSelectRace(_sender, _index); + if(mRaceList->getIndexSelected() == MyGUI::ITEM_NONE) + return; + eventDone(this); + } + void RaceDialog::getBodyParts (int part, std::vector& out) { out.clear(); diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index 914ae8096..340dcfa27 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -67,6 +67,7 @@ namespace MWGui void onSelectNextHair(MyGUI::Widget* _sender); void onSelectRace(MyGUI::ListBox* _sender, size_t _index); + void onAccept(MyGUI::ListBox* _sender, size_t _index); void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); From 434fd215848762c0582fdf45bfc3798f99074709 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Jan 2014 20:24:25 +0100 Subject: [PATCH 705/889] Don't set journal index if a higher index is currently set This is vanilla behaviour, and required for the Mehra Milo vivec informants quest. --- apps/openmw/mwdialogue/quest.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwdialogue/quest.cpp b/apps/openmw/mwdialogue/quest.cpp index 520331bc1..5de5b1cf1 100644 --- a/apps/openmw/mwdialogue/quest.cpp +++ b/apps/openmw/mwdialogue/quest.cpp @@ -82,7 +82,8 @@ namespace MWDialogue if (index==-1) throw std::runtime_error ("unknown journal entry for topic " + mTopic); - setIndex (index); + if (index > mIndex) + setIndex (index); for (TEntryIter iter (mEntries.begin()); iter!=mEntries.end(); ++iter) if (*iter==entry.mInfoId) From 23ffb8a4dc0f393f01272a97255606353ebe683f Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Jan 2014 20:57:37 +0100 Subject: [PATCH 706/889] Fixes #1143: Make getCurrentAiPackage return the package that was run last, not the package that will be run in the next frame. This makes the Mehra Milo script work properly. --- apps/openmw/mwmechanics/aisequence.cpp | 5 ++++- apps/openmw/mwmechanics/aisequence.hpp | 7 +++++++ apps/openmw/mwscript/aiextensions.cpp | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 989d9c6a2..2110393fd 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -23,7 +23,7 @@ void MWMechanics::AiSequence::copy (const AiSequence& sequence) mPackages.push_back ((*iter)->clone()); } -MWMechanics::AiSequence::AiSequence() : mDone (false) {} +MWMechanics::AiSequence::AiSequence() : mDone (false), mLastAiPackage(-1) {} MWMechanics::AiSequence::AiSequence (const AiSequence& sequence) : mDone (false) { @@ -84,6 +84,7 @@ void MWMechanics::AiSequence::execute (const MWWorld::Ptr& actor,float duration) { if (!mPackages.empty()) { + mLastAiPackage = mPackages.front()->getTypeId(); if (mPackages.front()->execute (actor,duration)) { delete *mPackages.begin(); @@ -91,7 +92,9 @@ void MWMechanics::AiSequence::execute (const MWWorld::Ptr& actor,float duration) mDone = true; } else + { mDone = false; + } } } } diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 351e04480..62f48f981 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -23,6 +23,9 @@ namespace MWMechanics void copy (const AiSequence& sequence); + // The type of AI package that ran last + int mLastAiPackage; + public: AiSequence(); @@ -36,6 +39,10 @@ namespace MWMechanics int getTypeId() const; ///< @see enum AiPackage::TypeId + int getLastRunTypeId() const { return mLastAiPackage; } + ///< Get the typeid of the Ai package that ran last, NOT the currently "active" Ai package that will be run in the next frame. + /// This difference is important when an Ai package has just finished and been removed. + bool getCombatTarget (std::string &targetActorId) const; ///< Return true and assign target if combat package is currently /// active, return false otherwise diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 05886c51c..8314d011a 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -352,7 +352,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().getTypeId (); + Interpreter::Type_Integer value = MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().getLastRunTypeId(); runtime.push (value); } From e717694b3670c7ffff81a0b403c0fb5f84b2e087 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Jan 2014 22:14:36 +0100 Subject: [PATCH 707/889] Reset Run stance in AiWander --- apps/openmw/mwmechanics/aiwander.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 2f8f1cbd4..c6ea2ee91 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -68,6 +68,7 @@ namespace MWMechanics bool AiWander::execute (const MWWorld::Ptr& actor,float duration) { actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); + actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false); MWBase::World *world = MWBase::Environment::get().getWorld(); if(mDuration) { From 305e78c9819201a85581d072b69652caee9c83bc Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 29 Jan 2014 00:05:32 +0100 Subject: [PATCH 708/889] Fix creature attack bug --- apps/openmw/mwclass/creature.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 812cd16f7..b1bd80596 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -332,7 +332,7 @@ namespace MWClass if (damage > 0) MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); - victim.getClass().onHit(victim, damage, true, MWWorld::Ptr(), ptr, true); + victim.getClass().onHit(victim, damage, true, weapon, ptr, true); } void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const From 2b15b8b4848469a9462f3cec765c55303c0dbb29 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 29 Jan 2014 15:22:07 +0100 Subject: [PATCH 709/889] Fix gold bugs (Fixes #1145, Fixes #1146) --- apps/openmw/mwclass/misc.cpp | 10 ++++------ apps/openmw/mwworld/containerstore.cpp | 10 ++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index a8a6c55ec..e58716f1c 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -155,11 +155,8 @@ namespace MWClass int count = ptr.getRefData().getCount(); bool gold = isGold(ptr); - - if (gold && ptr.getCellRef().mGoldValue != 1) - count = ptr.getCellRef().mGoldValue; - else if (gold) - count *= ref->mBase->mData.mValue; + if (gold) + count *= getValue(ptr); std::string countString; if (!gold) @@ -204,7 +201,7 @@ namespace MWClass MWBase::Environment::get().getWorld()->getStore(); if (isGold(ptr)) { - int goldAmount = ptr.getRefData().getCount(); + int goldAmount = getValue(ptr) * ptr.getRefData().getCount(); std::string base = "Gold_001"; if (goldAmount >= 100) @@ -223,6 +220,7 @@ namespace MWClass newRef.getPtr().get(); newPtr = MWWorld::Ptr(&cell.mMiscItems.insert(*ref), &cell); newPtr.getCellRef().mGoldValue = goldAmount; + newPtr.getRefData().setCount(1); } else { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 0c4226f9b..71eb9145e 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -192,13 +192,11 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr, || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_025") || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_100")) { - int realCount = ptr.getRefData().getCount(); - if (ptr.getCellRef().mGoldValue > 1 && realCount == 1) - realCount = ptr.getCellRef().mGoldValue; + int realCount = count * ptr.getClass().getValue(ptr); for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) { - if (Misc::StringUtils::ciEqual((*iter).get()->mRef.mRefID, MWWorld::ContainerStore::sGoldId)) + if (Misc::StringUtils::ciEqual((*iter).getCellRef().mRefID, MWWorld::ContainerStore::sGoldId)) { iter->getRefData().setCount(iter->getRefData().getCount() + realCount); flagAsModified(); @@ -206,8 +204,8 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr, } } - MWWorld::ManualRef ref(esmStore, MWWorld::ContainerStore::sGoldId, count); - return addNewStack(ref.getPtr(), count); + MWWorld::ManualRef ref(esmStore, MWWorld::ContainerStore::sGoldId, realCount); + return addNewStack(ref.getPtr(), realCount); } // determine whether to stack or not From ec7cb90ca466f11bcb67ee34271af5f46f896e3c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 30 Jan 2014 11:50:13 +0100 Subject: [PATCH 710/889] added support for serialisation of CustomData state --- apps/openmw/mwworld/class.cpp | 4 ++++ apps/openmw/mwworld/class.hpp | 13 +++++++++++++ apps/openmw/mwworld/livecellref.cpp | 8 ++++++++ 3 files changed, 25 insertions(+) diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 6c00b949c..6e43b8dc2 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -367,4 +367,8 @@ namespace MWWorld { throw std::runtime_error("class does not support gore"); } + + void Class::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const {} + + void Class::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) const {} } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index ec22d0306..6e3a56907 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -9,6 +9,11 @@ #include "ptr.hpp" +namespace ESM +{ + struct ObjectState; +} + namespace Ogre { class Vector3; @@ -299,6 +304,14 @@ namespace MWWorld virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; + virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) + const; + ///< Read additional state from \a state into \a ptr. + + virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + const; + ///< Write additional state from \a ptr into \a state. + static const Class& get (const std::string& key); ///< If there is no class for this \a key, an exception is thrown. diff --git a/apps/openmw/mwworld/livecellref.cpp b/apps/openmw/mwworld/livecellref.cpp index a12d20e6a..d71704fd7 100644 --- a/apps/openmw/mwworld/livecellref.cpp +++ b/apps/openmw/mwworld/livecellref.cpp @@ -3,16 +3,24 @@ #include +#include "ptr.hpp" +#include "class.hpp" + void MWWorld::LiveCellRefBase::loadImp (const ESM::ObjectState& state) { mRef = state.mRef; mData = RefData (state); + Ptr ptr (this); + mClass->readAdditionalState (ptr, state); } void MWWorld::LiveCellRefBase::saveImp (ESM::ObjectState& state) const { state.mRef = mRef; mData.write (state); + /// \todo get rid of this cast once const-correct Ptr are available + Ptr ptr (const_cast (this)); + mClass->writeAdditionalState (ptr, state); } bool MWWorld::LiveCellRefBase::checkStateImp (const ESM::ObjectState& state) From 900532a6ca2e5bb8394c1a96fb6f6d36792ef27b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 30 Jan 2014 12:37:33 +0100 Subject: [PATCH 711/889] store additional state of lights in saved game files --- apps/openmw/mwclass/light.cpp | 21 +++++++++++++++++++++ apps/openmw/mwclass/light.hpp | 8 ++++++++ apps/openmw/mwworld/cellstore.cpp | 5 +++-- components/CMakeLists.txt | 2 +- components/esm/lightstate.cpp | 21 +++++++++++++++++++++ components/esm/lightstate.hpp | 19 +++++++++++++++++++ 6 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 components/esm/lightstate.cpp create mode 100644 components/esm/lightstate.hpp diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index cc56ec4c8..ddb2c16d6 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -2,6 +2,7 @@ #include "light.hpp" #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -269,4 +270,24 @@ namespace MWClass } return std::make_pair(1,""); } + + void Light::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) + const + { + const ESM::LightState& state2 = dynamic_cast (state); + + ensureCustomData (ptr); + + dynamic_cast (*ptr.getRefData().getCustomData()).mTime = state2.mTime; + } + + void Light::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + const + { + ESM::LightState& state2 = dynamic_cast (state); + + ensureCustomData (ptr); + + state2.mTime = dynamic_cast (*ptr.getRefData().getCustomData()).mTime; + } } diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index c15228a6a..5568e1727 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -71,6 +71,14 @@ namespace MWClass virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; + + virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) + const; + ///< Read additional state from \a state into \a ptr. + + virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + const; + ///< Write additional state from \a ptr into \a state. }; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 88c241e1a..77fdc971d 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -331,7 +332,7 @@ namespace MWWorld writeReferenceCollection (writer, mIngreds); writeReferenceCollection (writer, mCreatureLists); writeReferenceCollection (writer, mItemLists); - writeReferenceCollection (writer, mLights); + writeReferenceCollection (writer, mLights); writeReferenceCollection (writer, mLockpicks); writeReferenceCollection (writer, mMiscItems); writeReferenceCollection (writer, mNpcs); @@ -413,7 +414,7 @@ namespace MWWorld case ESM::REC_LIGH: - readReferenceCollection (reader, mLights, contentFileMap); + readReferenceCollection (reader, mLights, contentFileMap); break; case ESM::REC_LOCK: diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index d73bcaf74..f37a537c5 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -40,7 +40,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap + savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate ) add_component_dir (misc diff --git a/components/esm/lightstate.cpp b/components/esm/lightstate.cpp new file mode 100644 index 000000000..1ef040823 --- /dev/null +++ b/components/esm/lightstate.cpp @@ -0,0 +1,21 @@ + +#include "lightstate.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::LightState::load (ESMReader &esm) +{ + ObjectState::load (esm); + + mTime = 0; + esm.getHNOT (mTime, "LTIM"); +} + +void ESM::LightState::save (ESMWriter &esm, bool inInventory) const +{ + ObjectState::save (esm, inInventory); + + if (mTime) + esm.writeHNT ("LTIM", mTime); +} \ No newline at end of file diff --git a/components/esm/lightstate.hpp b/components/esm/lightstate.hpp new file mode 100644 index 000000000..a22735e07 --- /dev/null +++ b/components/esm/lightstate.hpp @@ -0,0 +1,19 @@ +#ifndef OPENMW_ESM_LIGHTSTATE_H +#define OPENMW_ESM_LIGHTSTATE_H + +#include "objectstate.hpp" + +namespace ESM +{ + // format 0, saved games only + + struct LightState : public ObjectState + { + float mTime; + + virtual void load (ESMReader &esm); + virtual void save (ESMWriter &esm, bool inInventory = false) const; + }; +} + +#endif From 80d8aa4030f771b48fc612c4716a0e891d5fe12a Mon Sep 17 00:00:00 2001 From: mrcheko Date: Thu, 30 Jan 2014 23:54:26 +0200 Subject: [PATCH 712/889] bug with sequence of knockouts; giving sense to some hit state code --- apps/openmw/mwmechanics/character.cpp | 1 + apps/openmw/mwmechanics/creaturestats.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index a54f2365d..252271d32 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -764,6 +764,7 @@ bool CharacterController::updateWeaponState() //commenting out following 2 lines will give a bit different combat dynamics(slower) mHitState = CharState_None; mCurrentHit.clear(); + mPtr.getClass().getCreatureStats(mPtr).setHitRecovery(false); } } else if(mUpperBodyState == UpperCharState_UnEquipingWeap) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 30db59311..943c13a18 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -209,7 +209,10 @@ namespace MWMechanics mDynamic[index] = value; if (index == 2 && value.getCurrent() < 0) + { setKnockedDown(true); + mDynamic[2].setCurrent(0); + } if (index==0 && mDynamic[index].getCurrent()<1) { From 39d86a946803b2494c93934742c73e79e27c915e Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 29 Jan 2014 20:29:07 +0100 Subject: [PATCH 713/889] Improvements to smooth NPC steering --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 6 +-- apps/openmw/mwmechanics/aicombat.cpp | 57 ++++++++++------------- apps/openmw/mwmechanics/aicombat.hpp | 11 +++-- apps/openmw/mwmechanics/aiescort.cpp | 19 ++++---- apps/openmw/mwmechanics/aifollow.cpp | 62 ++++++++++++------------- apps/openmw/mwmechanics/aitravel.cpp | 9 ++-- apps/openmw/mwmechanics/aiwander.cpp | 13 ++++-- apps/openmw/mwmechanics/character.cpp | 2 +- apps/openmw/mwmechanics/pathfinding.hpp | 3 +- apps/openmw/mwmechanics/steering.cpp | 43 +++++++++++++++++ apps/openmw/mwmechanics/steering.hpp | 19 ++++++++ 12 files changed, 151 insertions(+), 95 deletions(-) create mode 100644 apps/openmw/mwmechanics/steering.cpp create mode 100644 apps/openmw/mwmechanics/steering.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 41cc320ad..ce7700ddd 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -74,7 +74,7 @@ add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting - disease pickpocket levelledlist combat + disease pickpocket levelledlist combat steering ) add_openmw_dir (mwbase diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 2ad667d3f..44ed59bb4 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -574,8 +574,6 @@ namespace MWInput double x = arg.xrel * mCameraSensitivity * (1.0f/256.f); double y = arg.yrel * mCameraSensitivity * (1.0f/256.f) * (mInvertY ? -1 : 1) * mCameraYMultiplier; - float scale = MWBase::Environment::get().getFrameDuration(); - if(scale <= 0.0f) scale = 1.0f; float rot[3]; rot[0] = -y; @@ -585,8 +583,8 @@ namespace MWInput // Only actually turn player when we're not in vanity mode if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot)) { - mPlayer->yaw(x/scale); - mPlayer->pitch(-y/scale); + mPlayer->yaw(x); + mPlayer->pitch(-y); } if (arg.zrel && mControlSwitch["playerviewswitch"]) //Check to make sure you are allowed to zoomout and there is a change diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index ab39e8f0f..cf08dabf8 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -1,23 +1,22 @@ #include "aicombat.hpp" -#include "aifollow.hpp" -#include "movement.hpp" +#include +#include + #include "../mwworld/class.hpp" #include "../mwworld/timestamp.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.hpp" -#include "character.hpp" -#include "../mwworld/inventorystore.hpp" -#include "creaturestats.hpp" #include "npcstats.hpp" - -#include -#include +#include "steering.hpp" +#include "movement.hpp" +#include "character.hpp" // fixme: for getActiveWeapon namespace { @@ -43,7 +42,9 @@ namespace MWMechanics mReadyToAttack(false), mStrike(false), mCombatMove(false), - mMovement() + mRotate(false), + mMovement(), + mTargetAngle(0) { } @@ -68,10 +69,16 @@ namespace MWMechanics mCombatMove = false; } } + actor.getClass().getMovementSettings(actor) = mMovement; + + if (mRotate) + { + if (zTurn(actor, Ogre::Degree(mTargetAngle))) + mRotate = false; + } - //actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mReadyToAttack); mTimerAttack -= duration; actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mStrike); @@ -156,12 +163,7 @@ namespace MWMechanics weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit) } - //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); - ESM::Position pos = actor.getRefData().getPosition(); - - float zAngle; - float rangeMelee; float rangeCloseUp; @@ -189,12 +191,8 @@ namespace MWMechanics //Melee and Close-up combat vDir.z = 0; float dirLen = vDir.length(); - zAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / dirLen) * sgn(Ogre::Math::ASin(vDir.x / dirLen)) ).valueDegrees(); - - // TODO: use movement settings instead of rotating directly - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); - - //MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / dirLen) * sgn(Ogre::Math::ASin(vDir.x / dirLen)) ).valueDegrees(); + mRotate = true; //bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget); if (mFollowTarget && distBetween > rangeMelee) @@ -237,12 +235,6 @@ namespace MWMechanics else { //target is at far distance: build path to target OR follow target (if previously actor had reached it once) - - /* - //apply when AIFOLLOW package implementation will be existent - if(mFollowTarget) - actor.getClass().getCreatureStats(actor).getAiSequence().stack(AiFollow(mTarget));*/ - mFollowTarget = false; buildNewPath(actor); @@ -252,13 +244,10 @@ namespace MWMechanics //try shortcut if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) - zAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); + mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); else - zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - - // TODO: use movement settings instead of rotating directly - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); - //mMovement.mRotation[2] = 10*(Ogre::Degree(zAngle).valueRadians()-pos.rot[2]); + mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + mRotate = true; mMovement.mPosition[1] = 1; mReadyToAttack = false; @@ -294,6 +283,8 @@ namespace MWMechanics } } + actor.getClass().getMovementSettings(actor) = mMovement; + return false; } diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 27f7f5d95..767a36292 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -29,16 +29,21 @@ namespace MWMechanics private: PathFinder mPathFinder; - //controls duration of the actual strike + // controls duration of the actual strike float mTimerAttack; float mTimerReact; - //controls duration of the sideway & forward moves - //when mCombatMove is true + // controls duration of the sideway & forward moves + // when mCombatMove is true float mTimerCombatMove; + // the z rotation angle (degrees) we want to reach + // used every frame when mRotate is true + float mTargetAngle; + bool mReadyToAttack, mStrike; bool mFollowTarget; bool mCombatMove; + bool mRotate; MWMechanics::Movement mMovement; MWWorld::Ptr mTarget; diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 901b8c31d..bac258425 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -8,6 +8,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "steering.hpp" + namespace { float sgn(float a) @@ -33,7 +35,7 @@ namespace MWMechanics { mMaxDist = 470; - // The CS Help File states that if a duration is givin, the AI package will run for that long + // The CS Help File states that if a duration is given, the AI package will run for that long // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location. if(mX != 0 || mY != 0 || mZ != 0) mDuration = 0; @@ -52,7 +54,7 @@ namespace MWMechanics { mMaxDist = 470; - // The CS Help File states that if a duration is givin, the AI package will run for that long + // The CS Help File states that if a duration is given, the AI package will run for that long // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location. if(mX != 0 || mY != 0 || mZ != 0) mDuration = 0; @@ -89,25 +91,23 @@ namespace MWMechanics if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX) { int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX); - // Check if actor is near the border of an inactive cell. If so, disable AiEscort. - // FIXME: This *should* pause the AiEscort package instead of terminating it. + // Check if actor is near the border of an inactive cell. If so, pause walking. if(sideX * (pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE / 2.0 - 200)) { MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; - return true; + return false; } } if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY) { int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY); - // Check if actor is near the border of an inactive cell. If so, disable AiEscort. - // FIXME: This *should* pause the AiEscort package instead of terminating it. + // Check if actor is near the border of an inactive cell. If so, pause walking. if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE / 2.0 - 200)) { MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; - return true; + return false; } } @@ -151,8 +151,7 @@ namespace MWMechanics if(distanceBetweenResult <= mMaxDist * mMaxDist) { float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - // TODO: use movement settings instead of rotating directly - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + zTurn(actor, Ogre::Degree(zAngle)); MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; mMaxDist = 470; } diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 10bff8356..cf5291fd3 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -6,15 +6,17 @@ #include "movement.hpp" #include - + +#include "steering.hpp" + MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) : mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), mTimer(0), mStuckTimer(0) -{ -} -MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) -: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), mTimer(0), mStuckTimer(0) -{ -} +{ +} +MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) +: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), mTimer(0), mStuckTimer(0) +{ +} bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) { @@ -45,14 +47,14 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) } } - ESM::Pathgrid::Point dest; - dest.mX = target.getRefData().getPosition().pos[0]; - dest.mY = target.getRefData().getPosition().pos[1]; + ESM::Pathgrid::Point dest; + dest.mX = target.getRefData().getPosition().pos[0]; + dest.mY = target.getRefData().getPosition().pos[1]; dest.mZ = target.getRefData().getPosition().pos[2]; - ESM::Pathgrid::Point start; - start.mX = pos.pos[0]; - start.mY = pos.pos[1]; + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; start.mZ = pos.pos[2]; if(mPathFinder.getPath().empty()) @@ -88,18 +90,14 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) if(!mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2])) { - float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - //MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); - MWWorld::Class::get(actor).getMovementSettings(actor).mRotation[2] = 10*(Ogre::Degree(zAngle).valueRadians()-pos.rot[2]); - //std::cout << Ogre::Degree(zAngle).valueDegrees()-Ogre::Radian(actor.getRefData().getPosition().rot[2]).valueDegrees() << " "<< pos.rot[2] << " " << zAngle << "\n"; - //MWWorld::Class::get(actor).get - } - - if((dest.mX - pos.pos[0])*(dest.mX - pos.pos[0])+(dest.mY - pos.pos[1])*(dest.mY - pos.pos[1])+(dest.mZ - pos.pos[2])*(dest.mZ - pos.pos[2]) - < 100*100) - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); + } + + if((dest.mX - pos.pos[0])*(dest.mX - pos.pos[0])+(dest.mY - pos.pos[1])*(dest.mY - pos.pos[1])+(dest.mZ - pos.pos[2])*(dest.mZ - pos.pos[2]) + < 100*100) + actor.getClass().getMovementSettings(actor).mPosition[1] = 0; else - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + actor.getClass().getMovementSettings(actor).mPosition[1] = 1; return false; } @@ -109,12 +107,12 @@ std::string MWMechanics::AiFollow::getFollowedActor() return mActorId; } -MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const -{ - return new AiFollow(*this); -} - - int MWMechanics::AiFollow::getTypeId() const -{ - return TypeIdFollow; +MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const +{ + return new AiFollow(*this); +} + + int MWMechanics::AiFollow::getTypeId() const +{ + return TypeIdFollow; } diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 9a1b98d8f..8a0b2ebd0 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -1,11 +1,12 @@ #include "aitravel.hpp" -#include "movement.hpp" - #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" +#include "steering.hpp" +#include "movement.hpp" + namespace { float sgn(float a) @@ -86,9 +87,7 @@ namespace MWMechanics return true; } - float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - // TODO: use movement settings instead of rotating directly - world->rotateObject(actor, 0, 0, zAngle, false); + zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); movement.mPosition[1] = 1; return false; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index c6ea2ee91..5be604ab1 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -11,6 +11,8 @@ #include "creaturestats.hpp" #include +#include "steering.hpp" + namespace { float sgn(float a) @@ -282,11 +284,6 @@ namespace MWMechanics if(mWalking) { - float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - // TODO: use movement settings instead of rotating directly - world->rotateObject(actor, 0, 0, zAngle, false); - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; - if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) { stopWalking(actor); @@ -294,6 +291,12 @@ namespace MWMechanics mWalking = false; mChooseAction = true; } + else + { + zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); + + actor.getClass().getMovementSettings(actor).mPosition[1] = 1; + } } return false; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index a54f2365d..32a05832c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1111,9 +1111,9 @@ void CharacterController::update(float duration) if (!mSkipAnim) { + rot *= Ogre::Math::RadiansToDegrees(1.0f); if(mHitState != CharState_KnockDown) { - rot *= duration * Ogre::Math::RadiansToDegrees(1.0f); world->rotateObject(mPtr, rot.x, rot.y, rot.z, true); } else //avoid z-rotating for knockdown diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index a3ac22012..8771ef0ca 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -3,7 +3,6 @@ #include #include -#include namespace MWWorld { @@ -26,8 +25,10 @@ namespace MWMechanics bool checkPathCompleted(float x, float y, float z); ///< \Returns true if the last point of the path has been reached. + bool checkWaypoint(float x, float y, float z); ///< \Returns true if a way point was reached + float getZAngleToNext(float x, float y) const; float getDistToNext(float x, float y, float z); diff --git a/apps/openmw/mwmechanics/steering.cpp b/apps/openmw/mwmechanics/steering.cpp new file mode 100644 index 000000000..d911fd81b --- /dev/null +++ b/apps/openmw/mwmechanics/steering.cpp @@ -0,0 +1,43 @@ +#include "steering.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/ptr.hpp" + +#include "../mwbase/environment.hpp" + +#include "movement.hpp" + +namespace MWMechanics +{ + +bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle) +{ + Ogre::Radian currentAngle (actor.getRefData().getPosition().rot[2]); + Ogre::Radian diff (targetAngle - currentAngle); + if (diff >= Ogre::Degree(180)) + { + // Turning the other way would be a better idea + diff = diff-Ogre::Degree(360); + } + else if (diff <= Ogre::Degree(-180)) + { + diff = Ogre::Degree(360)-diff; + } + Ogre::Radian absDiff = Ogre::Math::Abs(diff); + + // The turning animation actually moves you slightly, so the angle will be wrong again. + // Use epsilon to prevent jerkiness. + const Ogre::Degree epsilon (0.5); + if (absDiff < epsilon) + return true; + + // Max. speed of 10 radian per sec + Ogre::Radian limit = Ogre::Radian(10) * MWBase::Environment::get().getFrameDuration(); + if (absDiff > limit) + diff = Ogre::Math::Sign(diff) * limit; + + actor.getClass().getMovementSettings(actor).mRotation[2] = diff.valueRadians(); + return false; +} + +} diff --git a/apps/openmw/mwmechanics/steering.hpp b/apps/openmw/mwmechanics/steering.hpp new file mode 100644 index 000000000..504dc3ac3 --- /dev/null +++ b/apps/openmw/mwmechanics/steering.hpp @@ -0,0 +1,19 @@ +#ifndef OPENMW_MECHANICS_STEERING_H + +#include + +namespace MWWorld +{ +class Ptr; +} + +namespace MWMechanics +{ + +/// configure rotation settings for an actor to reach this target angle (eventually) +/// @return have we reached the target angle? +bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle); + +} + +#endif From 52f89e9a1754bc7a61d2df32e0bc61bc5803d090 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 29 Jan 2014 20:50:40 +0100 Subject: [PATCH 714/889] Don't play "idle" voice entries for NPCs with Hello 0 Not entirely sure if this is correct, but it prevents the NPCs in the starting boat from incorrectly playing them. --- apps/openmw/mwmechanics/aiwander.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 5be604ab1..77316fedf 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -191,15 +191,18 @@ namespace MWMechanics mIdleNow = true; // Play idle voiced dialogue entries randomly - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - float chance = store.get().find("fVoiceIdleOdds")->getFloat(); - int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + int hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified(); + if (hello > 0) + { + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + float chance = store.get().find("fVoiceIdleOdds")->getFloat(); + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - - // Don't bother if the player is out of hearing range - if (roll < chance && Ogre::Vector3(player.getRefData().getPosition().pos).distance(Ogre::Vector3(actor.getRefData().getPosition().pos)) < 1500) - MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); + // Don't bother if the player is out of hearing range + if (roll < chance && Ogre::Vector3(player.getRefData().getPosition().pos).distance(Ogre::Vector3(actor.getRefData().getPosition().pos)) < 1500) + MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); + } } } @@ -208,7 +211,7 @@ namespace MWMechanics // Play a random voice greeting if the player gets too close const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - float hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified(); + int hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified(); float helloDistance = hello; int iGreetDistanceMultiplier = store.get().find("iGreetDistanceMultiplier")->getInt(); helloDistance *= iGreetDistanceMultiplier; From d0c6ecd03ad8440f9fc5390a4c036ecdeaab5d2d Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 29 Jan 2014 21:04:52 +0100 Subject: [PATCH 715/889] Bug #1148: Workaround for broken images in some MW versions --- apps/openmw/mwgui/formatting.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index bd75c078c..4d3d04ced 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -12,6 +12,8 @@ #include #include +#include + namespace { int convertFromHex(std::string hex) @@ -288,6 +290,16 @@ namespace MWGui MyGUI::ImageBox* box = mParent->createWidget ("ImageBox", MyGUI::IntCoord(0, mHeight, width, height), MyGUI::Align::Left | MyGUI::Align::Top, mParent->getName() + boost::lexical_cast(mParent->getChildCount())); + + // Apparently a bug with some morrowind versions, they reference the image without the size suffix. + // So if the image isn't found, try appending the size. + if (!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("bookart\\"+image)) + { + std::stringstream str; + str << image.substr(0, image.rfind(".")) << "_" << width << "_" << height << image.substr(image.rfind(".")); + image = str.str(); + } + box->setImageTexture("bookart\\" + image); box->setProperty("NeedMouse", "false"); } From 7820ea5806f95065dbbb02cda5873203c4228004 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 30 Jan 2014 03:14:22 +0100 Subject: [PATCH 716/889] "Always best attack" should only affect the player --- apps/openmw/mwmechanics/character.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 32a05832c..63f363045 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -691,7 +691,8 @@ bool CharacterController::updateWeaponState() mAttackType = "shoot"; else { - if(isWeapon && Settings::Manager::getBool("best attack", "Game")) + if(isWeapon && mPtr.getRefData().getHandle() == "player" && + Settings::Manager::getBool("best attack", "Game")) mAttackType = getBestAttack(weapon->get()->mBase); else determineAttackType(); From 067c2bc2ec0d67fe3d05afba72d9c7f047944935 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 30 Jan 2014 03:18:34 +0100 Subject: [PATCH 717/889] Disable combat movements for creatures without weapons --- apps/openmw/mwmechanics/aicombat.cpp | 7 +++++-- apps/openmw/mwmechanics/character.cpp | 18 ++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index cf08dabf8..2ff7ec229 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -204,7 +204,10 @@ namespace MWMechanics { //Melee: stop running and attack mMovement.mPosition[1] = 0; - chooseBestAttack(weapon, mMovement); + + // When attacking with a weapon, choose between slash, thrust or chop + if (actor.getClass().hasInventoryStore(actor)) + chooseBestAttack(weapon, mMovement); if(mMovement.mPosition[0] || mMovement.mPosition[1]) { @@ -365,7 +368,7 @@ void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement { if (weapon == NULL) { - //hand-to-hand and creatures' attacks deal equal damage for each type + //hand-to-hand deal equal damage for each type float roll = static_cast(rand())/RAND_MAX; if(roll <= 0.333f) //side punch { diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 63f363045..74cda0a70 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -478,7 +478,14 @@ bool CharacterController::updateCreatureState() { MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); - determineAttackType(); + // These are unique animations and not linked to movement type. Just pick one randomly. + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 3; // [0, 2] + if (roll == 0) + mCurrentWeapon = "attack1"; + else if (roll == 1) + mCurrentWeapon = "attack2"; + else + mCurrentWeapon = "attack3"; mAnimation->play(mCurrentWeapon, Priority_Weapon, MWRender::Animation::Group_All, true, @@ -1308,15 +1315,6 @@ void CharacterController::determineAttackType() else mAttackType = "chop"; } - else - { - if (move[0] && !move[1]) //sideway - mCurrentWeapon = "attack2"; - else if (move[1]) //forward - mCurrentWeapon = "attack3"; - else - mCurrentWeapon = "attack1"; - } } } From da34f8bda129df330cd58acd553023bf89d7f3e5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 31 Jan 2014 00:15:59 +0100 Subject: [PATCH 718/889] Fix LastHitObject --- apps/openmw/mwmechanics/actors.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index b80fa9d7c..02adf3c16 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -776,12 +776,19 @@ namespace MWMechanics { if (!paused) { + for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) + { + // Reset last hit object, which is only valid for one frame + // Note, the new hit object for this frame may be set by CharacterController::update -> Animation::runAnimation + // (below) + iter->first.getClass().getCreatureStats(iter->first).setLastHitObject(std::string()); + } + for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++) { const MWWorld::Class &cls = MWWorld::Class::get(iter->first); CreatureStats &stats = cls.getCreatureStats(iter->first); - stats.setLastHitObject(std::string()); if(!stats.isDead()) { if(iter->second->isDead()) From 305a471c5ae72a68269c7dd104699587914e8bce Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 31 Jan 2014 01:17:51 +0100 Subject: [PATCH 719/889] Fix VisController in ascended sleeper's death animation --- components/nifogre/ogrenifloader.cpp | 38 ++++++++++++++-------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index d036844fc..9fca4d2fb 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -311,25 +311,26 @@ public: static void setVisible(Ogre::Node *node, int vis) { + // Skinned meshes are attached to the scene node, not the bone. + // We use the Node's user data to connect it with the mesh. + Ogre::Any customData = node->getUserObjectBindings().getUserAny(); + + if (!customData.isEmpty()) + Ogre::any_cast(customData)->setVisible(vis); + + Ogre::TagPoint *tag = dynamic_cast(node); + if(tag != NULL) + { + Ogre::MovableObject *obj = tag->getChildObject(); + if(obj != NULL) + obj->setVisible(vis); + } + Ogre::Node::ChildNodeIterator iter = node->getChildIterator(); while(iter.hasMoreElements()) { node = iter.getNext(); setVisible(node, vis); - - // Skinned meshes and particle systems are attached to the scene node, not the bone. - // We use the Node's user data to connect it with the mesh / particle system. - Ogre::Any customData = node->getUserObjectBindings().getUserAny(); - if (!customData.isEmpty()) - Ogre::any_cast(customData)->setVisible(vis); - - Ogre::TagPoint *tag = dynamic_cast(node); - if(tag != NULL) - { - Ogre::MovableObject *obj = tag->getChildObject(); - if(obj != NULL) - obj->setVisible(vis); - } } } @@ -622,15 +623,14 @@ class NIFObjectLoader scene->mEntities.push_back(entity); if(scene->mSkelBase) { + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex); + Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); + trgtbone->getUserObjectBindings().setUserAny(Ogre::Any(static_cast(entity))); + if(entity->hasSkeleton()) entity->shareSkeletonInstanceWith(scene->mSkelBase); else - { - int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex); - Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); - trgtbone->getUserObjectBindings().setUserAny(Ogre::Any(static_cast(entity))); scene->mSkelBase->attachObjectToBone(trgtbone->getName(), entity); - } } Nif::ControllerPtr ctrl = node->controller; From 60bbab52fecf580bf18db0c173de848a6d829c85 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 31 Jan 2014 03:23:47 +0100 Subject: [PATCH 720/889] Support keyframe controllers for bones that aren't in the skeleton base --- apps/openmw/mwrender/animation.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 3f45ce769..ba729e3da 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -532,12 +532,6 @@ static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bo bone->setScale(Ogre::Vector3::UNIT_SCALE); } } - else - { - // No matching bone in the source. Make sure it stays properly offset - // from its parent. - bone->resetToInitialState(); - } Ogre::Node::ChildNodeIterator boneiter = bone->getChildIterator(); while(boneiter.hasMoreElements()) From 5b300c1052d541bf05bde80319f1361c694a4b00 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 31 Jan 2014 03:24:09 +0100 Subject: [PATCH 721/889] Handle controllers for the root node. Fixes #1147 (incorrect dwemer crossbow rotation) --- components/nifogre/ogrenifloader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 9fca4d2fb..644a6df6b 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -1034,7 +1034,7 @@ class NIFObjectLoader e = e->extra; } - if(!node->controller.empty() && (node->parent || node->recType != Nif::RC_NiNode)) + if(!node->controller.empty()) createNodeControllers(name, node->controller, scene, animflags); if(node->recType == Nif::RC_NiCamera) From 4ec86d1c68accd42d0c24b23ee7d50fbafba4c73 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 31 Jan 2014 03:28:18 +0100 Subject: [PATCH 722/889] Bug #1147: Fix the inventory preview pose used for ranged weapons --- apps/openmw/mwrender/characterpreview.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 32145928e..08749fee7 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -160,11 +160,14 @@ namespace MWRender if(type == ESM::Weapon::ShortBladeOneHand || type == ESM::Weapon::LongBladeOneHand || type == ESM::Weapon::BluntOneHand || - type == ESM::Weapon::AxeOneHand) + type == ESM::Weapon::AxeOneHand || + type == ESM::Weapon::MarksmanThrown) groupname = "inventoryweapononehand"; else if(type == ESM::Weapon::LongBladeTwoHand || type == ESM::Weapon::BluntTwoClose || - type == ESM::Weapon::AxeTwoHand) + type == ESM::Weapon::AxeTwoHand || + type == ESM::Weapon::MarksmanCrossbow || + type == ESM::Weapon::MarksmanBow) groupname = "inventoryweapontwohand"; else if(type == ESM::Weapon::BluntTwoWide || type == ESM::Weapon::SpearTwoWide) From bbd15b185d5df067e1a64429f977392642b02ca9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 31 Jan 2014 05:59:58 +0100 Subject: [PATCH 723/889] Skip inactive controllers. Fixes those bloody bone boots again. --- components/nif/node.hpp | 3 + components/nifogre/mesh.cpp | 2 +- components/nifogre/ogrenifloader.cpp | 105 +++++++++++++++------------ components/nifogre/skeleton.cpp | 2 +- 4 files changed, 62 insertions(+), 50 deletions(-) diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 6816a79a2..eebcd8be8 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -140,6 +140,9 @@ struct NiNode : Node ParticleFlag_AutoPlay = 0x0020, ParticleFlag_LocalSpace = 0x0080 }; + enum ControllerFlags { + ControllerFlag_Active = 0x8 + }; void read(NIFStream *nif) { diff --git a/components/nifogre/mesh.cpp b/components/nifogre/mesh.cpp index 80e377a49..43622cb9a 100644 --- a/components/nifogre/mesh.cpp +++ b/components/nifogre/mesh.cpp @@ -341,7 +341,7 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape Nif::ControllerPtr ctrl = shape->controller; do { // Load GeomMorpherController into an Ogre::Pose and Animation - if(ctrl->recType == Nif::RC_NiGeomMorpherController) + if(ctrl->recType == Nif::RC_NiGeomMorpherController && ctrl->flags & Nif::NiNode::ControllerFlag_Active) { const Nif::NiGeomMorpherController *geom = static_cast(ctrl.getPtr()); diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 644a6df6b..4f755a85a 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -636,36 +636,39 @@ class NIFObjectLoader Nif::ControllerPtr ctrl = node->controller; while(!ctrl.empty()) { - if(ctrl->recType == Nif::RC_NiUVController) + if (ctrl->flags & Nif::NiNode::ControllerFlag_Active) { - const Nif::NiUVController *uv = static_cast(ctrl.getPtr()); + if(ctrl->recType == Nif::RC_NiUVController) + { + const Nif::NiUVController *uv = static_cast(ctrl.getPtr()); - Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? - Ogre::ControllerManager::getSingleton().getFrameTimeSource() : - Ogre::ControllerValueRealPtr()); - Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(entity, uv->data.getPtr(), &scene->mMaterialControllerMgr)); + Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(entity, uv->data.getPtr(), &scene->mMaterialControllerMgr)); - UVController::Function* function = OGRE_NEW UVController::Function(uv, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); - scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); - Ogre::ControllerFunctionRealPtr func(function); + UVController::Function* function = OGRE_NEW UVController::Function(uv, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); + scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); + Ogre::ControllerFunctionRealPtr func(function); - scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); - } - else if(ctrl->recType == Nif::RC_NiGeomMorpherController) - { - const Nif::NiGeomMorpherController *geom = static_cast(ctrl.getPtr()); + scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } + else if(ctrl->recType == Nif::RC_NiGeomMorpherController) + { + const Nif::NiGeomMorpherController *geom = static_cast(ctrl.getPtr()); - Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? - Ogre::ControllerManager::getSingleton().getFrameTimeSource() : - Ogre::ControllerValueRealPtr()); - Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value( - entity, geom->data.getPtr(), geom->recIndex)); + Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value( + entity, geom->data.getPtr(), geom->recIndex)); - GeomMorpherController::Function* function = OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); - scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); - Ogre::ControllerFunctionRealPtr func(function); + GeomMorpherController::Function* function = OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); + scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); + Ogre::ControllerFunctionRealPtr func(function); - scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } } ctrl = ctrl->next; } @@ -852,7 +855,7 @@ class NIFObjectLoader Nif::ControllerPtr ctrl = partnode->controller; while(!ctrl.empty()) { - if(ctrl->recType == Nif::RC_NiParticleSystemController) + if(ctrl->recType == Nif::RC_NiParticleSystemController && ctrl->flags & Nif::NiNode::ControllerFlag_Active) { const Nif::NiParticleSystemController *partctrl = static_cast(ctrl.getPtr()); @@ -893,42 +896,45 @@ class NIFObjectLoader static void createNodeControllers(const std::string &name, Nif::ControllerPtr ctrl, ObjectScenePtr scene, int animflags) { do { - if(ctrl->recType == Nif::RC_NiVisController) + if (ctrl->flags & Nif::NiNode::ControllerFlag_Active) { - const Nif::NiVisController *vis = static_cast(ctrl.getPtr()); - - int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); - Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); - Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? - Ogre::ControllerManager::getSingleton().getFrameTimeSource() : - Ogre::ControllerValueRealPtr()); - Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr())); - - VisController::Function* function = OGRE_NEW VisController::Function(vis, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); - scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); - Ogre::ControllerFunctionRealPtr func(function); - - scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); - } - else if(ctrl->recType == Nif::RC_NiKeyframeController) - { - const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); - if(!key->data.empty()) + if(ctrl->recType == Nif::RC_NiVisController) { + const Nif::NiVisController *vis = static_cast(ctrl.getPtr()); + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); - // The keyframe controller will control this bone manually - trgtbone->setManuallyControlled(true); Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : Ogre::ControllerValueRealPtr()); - Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr())); - KeyframeController::Function* function = OGRE_NEW KeyframeController::Function(key, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr())); + + VisController::Function* function = OGRE_NEW VisController::Function(vis, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); Ogre::ControllerFunctionRealPtr func(function); scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } + else if(ctrl->recType == Nif::RC_NiKeyframeController) + { + const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); + if(!key->data.empty()) + { + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); + Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); + // The keyframe controller will control this bone manually + trgtbone->setManuallyControlled(true); + Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr())); + KeyframeController::Function* function = OGRE_NEW KeyframeController::Function(key, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); + scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); + Ogre::ControllerFunctionRealPtr func(function); + + scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } + } } ctrl = ctrl->next; } while(!ctrl.empty()); @@ -1151,6 +1157,9 @@ public: continue; } + if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) + continue; + const Nif::NiStringExtraData *strdata = static_cast(extra.getPtr()); const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp index e01ae22ef..c0482cf5e 100644 --- a/components/nifogre/skeleton.cpp +++ b/components/nifogre/skeleton.cpp @@ -97,7 +97,7 @@ bool NIFSkeletonLoader::needSkeleton(const Nif::Node *node) { Nif::ControllerPtr ctrl = node->controller; do { - if(ctrl->recType == Nif::RC_NiKeyframeController) + if(ctrl->recType == Nif::RC_NiKeyframeController && ctrl->flags & Nif::NiNode::ControllerFlag_Active) return true; } while(!(ctrl=ctrl->next).empty()); } From dd674566a2aa85b4ed62dc4438777a97868e7ec8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 31 Jan 2014 13:25:32 +0100 Subject: [PATCH 724/889] store content of containers in saved game files --- apps/openmw/mwclass/container.cpp | 23 ++++++ apps/openmw/mwclass/container.hpp | 8 ++ apps/openmw/mwworld/cellstore.cpp | 5 +- apps/openmw/mwworld/containerstore.cpp | 100 +++++++++++++++++++++++++ apps/openmw/mwworld/containerstore.hpp | 9 ++- apps/openmw/mwworld/livecellref.hpp | 12 ++- apps/openmw/mwworld/ptr.cpp | 8 ++ apps/openmw/mwworld/ptr.hpp | 2 + components/CMakeLists.txt | 2 +- components/esm/containerstate.cpp | 16 ++++ components/esm/containerstate.hpp | 20 +++++ components/esm/inventorystate.cpp | 60 +++++++++++++++ components/esm/inventorystate.hpp | 28 +++++++ components/esm/objectstate.cpp | 4 +- 14 files changed, 288 insertions(+), 9 deletions(-) create mode 100644 components/esm/containerstate.cpp create mode 100644 components/esm/containerstate.hpp create mode 100644 components/esm/inventorystate.cpp create mode 100644 components/esm/inventorystate.hpp diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index f89a6bce0..546d6538c 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -2,6 +2,7 @@ #include "container.hpp" #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -258,4 +259,26 @@ namespace MWClass return MWWorld::Ptr(&cell.mContainers.insert(*ref), &cell); } + + void Container::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) + const + { + const ESM::ContainerState& state2 = dynamic_cast (state); + + ensureCustomData (ptr); + + dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore. + readState (state2.mInventory); + } + + void Container::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + const + { + ESM::ContainerState& state2 = dynamic_cast (state); + + ensureCustomData (ptr); + + dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore. + writeState (state2.mInventory); + } } diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp index 006e4bd22..c97867d35 100644 --- a/apps/openmw/mwclass/container.hpp +++ b/apps/openmw/mwclass/container.hpp @@ -54,6 +54,14 @@ namespace MWClass virtual void unlock (const MWWorld::Ptr& ptr) const; ///< Unlock object + virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) + const; + ///< Read additional state from \a state into \a ptr. + + virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + const; + ///< Write additional state from \a ptr into \a state. + static void registerSelf(); virtual std::string getModel(const MWWorld::Ptr &ptr) const; diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 77fdc971d..154a2d1e7 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -326,7 +327,7 @@ namespace MWWorld writeReferenceCollection (writer, mArmors); writeReferenceCollection (writer, mBooks); writeReferenceCollection (writer, mClothes); - writeReferenceCollection (writer, mContainers); + writeReferenceCollection (writer, mContainers); writeReferenceCollection (writer, mCreatures); writeReferenceCollection (writer, mDoors); writeReferenceCollection (writer, mIngreds); @@ -384,7 +385,7 @@ namespace MWWorld case ESM::REC_CONT: - readReferenceCollection (reader, mContainers, contentFileMap); + readReferenceCollection (reader, mContainers, contentFileMap); break; case ESM::REC_CREA: diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 0c4226f9b..2d5a9bbd3 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -5,6 +5,8 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -55,6 +57,43 @@ namespace return MWWorld::Ptr(); } + + template + void getState (MWWorld::CellRefList& collection, const ESM::ObjectState& state) + { + if (!MWWorld::LiveCellRef::checkState (state)) + return; // not valid anymore with current content files -> skip + + const T *record = MWBase::Environment::get().getWorld()->getStore(). + get().search (state.mRef.mRefID); + + if (!record) + return; + + MWWorld::LiveCellRef ref (record); + ref.load (state); + ref.mRef.mRefNum.mContentFile = -1; + collection.mList.push_back (ref); + } + + template + void storeState (const MWWorld::LiveCellRef& ref, ESM::ObjectState& state) + { + ref.save (state); + } + + template + void storeStates (const MWWorld::CellRefList& collection, + std::vector > >& states) + { + for (typename MWWorld::CellRefList::List::const_iterator iter (collection.mList.begin()); + iter!=collection.mList.end(); ++iter) + { + ESM::ObjectState state; + storeState (*iter, state); + states.push_back (std::make_pair (state, std::make_pair (T::sRecordId, -1))); + } + } } const std::string MWWorld::ContainerStore::sGoldId = "gold_001"; @@ -495,6 +534,67 @@ MWWorld::Ptr MWWorld::ContainerStore::search (const std::string& id) return Ptr(); } +void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const +{ + state.mItems.clear(); + + storeStates (potions, state.mItems); + storeStates (appas, state.mItems); + storeStates (armors, state.mItems); + storeStates (books, state.mItems); + storeStates (clothes, state.mItems); + storeStates (ingreds, state.mItems); + storeStates (lockpicks, state.mItems); + storeStates (miscItems, state.mItems); + storeStates (probes, state.mItems); + storeStates (repairs, state.mItems); + storeStates (weapons, state.mItems); + + state.mLights.clear(); + + for (MWWorld::CellRefList::List::const_iterator iter (lights.mList.begin()); + iter!=lights.mList.end(); ++iter) + { + ESM::LightState objectState; + storeState (*iter, objectState); + state.mLights.push_back (std::make_pair (objectState, -1)); + } +} + +void MWWorld::ContainerStore::readState (const ESM::InventoryState& state) +{ + clear(); + + for (std::vector > >::const_iterator + iter (state.mItems.begin()); iter!=state.mItems.end(); ++iter) + { + switch (iter->second.first) + { + case ESM::REC_ALCH: getState (potions, iter->first); break; + case ESM::REC_APPA: getState (appas, iter->first); break; + case ESM::REC_ARMO: getState (armors, iter->first); break; + case ESM::REC_BOOK: getState (books, iter->first); break; + case ESM::REC_CLOT: getState (clothes, iter->first); break; + case ESM::REC_INGR: getState (ingreds, iter->first); break; + case ESM::REC_LOCK: getState (lockpicks, iter->first); break; + case ESM::REC_MISC: getState (miscItems, iter->first); break; + case ESM::REC_PROB: getState (probes, iter->first); break; + case ESM::REC_REPA: getState (repairs, iter->first); break; + case ESM::REC_WEAP: getState (weapons, iter->first); break; + + default: + + std::cerr << "invalid item type in inventory state" << std::endl; + } + } + + for (std::vector >::const_iterator iter (state.mLights.begin()); + iter!=state.mLights.end(); ++iter) + { + getState (lights, iter->first); + } +} + MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container) : mType (-1), mMask (0), mContainer (container) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 0a1728740..3bdefb1ec 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -8,6 +8,7 @@ namespace ESM { struct InventoryList; + struct InventoryState; } namespace MWWorld @@ -123,6 +124,10 @@ namespace MWWorld Ptr search (const std::string& id); + void writeState (ESM::InventoryState& state) const; + + void readState (const ESM::InventoryState& state); + friend class ContainerStoreIterator; }; @@ -172,7 +177,7 @@ namespace MWWorld ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); - void copy (const ContainerStoreIterator& src); + void copy (const ContainerStoreIterator& src); void incType(); @@ -200,7 +205,7 @@ namespace MWWorld ContainerStoreIterator operator++ (int); - ContainerStoreIterator& operator= (const ContainerStoreIterator& rhs); + ContainerStoreIterator& operator= (const ContainerStoreIterator& rhs); bool isEqual (const ContainerStoreIterator& iter) const; diff --git a/apps/openmw/mwworld/livecellref.hpp b/apps/openmw/mwworld/livecellref.hpp index 46f49df78..b2089fa7a 100644 --- a/apps/openmw/mwworld/livecellref.hpp +++ b/apps/openmw/mwworld/livecellref.hpp @@ -35,6 +35,14 @@ namespace MWWorld /* Need this for the class to be recognized as polymorphic */ virtual ~LiveCellRefBase() { } + virtual void load (const ESM::ObjectState& state) = 0; + ///< Load state into a LiveCellRef, that has already been initialised with base and class. + /// + /// \attention Must not be called with an invalid \a state. + + virtual void save (ESM::ObjectState& state) const = 0; + ///< Save LiveCellRef state into \a state. + protected: void loadImp (const ESM::ObjectState& state); @@ -79,12 +87,12 @@ namespace MWWorld // The object that this instance is based on. const X* mBase; - void load (const ESM::ObjectState& state); + virtual void load (const ESM::ObjectState& state); ///< Load state into a LiveCellRef, that has already been initialised with base and class. /// /// \attention Must not be called with an invalid \a state. - void save (ESM::ObjectState& state) const; + virtual void save (ESM::ObjectState& state) const; ///< Save LiveCellRef state into \a state. static bool checkState (const ESM::ObjectState& state); diff --git a/apps/openmw/mwworld/ptr.cpp b/apps/openmw/mwworld/ptr.cpp index 384bd71b1..67bfe4900 100644 --- a/apps/openmw/mwworld/ptr.cpp +++ b/apps/openmw/mwworld/ptr.cpp @@ -21,6 +21,14 @@ const std::string& MWWorld::Ptr::getTypeName() const throw std::runtime_error("Can't get type name from an empty object."); } +MWWorld::LiveCellRefBase *MWWorld::Ptr::getBase() const +{ + if (!mRef) + throw std::runtime_error ("Can't access cell ref pointed to by null Ptr"); + + return mRef; +} + ESM::CellRef& MWWorld::Ptr::getCellRef() const { assert(mRef); diff --git a/apps/openmw/mwworld/ptr.hpp b/apps/openmw/mwworld/ptr.hpp index 8b70382d0..1212619d0 100644 --- a/apps/openmw/mwworld/ptr.hpp +++ b/apps/openmw/mwworld/ptr.hpp @@ -52,6 +52,8 @@ namespace MWWorld throw std::runtime_error(str.str()); } + MWWorld::LiveCellRefBase *getBase() const; + ESM::CellRef& getCellRef() const; RefData& getRefData() const; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index f37a537c5..854d1f1ae 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -40,7 +40,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate + savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate ) add_component_dir (misc diff --git a/components/esm/containerstate.cpp b/components/esm/containerstate.cpp new file mode 100644 index 000000000..5dcf17733 --- /dev/null +++ b/components/esm/containerstate.cpp @@ -0,0 +1,16 @@ + +#include "containerstate.hpp" + +void ESM::ContainerState::load (ESMReader &esm) +{ + ObjectState::load (esm); + + mInventory.load (esm); +} + +void ESM::ContainerState::save (ESMWriter &esm, bool inInventory) const +{ + ObjectState::save (esm, inInventory); + + mInventory.save (esm); +} \ No newline at end of file diff --git a/components/esm/containerstate.hpp b/components/esm/containerstate.hpp new file mode 100644 index 000000000..1ecf2b46e --- /dev/null +++ b/components/esm/containerstate.hpp @@ -0,0 +1,20 @@ +#ifndef OPENMW_ESM_CONTAINERSTATE_H +#define OPENMW_ESM_CONTAINERSTATE_H + +#include "objectstate.hpp" +#include "inventorystate.hpp" + +namespace ESM +{ + // format 0, saved games only + + struct ContainerState : public ObjectState + { + InventoryState mInventory; + + virtual void load (ESMReader &esm); + virtual void save (ESMWriter &esm, bool inInventory = false) const; + }; +} + +#endif diff --git a/components/esm/inventorystate.cpp b/components/esm/inventorystate.cpp new file mode 100644 index 000000000..4d8cbc622 --- /dev/null +++ b/components/esm/inventorystate.cpp @@ -0,0 +1,60 @@ + +#include "inventorystate.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +namespace +{ + void read (ESM::ESMReader &esm, ESM::ObjectState& state, int& slot) + { + slot = -1; + esm.getHNOT (slot, "SLOT"); + + state.load (esm); + } + + void write (ESM::ESMWriter &esm, const ESM::ObjectState& state, unsigned int type, int slot) + { + esm.writeHNT ("IOBJ", type); + + if (slot!=-1) + esm.writeHNT ("SLOT", slot); + + state.save (esm, true); + } +} + +void ESM::InventoryState::load (ESMReader &esm) +{ + while (esm.isNextSub ("IOBJ")) + { + unsigned int id = 0; + esm.getHT (id); + + if (id==ESM::REC_LIGH) + { + LightState state; + int slot; + read (esm, state, slot); + mLights.push_back (std::make_pair (state, slot)); + } + else + { + ObjectState state; + int slot; + read (esm, state, slot); + mItems.push_back (std::make_pair (state, std::make_pair (id, slot))); + } + } +} + +void ESM::InventoryState::save (ESMWriter &esm) const +{ + for (std::vector > >::const_iterator iter (mItems.begin()); iter!=mItems.end(); ++iter) + write (esm, iter->first, iter->second.first, iter->second.second); + + for (std::vector >::const_iterator iter (mLights.begin()); + iter!=mLights.end(); ++iter) + write (esm, iter->first, ESM::REC_LIGH, iter->second); +} \ No newline at end of file diff --git a/components/esm/inventorystate.hpp b/components/esm/inventorystate.hpp new file mode 100644 index 000000000..3cfffbccc --- /dev/null +++ b/components/esm/inventorystate.hpp @@ -0,0 +1,28 @@ +#ifndef OPENMW_ESM_INVENTORYSTATE_H +#define OPENMW_ESM_INVENTORYSTATE_H + +#include "objectstate.hpp" +#include "lightstate.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + /// \brief State for inventories and containers + struct InventoryState + { + // anything but lights (type, slot) + std::vector > > mItems; + + // lights (slot) + std::vector > mLights; + + virtual void load (ESMReader &esm); + virtual void save (ESMWriter &esm) const; + }; +} + +#endif diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index 6aa820599..be00f3ef6 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -20,9 +20,9 @@ void ESM::ObjectState::load (ESMReader &esm) mCount = 1; esm.getHNOT (mCount, "COUN"); - esm.getHNT (mPosition, "POS_", 24); + esm.getHNOT (mPosition, "POS_", 24); - esm.getHNT (mLocalRotation, "LROT", 12); + esm.getHNOT (mLocalRotation, "LROT", 12); } void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const From bcc5894e2d820b7497f2381ddef8af7da61cf836 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 1 Feb 2014 15:24:01 +0100 Subject: [PATCH 725/889] changed implementation functions for container serialisation from free functions to member functions (will need some polymorphism later) --- apps/openmw/mwworld/containerstore.cpp | 69 +++++++++++++------------- apps/openmw/mwworld/containerstore.hpp | 11 ++++ 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 2d5a9bbd3..7d12c7b6e 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -57,42 +57,43 @@ namespace return MWWorld::Ptr(); } +} - template - void getState (MWWorld::CellRefList& collection, const ESM::ObjectState& state) +template +void MWWorld::ContainerStore::getState (CellRefList& collection, const ESM::ObjectState& state) +{ + if (!LiveCellRef::checkState (state)) + return; // not valid anymore with current content files -> skip + + const T *record = MWBase::Environment::get().getWorld()->getStore(). + get().search (state.mRef.mRefID); + + if (!record) + return; + + LiveCellRef ref (record); + ref.load (state); + ref.mRef.mRefNum.mContentFile = -1; + collection.mList.push_back (ref); +} + +template +void MWWorld::ContainerStore::storeState (const LiveCellRef& ref, ESM::ObjectState& state) + const +{ + ref.save (state); +} + +template +void MWWorld::ContainerStore::storeStates (const CellRefList& collection, + std::vector > >& states) const +{ + for (typename CellRefList::List::const_iterator iter (collection.mList.begin()); + iter!=collection.mList.end(); ++iter) { - if (!MWWorld::LiveCellRef::checkState (state)) - return; // not valid anymore with current content files -> skip - - const T *record = MWBase::Environment::get().getWorld()->getStore(). - get().search (state.mRef.mRefID); - - if (!record) - return; - - MWWorld::LiveCellRef ref (record); - ref.load (state); - ref.mRef.mRefNum.mContentFile = -1; - collection.mList.push_back (ref); - } - - template - void storeState (const MWWorld::LiveCellRef& ref, ESM::ObjectState& state) - { - ref.save (state); - } - - template - void storeStates (const MWWorld::CellRefList& collection, - std::vector > >& states) - { - for (typename MWWorld::CellRefList::List::const_iterator iter (collection.mList.begin()); - iter!=collection.mList.end(); ++iter) - { - ESM::ObjectState state; - storeState (*iter, state); - states.push_back (std::make_pair (state, std::make_pair (T::sRecordId, -1))); - } + ESM::ObjectState state; + storeState (*iter, state); + states.push_back (std::make_pair (state, std::make_pair (T::sRecordId, -1))); } } diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 3bdefb1ec..5e305d408 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -57,6 +57,17 @@ namespace MWWorld ContainerStoreIterator addImp (const Ptr& ptr, int count); void addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, int count, bool topLevel=true); + template + void getState (CellRefList& collection, const ESM::ObjectState& state); + + template + void storeState (const LiveCellRef& ref, ESM::ObjectState& state) const; + + template + void storeStates (const CellRefList& collection, + std::vector > >& states) + const; + public: ContainerStore(); From d2ec3ffdc89f55eb496eaf4623ad04a879637412 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 1 Feb 2014 17:07:08 +0100 Subject: [PATCH 726/889] handle equipped items when serialising inventory state --- apps/openmw/mwworld/containerstore.cpp | 48 ++++++++++++++++---------- apps/openmw/mwworld/containerstore.hpp | 13 +++++-- apps/openmw/mwworld/inventorystore.cpp | 22 ++++++++++++ apps/openmw/mwworld/inventorystore.hpp | 9 +++++ 4 files changed, 71 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 7d12c7b6e..86cf3bbde 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -60,43 +60,53 @@ namespace } template -void MWWorld::ContainerStore::getState (CellRefList& collection, const ESM::ObjectState& state) +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::getState (CellRefList& collection, + const ESM::ObjectState& state) { if (!LiveCellRef::checkState (state)) - return; // not valid anymore with current content files -> skip + return ContainerStoreIterator (this); // not valid anymore with current content files -> skip const T *record = MWBase::Environment::get().getWorld()->getStore(). get().search (state.mRef.mRefID); if (!record) - return; + return ContainerStoreIterator (this); LiveCellRef ref (record); ref.load (state); ref.mRef.mRefNum.mContentFile = -1; collection.mList.push_back (ref); + + return ContainerStoreIterator (this, --collection.mList.end()); } template -void MWWorld::ContainerStore::storeState (const LiveCellRef& ref, ESM::ObjectState& state) - const +void MWWorld::ContainerStore::storeState (const LiveCellRef& ref, ESM::ObjectState& state) const { ref.save (state); } template void MWWorld::ContainerStore::storeStates (const CellRefList& collection, - std::vector > >& states) const + std::vector > >& states, bool equipable) const { for (typename CellRefList::List::const_iterator iter (collection.mList.begin()); iter!=collection.mList.end(); ++iter) { ESM::ObjectState state; storeState (*iter, state); - states.push_back (std::make_pair (state, std::make_pair (T::sRecordId, -1))); + int slot = equipable ? getSlot (*iter) : -1; + states.push_back (std::make_pair (state, std::make_pair (T::sRecordId, slot))); } } +int MWWorld::ContainerStore::getSlot (const MWWorld::LiveCellRefBase& ref) const +{ + return -1; +} + +void MWWorld::ContainerStore::setSlot (const MWWorld::ContainerStoreIterator& iter, int slot) {} + const std::string MWWorld::ContainerStore::sGoldId = "gold_001"; MWWorld::ContainerStore::ContainerStore() : mCachedWeight (0), mWeightUpToDate (false) {} @@ -541,15 +551,15 @@ void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const storeStates (potions, state.mItems); storeStates (appas, state.mItems); - storeStates (armors, state.mItems); + storeStates (armors, state.mItems, true); storeStates (books, state.mItems); - storeStates (clothes, state.mItems); + storeStates (clothes, state.mItems, true); storeStates (ingreds, state.mItems); - storeStates (lockpicks, state.mItems); + storeStates (lockpicks, state.mItems, true); storeStates (miscItems, state.mItems); - storeStates (probes, state.mItems); + storeStates (probes, state.mItems, true); storeStates (repairs, state.mItems); - storeStates (weapons, state.mItems); + storeStates (weapons, state.mItems, true); state.mLights.clear(); @@ -558,7 +568,7 @@ void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const { ESM::LightState objectState; storeState (*iter, objectState); - state.mLights.push_back (std::make_pair (objectState, -1)); + state.mLights.push_back (std::make_pair (objectState, getSlot (*iter))); } } @@ -569,19 +579,21 @@ void MWWorld::ContainerStore::readState (const ESM::InventoryState& state) for (std::vector > >::const_iterator iter (state.mItems.begin()); iter!=state.mItems.end(); ++iter) { + int slot = iter->second.second; + switch (iter->second.first) { case ESM::REC_ALCH: getState (potions, iter->first); break; case ESM::REC_APPA: getState (appas, iter->first); break; - case ESM::REC_ARMO: getState (armors, iter->first); break; + case ESM::REC_ARMO: setSlot (getState (armors, iter->first), slot); break; case ESM::REC_BOOK: getState (books, iter->first); break; - case ESM::REC_CLOT: getState (clothes, iter->first); break; + case ESM::REC_CLOT: setSlot (getState (clothes, iter->first), slot); break; case ESM::REC_INGR: getState (ingreds, iter->first); break; - case ESM::REC_LOCK: getState (lockpicks, iter->first); break; + case ESM::REC_LOCK: setSlot (getState (lockpicks, iter->first), slot); break; case ESM::REC_MISC: getState (miscItems, iter->first); break; - case ESM::REC_PROB: getState (probes, iter->first); break; + case ESM::REC_PROB: setSlot (getState (probes, iter->first), slot); break; case ESM::REC_REPA: getState (repairs, iter->first); break; - case ESM::REC_WEAP: getState (weapons, iter->first); break; + case ESM::REC_WEAP: setSlot (getState (weapons, iter->first), slot); break; default: diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 5e305d408..2e01eb856 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -58,15 +58,22 @@ namespace MWWorld void addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, int count, bool topLevel=true); template - void getState (CellRefList& collection, const ESM::ObjectState& state); + ContainerStoreIterator getState (CellRefList& collection, + const ESM::ObjectState& state); template void storeState (const LiveCellRef& ref, ESM::ObjectState& state) const; template void storeStates (const CellRefList& collection, - std::vector > >& states) - const; + std::vector > >& states, + bool equipable = false) const; + + virtual int getSlot (const MWWorld::LiveCellRefBase& ref) const; + ///< Return inventory slot that \a ref is in or -1 (if \a ref is not in a slot). + + virtual void setSlot (const MWWorld::ContainerStoreIterator& iter, int slot); + ///< Set slot for \a iter. Ignored if \a iter is an end iterator or if slot==-1. public: diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 82b827e75..93573b401 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -42,6 +42,21 @@ void MWWorld::InventoryStore::initSlots (TSlots& slots_) slots_.push_back (end()); } +int MWWorld::InventoryStore::getSlot (const MWWorld::LiveCellRefBase& ref) const +{ + for (int i = 0; i (mSlots.size()); ++i) + if (mSlots[i].getType()!=-1 && mSlots[i]->getBase()==&ref) + return i; + + return -1; +} + +void MWWorld::InventoryStore::setSlot (const MWWorld::ContainerStoreIterator& iter, int slot) +{ + if (iter!=end() && slot>=0 && slot Date: Sat, 1 Feb 2014 17:36:23 +0100 Subject: [PATCH 727/889] added creature/NPC state to saved games (only container/inventory for now) --- apps/openmw/mwclass/creature.cpp | 23 ++++++++++++++++++++++ apps/openmw/mwclass/creature.hpp | 10 +++++++++- apps/openmw/mwclass/npc.cpp | 27 ++++++++++++++++++++++++-- apps/openmw/mwclass/npc.hpp | 10 +++++++++- apps/openmw/mwworld/cellstore.cpp | 10 ++++++---- apps/openmw/mwworld/containerstore.hpp | 2 +- components/CMakeLists.txt | 2 +- components/esm/creaturestate.cpp | 16 +++++++++++++++ components/esm/creaturestate.hpp | 20 +++++++++++++++++++ components/esm/npcstate.cpp | 16 +++++++++++++++ components/esm/npcstate.hpp | 20 +++++++++++++++++++ components/esm/player.hpp | 4 ++-- 12 files changed, 148 insertions(+), 12 deletions(-) create mode 100644 components/esm/creaturestate.cpp create mode 100644 components/esm/creaturestate.hpp create mode 100644 components/esm/npcstate.cpp create mode 100644 components/esm/npcstate.hpp diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index a97268318..1174f1bd2 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -2,6 +2,7 @@ #include "creature.hpp" #include +#include #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/magiceffects.hpp" @@ -613,6 +614,28 @@ namespace MWClass return 0; } + void Creature::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) + const + { + const ESM::CreatureState& state2 = dynamic_cast (state); + + ensureCustomData (ptr); + + dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore. + readState (state2.mInventory); + } + + void Creature::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + const + { + ESM::CreatureState& state2 = dynamic_cast (state); + + ensureCustomData (ptr); + + dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore. + writeState (state2.mInventory); + } + const ESM::GameSetting* Creature::fMinWalkSpeedCreature; const ESM::GameSetting* Creature::fMaxWalkSpeedCreature; const ESM::GameSetting *Creature::fEncumberedMoveEffect; diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index d518d0056..484afdaf5 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -84,7 +84,7 @@ namespace MWClass virtual bool isEssential (const MWWorld::Ptr& ptr) const; ///< Is \a ptr essential? (i.e. may losing \a ptr make the game unwinnable) - + virtual int getServices (const MWWorld::Ptr& actor) const; virtual bool isPersistent (const MWWorld::Ptr& ptr) const; @@ -118,6 +118,14 @@ namespace MWClass /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) virtual int getBloodTexture (const MWWorld::Ptr& ptr) const; + + virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) + const; + ///< Read additional state from \a state into \a ptr. + + virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + const; + ///< Write additional state from \a ptr into \a state. }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index f93a3e342..7c0f0b6ea 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -514,7 +515,7 @@ namespace MWClass weapon.getCellRef().mCharge = weapmaxhealth; damage *= float(weapon.getCellRef().mCharge) / weapmaxhealth; } - + if (!MWBase::Environment::get().getWorld()->getGodModeState()) weapon.getCellRef().mCharge -= std::min(std::max(1, (int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weapon.getCellRef().mCharge); @@ -964,7 +965,7 @@ namespace MWClass return ref->mBase->mFlags & ESM::NPC::Essential; } - + void Npc::registerSelf() { boost::shared_ptr instance (new Npc); @@ -1233,6 +1234,28 @@ namespace MWClass return 0; } + void Npc::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) + const + { + const ESM::NpcState& state2 = dynamic_cast (state); + + ensureCustomData (ptr); + + dynamic_cast (*ptr.getRefData().getCustomData()).mInventoryStore. + readState (state2.mInventory); + } + + void Npc::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + const + { + ESM::NpcState& state2 = dynamic_cast (state); + + ensureCustomData (ptr); + + dynamic_cast (*ptr.getRefData().getCustomData()).mInventoryStore. + writeState (state2.mInventory); + } + const ESM::GameSetting *Npc::fMinWalkSpeed; const ESM::GameSetting *Npc::fMaxWalkSpeed; const ESM::GameSetting *Npc::fEncumberedMoveEffect; diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 497d0ced8..237746de8 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -131,7 +131,7 @@ namespace MWClass ///< Is \a ptr essential? (i.e. may losing \a ptr make the game unwinnable) virtual int getServices (const MWWorld::Ptr& actor) const; - + virtual bool isPersistent (const MWWorld::Ptr& ptr) const; virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const; @@ -152,6 +152,14 @@ namespace MWClass virtual bool isNpc() const { return true; } + + virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) + const; + ///< Read additional state from \a state into \a ptr. + + virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + const; + ///< Write additional state from \a ptr into \a state. }; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 154a2d1e7..42c954afb 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -328,7 +330,7 @@ namespace MWWorld writeReferenceCollection (writer, mBooks); writeReferenceCollection (writer, mClothes); writeReferenceCollection (writer, mContainers); - writeReferenceCollection (writer, mCreatures); + writeReferenceCollection (writer, mCreatures); writeReferenceCollection (writer, mDoors); writeReferenceCollection (writer, mIngreds); writeReferenceCollection (writer, mCreatureLists); @@ -336,7 +338,7 @@ namespace MWWorld writeReferenceCollection (writer, mLights); writeReferenceCollection (writer, mLockpicks); writeReferenceCollection (writer, mMiscItems); - writeReferenceCollection (writer, mNpcs); + writeReferenceCollection (writer, mNpcs); writeReferenceCollection (writer, mProbes); writeReferenceCollection (writer, mRepairs); writeReferenceCollection (writer, mStatics); @@ -390,7 +392,7 @@ namespace MWWorld case ESM::REC_CREA: - readReferenceCollection (reader, mCreatures, contentFileMap); + readReferenceCollection (reader, mCreatures, contentFileMap); break; case ESM::REC_DOOR: @@ -430,7 +432,7 @@ namespace MWWorld case ESM::REC_NPC_: - readReferenceCollection (reader, mNpcs, contentFileMap); + readReferenceCollection (reader, mNpcs, contentFileMap); break; case ESM::REC_PROB: diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 2e01eb856..68bad4b9b 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -130,7 +130,7 @@ namespace MWWorld void fill (const ESM::InventoryList& items, const std::string& owner, const std::string& faction, const MWWorld::ESMStore& store); ///< Insert items into *this. - void clear(); + virtual void clear(); ///< Empty container. float getWeight() const; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 854d1f1ae..f2b16d4d5 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -40,7 +40,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate + savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate ) add_component_dir (misc diff --git a/components/esm/creaturestate.cpp b/components/esm/creaturestate.cpp new file mode 100644 index 000000000..43cde3025 --- /dev/null +++ b/components/esm/creaturestate.cpp @@ -0,0 +1,16 @@ + +#include "creaturestate.hpp" + +void ESM::CreatureState::load (ESMReader &esm) +{ + ObjectState::load (esm); + + mInventory.load (esm); +} + +void ESM::CreatureState::save (ESMWriter &esm, bool inInventory) const +{ + ObjectState::save (esm, inInventory); + + mInventory.save (esm); +} \ No newline at end of file diff --git a/components/esm/creaturestate.hpp b/components/esm/creaturestate.hpp new file mode 100644 index 000000000..f7f9b8038 --- /dev/null +++ b/components/esm/creaturestate.hpp @@ -0,0 +1,20 @@ +#ifndef OPENMW_ESM_CREATURESTATE_H +#define OPENMW_ESM_CREATURESTATE_H + +#include "objectstate.hpp" +#include "inventorystate.hpp" + +namespace ESM +{ + // format 0, saved games only + + struct CreatureState : public ObjectState + { + InventoryState mInventory; + + virtual void load (ESMReader &esm); + virtual void save (ESMWriter &esm, bool inInventory = false) const; + }; +} + +#endif diff --git a/components/esm/npcstate.cpp b/components/esm/npcstate.cpp new file mode 100644 index 000000000..c452611a0 --- /dev/null +++ b/components/esm/npcstate.cpp @@ -0,0 +1,16 @@ + +#include "npcstate.hpp" + +void ESM::NpcState::load (ESMReader &esm) +{ + ObjectState::load (esm); + + mInventory.load (esm); +} + +void ESM::NpcState::save (ESMWriter &esm, bool inInventory) const +{ + ObjectState::save (esm, inInventory); + + mInventory.save (esm); +} \ No newline at end of file diff --git a/components/esm/npcstate.hpp b/components/esm/npcstate.hpp new file mode 100644 index 000000000..ceb18b88b --- /dev/null +++ b/components/esm/npcstate.hpp @@ -0,0 +1,20 @@ +#ifndef OPENMW_ESM_NPCSTATE_H +#define OPENMW_ESM_NPCSTATE_H + +#include "objectstate.hpp" +#include "inventorystate.hpp" + +namespace ESM +{ + // format 0, saved games only + + struct NpcState : public ObjectState + { + InventoryState mInventory; + + virtual void load (ESMReader &esm); + virtual void save (ESMWriter &esm, bool inInventory = false) const; + }; +} + +#endif diff --git a/components/esm/player.hpp b/components/esm/player.hpp index bd618457e..0d70ee090 100644 --- a/components/esm/player.hpp +++ b/components/esm/player.hpp @@ -3,7 +3,7 @@ #include -#include "objectstate.hpp" +#include "npcstate.hpp" #include "cellid.hpp" #include "defs.hpp" @@ -16,7 +16,7 @@ namespace ESM struct Player { - ObjectState mObject; + NpcState mObject; CellId mCellId; float mLastKnownExteriorPosition[3]; unsigned char mHasMark; From 49e26415be5838e6d08198979ed1cc64e6f740e7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 22:20:46 +0100 Subject: [PATCH 728/889] Don't call loadGame() from within the MWMechanics::Actors update sequence --- apps/openmw/engine.cpp | 6 ++--- apps/openmw/mwmechanics/character.cpp | 1 - apps/openmw/mwstate/statemanagerimp.cpp | 36 +++++++++++++------------ 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 874fad267..bf1cca5e0 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -91,6 +91,9 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode(); + // update game state + MWBase::Environment::get().getStateManager()->update (frametime); + if (MWBase::Environment::get().getStateManager()->getState()== MWBase::StateManager::State_Running) { @@ -110,9 +113,6 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) if (!paused) MWBase::Environment::get().getWorld()->advanceTime( frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); - - // update game state - MWBase::Environment::get().getStateManager()->update (frametime); } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 15bb137fc..d58dee9ad 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1244,7 +1244,6 @@ bool CharacterController::kill() //player's death animation is over if( mPtr.getRefData().getHandle()=="player" && !isAnimPlaying(mCurrentDeath) ) { - MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setHealth(0); MWBase::Environment::get().getStateManager()->askLoadRecent(); } return false; diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index fb2d10bbf..f68a01bf4 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -111,23 +111,6 @@ void MWState::StateManager::askLoadRecent() mAskLoadRecent = true; } } - else - { - int iButton = MWBase::Environment::get().getWindowManager()->readPressedButton(); - if(iButton==0) - { - mAskLoadRecent = false; - //Load last saved game for current character - MWState::Character *curCharacter = getCurrentCharacter(); - MWState::Slot lastSave = *curCharacter->begin(); - loadGame(curCharacter, &lastSave); - } - else if(iButton==1) - { - mAskLoadRecent = false; - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); - } - } } MWState::StateManager::State MWState::StateManager::getState() const @@ -340,4 +323,23 @@ MWState::StateManager::CharacterIterator MWState::StateManager::characterEnd() void MWState::StateManager::update (float duration) { mTimePlayed += duration; + + // Note: It would be nicer to trigger this from InputManager, i.e. the very beginning of the frame update. + if (mAskLoadRecent) + { + int iButton = MWBase::Environment::get().getWindowManager()->readPressedButton(); + if(iButton==0) + { + mAskLoadRecent = false; + //Load last saved game for current character + MWState::Character *curCharacter = getCurrentCharacter(); + MWState::Slot lastSave = *curCharacter->begin(); + loadGame(curCharacter, &lastSave); + } + else if(iButton==1) + { + mAskLoadRecent = false; + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + } + } } From 37ef8ec908bdf0e31aafb1236f169bfe31db40c2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 22:30:25 +0100 Subject: [PATCH 729/889] Savegame dialog: support loading saves using Enter key or double-click --- apps/openmw/mwgui/savegamedialog.cpp | 8 ++++++++ apps/openmw/mwgui/savegamedialog.hpp | 1 + 2 files changed, 9 insertions(+) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 91993b0be..17aaa1189 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -34,7 +34,13 @@ namespace MWGui mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked); mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected); mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected); + mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated); + } + void SaveGameDialog::onSlotActivated(MyGUI::ListBox *sender, size_t pos) + { + onSlotSelected(sender, pos); + onOkButtonClicked(mOkButton); } void SaveGameDialog::open() @@ -103,6 +109,8 @@ namespace MWGui void SaveGameDialog::onOkButtonClicked(MyGUI::Widget *sender) { + MyGUI::InputManager::getInstance().setKeyFocusWidget(NULL); + // Get the selected slot, if any unsigned int i=0; const MWState::Slot* slot = NULL; diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp index 2a188061c..bf6fab73a 100644 --- a/apps/openmw/mwgui/savegamedialog.hpp +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -24,6 +24,7 @@ namespace MWGui void onOkButtonClicked (MyGUI::Widget* sender); void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos); void onSlotSelected (MyGUI::ListBox* sender, size_t pos); + void onSlotActivated (MyGUI::ListBox* sender, size_t pos); void fillSaveList(); From a988a0d6dcdb6a5ef853a4fb7bf756bee397690c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 23:07:52 +0100 Subject: [PATCH 730/889] Don't add duplicate topic responses to the journal --- apps/openmw/mwdialogue/topic.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/openmw/mwdialogue/topic.cpp b/apps/openmw/mwdialogue/topic.cpp index 0e546f43b..f7df305c7 100644 --- a/apps/openmw/mwdialogue/topic.cpp +++ b/apps/openmw/mwdialogue/topic.cpp @@ -24,6 +24,13 @@ namespace MWDialogue if (entry.mTopic!=mTopic) throw std::runtime_error ("topic does not match: " + mTopic); + // bail out if we already have heard this + for (Topic::TEntryIter it = mEntries.begin(); it != mEntries.end(); ++it) + { + if (it->mInfoId == entry.mInfoId) + return; + } + mEntries.push_back (entry); // we want slicing here } From f89b3cac020c8bd45b6b952d9073212513ed210e Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 25 Jan 2014 23:53:50 +0100 Subject: [PATCH 731/889] Feature #764: Store the actor that gave the dialog response --- apps/openmw/mwbase/journal.hpp | 2 +- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 4 ++-- apps/openmw/mwdialogue/journalentry.cpp | 5 +++-- apps/openmw/mwdialogue/journalentry.hpp | 1 + apps/openmw/mwdialogue/journalimp.cpp | 6 ++++-- apps/openmw/mwdialogue/journalimp.hpp | 2 +- apps/openmw/mwgui/journalviewmodel.cpp | 6 +----- components/esm/journalentry.cpp | 6 +++++- components/esm/journalentry.hpp | 1 + 9 files changed, 19 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwbase/journal.hpp b/apps/openmw/mwbase/journal.hpp index 81b4ba0b4..56d9601fc 100644 --- a/apps/openmw/mwbase/journal.hpp +++ b/apps/openmw/mwbase/journal.hpp @@ -54,7 +54,7 @@ namespace MWBase virtual int getJournalIndex (const std::string& id) const = 0; ///< Get the journal index. - virtual void addTopic (const std::string& topicId, const std::string& infoId) = 0; + virtual void addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName) = 0; virtual TEntryIter begin() const = 0; ///< Iterator pointing to the begin of the main journal. diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 7fbebb9d7..845c3c07b 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -286,7 +286,7 @@ namespace MWDialogue MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), title); - MWBase::Environment::get().getJournal()->addTopic (topic, info->mId); + MWBase::Environment::get().getJournal()->addTopic (topic, info->mId, mActor.getClass().getName(mActor)); executeScript (info->mResultScript); @@ -451,7 +451,7 @@ namespace MWDialogue MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addResponse (Interpreter::fixDefinesDialog(text, interpreterContext)); - MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId); + MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId, mActor.getClass().getName(mActor)); executeScript (info->mResultScript); } } diff --git a/apps/openmw/mwdialogue/journalentry.cpp b/apps/openmw/mwdialogue/journalentry.cpp index 7828d18ad..9463e4c45 100644 --- a/apps/openmw/mwdialogue/journalentry.cpp +++ b/apps/openmw/mwdialogue/journalentry.cpp @@ -32,7 +32,7 @@ namespace MWDialogue throw std::runtime_error ("unknown info ID " + mInfoId + " for topic " + topic); } - Entry::Entry (const ESM::JournalEntry& record) : mInfoId (record.mInfo), mText (record.mText) {} + Entry::Entry (const ESM::JournalEntry& record) : mInfoId (record.mInfo), mText (record.mText), mActorName(record.mActorName) {} std::string Entry::getText() const { @@ -43,6 +43,7 @@ namespace MWDialogue { entry.mInfo = mInfoId; entry.mText = mText; + entry.mActorName = mActorName; } @@ -53,7 +54,7 @@ namespace MWDialogue {} JournalEntry::JournalEntry (const ESM::JournalEntry& record) - : Entry (record), mTopic (record.mTopic) + : Entry (record), mTopic (record.mTopic) {} void JournalEntry::write (ESM::JournalEntry& entry) const diff --git a/apps/openmw/mwdialogue/journalentry.hpp b/apps/openmw/mwdialogue/journalentry.hpp index 18d022aab..a77ba4f7c 100644 --- a/apps/openmw/mwdialogue/journalentry.hpp +++ b/apps/openmw/mwdialogue/journalentry.hpp @@ -15,6 +15,7 @@ namespace MWDialogue { std::string mInfoId; std::string mText; + std::string mActorName; // optional Entry(); diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index f24a93356..26383b3a7 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -103,11 +103,13 @@ namespace MWDialogue quest.setIndex (index); } - void Journal::addTopic (const std::string& topicId, const std::string& infoId) + void Journal::addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName) { Topic& topic = getTopic (topicId); - topic.addEntry (JournalEntry (topicId, infoId)); + JournalEntry entry(topicId, infoId); + entry.mActorName = actorName; + topic.addEntry (entry); } int Journal::getJournalIndex (const std::string& id) const diff --git a/apps/openmw/mwdialogue/journalimp.hpp b/apps/openmw/mwdialogue/journalimp.hpp index 86091a12d..1b4803ba2 100644 --- a/apps/openmw/mwdialogue/journalimp.hpp +++ b/apps/openmw/mwdialogue/journalimp.hpp @@ -38,7 +38,7 @@ namespace MWDialogue virtual int getJournalIndex (const std::string& id) const; ///< Get the journal index. - virtual void addTopic (const std::string& topicId, const std::string& infoId); + virtual void addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName); virtual TEntryIter begin() const; ///< Iterator pointing to the begin of the main journal. diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index 3464f283d..a0d67b025 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -311,8 +311,6 @@ struct JournalViewModelImpl : JournalViewModel { MWDialogue::Topic const & mTopic; - mutable std::string source_buffer; - TopicEntryImpl (JournalViewModelImpl const * model, MWDialogue::Topic const & topic, iterator_t itr) : BaseEntry (model, itr), mTopic (topic) {} @@ -324,9 +322,7 @@ struct JournalViewModelImpl : JournalViewModel Utf8Span source () const { - if (source_buffer.empty ()) - source_buffer = "someone"; - return toUtf8Span (source_buffer); + return toUtf8Span (itr->mActorName); } }; diff --git a/components/esm/journalentry.cpp b/components/esm/journalentry.cpp index 514bf3597..445213de4 100644 --- a/components/esm/journalentry.cpp +++ b/components/esm/journalentry.cpp @@ -17,6 +17,8 @@ void ESM::JournalEntry::load (ESMReader &esm) esm.getHNT (mMonth, "JEMO"); esm.getHNT (mDayOfMonth, "JEDM"); } + else if (mType==Type_Topic) + mActorName = esm.getHNOString("ACT_"); } void ESM::JournalEntry::save (ESMWriter &esm) const @@ -32,4 +34,6 @@ void ESM::JournalEntry::save (ESMWriter &esm) const esm.writeHNT ("JEMO", mMonth); esm.writeHNT ("JEDM", mDayOfMonth); } -} \ No newline at end of file + else if (mType==Type_Topic) + esm.writeHNString ("ACT_", mActorName); +} diff --git a/components/esm/journalentry.hpp b/components/esm/journalentry.hpp index 94808dde6..76901a4b6 100644 --- a/components/esm/journalentry.hpp +++ b/components/esm/journalentry.hpp @@ -23,6 +23,7 @@ namespace ESM std::string mTopic; std::string mInfo; std::string mText; + std::string mActorName; // Could also be Actor ID to allow switching of localisation, but since mText is plaintext anyway... int mDay; // time stamp int mMonth; int mDayOfMonth; From dea9d21db65fdd4edb1e25e58b796ced5f45b798 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 15:18:31 +0100 Subject: [PATCH 732/889] Some additional safety checks for global map loading --- apps/openmw/mwrender/globalmap.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 6fbcfdc6b..4a87d9f1a 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -265,6 +265,10 @@ namespace MWRender if (bounds.mMaxY-bounds.mMinY <= 0) return; + if (bounds.mMinX > bounds.mMaxX + || bounds.mMinY > bounds.mMaxY) + throw std::runtime_error("invalid map bounds"); + Ogre::Image image; Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&map.mImageData[0], map.mImageData.size())); image.load(stream, "png"); @@ -297,9 +301,15 @@ namespace MWRender // If cell bounds of the currently loaded content and the loaded savegame do not match, // we need to resize source/dest boxes to accommodate // This means nonexisting cells will be dropped silently - int cellImageSizeDst = 24; + // Completely off-screen? -> no need to blit anything + if (bounds.mMaxX < mMinX + || bounds.mMaxY < mMinY + || bounds.mMinX > mMaxX + || bounds.mMinY > mMaxY) + return; + int leftDiff = (mMinX - bounds.mMinX); int topDiff = (bounds.mMaxY - mMaxY); int rightDiff = (bounds.mMaxX - mMaxX); From 63284d21a0bd58297321786bde2a39e5af9f1726 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 15:22:31 +0100 Subject: [PATCH 733/889] Savegame dialog: 'Enter' while editing a name is equivalent to pressing the ok button --- apps/openmw/mwgui/savegamedialog.cpp | 6 ++++++ apps/openmw/mwgui/savegamedialog.hpp | 1 + 2 files changed, 7 insertions(+) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 17aaa1189..da0417e7b 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -35,6 +35,7 @@ namespace MWGui mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected); mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected); mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated); + mSaveNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onEditSelectAccept); } void SaveGameDialog::onSlotActivated(MyGUI::ListBox *sender, size_t pos) @@ -43,6 +44,11 @@ namespace MWGui onOkButtonClicked(mOkButton); } + void SaveGameDialog::onEditSelectAccept(MyGUI::EditBox *sender) + { + onOkButtonClicked(mOkButton); + } + void SaveGameDialog::open() { WindowModal::open(); diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp index bf6fab73a..6743b00b9 100644 --- a/apps/openmw/mwgui/savegamedialog.hpp +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -25,6 +25,7 @@ namespace MWGui void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos); void onSlotSelected (MyGUI::ListBox* sender, size_t pos); void onSlotActivated (MyGUI::ListBox* sender, size_t pos); + void onEditSelectAccept (MyGUI::EditBox* sender); void fillSaveList(); From 6d37cd7e86fcf8bcbdac2ca970b7e5915ec7ada8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 15:28:05 +0100 Subject: [PATCH 734/889] Savegame dialog: Don't allow empty save names --- apps/openmw/mwgui/savegamedialog.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index da0417e7b..d7c5c3a94 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -132,6 +132,11 @@ namespace MWGui if (mSaving) { + if (mSaveNameEdit->getCaption().empty()) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage65}"); + return; + } MWBase::Environment::get().getStateManager()->saveGame (mSaveNameEdit->getCaption(), slot); } else From ec46575671fbe4fb69b572cb63b93b5102f0d494 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Jan 2014 15:43:26 +0100 Subject: [PATCH 735/889] Overwriting saves fixes - copy description of overwritten slot, ask for confirmation --- apps/openmw/mwgui/savegamedialog.cpp | 39 +++++++++++++++++++++++++--- apps/openmw/mwgui/savegamedialog.hpp | 7 +++-- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index d7c5c3a94..731795a82 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -15,6 +15,8 @@ #include "../mwstate/character.hpp" +#include "confirmationdialog.hpp" + namespace MWGui { SaveGameDialog::SaveGameDialog() @@ -36,17 +38,25 @@ namespace MWGui mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected); mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated); mSaveNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onEditSelectAccept); + mSaveNameEdit->eventEditTextChange += MyGUI::newDelegate(this, &SaveGameDialog::onSaveNameChanged); } void SaveGameDialog::onSlotActivated(MyGUI::ListBox *sender, size_t pos) { onSlotSelected(sender, pos); - onOkButtonClicked(mOkButton); + accept(); + } + + void SaveGameDialog::onSaveNameChanged(MyGUI::EditBox *sender) + { + // This might have previously been a save slot from the list. If so, that is no longer the case + mSaveList->setIndexSelected(MyGUI::ITEM_NONE); + onSlotSelected(mSaveList, MyGUI::ITEM_NONE); } void SaveGameDialog::onEditSelectAccept(MyGUI::EditBox *sender) { - onOkButtonClicked(mOkButton); + accept(); } void SaveGameDialog::open() @@ -113,7 +123,12 @@ namespace MWGui setVisible(false); } - void SaveGameDialog::onOkButtonClicked(MyGUI::Widget *sender) + void SaveGameDialog::onConfirmationGiven() + { + accept(true); + } + + void SaveGameDialog::accept(bool reallySure) { MyGUI::InputManager::getInstance().setKeyFocusWidget(NULL); @@ -132,6 +147,16 @@ namespace MWGui if (mSaving) { + // If overwriting an existing slot, ask for confirmation first + if (slot != NULL && !reallySure) + { + ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); + dialog->open("#{sMessage4}"); + dialog->eventOkClicked.clear(); + dialog->eventOkClicked += MyGUI::newDelegate(this, &SaveGameDialog::onConfirmationGiven); + dialog->eventCancelClicked.clear(); + return; + } if (mSaveNameEdit->getCaption().empty()) { MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage65}"); @@ -154,6 +179,11 @@ namespace MWGui } } + void SaveGameDialog::onOkButtonClicked(MyGUI::Widget *sender) + { + accept(); + } + void SaveGameDialog::onCharacterSelected(MyGUI::ComboBox *sender, size_t pos) { MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager(); @@ -192,6 +222,9 @@ namespace MWGui return; } + if (mSaving) + mSaveNameEdit->setCaption(sender->getItemNameAt(pos)); + const MWState::Slot* slot = NULL; unsigned int i=0; for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it, ++i) diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp index 6743b00b9..8d09a1cbc 100644 --- a/apps/openmw/mwgui/savegamedialog.hpp +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -20,17 +20,20 @@ namespace MWGui void setLoadOrSave(bool load); + private: void onCancelButtonClicked (MyGUI::Widget* sender); void onOkButtonClicked (MyGUI::Widget* sender); void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos); void onSlotSelected (MyGUI::ListBox* sender, size_t pos); void onSlotActivated (MyGUI::ListBox* sender, size_t pos); void onEditSelectAccept (MyGUI::EditBox* sender); + void onSaveNameChanged (MyGUI::EditBox* sender); + void onConfirmationGiven(); + + void accept(bool reallySure=false); void fillSaveList(); - - private: MyGUI::ImageBox* mScreenshot; bool mSaving; From 1deb0a7cdfab0348a84ddcaf504343fca3ba01ce Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 29 Jan 2014 17:26:18 +0100 Subject: [PATCH 736/889] Savegame dialog: Set key focus to editbox --- apps/openmw/mwbase/windowmanager.hpp | 1 + apps/openmw/mwgui/container.cpp | 2 +- apps/openmw/mwgui/messagebox.cpp | 2 +- apps/openmw/mwgui/savegamedialog.cpp | 4 +++- apps/openmw/mwgui/windowmanagerimp.hpp | 1 + apps/openmw/mwinput/inputmanagerimp.cpp | 2 -- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index fa0fe888b..069e6311a 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -281,6 +281,7 @@ namespace MWBase virtual const Translation::Storage& getTranslationDataStorage() const = 0; + /// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this. virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0; virtual Loading::Listener* getLoadingScreen() = 0; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 858378c03..7e7ad5ec2 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -235,7 +235,7 @@ namespace MWGui mItemView->setModel (mSortModel); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mCloseButton); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); // Careful here. setTitle may cause size updates, causing itemview redraw, so make sure to do it last // or we end up using a possibly invalid model. diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 74231c008..b52aee706 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -332,7 +332,7 @@ namespace MWGui { if(Misc::StringUtils::ciEqual((*button)->getCaption(), ok)) { - MyGUI::InputManager::getInstance().setKeyFocusWidget(*button); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(*button); (*button)->eventKeyButtonPressed += MyGUI::newDelegate(this, &InteractiveMessageBox::onKeyPressed); break; } diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 731795a82..77ad98121 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -64,6 +64,8 @@ namespace MWGui WindowModal::open(); mSaveNameEdit->setCaption (""); + if (mSaving) + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveNameEdit); center(); @@ -130,7 +132,7 @@ namespace MWGui void SaveGameDialog::accept(bool reallySure) { - MyGUI::InputManager::getInstance().setKeyFocusWidget(NULL); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL); // Get the selected slot, if any unsigned int i=0; diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index bc440d818..db52d9f79 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -105,6 +105,7 @@ namespace MWGui */ virtual void update(); + /// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this. virtual void setKeyFocusWidget (MyGUI::Widget* widget); virtual void setNewGame(bool newgame); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 9f1ccb2df..5385ab388 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -251,8 +251,6 @@ namespace MWInput mInputManager->capture(loading); // inject some fake mouse movement to force updating MyGUI's widget states - // this shouldn't do any harm since we're moving back to the original position afterwards - MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX+1), int(mMouseY+1), mMouseWheel); MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX), int(mMouseY), mMouseWheel); // update values of channels (as a result of pressed keys) From a36cc264349584a1c0f6abfa1669d109843aadb3 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 1 Feb 2014 19:06:41 +0100 Subject: [PATCH 737/889] Created new files for TableMimeData class. --- apps/opencs/model/world/tablemimedata.cpp | 1 + apps/opencs/model/world/tablemimedata.hpp | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 apps/opencs/model/world/tablemimedata.cpp create mode 100644 apps/opencs/model/world/tablemimedata.hpp diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp new file mode 100644 index 000000000..a8947394a --- /dev/null +++ b/apps/opencs/model/world/tablemimedata.cpp @@ -0,0 +1 @@ +#include "tablemimedata.hpp" diff --git a/apps/opencs/model/world/tablemimedata.hpp b/apps/opencs/model/world/tablemimedata.hpp new file mode 100644 index 000000000..d96263ae2 --- /dev/null +++ b/apps/opencs/model/world/tablemimedata.hpp @@ -0,0 +1,10 @@ +#ifndef TABLEMIMEDATA_H +#define TABLEMIMEDATA_H + +#include + +class TableMimeData : public QMimeData +{ +}; + +#endif // TABLEMIMEDATA_H From 8bcdf54570bc01659d06f8691d089ad5864bc1dd Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 2 Feb 2014 13:55:03 +0100 Subject: [PATCH 738/889] added warning mode to script compiler error handler --- components/compiler/errorhandler.cpp | 16 +++++++++++++--- components/compiler/errorhandler.hpp | 6 +++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/components/compiler/errorhandler.cpp b/components/compiler/errorhandler.cpp index ee13c837d..fe58836cc 100644 --- a/components/compiler/errorhandler.cpp +++ b/components/compiler/errorhandler.cpp @@ -5,7 +5,7 @@ namespace Compiler { // constructor - ErrorHandler::ErrorHandler() : mWarnings (0), mErrors (0) {} + ErrorHandler::ErrorHandler() : mWarnings (0), mErrors (0), mWarningsMode (1) {} // destructor @@ -36,8 +36,13 @@ namespace Compiler void ErrorHandler::warning (const std::string& message, const TokenLoc& loc) { - ++mWarnings; - report (message, loc, WarningMessage); + if (mWarningsMode==1) + { + ++mWarnings; + report (message, loc, WarningMessage); + } + else if (mWarningsMode==2) + error (message, loc); } // Generate an error message. @@ -62,4 +67,9 @@ namespace Compiler { mErrors = mWarnings = 0; } + + void ErrorHandler::setWarningsMode (int mode) + { + mWarningsMode = mode; + } } diff --git a/components/compiler/errorhandler.hpp b/components/compiler/errorhandler.hpp index 256065854..e5922a6be 100644 --- a/components/compiler/errorhandler.hpp +++ b/components/compiler/errorhandler.hpp @@ -16,6 +16,7 @@ namespace Compiler { int mWarnings; int mErrors; + int mWarningsMode; protected: @@ -60,8 +61,11 @@ namespace Compiler void endOfFile(); ///< Generate an error message for an unexpected EOF. - virtual void reset(); + virtual void reset(); ///< Remove all previous error/warning events + + void setWarningsMode (int mode); + ///< // 0 ignore, 1 rate as warning, 2 rate as error }; } From e3d85af70ac4a3515470351beb9064c7c435ba3e Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Sun, 2 Feb 2014 13:57:19 +0100 Subject: [PATCH 739/889] Fix an issue with int32_t being unknown on windows. --- apps/openmw/mwgui/mapwindow.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 2f5ded012..a07adc1f3 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -2,6 +2,7 @@ #define MWGUI_MAPWINDOW_H #include "windowpinnablebase.hpp" +#include namespace MWRender { From b85a4dd35e595ec0dd29f5354348ae95244ff2f9 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sun, 2 Feb 2014 15:01:49 +0200 Subject: [PATCH 740/889] pos accum without conformity with animation bug/creature speed --- apps/openmw/mwmechanics/character.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 252271d32..eff48ca08 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -301,8 +301,9 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat if(!mCurrentMovement.empty()) { float vel, speedmult = 1.0f; - if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f) + if(mPtr.getClass().isNpc() && mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f) speedmult = mMovementSpeed / vel; + mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false, speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul); } @@ -1120,7 +1121,13 @@ void CharacterController::update(float duration) else //avoid z-rotating for knockdown world->rotateObject(mPtr, rot.x, rot.y, 0.0f, true); - world->queueMovement(mPtr, vec); + // all actual movement in 3rd person controlled by animations, except for jump + // !mAnimation->hasAnimation("death1") identifies 1st person mode + if(mJumpState != JumpState_None || vec.z > 0 + || (mPtr.getRefData().getHandle() == "player" && !mAnimation->hasAnimation("death1"))) + { + world->queueMovement(mPtr, vec); + } } movement = vec; From 4ee43612f66915ea1dc768b63a012c3cb06e9d89 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 2 Feb 2014 14:09:59 +0100 Subject: [PATCH 741/889] added new switch: --script-warn --- apps/openmw/engine.cpp | 9 +++-- apps/openmw/engine.hpp | 3 ++ apps/openmw/main.cpp | 8 +++++ apps/openmw/mwscript/scriptmanagerimp.cpp | 6 ++-- apps/openmw/mwscript/scriptmanagerimp.hpp | 2 +- readme.txt | 42 +++++++++++++---------- 6 files changed, 47 insertions(+), 23 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index bf1cca5e0..e80bd954e 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -155,6 +155,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) , mSkipMenu (false) , mUseSound (true) , mCompileAll (false) + , mWarningsMode (1) , mScriptContext (0) , mFSStrict (false) , mScriptConsoleMode (false) @@ -424,7 +425,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mScriptContext->setExtensions (&mExtensions); mEnvironment.setScriptManager (new MWScript::ScriptManager (MWBase::Environment::get().getWorld()->getStore(), - mVerboseScripts, *mScriptContext)); + mVerboseScripts, *mScriptContext, mWarningsMode)); // Create game mechanics system MWMechanics::MechanicsManager* mechanics = new MWMechanics::MechanicsManager; @@ -612,8 +613,12 @@ void OMW::Engine::setStartupScript (const std::string& path) mStartupScript = path; } - void OMW::Engine::setActivationDistanceOverride (int distance) { mActivationDistanceOverride = distance; } + +void OMW::Engine::setWarningsMode (int mode) +{ + mWarningsMode = mode; +} \ No newline at end of file diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 8b2a65b7e..5c15ddf6f 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -74,6 +74,7 @@ namespace OMW bool mSkipMenu; bool mUseSound; bool mCompileAll; + int mWarningsMode; std::string mFocusName; std::map mFallbackMap; bool mScriptConsoleMode; @@ -181,6 +182,8 @@ namespace OMW /// Override the game setting specified activation distance. void setActivationDistanceOverride (int distance); + void setWarningsMode (int mode); + private: Files::ConfigurationManager& mCfgMgr; }; diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 570c2b198..764c4f39b 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -137,6 +137,13 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("script-run", bpo::value()->default_value(""), "select a file containing a list of console commands that is executed on startup") + ("script-warn", bpo::value()->implicit_value (1) + ->default_value (1), + "handling of warnings when compiling scripts\n" + "\t0 - ignore warning\n" + "\t1 - show warning but consider script as correctly compiled anyway\n" + "\t2 - treat warnings as errors") + ("skip-menu", bpo::value()->implicit_value(true) ->default_value(false), "skip main menu on game startup") @@ -242,6 +249,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setScriptConsoleMode (variables["script-console"].as()); engine.setStartupScript (variables["script-run"].as()); engine.setActivationDistanceOverride (variables["activate-dist"].as()); + engine.setWarningsMode (variables["script-warn"].as()); return true; } diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index a3e061546..6862b9f83 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -18,11 +18,13 @@ namespace MWScript { ScriptManager::ScriptManager (const MWWorld::ESMStore& store, bool verbose, - Compiler::Context& compilerContext) + Compiler::Context& compilerContext, int warningsMode) : mErrorHandler (std::cerr), mStore (store), mVerbose (verbose), mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext), mOpcodesInstalled (false), mGlobalScripts (store) - {} + { + mErrorHandler.setWarningsMode (warningsMode); + } bool ScriptManager::compile (const std::string& name) { diff --git a/apps/openmw/mwscript/scriptmanagerimp.hpp b/apps/openmw/mwscript/scriptmanagerimp.hpp index 1a856e0c5..da3abc60b 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.hpp +++ b/apps/openmw/mwscript/scriptmanagerimp.hpp @@ -52,7 +52,7 @@ namespace MWScript public: ScriptManager (const MWWorld::ESMStore& store, bool verbose, - Compiler::Context& compilerContext); + Compiler::Context& compilerContext, int warningsMode); virtual void run (const std::string& name, Interpreter::Context& interpreterContext); ///< Run the script with the given name (compile first, if not compiled yet) diff --git a/readme.txt b/readme.txt index 6b388dc72..f4d0c0b8f 100644 --- a/readme.txt +++ b/readme.txt @@ -48,42 +48,48 @@ Allowed options: --version print version information and quit --data arg (=data) set data directories (later directories have higher priority) - --data-local arg set local data directory (highest + --data-local arg set local data directory (highest priority) --fallback-archive arg (=fallback-archive) - set fallback BSA archives (later + set fallback BSA archives (later archives have higher priority) --resources arg (=resources) set resources directory --start arg (=Beshara) set initial cell - --content arg content file(s): esm/esp, or + --content arg content file(s): esm/esp, or omwgame/omwaddon --anim-verbose [=arg(=1)] (=0) output animation indices files --no-sound [=arg(=1)] (=0) disable all sounds --script-verbose [=arg(=1)] (=0) verbose script output --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue scripts) at startup - --script-console [=arg(=1)] (=0) enable console-only script + --script-console [=arg(=1)] (=0) enable console-only script functionality - --script-run arg select a file containing a list of - console commands that is executed on + --script-run arg select a file containing a list of + console commands that is executed on startup + --script-warn [=arg(=1)] (=1) handling of warnings when compiling + scripts + 0 - ignore warning + 1 - show warning but consider script as + correctly compiled anyway + 2 - treat warnings as errors --new-game [=arg(=1)] (=0) activate char gen/new game mechanics - --fs-strict [=arg(=1)] (=0) strict file system handling (no case + --fs-strict [=arg(=1)] (=0) strict file system handling (no case folding) - --encoding arg (=win1252) Character encoding used in OpenMW game + --encoding arg (=win1252) Character encoding used in OpenMW game messages: - - win1250 - Central and Eastern European - such as Polish, Czech, Slovak, - Hungarian, Slovene, Bosnian, Croatian, - Serbian (Latin script), Romanian and + + win1250 - Central and Eastern European + such as Polish, Czech, Slovak, + Hungarian, Slovene, Bosnian, Croatian, + Serbian (Latin script), Romanian and Albanian languages - - win1251 - Cyrillic alphabet such as - Russian, Bulgarian, Serbian Cyrillic + + win1251 - Cyrillic alphabet such as + Russian, Bulgarian, Serbian Cyrillic and other languages - - win1252 - Western European (Latin) + + win1252 - Western European (Latin) alphabet, used by default --fallback arg fallback values --no-grab Don't grab mouse cursor From 858dc80292a31c7359fc728f6d2c964a64f0f838 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 2 Feb 2014 14:10:57 +0100 Subject: [PATCH 742/889] corrected command line switch documentation in readme file --- readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.txt b/readme.txt index f4d0c0b8f..b1fef2d09 100644 --- a/readme.txt +++ b/readme.txt @@ -73,7 +73,7 @@ Allowed options: 1 - show warning but consider script as correctly compiled anyway 2 - treat warnings as errors - --new-game [=arg(=1)] (=0) activate char gen/new game mechanics + --skip-menu [=arg(=1)] (=0) skip main menu on game startup --fs-strict [=arg(=1)] (=0) strict file system handling (no case folding) --encoding arg (=win1252) Character encoding used in OpenMW game From cd9b13712921be4c61968e7959cff2b287f6fc17 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 2 Feb 2014 14:24:58 +0100 Subject: [PATCH 743/889] allow elseif without matching if (grrrrr) --- components/compiler/controlparser.cpp | 6 +++++- components/compiler/scriptparser.cpp | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/components/compiler/controlparser.cpp b/components/compiler/controlparser.cpp index 5d74ee9d4..3be470c27 100644 --- a/components/compiler/controlparser.cpp +++ b/components/compiler/controlparser.cpp @@ -7,6 +7,7 @@ #include "scanner.hpp" #include "generator.hpp" +#include "errorhandler.hpp" namespace Compiler { @@ -186,8 +187,11 @@ namespace Compiler { if (mState==StartState) { - if (keyword==Scanner::K_if) + if (keyword==Scanner::K_if || keyword==Scanner::K_elseif) { + if (keyword==Scanner::K_elseif) + getErrorHandler().warning ("elseif without matching if", loc); + mExprParser.reset(); scanner.scan (mExprParser); diff --git a/components/compiler/scriptparser.cpp b/components/compiler/scriptparser.cpp index 5b3d244b0..1b613595a 100644 --- a/components/compiler/scriptparser.cpp +++ b/components/compiler/scriptparser.cpp @@ -32,7 +32,7 @@ namespace Compiler bool ScriptParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { - if (keyword==Scanner::K_while || keyword==Scanner::K_if) + if (keyword==Scanner::K_while || keyword==Scanner::K_if || keyword==Scanner::K_elseif) { mControlParser.reset(); if (mControlParser.parseKeyword (keyword, loc, scanner)) From 914ab1b8ab689f1462d2f396d936ab0377bb2b2f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 2 Feb 2014 15:08:27 +0100 Subject: [PATCH 744/889] allow 'x' instead of 'getjournalindex x' --- apps/opencs/model/world/scriptcontext.cpp | 6 ++++++ apps/opencs/model/world/scriptcontext.hpp | 3 +++ apps/openmw/mwscript/compilercontext.cpp | 12 ++++++++++++ apps/openmw/mwscript/compilercontext.hpp | 3 +++ components/compiler/context.hpp | 3 +++ components/compiler/exprparser.cpp | 19 ++++++++++++++++++- 6 files changed, 45 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/world/scriptcontext.cpp b/apps/opencs/model/world/scriptcontext.cpp index 86689d823..8190c68eb 100644 --- a/apps/opencs/model/world/scriptcontext.cpp +++ b/apps/opencs/model/world/scriptcontext.cpp @@ -38,6 +38,12 @@ bool CSMWorld::ScriptContext::isId (const std::string& name) const return std::binary_search (mIds.begin(), mIds.end(), Misc::StringUtils::lowerCase (name)); } +bool CSMWorld::ScriptContext::isJournalId (const std::string& name) const +{ + /// \todo fix this after isId is fixed + return isId (name); +} + void CSMWorld::ScriptContext::invalidateIds() { mIdsUpdated = false; diff --git a/apps/opencs/model/world/scriptcontext.hpp b/apps/opencs/model/world/scriptcontext.hpp index b839b5a43..961da9143 100644 --- a/apps/opencs/model/world/scriptcontext.hpp +++ b/apps/opencs/model/world/scriptcontext.hpp @@ -32,6 +32,9 @@ namespace CSMWorld virtual bool isId (const std::string& name) const; ///< Does \a name match an ID, that can be referenced? + virtual bool isJournalId (const std::string& name) const; + ///< Does \a name match a journal ID? + void invalidateIds(); }; } diff --git a/apps/openmw/mwscript/compilercontext.cpp b/apps/openmw/mwscript/compilercontext.cpp index 7e63a33b2..b094e5414 100644 --- a/apps/openmw/mwscript/compilercontext.cpp +++ b/apps/openmw/mwscript/compilercontext.cpp @@ -3,6 +3,8 @@ #include "../mwworld/esmstore.hpp" +#include + #include #include "../mwbase/environment.hpp" @@ -67,4 +69,14 @@ namespace MWScript store.get().search (name) || store.get().search (name); } + + bool CompilerContext::isJournalId (const std::string& name) const + { + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); + + const ESM::Dialogue *topic = store.get().search (name); + + return topic && topic->mType==ESM::Dialogue::Journal; + } } diff --git a/apps/openmw/mwscript/compilercontext.hpp b/apps/openmw/mwscript/compilercontext.hpp index 5ec98e09a..50256f942 100644 --- a/apps/openmw/mwscript/compilercontext.hpp +++ b/apps/openmw/mwscript/compilercontext.hpp @@ -35,6 +35,9 @@ namespace MWScript virtual bool isId (const std::string& name) const; ///< Does \a name match an ID, that can be referenced? + + virtual bool isJournalId (const std::string& name) const; + ///< Does \a name match a journal ID? }; } diff --git a/components/compiler/context.hpp b/components/compiler/context.hpp index 1b02613c5..69146e285 100644 --- a/components/compiler/context.hpp +++ b/components/compiler/context.hpp @@ -38,6 +38,9 @@ namespace Compiler virtual bool isId (const std::string& name) const = 0; ///< Does \a name match an ID, that can be referenced? + + virtual bool isJournalId (const std::string& name) const = 0; + ///< Does \a name match a journal ID? }; } diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 94240c5eb..42c88b75a 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -7,6 +7,8 @@ #include #include +#include + #include "generator.hpp" #include "scanner.hpp" #include "errorhandler.hpp" @@ -14,7 +16,6 @@ #include "stringparser.hpp" #include "extensions.hpp" #include "context.hpp" -#include namespace Compiler { @@ -308,6 +309,22 @@ namespace Compiler return true; } + // die in a fire, Morrowind script compiler! + if (const Extensions *extensions = getContext().getExtensions()) + { + if (getContext().isJournalId (name2)) + { + // JournalID used as an argument. Use the index of that JournalID + Generator::pushString (mCode, mLiterals, name2); + int keyword = extensions->searchKeyword ("getjournalindex"); + extensions->generateFunctionCode (keyword, mCode, mLiterals, mExplicit, 0); + mNextOperand = false; + mOperands.push_back ('l'); + + return 2; + } + } + if (mExplicit.empty() && getContext().isId (name2)) { mExplicit = name2; From 761f13d3cef95cf2dcdba32b0da3b047157e9aa2 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sun, 2 Feb 2014 16:29:51 +0200 Subject: [PATCH 745/889] activate whole-body attack animations --- apps/openmw/mwmechanics/character.cpp | 23 +++++++++++++++++++++++ apps/openmw/mwrender/animation.cpp | 5 ++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index eff48ca08..6c1a40249 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -781,6 +781,13 @@ bool CharacterController::updateWeaponState() stop = mAttackType+" max attack"; mUpperBodyState = UpperCharState_MinAttackToMaxAttack; break; + case UpperCharState_MinAttackToMaxAttack: + //hack to avoid body pos desync when jumping/sneaking in 'max attack' state + if(!mAnimation->isPlaying(mCurrentWeapon)) + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + 0, mAttackType+" min attack", mAttackType+" max attack", 0.999f, 0); + break; case UpperCharState_MaxAttackToMinHit: if(mAttackType == "shoot") { @@ -830,6 +837,22 @@ bool CharacterController::updateWeaponState() } } + //if playing combat animation and lowerbody is not busy switch to whole body animation + if((weaptype != WeapType_None || UpperCharState_UnEquipingWeap) && animPlaying) + { + if( mMovementState != CharState_None || + mJumpState != JumpState_None || + mHitState != CharState_None || + MWBase::Environment::get().getWorld()->isSwimming(mPtr) || + cls.getCreatureStats(mPtr).getMovementFlag(CreatureStats::Flag_Sneak)) + { + mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_UpperBody); + } + else + { + mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_All); + } + } MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name() diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 3f45ce769..c62515c8c 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1050,9 +1050,8 @@ bool Animation::allowSwitchViewMode() const { for (AnimStateMap::const_iterator stateiter = mStates.begin(); stateiter != mStates.end(); ++stateiter) { - if(stateiter->second.mGroups == Group_UpperBody - || (stateiter->first.size()==4 && stateiter->first.find("hit") != std::string::npos) - || (stateiter->first.find("knock") != std::string::npos) ) + if(stateiter->second.mPriority > MWMechanics::Priority_Movement + && stateiter->second.mPriority < MWMechanics::Priority_Torch) return false; } return true; From 32860a05e38f7b67afc8725beed30254d4c69dc1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 2 Feb 2014 15:35:18 +0100 Subject: [PATCH 746/889] added dummy implementations for getPcInJail and getPcTraveling --- apps/openmw/mwscript/docs/vmformat.txt | 4 +++- apps/openmw/mwscript/miscextensions.cpp | 24 ++++++++++++++++++++++++ components/compiler/extensions0.cpp | 4 +++- components/compiler/opcodes.hpp | 2 ++ 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 25f3c38aa..2fe4c768b 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -381,5 +381,7 @@ op 0x200023a: StartCombat op 0x200023b: StartCombatExplicit op 0x200023c: StopCombat op 0x200023d: StopCombatExplicit +op 0x200023e: GetPcInJail +op 0x200023f: GetPcTraveling -opcodes 0x200023e-0x3ffffff unused +opcodes 0x2000240-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index d117c8ded..aa0e775af 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -818,6 +818,28 @@ namespace MWScript } }; + class OpGetPcInJail : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime &runtime) + { + /// \todo implement jail check + runtime.push (0); + } + }; + + class OpGetPcTraveling : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime &runtime) + { + /// \todo implement traveling check + runtime.push (0); + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox); @@ -888,6 +910,8 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeCastExplicit, new OpCast); interpreter.installSegment5 (Compiler::Misc::opcodeExplodeSpell, new OpExplodeSpell); interpreter.installSegment5 (Compiler::Misc::opcodeExplodeSpellExplicit, new OpExplodeSpell); + interpreter.installSegment5 (Compiler::Misc::opcodeGetPcInJail, new OpGetPcInJail); + interpreter.installSegment5 (Compiler::Misc::opcodeGetPcTraveling, new OpGetPcTraveling); } } } diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index baec9987f..ebe46d282 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -190,7 +190,7 @@ namespace Compiler extensions.registerInstruction ("enableclassmenu", "", opcodeEnableClassMenu); extensions.registerInstruction ("enablenamemenu", "", opcodeEnableNameMenu); extensions.registerInstruction ("enableracemenu", "", opcodeEnableRaceMenu); - extensions.registerInstruction ("enablestatreviewmenu", "", + extensions.registerInstruction ("enablestatreviewmenu", "", opcodeEnableStatsReviewMenu); extensions.registerInstruction ("enableinventorymenu", "", opcodeEnableInventoryMenu); @@ -276,6 +276,8 @@ namespace Compiler extensions.registerInstruction("togglegodmode", "", opcodeToggleGodMode); extensions.registerInstruction ("disablelevitation", "", opcodeDisableLevitation); extensions.registerInstruction ("enablelevitation", "", opcodeEnableLevitation); + extensions.registerFunction ("getpcinjail", 'l', "", opcodeGetPcInJail); + extensions.registerFunction ("getpctraveling", 'l', "", opcodeGetPcTraveling); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 57d86e62b..583cf4eea 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -245,6 +245,8 @@ namespace Compiler const int opcodeCastExplicit = 0x2000228; const int opcodeExplodeSpell = 0x2000229; const int opcodeExplodeSpellExplicit = 0x200022a; + const int opcodeGetPcInJail = 0x200023e; + const int opcodeGetPcTraveling = 0x200023f; } namespace Sky From 2b2ac6f62b7db5972fbf461056d06576e9e4557f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 2 Feb 2014 15:43:48 +0100 Subject: [PATCH 747/889] allow declaration of local variables with keywords as names --- components/compiler/lineparser.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 3d9ac0a93..2904df6e1 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -370,6 +370,12 @@ namespace Compiler mState = EndState; return true; } + else if (mState==ShortState || mState==LongState || mState==FloatState) + { + // allow keywords to be used as local variable names. MW script compiler, you suck! + /// \todo option to disable this atrocity. + return parseName (loc.mLiteral, loc, scanner); + } if (mAllowExpression) { From 4e03e9cf87998129c432e833a186b77b863dcfbd Mon Sep 17 00:00:00 2001 From: pvdk Date: Sun, 2 Feb 2014 20:10:47 +0100 Subject: [PATCH 748/889] Changed development version info text and the tooltip now works on all platforms --- apps/launcher/maindialog.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 67665adf0..56b3186ff 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -79,15 +79,14 @@ Launcher::MainDialog::MainDialog(QWidget *parent) if (revision == tag) { versionLabel->setText(tr("OpenMW %0 release").arg(OPENMW_VERSION)); } else { - versionLabel->setText(tr("OpenMW unstable, revision %0").arg(revision.left(10))); + versionLabel->setText(tr("OpenMW development (%0)").arg(revision.left(10))); } // Add the compile date and time - QDate date(QDate::fromString(__DATE__, QLatin1String("MMM dd yyyy"))); - QTime time(QTime::fromString(__TIME__, QLatin1String("hh:m:ss"))); - - versionLabel->setToolTip(tr("Compiled on %0 %1").arg(date.toString(Qt::SystemLocaleShortDate), - time.toString(Qt::SystemLocaleShortDate))); + versionLabel->setToolTip(tr("Compiled on %0 %1").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(), + QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate), + QLocale(QLocale::C).toTime(QString(__TIME__).simplified(), + QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate))); createIcons(); } From e50a393de485f2425ed15fb82c2cc317090c37a4 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Sun, 2 Feb 2014 23:59:57 +0100 Subject: [PATCH 749/889] Fix linking issues on Windows using MSVC. --- apps/openmw/mwworld/livecellref.hpp | 2 +- apps/openmw/mwworld/refdata.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/livecellref.hpp b/apps/openmw/mwworld/livecellref.hpp index b2089fa7a..b2e4d6d56 100644 --- a/apps/openmw/mwworld/livecellref.hpp +++ b/apps/openmw/mwworld/livecellref.hpp @@ -9,7 +9,7 @@ namespace ESM { - class ObjectState; + struct ObjectState; } namespace MWWorld diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index d9f5697bd..19e3d4882 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -14,7 +14,7 @@ namespace ESM { class Script; class CellRef; - class ObjectState; + struct ObjectState; } namespace MWWorld From 21f502df349107029273300ba5db4ecdc5722b62 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 3 Feb 2014 11:20:55 +0100 Subject: [PATCH 750/889] compatibility fix --- apps/openmw/mwgui/mapwindow.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index a07adc1f3..1e52ff26a 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -1,8 +1,9 @@ #ifndef MWGUI_MAPWINDOW_H #define MWGUI_MAPWINDOW_H +#include + #include "windowpinnablebase.hpp" -#include namespace MWRender { From 688f359a333420753dd5fc1a6341b55d8edb7519 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Mon, 3 Feb 2014 23:09:26 +0200 Subject: [PATCH 751/889] discard creatures speed and negative fatique changes --- apps/openmw/mwmechanics/character.cpp | 2 +- apps/openmw/mwmechanics/creaturestats.cpp | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 6c1a40249..7647a940e 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -301,7 +301,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat if(!mCurrentMovement.empty()) { float vel, speedmult = 1.0f; - if(mPtr.getClass().isNpc() && mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f) + if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f) speedmult = mMovementSpeed / vel; mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false, diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 943c13a18..30db59311 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -209,10 +209,7 @@ namespace MWMechanics mDynamic[index] = value; if (index == 2 && value.getCurrent() < 0) - { setKnockedDown(true); - mDynamic[2].setCurrent(0); - } if (index==0 && mDynamic[index].getCurrent()<1) { From dc80bfff554404fa998d5b762da9afc62ed50f1d Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 4 Feb 2014 09:13:40 +0100 Subject: [PATCH 752/889] Attempt to get basic tablemimedata subclass. --- apps/opencs/model/world/tablemimedata.cpp | 21 +++++++++++++++++++++ apps/opencs/model/world/tablemimedata.hpp | 20 ++++++++++++++++++-- apps/opencs/view/world/table.cpp | 1 + 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp index a8947394a..457f5af1b 100644 --- a/apps/opencs/model/world/tablemimedata.cpp +++ b/apps/opencs/model/world/tablemimedata.cpp @@ -1 +1,22 @@ #include "tablemimedata.hpp" +#include "universalid.hpp" + +CSMWorld::TableMimeData::TableMimeData (CSMWorld::UniversalId& UniversalId) : +mUniversalId(UniversalId) +{ + mSupportedFormats << UniversalId.toString().c_str(); +} + +QStringList CSMWorld::TableMimeData::formats() const +{ + return QMimeData::formats(); +} + +CSMWorld::TableMimeData::~TableMimeData() +{ +} + +CSMWorld::UniversalId& CSMWorld::TableMimeData::getId() +{ + return mUniversalId; +} diff --git a/apps/opencs/model/world/tablemimedata.hpp b/apps/opencs/model/world/tablemimedata.hpp index d96263ae2..d142c66c1 100644 --- a/apps/opencs/model/world/tablemimedata.hpp +++ b/apps/opencs/model/world/tablemimedata.hpp @@ -1,10 +1,26 @@ +/*This class provides way to construct mimedata object holding the reference to the +* universalid. universalid is used in the majority of the tables to store type, id, argument types*/ + #ifndef TABLEMIMEDATA_H #define TABLEMIMEDATA_H #include +#include -class TableMimeData : public QMimeData +namespace CSMWorld { -}; + class UniversalId; + class TableMimeData : public QMimeData + { + public: + TableMimeData(UniversalId& UniversalId); + ~TableMimeData(); + virtual QStringList formats() const; + UniversalId& getId(); + private: + QStringList mSupportedFormats; + UniversalId& mUniversalId; + }; +} #endif // TABLEMIMEDATA_H diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 71bdb9000..7d0255529 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -13,6 +13,7 @@ #include "../../model/world/idtable.hpp" #include "../../model/world/record.hpp" #include "../../model/world/columns.hpp" +#include "../../model/world/tablemimedata.hpp" #include "recordstatusdelegate.hpp" #include "util.hpp" From 423b2906be19ad6a448fd9fda1e338972090510b Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 4 Feb 2014 11:40:48 +0100 Subject: [PATCH 753/889] Yes, you can drag. But not drop. --- CMakeLists.txt | 2 +- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/world/tablemimedata.cpp | 8 ++++---- apps/opencs/model/world/tablemimedata.hpp | 10 +++++++--- apps/opencs/view/world/table.cpp | 21 +++++++++++++++++++++ apps/opencs/view/world/table.hpp | 3 +++ 6 files changed, 37 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5cb2fd5b2..c4981841a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -324,7 +324,7 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg configure_file(${OpenMW_SOURCE_DIR}/files/opencs.cfg "${OpenMW_BINARY_DIR}/opencs.cfg") - + configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters "${OpenMW_BINARY_DIR}/resources/defaultfilters" COPYONLY) diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index e2dffdbde..2c53e2fdd 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -24,7 +24,7 @@ opencs_units (model/world opencs_units_noqt (model/world universalid record commands columnbase scriptcontext cell refidcollection - refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection + refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata ) opencs_hdrs_noqt (model/world diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp index 457f5af1b..5f04d316a 100644 --- a/apps/opencs/model/world/tablemimedata.cpp +++ b/apps/opencs/model/world/tablemimedata.cpp @@ -1,10 +1,10 @@ #include "tablemimedata.hpp" #include "universalid.hpp" -CSMWorld::TableMimeData::TableMimeData (CSMWorld::UniversalId& UniversalId) : -mUniversalId(UniversalId) +CSMWorld::TableMimeData::TableMimeData (UniversalId id) : +mUniversalId(id) { - mSupportedFormats << UniversalId.toString().c_str(); + mSupportedFormats << QString::fromStdString("application/Type_" + id.getTypeName()); } QStringList CSMWorld::TableMimeData::formats() const @@ -19,4 +19,4 @@ CSMWorld::TableMimeData::~TableMimeData() CSMWorld::UniversalId& CSMWorld::TableMimeData::getId() { return mUniversalId; -} +} \ No newline at end of file diff --git a/apps/opencs/model/world/tablemimedata.hpp b/apps/opencs/model/world/tablemimedata.hpp index d142c66c1..5883966cd 100644 --- a/apps/opencs/model/world/tablemimedata.hpp +++ b/apps/opencs/model/world/tablemimedata.hpp @@ -5,7 +5,11 @@ #define TABLEMIMEDATA_H #include -#include +#include + +#include "universalid.hpp" + +class QStringList; namespace CSMWorld { @@ -13,14 +17,14 @@ namespace CSMWorld class TableMimeData : public QMimeData { public: - TableMimeData(UniversalId& UniversalId); + TableMimeData(UniversalId id); ~TableMimeData(); virtual QStringList formats() const; UniversalId& getId(); private: QStringList mSupportedFormats; - UniversalId& mUniversalId; + UniversalId mUniversalId; }; } #endif // TABLEMIMEDATA_H diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 7d0255529..450af191e 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -412,3 +412,24 @@ void CSVWorld::Table::recordFilterChanged (boost::shared_ptr fi { mProxyModel->setFilter (filter); } + +void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) +{ + if (event->buttons() & Qt::LeftButton) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size() == 0) + { + return; + } + + if (selectedRows.size() == 1) //tmp solution + { + CSMWorld::TableMimeData *mime = new CSMWorld::TableMimeData(getUniversalId(selectedRows.begin()->row())); + QDrag *drag = new QDrag(this); + drag->setMimeData(mime); + drag->start(); + } + } +} \ No newline at end of file diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 889e2847a..ec08e4e2b 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -5,6 +5,7 @@ #include #include +#include #include "../../model/filter/node.hpp" @@ -49,6 +50,8 @@ namespace CSVWorld std::vector listDeletableSelectedIds() const; + void mouseMoveEvent(QMouseEvent *event); + public: Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, bool sorting); From 3439940a8e3fd63e594ca631f30e3390c1bb45d2 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 4 Feb 2014 18:30:47 +0100 Subject: [PATCH 754/889] Tablemimedata able to handle vector of objects and return icon. --- apps/opencs/model/world/tablemimedata.cpp | 56 ++++++++++++++++++++--- apps/opencs/model/world/tablemimedata.hpp | 12 +++-- 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp index 5f04d316a..b501ce2ea 100644 --- a/apps/opencs/model/world/tablemimedata.cpp +++ b/apps/opencs/model/world/tablemimedata.cpp @@ -1,22 +1,64 @@ #include "tablemimedata.hpp" #include "universalid.hpp" +#include -CSMWorld::TableMimeData::TableMimeData (UniversalId id) : -mUniversalId(id) +CSMWorld::TableMimeData::TableMimeData (UniversalId id) { - mSupportedFormats << QString::fromStdString("application/Type_" + id.getTypeName()); + mUniversalId.push_back(id); + mObjectsFormats << QString::fromStdString("application/Type_" + id.getTypeName()); +} + +CSMWorld::TableMimeData::TableMimeData (std::vector< CSMWorld::UniversalId >& id) +{ + mUniversalId = id; + for (std::vector::iterator it(mUniversalId.begin()); it != mUniversalId.end(); ++it) + { + mObjectsFormats << QString::fromStdString("application/Type_" + it->getTypeName()); + } } QStringList CSMWorld::TableMimeData::formats() const { - return QMimeData::formats(); + return mObjectsFormats; } CSMWorld::TableMimeData::~TableMimeData() { } -CSMWorld::UniversalId& CSMWorld::TableMimeData::getId() +CSMWorld::UniversalId CSMWorld::TableMimeData::getId(unsigned int index) const { - return mUniversalId; -} \ No newline at end of file + if (mUniversalId.empty()) + { + throw("TableMimeData holds no UniversalId"); + } + return mUniversalId[index]; +} + +std::string CSMWorld::TableMimeData::getIcon() const +{ + if (mUniversalId.empty()) + { + throw("TableMimeData holds no UniversalId"); + } + + std::string tmpIcon; + bool firstIteration = true; + for (unsigned i = 0; i < mUniversalId.size(); ++i) + { + if (firstIteration) + { + firstIteration = false; + tmpIcon = mUniversalId[i].getIcon(); + continue; + } + + if (tmpIcon != mUniversalId[i].getIcon()) + { + return ""; //should return multiple types icon, but at the moment we don't have one + } + + tmpIcon = mUniversalId[i].getIcon(); + } + return mUniversalId.begin()->getIcon(); //All objects are of the same type; +} diff --git a/apps/opencs/model/world/tablemimedata.hpp b/apps/opencs/model/world/tablemimedata.hpp index 5883966cd..789f4b524 100644 --- a/apps/opencs/model/world/tablemimedata.hpp +++ b/apps/opencs/model/world/tablemimedata.hpp @@ -4,27 +4,29 @@ #ifndef TABLEMIMEDATA_H #define TABLEMIMEDATA_H +#include + #include #include #include "universalid.hpp" -class QStringList; namespace CSMWorld { - class UniversalId; class TableMimeData : public QMimeData { public: TableMimeData(UniversalId id); + TableMimeData(std::vector& id); ~TableMimeData(); virtual QStringList formats() const; - UniversalId& getId(); + UniversalId getId(unsigned int index) const; + std::string getIcon() const; private: - QStringList mSupportedFormats; - UniversalId mUniversalId; + std::vector mUniversalId; + QStringList mObjectsFormats; }; } #endif // TABLEMIMEDATA_H From 8b799683c1e41dd87d2af6bfaa7fe6ed74e44133 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 4 Feb 2014 18:48:18 +0100 Subject: [PATCH 755/889] Display QPixMap with dragged object. --- apps/opencs/view/world/table.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 450af191e..e7ae62e02 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -421,7 +421,7 @@ void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) if (selectedRows.size() == 0) { - return; + return; } if (selectedRows.size() == 1) //tmp solution @@ -429,6 +429,7 @@ void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) CSMWorld::TableMimeData *mime = new CSMWorld::TableMimeData(getUniversalId(selectedRows.begin()->row())); QDrag *drag = new QDrag(this); drag->setMimeData(mime); + drag->setPixmap(QString::fromStdString(mime->getIcon())); drag->start(); } } From 04287cb87ab01b7458d842b26ae289887c798ca8 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 4 Feb 2014 19:42:52 +0100 Subject: [PATCH 756/889] Provide method to return whole data vector from tablemimedata. --- apps/opencs/model/world/tablemimedata.cpp | 5 +++++ apps/opencs/model/world/tablemimedata.hpp | 1 + 2 files changed, 6 insertions(+) diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp index b501ce2ea..7ca287521 100644 --- a/apps/opencs/model/world/tablemimedata.cpp +++ b/apps/opencs/model/world/tablemimedata.cpp @@ -62,3 +62,8 @@ std::string CSMWorld::TableMimeData::getIcon() const } return mUniversalId.begin()->getIcon(); //All objects are of the same type; } + +std::vector< CSMWorld::UniversalId > CSMWorld::TableMimeData::getData() const +{ + return mUniversalId; +} \ No newline at end of file diff --git a/apps/opencs/model/world/tablemimedata.hpp b/apps/opencs/model/world/tablemimedata.hpp index 789f4b524..90f7aefe2 100644 --- a/apps/opencs/model/world/tablemimedata.hpp +++ b/apps/opencs/model/world/tablemimedata.hpp @@ -23,6 +23,7 @@ namespace CSMWorld virtual QStringList formats() const; UniversalId getId(unsigned int index) const; std::string getIcon() const; + std::vector getData() const; private: std::vector mUniversalId; From df78357e0512e05c444ac41f98b7f3118e5c4bf9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 2 Feb 2014 03:24:40 +0100 Subject: [PATCH 757/889] Handle knockout separately (Closes #1151) --- apps/openmw/mwmechanics/character.cpp | 17 +++++++++++++++-- apps/openmw/mwmechanics/character.hpp | 1 + apps/openmw/mwmechanics/creaturestats.cpp | 3 --- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index d58dee9ad..2f234db7b 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -157,7 +157,14 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat bool block = mPtr.getClass().getCreatureStats(mPtr).getBlock(); if(mHitState == CharState_None) { - if(knockdown) + if (mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() < 0) + { + mHitState = CharState_KnockOut; + mCurrentHit = "knockout"; + mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, false, 1, "start", "stop", 0.0f, ~0ul); + mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(true); + } + else if(knockdown) { mHitState = CharState_KnockDown; mCurrentHit = "knockdown"; @@ -187,6 +194,12 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat mPtr.getClass().getCreatureStats(mPtr).setBlock(false); mHitState = CharState_None; } + else if (mHitState == CharState_KnockOut && mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() > 0) + { + mHitState = CharState_KnockDown; + mAnimation->disable(mCurrentHit); + mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, true, 1, "loop stop", "stop", 0.0f, 0); + } } const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); @@ -1121,7 +1134,7 @@ void CharacterController::update(float duration) if (!mSkipAnim) { rot *= Ogre::Math::RadiansToDegrees(1.0f); - if(mHitState != CharState_KnockDown) + if(mHitState != CharState_KnockDown && mHitState != CharState_KnockOut) { world->rotateObject(mPtr, rot.x, rot.y, rot.z, true); } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 8b6dc6c94..0c022c462 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -93,6 +93,7 @@ enum CharacterState { CharState_Hit, CharState_KnockDown, + CharState_KnockOut, CharState_Block }; diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index c862c0ab4..8f890befb 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -179,9 +179,6 @@ namespace MWMechanics mDynamic[index] = value; - if (index == 2 && value.getCurrent() < 0) - setKnockedDown(true); - if (index==0 && mDynamic[index].getCurrent()<1) { if (!mDead) From cf3812188f73984c0e7c53a829c440e8da6717bb Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 3 Feb 2014 20:56:17 +0100 Subject: [PATCH 758/889] Fix bounding box assertions due to negative particle life time Other parts of the code could not deal with negative life times and produced negative particle sizes as a result (which Ogre could not handle) --- components/nifogre/ogrenifloader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 4f755a85a..adf379af0 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -749,8 +749,8 @@ class NIFObjectLoader emitter->setParticleVelocity(partctrl->velocity - partctrl->velocityRandom*0.5f, partctrl->velocity + partctrl->velocityRandom*0.5f); emitter->setEmissionRate(partctrl->emitRate); - emitter->setTimeToLive(partctrl->lifetime - partctrl->lifetimeRandom*0.5f, - partctrl->lifetime + partctrl->lifetimeRandom*0.5f); + emitter->setTimeToLive(partctrl->lifetime, + partctrl->lifetime + partctrl->lifetimeRandom); emitter->setParameter("width", Ogre::StringConverter::toString(partctrl->offsetRandom.x)); emitter->setParameter("height", Ogre::StringConverter::toString(partctrl->offsetRandom.y)); emitter->setParameter("depth", Ogre::StringConverter::toString(partctrl->offsetRandom.z)); From 2c0d46525b74672b3f68a44abddc2efb22b67b49 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 3 Feb 2014 20:57:46 +0100 Subject: [PATCH 759/889] Fast-forward particle systems to make cell loads less obvious. --- components/nifogre/ogrenifloader.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index adf379af0..43e8934c8 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -882,6 +882,9 @@ class NIFObjectLoader Ogre::ControllerFunctionRealPtr func(function); scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + + if (partflags&Nif::NiNode::ParticleFlag_AutoPlay) + partsys->fastForward(1, 0.1); } ctrl = ctrl->next; } From 5b076aa570bd1e28838b6045201bb5d83aadbdc9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 3 Feb 2014 21:04:30 +0100 Subject: [PATCH 760/889] Revert "Bug #1074: Inventory paperdoll obscures armour rating" This reverts commit b017a3be3e4e88d8aea6150287893f3677d6fb69. --- apps/openmw/mwrender/characterpreview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 08749fee7..f46389850 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -128,7 +128,7 @@ namespace MWRender InventoryPreview::InventoryPreview(MWWorld::Ptr character) - : CharacterPreview(character, 512, 1024, "CharacterPreview", Ogre::Vector3(0, 62, -200), Ogre::Vector3(0, 62, 0)) + : CharacterPreview(character, 512, 1024, "CharacterPreview", Ogre::Vector3(0, 65, -180), Ogre::Vector3(0,65,0)) , mSelectionBuffer(NULL) { } From 8aed4fcfa408c45ba99b91cabc0f53f550470605 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 3 Feb 2014 22:24:03 +0100 Subject: [PATCH 761/889] Correction for marksman weapon inventory preview --- apps/openmw/mwrender/characterpreview.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index f46389850..280828652 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -161,13 +161,13 @@ namespace MWRender type == ESM::Weapon::LongBladeOneHand || type == ESM::Weapon::BluntOneHand || type == ESM::Weapon::AxeOneHand || - type == ESM::Weapon::MarksmanThrown) + type == ESM::Weapon::MarksmanThrown || + type == ESM::Weapon::MarksmanCrossbow || + type == ESM::Weapon::MarksmanBow) groupname = "inventoryweapononehand"; else if(type == ESM::Weapon::LongBladeTwoHand || type == ESM::Weapon::BluntTwoClose || - type == ESM::Weapon::AxeTwoHand || - type == ESM::Weapon::MarksmanCrossbow || - type == ESM::Weapon::MarksmanBow) + type == ESM::Weapon::AxeTwoHand) groupname = "inventoryweapontwohand"; else if(type == ESM::Weapon::BluntTwoWide || type == ESM::Weapon::SpearTwoWide) From f608ceeffcbaab71b50a053513e5dfd11eccf403 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 3 Feb 2014 22:32:50 +0100 Subject: [PATCH 762/889] Fixes #1158 (Armor rating label issues) --- apps/openmw/mwgui/inventorywindow.cpp | 36 +++++++++++----------- apps/openmw/mwgui/inventorywindow.hpp | 2 -- apps/openmw/mwgui/tooltips.cpp | 2 +- files/mygui/openmw_inventory_window.layout | 7 ++--- 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 7139c1b2c..2ea09db61 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -55,7 +55,7 @@ namespace MWGui getWidget(mRightPane, "RightPane"); getWidget(mArmorRating, "ArmorRating"); - mAvatar->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onAvatarClicked); + mAvatarImage->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onAvatarClicked); getWidget(mItemView, "ItemView"); mItemView->eventItemClicked += MyGUI::newDelegate(this, &InventoryWindow::onItemSelected); @@ -76,11 +76,12 @@ namespace MWGui void InventoryWindow::adjustPanes() { - const float aspect = 0.5; // fixed aspect ratio for the left pane - mLeftPane->setSize( (mMainWidget->getSize().height-44) * aspect, mMainWidget->getSize().height-44 ); - mRightPane->setCoord( mLeftPane->getPosition().left + (mMainWidget->getSize().height-44) * aspect + 4, + const float aspect = 0.5; // fixed aspect ratio for the avatar image + float leftPaneWidth = (mMainWidget->getSize().height-44-mArmorRating->getHeight()) * aspect; + mLeftPane->setSize( leftPaneWidth, mMainWidget->getSize().height-44 ); + mRightPane->setCoord( mLeftPane->getPosition().left + leftPaneWidth + 4, mRightPane->getPosition().top, - mMainWidget->getSize().width - 12 - (mMainWidget->getSize().height-44) * aspect - 15, + mMainWidget->getSize().width - 12 - leftPaneWidth - 15, mMainWidget->getSize().height-44 ); } @@ -418,9 +419,9 @@ namespace MWGui else { MyGUI::IntPoint mousePos = MyGUI::InputManager::getInstance ().getLastPressedPosition (MyGUI::MouseButton::Left); - MyGUI::IntPoint relPos = mousePos - mAvatar->getAbsolutePosition (); - int realX = int(float(relPos.left) / float(mAvatar->getSize().width) * 512.f ); - int realY = int(float(relPos.top) / float(mAvatar->getSize().height) * 1024.f ); + MyGUI::IntPoint relPos = mousePos - mAvatarImage->getAbsolutePosition (); + int realX = int(float(relPos.left) / float(mAvatarImage->getSize().width) * 512.f ); + int realY = int(float(relPos.top) / float(mAvatarImage->getSize().height) * 1024.f ); MWWorld::Ptr itemSelected = getAvatarSelectedItem (realX, realY); if (itemSelected.isEmpty ()) @@ -487,11 +488,18 @@ namespace MWGui if (mPreviewDirty) { mPreviewDirty = false; - MyGUI::IntSize size = mAvatar->getSize(); + MyGUI::IntSize size = mAvatarImage->getSize(); mPreview.update (size.width, size.height); - mAvatarImage->setSize(MyGUI::IntSize(std::max(mAvatar->getSize().width, 512), std::max(mAvatar->getSize().height, 1024))); + mAvatarImage->setImageTexture("CharacterPreview"); + mAvatarImage->setImageCoord(MyGUI::IntCoord(0, 0, std::min(512, size.width), std::min(1024, size.height))); + mAvatarImage->setImageTile(MyGUI::IntSize(std::min(512, size.width), std::min(1024, size.height))); + + mArmorRating->setCaptionWithReplacing ("#{sArmor}: " + + boost::lexical_cast(static_cast(MWWorld::Class::get(mPtr).getArmorRating(mPtr)))); + if (mArmorRating->getTextSize().width > mArmorRating->getSize().width) + mArmorRating->setCaptionWithReplacing (boost::lexical_cast(static_cast(MWWorld::Class::get(mPtr).getArmorRating(mPtr)))); } } @@ -502,9 +510,6 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->getSpellWindow()->updateSpells(); mPreviewDirty = true; - - mArmorRating->setCaptionWithReplacing ("#{sArmor}: " - + boost::lexical_cast(static_cast(MWWorld::Class::get(mPtr).getArmorRating(mPtr)))); } void InventoryWindow::pickUpObject (MWWorld::Ptr object) @@ -551,9 +556,4 @@ namespace MWGui MWBase::Environment::get().getMechanicsManager()->itemTaken(player, newObject, count); } - - MyGUI::IntCoord InventoryWindow::getAvatarScreenCoord () - { - return mAvatar->getAbsoluteCoord (); - } } diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 7e5a0fe10..7ef168e98 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -31,8 +31,6 @@ namespace MWGui void pickUpObject (MWWorld::Ptr object); - MyGUI::IntCoord getAvatarScreenCoord(); - MWWorld::Ptr getAvatarSelectedItem(int x, int y); void rebuildAvatar() { diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 0fe500879..8716c4dea 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -182,7 +182,7 @@ namespace MWGui } else if (type == "AvatarItemSelection") { - MyGUI::IntCoord avatarPos = MWBase::Environment::get().getWindowManager()->getInventoryWindow ()->getAvatarScreenCoord (); + MyGUI::IntCoord avatarPos = focus->getAbsoluteCoord(); MyGUI::IntPoint relMousePos = MyGUI::InputManager::getInstance ().getMousePosition () - MyGUI::IntPoint(avatarPos.left, avatarPos.top); int realX = int(float(relMousePos.left) / float(avatarPos.width) * 512.f ); int realY = int(float(relMousePos.top) / float(avatarPos.height) * 1024.f ); diff --git a/files/mygui/openmw_inventory_window.layout b/files/mygui/openmw_inventory_window.layout index ba6bf820e..09e5ed9c7 100644 --- a/files/mygui/openmw_inventory_window.layout +++ b/files/mygui/openmw_inventory_window.layout @@ -12,11 +12,10 @@ - - - + + - + From e1e7a492e200b517b730b06546c5089741d8d64c Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 4 Feb 2014 00:14:06 +0100 Subject: [PATCH 763/889] Fix movement speed formula for flying creatures --- apps/openmw/mwclass/creature.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 1f07ede7e..6af8373c5 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -529,8 +529,8 @@ namespace MWClass float moveSpeed; if(normalizedEncumbrance >= 1.0f) moveSpeed = 0.0f; - else if(mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 && - world->isLevitationEnabled()) + else if(isFlying(ptr) || (mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 && + world->isLevitationEnabled())) { float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() + mageffects.get(ESM::MagicEffect::Levitate).mMagnitude); From 975f0e05b48450aa2606a179ef32217bfeef7c92 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 4 Feb 2014 01:07:27 +0100 Subject: [PATCH 764/889] Fixes #1157 (attribute layout issue) --- files/mygui/openmw_stats_window.layout | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/files/mygui/openmw_stats_window.layout b/files/mygui/openmw_stats_window.layout index efec0ab37..6cdd4c02a 100644 --- a/files/mygui/openmw_stats_window.layout +++ b/files/mygui/openmw_stats_window.layout @@ -100,7 +100,9 @@ - + + + @@ -114,7 +116,7 @@ - + @@ -128,7 +130,7 @@ - + @@ -142,7 +144,7 @@ - + @@ -156,7 +158,7 @@ - + @@ -170,7 +172,7 @@ - + @@ -184,7 +186,7 @@ - + @@ -198,7 +200,7 @@ - + @@ -212,6 +214,7 @@ + From 7cf22391a5343bf82228803eeeaa756c0e3436de Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 4 Feb 2014 03:46:15 +0100 Subject: [PATCH 765/889] Feature #50: Handle weapon controllers (i.e. bowstring animations, etc) --- apps/openmw/mwmechanics/character.cpp | 1 + apps/openmw/mwrender/animation.cpp | 21 +++++++++++++++++++ apps/openmw/mwrender/animation.hpp | 8 ++++++++ apps/openmw/mwrender/npcanimation.cpp | 29 ++++++++++++++++++++++++--- apps/openmw/mwrender/npcanimation.hpp | 20 ++++++++++++++++++ 5 files changed, 76 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 2f234db7b..b73283e7c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -547,6 +547,7 @@ bool CharacterController::updateWeaponState() { getWeaponGroup(weaptype, weapgroup); mAnimation->showWeapons(false); + mAnimation->setWeaponGroup(weapgroup); mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, true, diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index ba729e3da..5417f1820 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -870,6 +870,27 @@ bool Animation::getInfo(const std::string &groupname, float *complete, float *sp return true; } +float Animation::getStartTime(const std::string &groupname) const +{ + AnimSourceList::const_iterator iter(mAnimSources.begin()); + for(;iter != mAnimSources.end();iter++) + { + const NifOgre::TextKeyMap &keys = (*iter)->mTextKeys; + NifOgre::TextKeyMap::const_iterator found = findGroupStart(keys, groupname); + if(found != keys.end()) + return found->first; + } + return -1.f; +} + +float Animation::getCurrentTime(const std::string &groupname) const +{ + AnimStateMap::const_iterator iter = mStates.find(groupname); + if(iter == mStates.end()) + return -1.f; + + return iter->second.mTime; +} void Animation::disable(const std::string &groupname) { diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index da1c1628c..27778d373 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -271,12 +271,20 @@ public: */ bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL) const; + /// Get the absolute position in the animation track of the first text key with the given group. + float getStartTime(const std::string &groupname) const; + + /// Get the current absolute position in the animation track for the animation that is currently playing from the given group. + float getCurrentTime(const std::string& groupname) const; + /** Disables the specified animation group; * \param groupname Animation group to disable. */ void disable(const std::string &groupname); void changeGroups(const std::string &groupname, int group); + virtual void setWeaponGroup(const std::string& group) {} + /** Retrieves the velocity (in units per second) that the animation will move. */ float getVelocity(const std::string &groupname) const; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index bcb6a374c..531d90162 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -71,6 +71,27 @@ float HeadAnimationTime::getValue() const return 1; } +float WeaponAnimationTime::getValue() const +{ + if (mWeaponGroup.empty()) + return 0; + float current = mAnimation->getCurrentTime(mWeaponGroup); + if (current == -1) + return 0; + return current - mStartTime; +} + +void WeaponAnimationTime::setGroup(const std::string &group) +{ + mWeaponGroup = group; + mStartTime = mAnimation->getStartTime(mWeaponGroup); +} + +void WeaponAnimationTime::updateStartTime() +{ + setGroup(mWeaponGroup); +} + static NpcAnimation::PartBoneMap createPartListMap() { NpcAnimation::PartBoneMap result; @@ -126,6 +147,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v mNpc = mPtr.get()->mBase; mHeadAnimationTime = Ogre::SharedPtr(new HeadAnimationTime(mPtr)); + mWeaponAnimationTime = Ogre::SharedPtr(new WeaponAnimationTime(this)); for(size_t i = 0;i < ESM::PRT_Count;i++) { @@ -223,6 +245,8 @@ void NpcAnimation::updateNpcBase() for(size_t i = 0;i < ESM::PRT_Count;i++) removeIndividualPart((ESM::PartReferenceType)i); updateParts(); + + mWeaponAnimationTime->updateStartTime(); } void NpcAnimation::updateParts() @@ -588,9 +612,6 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g updateSkeletonInstance(mSkelBase->getSkeleton(), skel); } - // TODO: - // type == ESM::PRT_Weapon should get an animation source based on the current offset - // of the weapon attack animation (from its beginning, or start marker?) std::vector >::iterator ctrl(mObjectParts[type]->mControllers.begin()); for(;ctrl != mObjectParts[type]->mControllers.end();ctrl++) { @@ -600,6 +621,8 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g if (type == ESM::PRT_Head) ctrl->setSource(mHeadAnimationTime); + else if (type == ESM::PRT_Weapon) + ctrl->setSource(mWeaponAnimationTime); } } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index e86ec7d4e..1dd0b41e6 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -25,6 +25,23 @@ public: { } }; +class WeaponAnimationTime : public Ogre::ControllerValue +{ +private: + Animation* mAnimation; + std::string mWeaponGroup; + float mStartTime; +public: + WeaponAnimationTime(Animation* animation) : mAnimation(animation), mStartTime(0) {} + void setGroup(const std::string& group); + void updateStartTime(); + + virtual Ogre::Real getValue() const; + virtual void setValue(Ogre::Real value) + { } +}; + + class NpcAnimation : public Animation, public MWWorld::InventoryStoreListener { public: @@ -71,6 +88,7 @@ private: Ogre::Vector3 mFirstPersonOffset; Ogre::SharedPtr mHeadAnimationTime; + Ogre::SharedPtr mWeaponAnimationTime; float mAlpha; @@ -105,6 +123,8 @@ public: ViewMode viewMode=VM_Normal); virtual ~NpcAnimation(); + virtual void setWeaponGroup(const std::string& group) { mWeaponAnimationTime->setGroup(group); } + virtual Ogre::Vector3 runAnimation(float timepassed); virtual void showWeapons(bool showWeapon); From 5ee105c8120b6c864ca73d52183991fa39e6aed7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 4 Feb 2014 03:55:40 +0100 Subject: [PATCH 766/889] Fix typo --- apps/openmw/mwmechanics/aicombat.cpp | 2 +- apps/openmw/mwmechanics/character.cpp | 6 +++--- apps/openmw/mwmechanics/character.hpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 2ff7ec229..3653587f8 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -168,7 +168,7 @@ namespace MWMechanics float rangeMelee; float rangeCloseUp; bool distantCombat = false; - if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_ThowWeapon) + if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_Thrown) { rangeMelee = 1000; // TODO: should depend on archer skill rangeCloseUp = 0; //doesn't needed when attacking from distance diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index b73283e7c..9ea159e03 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -119,7 +119,7 @@ static const struct WeaponInfo { { WeapType_TwoWide, "2w", "weapontwowide" }, { WeapType_BowAndArrow, "1h", "bowandarrow" }, { WeapType_Crossbow, "crossbow", "crossbow" }, - { WeapType_ThowWeapon, "1h", "throwweapon" }, + { WeapType_Thrown, "1h", "throwweapon" }, { WeapType_PickProbe, "1h", "pickprobe" }, { WeapType_Spell, "spell", "spellcast" }, }; @@ -380,7 +380,7 @@ MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::I *weaptype = WeapType_Crossbow; break; case ESM::Weapon::MarksmanThrown: - *weaptype = WeapType_ThowWeapon; + *weaptype = WeapType_Thrown; break; } } @@ -709,7 +709,7 @@ bool CharacterController::updateWeaponState() else { if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || - mWeaponType == WeapType_ThowWeapon) + mWeaponType == WeapType_Thrown) mAttackType = "shoot"; else { diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 0c022c462..cf684d320 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -106,7 +106,7 @@ enum WeaponType { WeapType_TwoWide, WeapType_BowAndArrow, WeapType_Crossbow, - WeapType_ThowWeapon, + WeapType_Thrown, WeapType_PickProbe, WeapType_Spell From a07eaa0c0ddd237529b357acfd59e2bc85d4e23a Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 4 Feb 2014 04:00:52 +0100 Subject: [PATCH 767/889] Feature #50: Allow body pitch in third person for ranged weapon aiming --- apps/openmw/mwmechanics/character.cpp | 44 +++++++++++++++++++++++++++ apps/openmw/mwrender/animation.cpp | 1 - apps/openmw/mwrender/animation.hpp | 8 ++--- apps/openmw/mwrender/camera.cpp | 2 -- apps/openmw/mwrender/npcanimation.cpp | 18 ++++++++--- apps/openmw/mwrender/npcanimation.hpp | 5 +++ 6 files changed, 67 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 9ea159e03..df6e7938f 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -773,6 +773,50 @@ bool CharacterController::updateWeaponState() } } + mAnimation->setPitchFactor(0.f); + if (mWeaponType == WeapType_BowAndArrow || mWeaponType == WeapType_Thrown) + { + switch (mUpperBodyState) + { + case UpperCharState_StartToMinAttack: + mAnimation->setPitchFactor(complete); + break; + case UpperCharState_MinAttackToMaxAttack: + case UpperCharState_MaxAttackToMinHit: + case UpperCharState_MinHitToHit: + mAnimation->setPitchFactor(1.f); + break; + case UpperCharState_FollowStartToFollowStop: + if (animPlaying) + mAnimation->setPitchFactor(1.f-complete); + break; + default: + break; + } + } + else if (mWeaponType == WeapType_Crossbow) + { + switch (mUpperBodyState) + { + case UpperCharState_EquipingWeap: + mAnimation->setPitchFactor(complete); + break; + case UpperCharState_UnEquipingWeap: + mAnimation->setPitchFactor(1.f-complete); + break; + case UpperCharState_WeapEquiped: + case UpperCharState_StartToMinAttack: + case UpperCharState_MinAttackToMaxAttack: + case UpperCharState_MaxAttackToMinHit: + case UpperCharState_MinHitToHit: + case UpperCharState_FollowStartToFollowStop: + mAnimation->setPitchFactor(1.f); + break; + default: + break; + } + } + if(!animPlaying) { if(mUpperBodyState == UpperCharState_EquipingWeap || diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 5417f1820..66153a7e0 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -53,7 +53,6 @@ void Animation::EffectAnimationTime::setValue(Ogre::Real) Animation::Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node) : mPtr(ptr) - , mCamera(NULL) , mInsert(node) , mSkelBase(NULL) , mAccumRoot(NULL) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 27778d373..ed7ed819f 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -121,7 +121,6 @@ protected: std::vector mEffects; MWWorld::Ptr mPtr; - Camera *mCamera; Ogre::SceneNode *mInsert; Ogre::Entity *mSkelBase; @@ -288,6 +287,10 @@ public: /** Retrieves the velocity (in units per second) that the animation will move. */ float getVelocity(const std::string &groupname) const; + /// A relative factor (0-1) that decides if and how much the skeleton should be pitched + /// to indicate the facing orientation of the character. + virtual void setPitchFactor(float factor) {} + virtual Ogre::Vector3 runAnimation(float duration); virtual void showWeapons(bool showWeapon); @@ -297,9 +300,6 @@ public: Ogre::AxisAlignedBox getWorldBounds(); - void setCamera(Camera *cam) - { mCamera = cam; } - Ogre::Node *getNode(const std::string &name); // Attaches the given object to a bone on this object's base skeleton. If the bone doesn't diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 9c8387b83..9ae9c5878 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -338,11 +338,9 @@ namespace MWRender if(mAnimation && mAnimation != anim) { mAnimation->setViewMode(NpcAnimation::VM_Normal); - mAnimation->setCamera(NULL); mAnimation->detachObjectFromBone(mCamera); } mAnimation = anim; - mAnimation->setCamera(this); processViewChange(); } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 531d90162..d051bde52 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -142,7 +142,8 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v mShowCarriedLeft(true), mFirstPersonOffset(0.f, 0.f, 0.f), mAlpha(1.f), - mNpcType(Type_Normal) + mNpcType(Type_Normal), + mPitchFactor(0) { mNpc = mPtr.get()->mBase; @@ -522,16 +523,25 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) Ogre::Vector3 ret = Animation::runAnimation(timepassed); Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton(); - if(mViewMode == VM_FirstPerson && mCamera) + if(mViewMode == VM_FirstPerson) { - float pitch = mCamera->getPitch(); + float pitch = mPtr.getRefData().getPosition().rot[0]; Ogre::Node *node = baseinst->getBone("Bip01 Neck"); - node->pitch(Ogre::Radian(pitch*0.75f), Ogre::Node::TS_WORLD); + node->pitch(Ogre::Radian(pitch), Ogre::Node::TS_WORLD); // This has to be done before this function ends; // updateSkeletonInstance, below, touches the hands. node->translate(mFirstPersonOffset, Ogre::Node::TS_WORLD); } + else if (mPitchFactor > 0) + { + // In third person mode we may still need pitch for ranged weapon targeting + float pitch = mPtr.getRefData().getPosition().rot[0] * mPitchFactor; + Ogre::Node *node = baseinst->getBone("Bip01 Spine2"); + node->pitch(Ogre::Radian(pitch/2), Ogre::Node::TS_WORLD); + node = baseinst->getBone("Bip01 Spine1"); + node->pitch(Ogre::Radian(pitch/2), Ogre::Node::TS_WORLD); + } mFirstPersonOffset = 0.f; // reset the X, Y, Z offset for the next frame. for(size_t i = 0;i < ESM::PRT_Count;i++) diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 1dd0b41e6..9baf975f1 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -91,6 +91,7 @@ private: Ogre::SharedPtr mWeaponAnimationTime; float mAlpha; + float mPitchFactor; void updateNpcBase(); @@ -127,6 +128,10 @@ public: virtual Ogre::Vector3 runAnimation(float timepassed); + /// A relative factor (0-1) that decides if and how much the skeleton should be pitched + /// to indicate the facing orientation of the character. + virtual void setPitchFactor(float factor) { mPitchFactor = factor; } + virtual void showWeapons(bool showWeapon); virtual void showCarriedLeft(bool showa); From 8b8fb931a009a87b874c73a6f87a756bad73b0bf Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 4 Feb 2014 04:05:01 +0100 Subject: [PATCH 768/889] Feature #50: Don't allow ranged weapon attack when ammunition is empty --- apps/openmw/mwmechanics/character.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index df6e7938f..5d7794e66 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -602,6 +602,19 @@ bool CharacterController::updateWeaponState() if(isWeapon) weapSpeed = weapon->get()->mBase->mData.mSpeed; + // Cancel attack if we no longer have ammunition + bool ammunition = true; + MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); + if (mWeaponType == WeapType_Crossbow) + ammunition = (ammo != inv.end() && ammo->get()->mBase->mData.mType == ESM::Weapon::Bolt); + else if (mWeaponType == WeapType_BowAndArrow) + ammunition = (ammo != inv.end() && ammo->get()->mBase->mData.mType == ESM::Weapon::Arrow); + if (!ammunition && mUpperBodyState > UpperCharState_WeapEquiped) + { + mAnimation->disable(mCurrentWeapon); + mUpperBodyState = UpperCharState_WeapEquiped; + } + float complete; bool animPlaying; if(stats.getAttackingOrSpell()) @@ -706,7 +719,7 @@ bool CharacterController::updateWeaponState() if(item.getRefData().getCount()) MWBase::Environment::get().getWindowManager()->setSelectedWeapon(item); } - else + else if (ammunition) { if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || mWeaponType == WeapType_Thrown) From ffe19e7a523cc061d3bde7210023aeded6b1b5dd Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 4 Feb 2014 04:11:46 +0100 Subject: [PATCH 769/889] Feature #50: Handle attach & release of projectiles --- apps/openmw/mwmechanics/character.cpp | 3 ++ apps/openmw/mwrender/animation.cpp | 6 +++ apps/openmw/mwrender/animation.hpp | 3 +- apps/openmw/mwrender/npcanimation.cpp | 57 ++++++++++++++++++++++++++ apps/openmw/mwrender/npcanimation.hpp | 5 +++ apps/openmw/mwworld/inventorystore.cpp | 44 ++++++++++---------- apps/openmw/mwworld/inventorystore.hpp | 4 +- 7 files changed, 96 insertions(+), 26 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 5d7794e66..0a94e4ed4 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -836,6 +836,9 @@ bool CharacterController::updateWeaponState() mUpperBodyState == UpperCharState_FollowStartToFollowStop || mUpperBodyState == UpperCharState_CastingSpell) { + if (ammunition && mWeaponType == WeapType_Crossbow) + mAnimation->attachArrow(); + mUpperBodyState = UpperCharState_WeapEquiped; //don't allow to continue playing hit animation on UpperBody after actor had attacked during it if(mHitState == CharState_Hit) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 66153a7e0..ba662be88 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -695,6 +695,12 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co else mPtr.getClass().hit(mPtr); } + else if (evt.compare(off, len, "shoot attach") == 0) + attachArrow(); + else if (evt.compare(off, len, "shoot release") == 0) + releaseArrow(); + else if (evt.compare(off, len, "shoot follow attach") == 0) + attachArrow(); else if (groupname == "spellcast" && evt.substr(evt.size()-7, 7) == "release") MWBase::Environment::get().getWorld()->castSpell(mPtr); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index ed7ed819f..c0cb18010 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -295,7 +295,8 @@ public: virtual void showWeapons(bool showWeapon); virtual void showCarriedLeft(bool show) {} - + virtual void attachArrow() {} + virtual void releaseArrow() {} void enableLights(bool enable); Ogre::AxisAlignedBox getWorldBounds(); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index d051bde52..bcbcbc737 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -696,6 +696,17 @@ void NpcAnimation::showWeapons(bool showWeapon) std::string mesh = MWWorld::Class::get(*weapon).getModel(*weapon); addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, mesh, !weapon->getClass().getEnchantment(*weapon).empty(), &glowColor); + + if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) + { + MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); + if (ammo != inv.end() && ammo->get()->mBase->mData.mType == ESM::Weapon::Bolt) + attachArrow(); + else + mAmmunition.setNull(); + } + else + mAmmunition.setNull(); } } else @@ -726,6 +737,52 @@ void NpcAnimation::showCarriedLeft(bool show) removeIndividualPart(ESM::PRT_Shield); } +void NpcAnimation::attachArrow() +{ + MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); + MWWorld::ContainerStoreIterator weaponSlot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if (weaponSlot != inv.end() && weaponSlot->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) + showWeapons(true); + else + { + NifOgre::ObjectScenePtr weapon = mObjectParts[ESM::PRT_Weapon]; + + MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); + if (ammo == inv.end()) + return; + std::string model = ammo->getClass().getModel(*ammo); + + mAmmunition = NifOgre::Loader::createObjects(weapon->mSkelBase, "ArrowBone", mInsert, model); + Ogre::Vector3 glowColor = getEnchantmentColor(*ammo); + setRenderProperties(mAmmunition, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha, 0, + !ammo->getClass().getEnchantment(*ammo).empty(), &glowColor); + + std::for_each(mAmmunition->mEntities.begin(), mAmmunition->mEntities.end(), SetObjectGroup(MWWorld::InventoryStore::Slot_Ammunition)); + std::for_each(mAmmunition->mParticles.begin(), mAmmunition->mParticles.end(), SetObjectGroup(MWWorld::InventoryStore::Slot_Ammunition)); + } +} + +void NpcAnimation::releaseArrow() +{ + // Thrown weapons get detached now + MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); + MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if (weapon != inv.end() && weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) + { + showWeapons(false); + inv.remove(*weapon, 1, mPtr); + } + else + { + // With bows and crossbows only the used arrow/bolt gets detached + MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); + if (ammo == inv.end()) + return; + inv.remove(*ammo, 1, mPtr); + mAmmunition.setNull(); + } +} + void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound) { // During first auto equip, we don't play any sounds. diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 9baf975f1..725fde01d 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -135,6 +135,11 @@ public: virtual void showWeapons(bool showWeapon); virtual void showCarriedLeft(bool showa); + virtual void attachArrow(); + virtual void releaseArrow(); + + NifOgre::ObjectScenePtr mAmmunition; + void setViewMode(ViewMode viewMode); void updateParts(); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index e6dfe7a3e..e00276293 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -462,21 +462,23 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSelectedEnchantItem( int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor) { - for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) - { - if (mSlots[slot] == end()) - continue; + int retCount = ContainerStore::remove(item, count, actor); - if (*mSlots[slot] == item) + if (!item.getRefData().getCount()) + { + for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) { - // restacking is disabled cause it may break removal - unequipSlot(slot, actor, false); - break; + if (mSlots[slot] == end()) + continue; + + if (*mSlots[slot] == item) + { + unequipSlot(slot, actor); + break; + } } } - int retCount = ContainerStore::remove(item, count, actor); - // If an armor/clothing item is removed, try to find a replacement, // but not for the player nor werewolves. if ((actor.getRefData().getHandle() != "player") @@ -500,9 +502,9 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor return retCount; } -MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, const MWWorld::Ptr& actor, bool restack) +MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, const MWWorld::Ptr& actor) { - ContainerStoreIterator it = getSlot(slot); + ContainerStoreIterator it = mSlots[slot]; if (it != end()) { @@ -511,17 +513,15 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, c // empty this slot mSlots[slot] = end(); - if (restack) { - // restack item previously in this slot - for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter) + // restack the previously equipped item with other (non-equipped) items + for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter) + { + if (stacks(*iter, *it)) { - if (stacks(*iter, *it)) - { - iter->getRefData().setCount(iter->getRefData().getCount() + it->getRefData().getCount()); - it->getRefData().setCount(0); - retval = iter; - break; - } + iter->getRefData().setCount(iter->getRefData().getCount() + it->getRefData().getCount()); + it->getRefData().setCount(0); + retval = iter; + break; } } diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 868f3e517..714ba47da 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -168,12 +168,10 @@ namespace MWWorld /// /// @return the number of items actually removed - ContainerStoreIterator unequipSlot(int slot, const Ptr& actor, bool restack = true); + ContainerStoreIterator unequipSlot(int slot, const Ptr& actor); ///< Unequip \a slot. /// /// @return an iterator to the item that was previously in the slot - /// (if \a restack is true, the item can be re-stacked so its count - /// may differ from when it was equipped). ContainerStoreIterator unequipItem(const Ptr& item, const Ptr& actor); ///< Unequip an item identified by its Ptr. An exception is thrown From 7907181c0cc0009937a9705d0c4f12079ceb720a Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 4 Feb 2014 07:51:18 +0100 Subject: [PATCH 770/889] Fix uninitialized member --- apps/openmw/mwmechanics/pathfinding.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 70fa197d0..4407363a6 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -158,7 +158,9 @@ namespace namespace MWMechanics { PathFinder::PathFinder() - :mIsPathConstructed(false),mIsGraphConstructed(false) + : mIsPathConstructed(false), + mIsGraphConstructed(false), + mCell(NULL) { } From 0cc1cd8f7ec02882130d49d7b9f25794fcf0d50a Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 4 Feb 2014 23:52:13 +0100 Subject: [PATCH 771/889] Fix message box formatting bug --- apps/openmw/mwgui/messagebox.cpp | 8 ++++---- components/interpreter/miscopcodes.hpp | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index b52aee706..1ce167c33 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -245,11 +245,11 @@ namespace MWGui } mainWidgetSize.height = textSize.height + textButtonPadding + buttonHeight + buttonMainPadding; - MyGUI::IntCoord absCoord; - absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2; - absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2; + MyGUI::IntPoint absPos; + absPos.left = (gameWindowSize.width - mainWidgetSize.width)/2; + absPos.top = (gameWindowSize.height - mainWidgetSize.height)/2; - mMainWidget->setCoord(absCoord); + mMainWidget->setPosition(absPos); mMainWidget->setSize(mainWidgetSize); MyGUI::IntCoord messageWidgetCoord; diff --git a/components/interpreter/miscopcodes.hpp b/components/interpreter/miscopcodes.hpp index 1b4c823a0..1da8cf695 100644 --- a/components/interpreter/miscopcodes.hpp +++ b/components/interpreter/miscopcodes.hpp @@ -48,9 +48,10 @@ namespace Interpreter } else if (c=='f' || c=='F' || c=='.') { - while (c!='f' && i Date: Wed, 5 Feb 2014 08:48:15 +0100 Subject: [PATCH 772/889] Add generated version.hpp to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c061ca637..3975c4521 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ resources ## generated objects apps/openmw/config.hpp +components/version/version.hpp Docs/mainpage.hpp moc_*.cxx *.cxx_parameters From 677fc84223d03a462cbd1a6d4878c9e35eac6f54 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 5 Feb 2014 09:50:21 +0100 Subject: [PATCH 773/889] Refactor actors update --- apps/openmw/mwmechanics/actors.cpp | 52 +++++++++++++++++------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 71b1702e8..1fb22ce63 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -776,24 +776,7 @@ namespace MWMechanics { if(!paused) { - // Note: we need to do this before any of the animations are updated. - // Reaching the text keys may trigger Hit / Spellcast (and as such, particles), - // so updating VFX immediately after that would just remove the particle effects instantly. - // There needs to be a magic effect update in between. - for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) - iter->second->updateContinuousVfx(); - - for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) - { - if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects().get( - ESM::MagicEffect::Paralyze).mMagnitude > 0) - iter->second->skipAnim(); - iter->second->update(duration); - } - } - - if (!paused) - { + // Reset data from previous frame for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { // Reset last hit object, which is only valid for one frame @@ -802,6 +785,35 @@ namespace MWMechanics iter->first.getClass().getCreatureStats(iter->first).setLastHitObject(std::string()); } + // AI and magic effects update + for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + { + if (!iter->first.getClass().getCreatureStats(iter->first).isDead()) + { + updateActor(iter->first, duration); + if(iter->first.getTypeName() == typeid(ESM::NPC).name()) + updateNpc(iter->first, duration, paused); + } + } + + // Looping magic VFX update + // Note: we need to do this before any of the animations are updated. + // Reaching the text keys may trigger Hit / Spellcast (and as such, particles), + // so updating VFX immediately after that would just remove the particle effects instantly. + // There needs to be a magic effect update in between. + for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + iter->second->updateContinuousVfx(); + + // Animation/movement update + for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + { + if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects().get( + ESM::MagicEffect::Paralyze).mMagnitude > 0) + iter->second->skipAnim(); + iter->second->update(duration); + } + + // Kill dead actors for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++) { const MWWorld::Class &cls = MWWorld::Class::get(iter->first); @@ -812,10 +824,6 @@ namespace MWMechanics if(iter->second->isDead()) iter->second->resurrect(); - updateActor(iter->first, duration); - if(iter->first.getTypeName() == typeid(ESM::NPC).name()) - updateNpc(iter->first, duration, paused); - if(!stats.isDead()) continue; } From 76b729ac9baf57049e8b1e50ba7ca67e7a9371bc Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 5 Feb 2014 11:19:55 +0100 Subject: [PATCH 774/889] Removed getUniversalid interface. --- apps/opencs/model/world/tablemimedata.cpp | 9 --------- apps/opencs/model/world/tablemimedata.hpp | 3 +-- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp index 7ca287521..1be694dac 100644 --- a/apps/opencs/model/world/tablemimedata.cpp +++ b/apps/opencs/model/world/tablemimedata.cpp @@ -26,15 +26,6 @@ CSMWorld::TableMimeData::~TableMimeData() { } -CSMWorld::UniversalId CSMWorld::TableMimeData::getId(unsigned int index) const -{ - if (mUniversalId.empty()) - { - throw("TableMimeData holds no UniversalId"); - } - return mUniversalId[index]; -} - std::string CSMWorld::TableMimeData::getIcon() const { if (mUniversalId.empty()) diff --git a/apps/opencs/model/world/tablemimedata.hpp b/apps/opencs/model/world/tablemimedata.hpp index 90f7aefe2..193c62122 100644 --- a/apps/opencs/model/world/tablemimedata.hpp +++ b/apps/opencs/model/world/tablemimedata.hpp @@ -21,7 +21,6 @@ namespace CSMWorld TableMimeData(std::vector& id); ~TableMimeData(); virtual QStringList formats() const; - UniversalId getId(unsigned int index) const; std::string getIcon() const; std::vector getData() const; @@ -30,4 +29,4 @@ namespace CSMWorld QStringList mObjectsFormats; }; } -#endif // TABLEMIMEDATA_H +#endif // TABLEMIMEDATA_H \ No newline at end of file From f01b02c42d2272e0596e31e72bd6113a1d2e2253 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 5 Feb 2014 11:24:33 +0100 Subject: [PATCH 775/889] Changed format. --- apps/opencs/model/world/tablemimedata.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp index 1be694dac..0cdcdba3a 100644 --- a/apps/opencs/model/world/tablemimedata.cpp +++ b/apps/opencs/model/world/tablemimedata.cpp @@ -5,7 +5,7 @@ CSMWorld::TableMimeData::TableMimeData (UniversalId id) { mUniversalId.push_back(id); - mObjectsFormats << QString::fromStdString("application/Type_" + id.getTypeName()); + mObjectsFormats << QString::fromStdString("tabledata/" + id.getTypeName()); } CSMWorld::TableMimeData::TableMimeData (std::vector< CSMWorld::UniversalId >& id) @@ -13,7 +13,7 @@ CSMWorld::TableMimeData::TableMimeData (std::vector< CSMWorld::UniversalId >& id mUniversalId = id; for (std::vector::iterator it(mUniversalId.begin()); it != mUniversalId.end(); ++it) { - mObjectsFormats << QString::fromStdString("application/Type_" + it->getTypeName()); + mObjectsFormats << QString::fromStdString("tabledata/" + it->getTypeName()); } } From 3b8f04c0f345ddd4588321d2e43b6ff132774f40 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 5 Feb 2014 11:44:08 +0100 Subject: [PATCH 776/889] Allow multi-item drag. --- apps/opencs/view/world/table.cpp | 42 +++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index e7ae62e02..ff0eabf27 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -415,22 +415,36 @@ void CSVWorld::Table::recordFilterChanged (boost::shared_ptr fi void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) { - if (event->buttons() & Qt::LeftButton) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); + if (event->buttons() & Qt::LeftButton) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); - if (selectedRows.size() == 0) - { - return; - } + if (selectedRows.size() == 0) + { + return; + } - if (selectedRows.size() == 1) //tmp solution - { - CSMWorld::TableMimeData *mime = new CSMWorld::TableMimeData(getUniversalId(selectedRows.begin()->row())); - QDrag *drag = new QDrag(this); - drag->setMimeData(mime); - drag->setPixmap(QString::fromStdString(mime->getIcon())); + QDrag* drag = new QDrag (this); + CSMWorld::TableMimeData* mime = NULL; + + if (selectedRows.size() == 1) + { + mime = new CSMWorld::TableMimeData (getUniversalId (selectedRows.begin()->row())); + } + else + { + std::vector idToDrag; + + foreach (QModelIndex it, selectedRows) + { + idToDrag.push_back (getUniversalId (it.row())); + } + + mime = new CSMWorld::TableMimeData (idToDrag); + } + + drag->setMimeData (mime); + drag->setPixmap (QString::fromStdString (mime->getIcon())); drag->start(); - } } } \ No newline at end of file From 4a26909172d0584acd344590410b6d1ebac05f7f Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 5 Feb 2014 12:15:07 +0100 Subject: [PATCH 777/889] Fixes #1159: Don't allow quick keys menu in chargen --- apps/openmw/mwinput/inputmanagerimp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 5385ab388..c4b8d0a89 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -748,7 +748,8 @@ namespace MWInput void InputManager::showQuickKeysMenu() { - if (!MWBase::Environment::get().getWindowManager()->isGuiMode ()) + if (!MWBase::Environment::get().getWindowManager()->isGuiMode () + && MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")==-1) MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_QuickKeysMenu); else if (MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_QuickKeysMenu) MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_QuickKeysMenu); From 632834ce1094fb5e0b56910797e049455e71cea4 Mon Sep 17 00:00:00 2001 From: gus Date: Wed, 5 Feb 2014 16:12:50 +0100 Subject: [PATCH 778/889] WIP --- apps/openmw/mwmechanics/aiactivate.cpp | 103 ++++++++++++++++++++++++- apps/openmw/mwmechanics/aiactivate.hpp | 6 ++ apps/openmw/mwmechanics/aitravel.cpp | 10 +-- apps/openmw/mwmechanics/aitravel.hpp | 4 +- 4 files changed, 113 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index 531ba5568..e751140c0 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -1,8 +1,24 @@ #include "aiactivate.hpp" #include +#include "movement.hpp" + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwworld/class.hpp" + +namespace +{ + float sgn(float a) + { + if(a > 0) + return 1.0; + return -1.0; + } +} + MWMechanics::AiActivate::AiActivate(const std::string &objectId) -: mObjectId(objectId) + : mObjectId(objectId) { } MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const @@ -11,8 +27,89 @@ MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const } bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) { - std::cout << "AiActivate completed.\n"; - return true; + MWBase::World *world = MWBase::Environment::get().getWorld(); + ESM::Position pos = actor.getRefData().getPosition(); + Movement &movement = actor.getClass().getMovementSettings(actor); + const ESM::Cell *cell = actor.getCell()->mCell; + + MWWorld::Ptr player = world->getPlayerPtr(); + if(cell->mData.mX != player.getCell()->mCell->mData.mX) + { + int sideX = sgn(cell->mData.mX - player.getCell()->mCell->mData.mX); + //check if actor is near the border of an inactive cell. If so, stop walking. + if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) > + sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) + { + movement.mPosition[1] = 0; + return false; + } + } + if(cell->mData.mY != player.getCell()->mCell->mData.mY) + { + int sideY = sgn(cell->mData.mY - player.getCell()->mCell->mData.mY); + //check if actor is near the border of an inactive cell. If so, stop walking. + if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) > + sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) + { + movement.mPosition[1] = 0; + return false; + } + } + + MWWorld::Ptr target = world->getPtr(mObjectId,false); + ESM::Position targetPos = target.getRefData().getPosition(); + + bool cellChange = cell->mData.mX != mCellX || cell->mData.mY != mCellY; + if(!mPathFinder.isPathConstructed() || cellChange) + { + mCellX = cell->mData.mX; + mCellY = cell->mData.mY; + float xCell = 0; + float yCell = 0; + + if(cell->isExterior()) + { + xCell = cell->mData.mX * ESM::Land::REAL_SIZE; + yCell = cell->mData.mY * ESM::Land::REAL_SIZE; + } + + ESM::Pathgrid::Point dest; + dest.mX = targetPos.pos[0]; + dest.mY = targetPos.pos[0]; + dest.mZ = targetPos.pos[0]; + + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; + start.mZ = pos.pos[2]; + + mPathFinder.buildPath(start, dest, actor.getCell(), true); + } + + if((pos.pos[0]-targetPos.pos[0])*(pos.pos[0]-targetPos.pos[0])+ + (pos.pos[1]-targetPos.pos[1])*(pos.pos[1]-targetPos.pos[1])+ + (pos.pos[2]-targetPos.pos[2])*(pos.pos[2]-targetPos.pos[2]) < 200*200) + { + movement.mPosition[1] = 0; + MWWorld::Ptr target = world->getPtr(mObjectId,false); + MWWorld::Class::get(target).activate(target,actor); + return true; + } + + if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) + { + movement.mPosition[1] = 0; + MWWorld::Ptr target = world->getPtr(mObjectId,false); + MWWorld::Class::get(target).activate(target,actor); + return true; + } + + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + // TODO: use movement settings instead of rotating directly + world->rotateObject(actor, 0, 0, zAngle, false); + movement.mPosition[1] = 1; + + return false; } int MWMechanics::AiActivate::getTypeId() const diff --git a/apps/openmw/mwmechanics/aiactivate.hpp b/apps/openmw/mwmechanics/aiactivate.hpp index f922e238c..7c94c2589 100644 --- a/apps/openmw/mwmechanics/aiactivate.hpp +++ b/apps/openmw/mwmechanics/aiactivate.hpp @@ -4,6 +4,8 @@ #include "aipackage.hpp" #include +#include "pathfinding.hpp" + namespace MWMechanics { @@ -18,6 +20,10 @@ namespace MWMechanics private: std::string mObjectId; + + PathFinder mPathFinder; + int mCellX; + int mCellY; }; } #endif // GAME_MWMECHANICS_AIACTIVATE_H diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index ba75cb418..377f9de0f 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -20,8 +20,8 @@ namespace MWMechanics { AiTravel::AiTravel(float x, float y, float z) : mX(x),mY(y),mZ(z),mPathFinder() - , cellX(std::numeric_limits::max()) - , cellY(std::numeric_limits::max()) + , mCellX(std::numeric_limits::max()) + , mCellY(std::numeric_limits::max()) { } @@ -61,11 +61,11 @@ namespace MWMechanics } } - bool cellChange = cell->mData.mX != cellX || cell->mData.mY != cellY; + bool cellChange = cell->mData.mX != mCellX || cell->mData.mY != mCellY; if(!mPathFinder.isPathConstructed() || cellChange) { - cellX = cell->mData.mX; - cellY = cell->mData.mY; + mCellX = cell->mData.mX; + mCellY = cell->mData.mY; float xCell = 0; float yCell = 0; diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index b479dfd43..72f3e0298 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -23,8 +23,8 @@ namespace MWMechanics float mY; float mZ; - int cellX; - int cellY; + int mCellX; + int mCellY; PathFinder mPathFinder; }; From ff11d85a62cc72b10ad5cbe2f5a666a05c42a571 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 6 Feb 2014 03:15:01 +0100 Subject: [PATCH 779/889] Fix wrong assertion --- apps/openmw/mwrender/globalmap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 4a87d9f1a..018dc082a 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -291,7 +291,7 @@ namespace MWRender unsigned int imageY = (yLength - (y + 1)) * cellImageSizeSrc; assert(imageX < image.getWidth()); - assert(imageY < image.getWidth()); + assert(imageY < image.getHeight()); if (image.getColourAt(imageX, imageY, 0).a > 0) exploredCells.push_back(std::make_pair(x+bounds.mMinX,y+bounds.mMinY)); From 6ce499f0e6df8994444cf3116065db806574f432 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 6 Feb 2014 03:58:07 +0100 Subject: [PATCH 780/889] Fixes #848: Use hardcoded animation velocity for first person movement --- apps/openmw/mwmechanics/character.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 0a94e4ed4..f280f3be1 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -315,8 +315,18 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat if(!mCurrentMovement.empty()) { float vel, speedmult = 1.0f; + + bool isrunning = mPtr.getClass().getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run); + if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f) speedmult = mMovementSpeed / vel; + else if (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight) + speedmult = 1.f; // TODO: should get a speed mult depending on the current turning speed + else if (mMovementSpeed > 0.0f) + // The first person anims don't have any velocity to calculate a speed multiplier from. + // We use the third person velocities instead. + // FIXME: should be pulled from the actual animation, but it is not presently loaded. + speedmult = mMovementSpeed / (isrunning ? 222.857f : 154.064f); mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false, speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul); } From 8f949c6ae217c662e61f926fda0c5f9812b7fe09 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 8 Feb 2014 07:35:34 +0100 Subject: [PATCH 781/889] Fix lockpicks --- apps/openmw/mwrender/npcanimation.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index bcbcbc737..d07aad31d 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -697,7 +697,8 @@ void NpcAnimation::showWeapons(bool showWeapon) addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, mesh, !weapon->getClass().getEnchantment(*weapon).empty(), &glowColor); - if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) + if (weapon->getTypeName() == typeid(ESM::Weapon).name() && + weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) { MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); if (ammo != inv.end() && ammo->get()->mBase->mData.mType == ESM::Weapon::Bolt) From b5006c5dbd7f2840b3907ae59f367bc4a19b87ad Mon Sep 17 00:00:00 2001 From: gus Date: Sun, 9 Feb 2014 15:33:00 +0100 Subject: [PATCH 782/889] compile fix --- apps/opencs/model/world/tablemimedata.hpp | 2 +- apps/opencs/view/world/table.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/tablemimedata.hpp b/apps/opencs/model/world/tablemimedata.hpp index 193c62122..7509bd905 100644 --- a/apps/opencs/model/world/tablemimedata.hpp +++ b/apps/opencs/model/world/tablemimedata.hpp @@ -6,7 +6,7 @@ #include -#include +#include #include #include "universalid.hpp" diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index ec08e4e2b..db45a9632 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include "../../model/filter/node.hpp" From 3147aebf7501cc1fc7c64d3b93832261c2eed47c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 10 Feb 2014 13:01:52 +0100 Subject: [PATCH 783/889] factored out declaration parser --- components/CMakeLists.txt | 2 +- components/compiler/declarationparser.cpp | 79 +++++++++++++++++++++++ components/compiler/declarationparser.hpp | 41 ++++++++++++ components/compiler/lineparser.cpp | 64 +++++++----------- components/compiler/lineparser.hpp | 1 - 5 files changed, 144 insertions(+), 43 deletions(-) create mode 100644 components/compiler/declarationparser.cpp create mode 100644 components/compiler/declarationparser.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index f2b16d4d5..4f6639e9b 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -55,7 +55,7 @@ add_component_dir (files add_component_dir (compiler context controlparser errorhandler exception exprparser extensions fileparser generator lineparser literals locals output parser scanner scriptparser skipparser streamerrorhandler - stringparser tokenloc nullerrorhandler opcodes extensions0 + stringparser tokenloc nullerrorhandler opcodes extensions0 declarationparser ) add_component_dir (interpreter diff --git a/components/compiler/declarationparser.cpp b/components/compiler/declarationparser.cpp new file mode 100644 index 000000000..586b28bc2 --- /dev/null +++ b/components/compiler/declarationparser.cpp @@ -0,0 +1,79 @@ + +#include "declarationparser.hpp" + +#include + +#include "scanner.hpp" +#include "errorhandler.hpp" +#include "skipparser.hpp" +#include "locals.hpp" + +Compiler::DeclarationParser::DeclarationParser (ErrorHandler& errorHandler, Context& context, + Locals& locals) +: Parser (errorHandler, context), mLocals (locals), mState (State_Begin), mType (0) +{} + +bool Compiler::DeclarationParser::parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner) +{ + if (mState==State_Name) + { + std::string name2 = Misc::StringUtils::lowerCase (name); + + char type = mLocals.getType (name2); + + if (type!=' ') + { + /// \todo add option to make re-declared local variables an error + getErrorHandler().warning ("can't re-declare local variable (ignoring declaration)", + loc); + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + mState = State_End; + return true; + } + + mLocals.declare (mType, name2); + + mState = State_End; + return true; + } + + return Parser::parseName (name, loc, scanner); +} + +bool Compiler::DeclarationParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) +{ + if (mState==State_Begin) + { + switch (keyword) + { + case Scanner::K_short: mType = 's'; break; + case Scanner::K_long: mType = 'l'; break; + case Scanner::K_float: mType = 'f'; break; + default: mType = 0; + } + + if (mType) + { + mState = State_Name; + return true; + } + } + else if (mState==State_Name) + { + // allow keywords to be used as local variable names. MW script compiler, you suck! + /// \todo option to disable this atrocity. + return parseName (loc.mLiteral, loc, scanner); + } + + return Parser::parseKeyword (keyword, loc, scanner); +} + +bool Compiler::DeclarationParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) +{ + if (code==Scanner::S_newline && mState==State_End) + return false; + + return Parser::parseSpecial (code, loc, scanner); +} \ No newline at end of file diff --git a/components/compiler/declarationparser.hpp b/components/compiler/declarationparser.hpp new file mode 100644 index 000000000..3e0ac57c3 --- /dev/null +++ b/components/compiler/declarationparser.hpp @@ -0,0 +1,41 @@ +#ifndef COMPILER_DECLARATIONPARSER_H_INCLUDED +#define COMPILER_DECLARATIONPARSER_H_INCLUDED + +#include "parser.hpp" + +namespace Compiler +{ + class Locals; + + class DeclarationParser : public Parser + { + enum State + { + State_Begin, State_Name, State_End + }; + + Locals& mLocals; + State mState; + char mType; + + public: + + DeclarationParser (ErrorHandler& errorHandler, Context& context, Locals& locals); + + virtual bool parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner); + ///< Handle a name token. + /// \return fetch another token? + + virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner); + ///< Handle a keyword token. + /// \return fetch another token? + + virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); + ///< Handle a special character token. + /// \return fetch another token? + + }; +} + +#endif diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 2904df6e1..368152fd9 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -1,6 +1,8 @@ #include "lineparser.hpp" +#include + #include "scanner.hpp" #include "context.hpp" #include "errorhandler.hpp" @@ -8,7 +10,7 @@ #include "locals.hpp" #include "generator.hpp" #include "extensions.hpp" -#include +#include "declarationparser.hpp" namespace Compiler { @@ -82,37 +84,6 @@ namespace Compiler bool LineParser::parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner) { - if (mState==ShortState || mState==LongState || mState==FloatState) - { - if (!getContext().canDeclareLocals()) - { - getErrorHandler().error ("local variables can't be declared in this context", loc); - SkipParser skip (getErrorHandler(), getContext()); - scanner.scan (skip); - return false; - } - - std::string name2 = Misc::StringUtils::lowerCase (name); - - char type = mLocals.getType (name2); - - if (type!=' ') - { - /// \todo add option to make re-declared local variables an error - getErrorHandler().warning ("can't re-declare local variable", loc); - SkipParser skip (getErrorHandler(), getContext()); - scanner.scan (skip); - mState = EndState; - return true; - } - - mLocals.declare (mState==ShortState ? 's' : (mState==LongState ? 'l' : 'f'), - name2); - - mState = EndState; - return true; - } - if (mState==SetState) { std::string name2 = Misc::StringUtils::lowerCase (name); @@ -303,9 +274,26 @@ namespace Compiler { switch (keyword) { - case Scanner::K_short: mState = ShortState; return true; - case Scanner::K_long: mState = LongState; return true; - case Scanner::K_float: mState = FloatState; return true; + case Scanner::K_short: + case Scanner::K_long: + case Scanner::K_float: + { + if (!getContext().canDeclareLocals()) + { + getErrorHandler().error ( + "local variables can't be declared in this context", loc); + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + return true; + } + + DeclarationParser declaration (getErrorHandler(), getContext(), mLocals); + if (declaration.parseKeyword (keyword, loc, scanner)) + scanner.scan (declaration); + + return true; + } + case Scanner::K_set: mState = SetState; return true; case Scanner::K_messagebox: mState = MessageState; return true; @@ -370,12 +358,6 @@ namespace Compiler mState = EndState; return true; } - else if (mState==ShortState || mState==LongState || mState==FloatState) - { - // allow keywords to be used as local variable names. MW script compiler, you suck! - /// \todo option to disable this atrocity. - return parseName (loc.mLiteral, loc, scanner); - } if (mAllowExpression) { diff --git a/components/compiler/lineparser.hpp b/components/compiler/lineparser.hpp index aa74cd232..cf72f26a8 100644 --- a/components/compiler/lineparser.hpp +++ b/components/compiler/lineparser.hpp @@ -20,7 +20,6 @@ namespace Compiler enum State { BeginState, - ShortState, LongState, FloatState, SetState, SetLocalVarState, SetGlobalVarState, SetPotentialMemberVarState, SetMemberVarState, SetMemberVarState2, MessageState, MessageCommaState, MessageButtonState, MessageButtonCommaState, From 3b990795c4da85fcbf7373b47ff4914aa17b5635 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 10 Feb 2014 14:45:55 +0100 Subject: [PATCH 784/889] added access to remote access of local variables of global scripts --- apps/opencs/model/world/scriptcontext.cpp | 5 +- apps/opencs/model/world/scriptcontext.hpp | 8 +- apps/openmw/mwscript/compilercontext.cpp | 27 ++++- apps/openmw/mwscript/compilercontext.hpp | 8 +- apps/openmw/mwscript/globalscripts.cpp | 21 ++++ apps/openmw/mwscript/globalscripts.hpp | 4 + apps/openmw/mwscript/interpretercontext.cpp | 123 +++++++++++++------- apps/openmw/mwscript/interpretercontext.hpp | 44 ++++--- components/compiler/context.hpp | 8 +- components/compiler/exprparser.cpp | 9 +- components/compiler/generator.cpp | 41 +++---- components/compiler/generator.hpp | 6 +- components/compiler/lineparser.cpp | 10 +- components/compiler/lineparser.hpp | 1 + components/interpreter/context.hpp | 36 +++--- components/interpreter/docs/vmformat.txt | 8 +- components/interpreter/installopcodes.cpp | 18 ++- components/interpreter/localopcodes.hpp | 36 +++++- 18 files changed, 277 insertions(+), 136 deletions(-) diff --git a/apps/opencs/model/world/scriptcontext.cpp b/apps/opencs/model/world/scriptcontext.cpp index 8190c68eb..1b3028159 100644 --- a/apps/opencs/model/world/scriptcontext.cpp +++ b/apps/opencs/model/world/scriptcontext.cpp @@ -19,9 +19,10 @@ char CSMWorld::ScriptContext::getGlobalType (const std::string& name) const return ' '; } -char CSMWorld::ScriptContext::getMemberType (const std::string& name, const std::string& id) const +std::pair CSMWorld::ScriptContext::getMemberType (const std::string& name, + const std::string& id) const { - return ' '; + return std::make_pair (' ', false); } bool CSMWorld::ScriptContext::isId (const std::string& name) const diff --git a/apps/opencs/model/world/scriptcontext.hpp b/apps/opencs/model/world/scriptcontext.hpp index 961da9143..3baca99b2 100644 --- a/apps/opencs/model/world/scriptcontext.hpp +++ b/apps/opencs/model/world/scriptcontext.hpp @@ -26,8 +26,12 @@ namespace CSMWorld virtual char getGlobalType (const std::string& name) const; ///< 'l: long, 's': short, 'f': float, ' ': does not exist. - virtual char getMemberType (const std::string& name, const std::string& id) const; - ///< 'l: long, 's': short, 'f': float, ' ': does not exist. + virtual std::pair getMemberType (const std::string& name, + const std::string& id) const; + ///< Return type of member variable \a name in script \a id or in script of reference of + /// \a id + /// \return first: 'l: long, 's': short, 'f': float, ' ': does not exist. + /// second: true: script of reference virtual bool isId (const std::string& name) const; ///< Does \a name match an ID, that can be referenced? diff --git a/apps/openmw/mwscript/compilercontext.cpp b/apps/openmw/mwscript/compilercontext.cpp index b094e5414..8018b86e0 100644 --- a/apps/openmw/mwscript/compilercontext.cpp +++ b/apps/openmw/mwscript/compilercontext.cpp @@ -30,16 +30,31 @@ namespace MWScript return MWBase::Environment::get().getWorld()->getGlobalVariableType (name); } - char CompilerContext::getMemberType (const std::string& name, const std::string& id) const + std::pair CompilerContext::getMemberType (const std::string& name, + const std::string& id) const { - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPtr (id, false); + std::string script; + bool reference = false; - std::string script = MWWorld::Class::get (ptr).getScript (ptr); + if (const ESM::Script *scriptRecord = + MWBase::Environment::get().getWorld()->getStore().get().search (id)) + { + script = scriptRecord->mId; + } + else + { + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPtr (id, false); - if (script.empty()) - return ' '; + script = MWWorld::Class::get (ptr).getScript (ptr); + reference = true; + } - return MWBase::Environment::get().getScriptManager()->getLocals (script).getType (name); + char type = ' '; + + if (!script.empty()) + type = MWBase::Environment::get().getScriptManager()->getLocals (script).getType (name); + + return std::make_pair (type, reference); } bool CompilerContext::isId (const std::string& name) const diff --git a/apps/openmw/mwscript/compilercontext.hpp b/apps/openmw/mwscript/compilercontext.hpp index 50256f942..95719ab69 100644 --- a/apps/openmw/mwscript/compilercontext.hpp +++ b/apps/openmw/mwscript/compilercontext.hpp @@ -30,8 +30,12 @@ namespace MWScript /// 'l: long, 's': short, 'f': float, ' ': does not exist. virtual char getGlobalType (const std::string& name) const; - virtual char getMemberType (const std::string& name, const std::string& id) const; - ///< 'l: long, 's': short, 'f': float, ' ': does not exist. + virtual std::pair getMemberType (const std::string& name, + const std::string& id) const; + ///< Return type of member variable \a name in script \a id or in script of reference of + /// \a id + /// \return first: 'l: long, 's': short, 'f': float, ' ': does not exist. + /// second: true: script of reference virtual bool isId (const std::string& name) const; ///< Does \a name match an ID, that can be referenced? diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 8f269a015..179e2bb0b 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -148,4 +148,25 @@ namespace MWScript return false; } + + Locals& GlobalScripts::getLocals (const std::string& name) + { + std::string name2 = Misc::StringUtils::lowerCase (name); + std::map >::iterator iter = + mScripts.find (name2); + + if (iter==mScripts.end()) + { + if (const ESM::Script *script = mStore.get().find (name)) + { + Locals locals; + + locals.configure (*script); + + iter = mScripts.insert (std::make_pair (name, std::make_pair (false, locals))).first; + } + } + + return iter->second.second; + } } diff --git a/apps/openmw/mwscript/globalscripts.hpp b/apps/openmw/mwscript/globalscripts.hpp index cf716c8e4..a4a766226 100644 --- a/apps/openmw/mwscript/globalscripts.hpp +++ b/apps/openmw/mwscript/globalscripts.hpp @@ -52,6 +52,10 @@ namespace MWScript ///< Records for variables that do not exist are dropped silently. /// /// \return Known type? + + Locals& getLocals (const std::string& name); + ///< If the script \a name has not been added as a global script yet, it is added + /// automatically, but is not set to running state. }; } diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 10e98e398..17092b41c 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -54,6 +54,47 @@ namespace MWScript } } + const Locals& InterpreterContext::getMemberLocals (std::string& id, bool global) + const + { + if (global) + { + return MWBase::Environment::get().getScriptManager()->getGlobalScripts(). + getLocals (id); + } + else + { + const MWWorld::Ptr ptr = getReference (id, false); + + std::string id = MWWorld::Class::get (ptr).getScript (ptr); + + ptr.getRefData().setLocals ( + *MWBase::Environment::get().getWorld()->getStore().get().find (id)); + + return ptr.getRefData().getLocals(); + } + } + + Locals& InterpreterContext::getMemberLocals (std::string& id, bool global) + { + if (global) + { + return MWBase::Environment::get().getScriptManager()->getGlobalScripts(). + getLocals (id); + } + else + { + const MWWorld::Ptr ptr = getReference (id, false); + + std::string id = MWWorld::Class::get (ptr).getScript (ptr); + + ptr.getRefData().setLocals ( + *MWBase::Environment::get().getWorld()->getStore().get().find (id)); + + return ptr.getRefData().getLocals(); + } + } + InterpreterContext::InterpreterContext ( MWScript::Locals *locals, MWWorld::Ptr reference) : mLocals (locals), mReference (reference), @@ -407,82 +448,80 @@ namespace MWScript MWBase::Environment::get().getWorld()->disable (ref); } - int InterpreterContext::getMemberShort (const std::string& id, const std::string& name) const + int InterpreterContext::getMemberShort (const std::string& id, const std::string& name, + bool global) const { - const MWWorld::Ptr ptr = getReference (id, false); + std::string scriptId (id); - std::string scriptId = MWWorld::Class::get (ptr).getScript (ptr); + const Locals& locals = getMemberLocals (scriptId, global); - int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 's'); + int index = MWBase::Environment::get().getScriptManager()->getLocalIndex ( + scriptId, name, 's'); - ptr.getRefData().setLocals ( - *MWBase::Environment::get().getWorld()->getStore().get().find (scriptId)); - return ptr.getRefData().getLocals().mShorts[index]; + return locals.mShorts[index]; } - int InterpreterContext::getMemberLong (const std::string& id, const std::string& name) const + int InterpreterContext::getMemberLong (const std::string& id, const std::string& name, + bool global) const { - const MWWorld::Ptr ptr = getReference (id, false); + std::string scriptId (id); - std::string scriptId = MWWorld::Class::get (ptr).getScript (ptr); + const Locals& locals = getMemberLocals (scriptId, global); - int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'l'); + int index = MWBase::Environment::get().getScriptManager()->getLocalIndex ( + scriptId, name, 'l'); - ptr.getRefData().setLocals ( - *MWBase::Environment::get().getWorld()->getStore().get().find (scriptId)); - return ptr.getRefData().getLocals().mLongs[index]; + return locals.mLongs[index]; } - float InterpreterContext::getMemberFloat (const std::string& id, const std::string& name) const + float InterpreterContext::getMemberFloat (const std::string& id, const std::string& name, + bool global) const { - const MWWorld::Ptr ptr = getReference (id, false); + std::string scriptId (id); - std::string scriptId = MWWorld::Class::get (ptr).getScript (ptr); + const Locals& locals = getMemberLocals (scriptId, global); - int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'f'); + int index = MWBase::Environment::get().getScriptManager()->getLocalIndex ( + scriptId, name, 'f'); - ptr.getRefData().setLocals ( - *MWBase::Environment::get().getWorld()->getStore().get().find (scriptId)); - return ptr.getRefData().getLocals().mFloats[index]; + return locals.mFloats[index]; } - void InterpreterContext::setMemberShort (const std::string& id, const std::string& name, int value) + void InterpreterContext::setMemberShort (const std::string& id, const std::string& name, + int value, bool global) { - const MWWorld::Ptr ptr = getReference (id, false); + std::string scriptId (id); - std::string scriptId = MWWorld::Class::get (ptr).getScript (ptr); + Locals& locals = getMemberLocals (scriptId, global); - int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 's'); + int index = + MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 's'); - ptr.getRefData().setLocals ( - *MWBase::Environment::get().getWorld()->getStore().get().find (scriptId)); - ptr.getRefData().getLocals().mShorts[index] = value; + locals.mShorts[index] = value; } - void InterpreterContext::setMemberLong (const std::string& id, const std::string& name, int value) + void InterpreterContext::setMemberLong (const std::string& id, const std::string& name, int value, bool global) { - const MWWorld::Ptr ptr = getReference (id, false); + std::string scriptId (id); - std::string scriptId = MWWorld::Class::get (ptr).getScript (ptr); + Locals& locals = getMemberLocals (scriptId, global); - int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'l'); + int index = + MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'l'); - ptr.getRefData().setLocals ( - *MWBase::Environment::get().getWorld()->getStore().get().find (scriptId)); - ptr.getRefData().getLocals().mLongs[index] = value; + locals.mLongs[index] = value; } - void InterpreterContext::setMemberFloat (const std::string& id, const std::string& name, float value) + void InterpreterContext::setMemberFloat (const std::string& id, const std::string& name, float value, bool global) { - const MWWorld::Ptr ptr = getReference (id, false); + std::string scriptId (id); - std::string scriptId = MWWorld::Class::get (ptr).getScript (ptr); + Locals& locals = getMemberLocals (scriptId, global); - int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'f'); + int index = + MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'f'); - ptr.getRefData().setLocals ( - *MWBase::Environment::get().getWorld()->getStore().get().find (scriptId)); - ptr.getRefData().getLocals().mFloats[index] = value; + locals.mFloats[index] = value; } MWWorld::Ptr InterpreterContext::getReference(bool required) diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index 04546faed..9fb7fa2bf 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -37,6 +37,12 @@ namespace MWScript const MWWorld::Ptr getReference (const std::string& id, bool activeOnly, bool doThrow=true) const; + const Locals& getMemberLocals (std::string& id, bool global) const; + ///< \a id is changed to the respective script ID, if \a id wasn't a script ID before + + Locals& getMemberLocals (std::string& id, bool global); + ///< \a id is changed to the respective script ID, if \a id wasn't a script ID before + public: InterpreterContext (MWScript::Locals *locals, MWWorld::Ptr reference); @@ -75,35 +81,35 @@ namespace MWScript virtual void setGlobalLong (const std::string& name, int value); virtual void setGlobalFloat (const std::string& name, float value); - + virtual std::vector getGlobals () const; virtual char getGlobalType (const std::string& name) const; - + virtual std::string getActionBinding(const std::string& action) const; - + virtual std::string getNPCName() const; - + virtual std::string getNPCRace() const; - + virtual std::string getNPCClass() const; - + virtual std::string getNPCFaction() const; virtual std::string getNPCRank() const; - + virtual std::string getPCName() const; - + virtual std::string getPCRace() const; - + virtual std::string getPCClass() const; - + virtual std::string getPCRank() const; - + virtual std::string getPCNextRank() const; - + virtual int getPCBounty() const; - + virtual std::string getCurrentCellName() const; virtual bool isScriptRunning (const std::string& name) const; @@ -138,17 +144,17 @@ namespace MWScript virtual void disable (const std::string& id = ""); - virtual int getMemberShort (const std::string& id, const std::string& name) const; + virtual int getMemberShort (const std::string& id, const std::string& name, bool global) const; - virtual int getMemberLong (const std::string& id, const std::string& name) const; + virtual int getMemberLong (const std::string& id, const std::string& name, bool global) const; - virtual float getMemberFloat (const std::string& id, const std::string& name) const; + virtual float getMemberFloat (const std::string& id, const std::string& name, bool global) const; - virtual void setMemberShort (const std::string& id, const std::string& name, int value); + virtual void setMemberShort (const std::string& id, const std::string& name, int value, bool global); - virtual void setMemberLong (const std::string& id, const std::string& name, int value); + virtual void setMemberLong (const std::string& id, const std::string& name, int value, bool global); - virtual void setMemberFloat (const std::string& id, const std::string& name, float value); + virtual void setMemberFloat (const std::string& id, const std::string& name, float value, bool global); MWWorld::Ptr getReference(bool required=true); ///< Reference, that the script is running from (can be empty) diff --git a/components/compiler/context.hpp b/components/compiler/context.hpp index 69146e285..84bb89bdc 100644 --- a/components/compiler/context.hpp +++ b/components/compiler/context.hpp @@ -33,8 +33,12 @@ namespace Compiler virtual char getGlobalType (const std::string& name) const = 0; ///< 'l: long, 's': short, 'f': float, ' ': does not exist. - virtual char getMemberType (const std::string& name, const std::string& id) const = 0; - ///< 'l: long, 's': short, 'f': float, ' ': does not exist. + virtual std::pair getMemberType (const std::string& name, + const std::string& id) const = 0; + ///< Return type of member variable \a name in script \a id or in script of reference of + /// \a id + /// \return first: 'l: long, 's': short, 'f': float, ' ': does not exist. + /// second: true: script of reference virtual bool isId (const std::string& name) const = 0; ///< Does \a name match an ID, that can be referenced? diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 42c88b75a..283da854c 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -204,14 +204,15 @@ namespace Compiler std::string name2 = Misc::StringUtils::lowerCase (name); std::string id = Misc::StringUtils::lowerCase (mExplicit); - char type = getContext().getMemberType (name2, id); + std::pair type = getContext().getMemberType (name2, id); - if (type!=' ') + if (type.first!=' ') { - Generator::fetchMember (mCode, mLiterals, type, name2, id); + Generator::fetchMember (mCode, mLiterals, type.first, name2, id, !type.second); + mNextOperand = false; mExplicit.clear(); - mOperands.push_back (type=='f' ? 'f' : 'l'); + mOperands.push_back (type.first=='f' ? 'f' : 'l'); return true; } diff --git a/components/compiler/generator.cpp b/components/compiler/generator.cpp index 9b02e4273..83e46d5f7 100644 --- a/components/compiler/generator.cpp +++ b/components/compiler/generator.cpp @@ -260,34 +260,34 @@ namespace code.push_back (Compiler::Generator::segment5 (44)); } - void opStoreMemberShort (Compiler::Generator::CodeContainer& code) + void opStoreMemberShort (Compiler::Generator::CodeContainer& code, bool global) { - code.push_back (Compiler::Generator::segment5 (59)); + code.push_back (Compiler::Generator::segment5 (global ? 65 : 59)); } - void opStoreMemberLong (Compiler::Generator::CodeContainer& code) + void opStoreMemberLong (Compiler::Generator::CodeContainer& code, bool global) { - code.push_back (Compiler::Generator::segment5 (60)); + code.push_back (Compiler::Generator::segment5 (global ? 66 : 60)); } - void opStoreMemberFloat (Compiler::Generator::CodeContainer& code) + void opStoreMemberFloat (Compiler::Generator::CodeContainer& code, bool global) { - code.push_back (Compiler::Generator::segment5 (61)); + code.push_back (Compiler::Generator::segment5 (global ? 67 : 61)); } - void opFetchMemberShort (Compiler::Generator::CodeContainer& code) + void opFetchMemberShort (Compiler::Generator::CodeContainer& code, bool global) { - code.push_back (Compiler::Generator::segment5 (62)); + code.push_back (Compiler::Generator::segment5 (global ? 68 : 62)); } - void opFetchMemberLong (Compiler::Generator::CodeContainer& code) + void opFetchMemberLong (Compiler::Generator::CodeContainer& code, bool global) { - code.push_back (Compiler::Generator::segment5 (63)); + code.push_back (Compiler::Generator::segment5 (global ? 69 : 63)); } - void opFetchMemberFloat (Compiler::Generator::CodeContainer& code) + void opFetchMemberFloat (Compiler::Generator::CodeContainer& code, bool global) { - code.push_back (Compiler::Generator::segment5 (64)); + code.push_back (Compiler::Generator::segment5 (global ? 70 : 64)); } void opRandom (Compiler::Generator::CodeContainer& code) @@ -738,7 +738,8 @@ namespace Compiler } void assignToMember (CodeContainer& code, Literals& literals, char localType, - const std::string& name, const std::string& id, const CodeContainer& value, char valueType) + const std::string& name, const std::string& id, const CodeContainer& value, + char valueType, bool global) { int index = literals.addString (name); @@ -766,17 +767,17 @@ namespace Compiler { case 'f': - opStoreMemberFloat (code); + opStoreMemberFloat (code, global); break; case 's': - opStoreMemberShort (code); + opStoreMemberShort (code, global); break; case 'l': - opStoreMemberLong (code); + opStoreMemberLong (code, global); break; default: @@ -786,7 +787,7 @@ namespace Compiler } void fetchMember (CodeContainer& code, Literals& literals, char localType, - const std::string& name, const std::string& id) + const std::string& name, const std::string& id, bool global) { int index = literals.addString (name); @@ -800,17 +801,17 @@ namespace Compiler { case 'f': - opFetchMemberFloat (code); + opFetchMemberFloat (code, global); break; case 's': - opFetchMemberShort (code); + opFetchMemberShort (code, global); break; case 'l': - opFetchMemberLong (code); + opFetchMemberLong (code, global); break; default: diff --git a/components/compiler/generator.hpp b/components/compiler/generator.hpp index feab26c93..b51116122 100644 --- a/components/compiler/generator.hpp +++ b/components/compiler/generator.hpp @@ -102,10 +102,12 @@ namespace Compiler const std::string& name); void assignToMember (CodeContainer& code, Literals& literals, char memberType, - const std::string& name, const std::string& id, const CodeContainer& value, char valueType); + const std::string& name, const std::string& id, const CodeContainer& value, char valueType, bool global); + ///< \param global Member of a global script instead of a script of a reference. void fetchMember (CodeContainer& code, Literals& literals, char memberType, - const std::string& name, const std::string& id); + const std::string& name, const std::string& id, bool global); + ///< \param global Member of a global script instead of a script of a reference. void random (CodeContainer& code); diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 368152fd9..5987a4803 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -113,12 +113,13 @@ namespace Compiler if (mState==SetMemberVarState) { mMemberName = name; - char type = getContext().getMemberType (mMemberName, mName); + std::pair type = getContext().getMemberType (mMemberName, mName); - if (type!=' ') + if (type.first!=' ') { mState = SetMemberVarState2; - mType = type; + mType = type.first; + mReferenceMember = type.second; return true; } @@ -353,7 +354,8 @@ namespace Compiler std::vector code; char type = mExprParser.append (code); - Generator::assignToMember (mCode, mLiterals, mType, mMemberName, mName, code, type); + Generator::assignToMember (mCode, mLiterals, mType, mMemberName, mName, code, type, + !mReferenceMember); mState = EndState; return true; diff --git a/components/compiler/lineparser.hpp b/components/compiler/lineparser.hpp index cf72f26a8..0aba30d4b 100644 --- a/components/compiler/lineparser.hpp +++ b/components/compiler/lineparser.hpp @@ -33,6 +33,7 @@ namespace Compiler State mState; std::string mName; std::string mMemberName; + bool mReferenceMember; int mButtons; std::string mExplicit; char mType; diff --git a/components/interpreter/context.hpp b/components/interpreter/context.hpp index bdba7b6af..97e4fad4f 100644 --- a/components/interpreter/context.hpp +++ b/components/interpreter/context.hpp @@ -50,33 +50,33 @@ namespace Interpreter virtual void setGlobalFloat (const std::string& name, float value) = 0; virtual std::vector getGlobals () const = 0; - + virtual char getGlobalType (const std::string& name) const = 0; virtual std::string getActionBinding(const std::string& action) const = 0; - + virtual std::string getNPCName() const = 0; - + virtual std::string getNPCRace() const = 0; - + virtual std::string getNPCClass() const = 0; - + virtual std::string getNPCFaction() const = 0; - + virtual std::string getNPCRank() const = 0; virtual std::string getPCName() const = 0; - + virtual std::string getPCRace() const = 0; - + virtual std::string getPCClass() const = 0; - + virtual std::string getPCRank() const = 0; - + virtual std::string getPCNextRank() const = 0; - + virtual int getPCBounty() const = 0; - + virtual std::string getCurrentCellName() const = 0; virtual bool isScriptRunning (const std::string& name) const = 0; @@ -96,17 +96,17 @@ namespace Interpreter virtual void disable (const std::string& id = "") = 0; - virtual int getMemberShort (const std::string& id, const std::string& name) const = 0; + virtual int getMemberShort (const std::string& id, const std::string& name, bool global) const = 0; - virtual int getMemberLong (const std::string& id, const std::string& name) const = 0; + virtual int getMemberLong (const std::string& id, const std::string& name, bool global) const = 0; - virtual float getMemberFloat (const std::string& id, const std::string& name) const = 0; + virtual float getMemberFloat (const std::string& id, const std::string& name, bool global) const = 0; - virtual void setMemberShort (const std::string& id, const std::string& name, int value) = 0; + virtual void setMemberShort (const std::string& id, const std::string& name, int value, bool global) = 0; - virtual void setMemberLong (const std::string& id, const std::string& name, int value) = 0; + virtual void setMemberLong (const std::string& id, const std::string& name, int value, bool global) = 0; - virtual void setMemberFloat (const std::string& id, const std::string& name, float value) + virtual void setMemberFloat (const std::string& id, const std::string& name, float value, bool global) = 0; }; } diff --git a/components/interpreter/docs/vmformat.txt b/components/interpreter/docs/vmformat.txt index 91e0c060e..990762268 100644 --- a/components/interpreter/docs/vmformat.txt +++ b/components/interpreter/docs/vmformat.txt @@ -127,5 +127,11 @@ op 61: store stack[0] in member float stack[2] of object with ID stack[1] op 62: replace stack[0] with member short stack[1] of object with ID stack[0] op 63: replace stack[0] with member short stack[1] of object with ID stack[0] op 64: replace stack[0] with member short stack[1] of object with ID stack[0] -opcodes 65-33554431 unused +op 65: store stack[0] in member short stack[2] of global script with ID stack[1] +op 66: store stack[0] in member long stack[2] of global script with ID stack[1] +op 67: store stack[0] in member float stack[2] of global script with ID stack[1] +op 68: replace stack[0] with member short stack[1] of global script with ID stack[0] +op 69: replace stack[0] with member short stack[1] of global script with ID stack[0] +op 70: replace stack[0] with member short stack[1] of global script with ID stack[0] +opcodes 71-33554431 unused opcodes 33554432-67108863 reserved for extensions diff --git a/components/interpreter/installopcodes.cpp b/components/interpreter/installopcodes.cpp index 05f71f1cc..721cde3d8 100644 --- a/components/interpreter/installopcodes.cpp +++ b/components/interpreter/installopcodes.cpp @@ -40,12 +40,18 @@ namespace Interpreter interpreter.installSegment5 (42, new OpFetchGlobalShort); interpreter.installSegment5 (43, new OpFetchGlobalLong); interpreter.installSegment5 (44, new OpFetchGlobalFloat); - interpreter.installSegment5 (59, new OpStoreMemberShort); - interpreter.installSegment5 (60, new OpStoreMemberLong); - interpreter.installSegment5 (61, new OpStoreMemberFloat); - interpreter.installSegment5 (62, new OpFetchMemberShort); - interpreter.installSegment5 (63, new OpFetchMemberLong); - interpreter.installSegment5 (64, new OpFetchMemberFloat); + interpreter.installSegment5 (59, new OpStoreMemberShort (false)); + interpreter.installSegment5 (60, new OpStoreMemberLong (false)); + interpreter.installSegment5 (61, new OpStoreMemberFloat (false)); + interpreter.installSegment5 (62, new OpFetchMemberShort (false)); + interpreter.installSegment5 (63, new OpFetchMemberLong (false)); + interpreter.installSegment5 (64, new OpFetchMemberFloat (false)); + interpreter.installSegment5 (65, new OpStoreMemberShort (true)); + interpreter.installSegment5 (66, new OpStoreMemberLong (true)); + interpreter.installSegment5 (67, new OpStoreMemberFloat (true)); + interpreter.installSegment5 (68, new OpFetchMemberShort (true)); + interpreter.installSegment5 (69, new OpFetchMemberLong (true)); + interpreter.installSegment5 (70, new OpFetchMemberFloat (true)); // math interpreter.installSegment5 (9, new OpAddInt); diff --git a/components/interpreter/localopcodes.hpp b/components/interpreter/localopcodes.hpp index 731c16276..7844a9ea7 100644 --- a/components/interpreter/localopcodes.hpp +++ b/components/interpreter/localopcodes.hpp @@ -208,8 +208,12 @@ namespace Interpreter class OpStoreMemberShort : public Opcode0 { + bool mGlobal; + public: + OpStoreMemberShort (bool global) : mGlobal (global) {} + virtual void execute (Runtime& runtime) { Type_Integer data = runtime[0].mInteger; @@ -218,7 +222,7 @@ namespace Interpreter index = runtime[2].mInteger; std::string variable = runtime.getStringLiteral (index); - runtime.getContext().setMemberShort (id, variable, data); + runtime.getContext().setMemberShort (id, variable, data, mGlobal); runtime.pop(); runtime.pop(); @@ -228,8 +232,12 @@ namespace Interpreter class OpStoreMemberLong : public Opcode0 { + bool mGlobal; + public: + OpStoreMemberLong (bool global) : mGlobal (global) {} + virtual void execute (Runtime& runtime) { Type_Integer data = runtime[0].mInteger; @@ -238,7 +246,7 @@ namespace Interpreter index = runtime[2].mInteger; std::string variable = runtime.getStringLiteral (index); - runtime.getContext().setMemberLong (id, variable, data); + runtime.getContext().setMemberLong (id, variable, data, mGlobal); runtime.pop(); runtime.pop(); @@ -248,8 +256,12 @@ namespace Interpreter class OpStoreMemberFloat : public Opcode0 { + bool mGlobal; + public: + OpStoreMemberFloat (bool global) : mGlobal (global) {} + virtual void execute (Runtime& runtime) { Type_Float data = runtime[0].mFloat; @@ -258,7 +270,7 @@ namespace Interpreter index = runtime[2].mInteger; std::string variable = runtime.getStringLiteral (index); - runtime.getContext().setMemberFloat (id, variable, data); + runtime.getContext().setMemberFloat (id, variable, data, mGlobal); runtime.pop(); runtime.pop(); @@ -268,8 +280,12 @@ namespace Interpreter class OpFetchMemberShort : public Opcode0 { + bool mGlobal; + public: + OpFetchMemberShort (bool global) : mGlobal (global) {} + virtual void execute (Runtime& runtime) { Type_Integer index = runtime[0].mInteger; @@ -278,15 +294,19 @@ namespace Interpreter std::string variable = runtime.getStringLiteral (index); runtime.pop(); - int value = runtime.getContext().getMemberShort (id, variable); + int value = runtime.getContext().getMemberShort (id, variable, mGlobal); runtime[0].mInteger = value; } }; class OpFetchMemberLong : public Opcode0 { + bool mGlobal; + public: + OpFetchMemberLong (bool global) : mGlobal (global) {} + virtual void execute (Runtime& runtime) { Type_Integer index = runtime[0].mInteger; @@ -295,15 +315,19 @@ namespace Interpreter std::string variable = runtime.getStringLiteral (index); runtime.pop(); - int value = runtime.getContext().getMemberLong (id, variable); + int value = runtime.getContext().getMemberLong (id, variable, mGlobal); runtime[0].mInteger = value; } }; class OpFetchMemberFloat : public Opcode0 { + bool mGlobal; + public: + OpFetchMemberFloat (bool global) : mGlobal (global) {} + virtual void execute (Runtime& runtime) { Type_Integer index = runtime[0].mInteger; @@ -312,7 +336,7 @@ namespace Interpreter std::string variable = runtime.getStringLiteral (index); runtime.pop(); - float value = runtime.getContext().getMemberFloat (id, variable); + float value = runtime.getContext().getMemberFloat (id, variable, mGlobal); runtime[0].mFloat = value; } }; From 5c0071f3205b1e796bcb27a83e451d043a24bd6e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 10 Feb 2014 14:59:20 +0100 Subject: [PATCH 785/889] fixed spelling of an error message --- components/compiler/generator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/compiler/generator.cpp b/components/compiler/generator.cpp index 83e46d5f7..c67e51e57 100644 --- a/components/compiler/generator.cpp +++ b/components/compiler/generator.cpp @@ -593,7 +593,7 @@ namespace Compiler else if (offset<0) opJumpBackward (code, -offset); else - throw std::logic_error ("inifite loop"); + throw std::logic_error ("infinite loop"); } void jumpOnZero (CodeContainer& code, int offset) From df46218acc9132e034e54af788b1f2c2b83f4084 Mon Sep 17 00:00:00 2001 From: gus Date: Mon, 10 Feb 2014 16:48:04 +0100 Subject: [PATCH 786/889] try droping --- apps/opencs/view/world/table.cpp | 32 +++++++++++++++++++++++++------- apps/opencs/view/world/table.hpp | 4 ++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index ff0eabf27..1a9499f52 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -85,7 +85,7 @@ std::vector CSVWorld::Table::listRevertableSelectedIds() const QModelIndexList selectedRows = selectionModel()->selectedRows(); for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); - ++iter) + ++iter) { QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); @@ -228,6 +228,8 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q connect (selectionModel(), SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)), this, SLOT (selectionSizeUpdate ())); + + setAcceptDrops(true); } void CSVWorld::Table::setEditLock (bool locked) @@ -384,10 +386,10 @@ void CSVWorld::Table::tableSizeUpdate() switch (state) { - case CSMWorld::RecordBase::State_BaseOnly: ++size; break; - case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break; - case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break; - case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break; + case CSMWorld::RecordBase::State_BaseOnly: ++size; break; + case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break; + case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break; + case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break; } } } @@ -445,6 +447,22 @@ void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) drag->setMimeData (mime); drag->setPixmap (QString::fromStdString (mime->getIcon())); - drag->start(); - } + drag->exec(); + std::cout << "startdrag"; + } + +} + +void CSVWorld::Table::dragEnterEvent(QDragEnterEvent *event) +{ + //if (event->mimeData()->hasFormat("text/plain")) + std::cout << "accept drag event"; + event->acceptProposedAction(); + +} + +void CSVWorld::Table::dropEvent(QDropEvent *event) +{ + std::cout << "drop"; + event->acceptProposedAction(); } \ No newline at end of file diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index db45a9632..44d72f7fb 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -52,6 +52,10 @@ namespace CSVWorld void mouseMoveEvent(QMouseEvent *event); + void dragEnterEvent(QDragEnterEvent *event); + + void dropEvent(QDropEvent *event); + public: Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, bool sorting); From 8c8f4cd420f0c192fb3092955b88048820a41bfd Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Mon, 10 Feb 2014 22:53:16 +0100 Subject: [PATCH 787/889] Fix typo --- components/compiler/extensions.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/compiler/extensions.hpp b/components/compiler/extensions.hpp index 53ebfa31b..34551374a 100644 --- a/components/compiler/extensions.hpp +++ b/components/compiler/extensions.hpp @@ -1,5 +1,5 @@ #ifndef COMPILER_EXTENSIONS_H_INCLUDED -#define COMPILER_EXTENSINOS_H_INCLUDED +#define COMPILER_EXTENSIONS_H_INCLUDED #include #include From 53e2e8415d9c6d0c61d87d09b93a18f575f255b5 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 11 Feb 2014 12:25:21 +0100 Subject: [PATCH 788/889] replaced start with exec --- apps/opencs/view/world/table.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index ff0eabf27..d343440f1 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -435,7 +435,7 @@ void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) { std::vector idToDrag; - foreach (QModelIndex it, selectedRows) + foreach (QModelIndex it, selectedRows) //I had a dream. Dream where you could use C++11 in OpenMW. { idToDrag.push_back (getUniversalId (it.row())); } @@ -445,6 +445,6 @@ void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) drag->setMimeData (mime); drag->setPixmap (QString::fromStdString (mime->getIcon())); - drag->start(); + drag->exec(); } } \ No newline at end of file From f26aa4f6455fe5e8b2fe110ba1a5df3a95a200db Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 11 Feb 2014 13:31:04 +0100 Subject: [PATCH 789/889] fixed while loop implementation --- components/compiler/controlparser.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/compiler/controlparser.cpp b/components/compiler/controlparser.cpp index 3be470c27..ba2db62b6 100644 --- a/components/compiler/controlparser.cpp +++ b/components/compiler/controlparser.cpp @@ -9,6 +9,7 @@ #include "generator.hpp" #include "errorhandler.hpp" +#include namespace Compiler { bool ControlParser::parseIfBody (int keyword, const TokenLoc& loc, Scanner& scanner) @@ -107,7 +108,7 @@ namespace Compiler Codes expr; mExprParser.append (expr); - Generator::jump (loop, -static_cast (mCodeBlock.size()-expr.size())); + Generator::jump (loop, -static_cast (mCodeBlock.size()+expr.size())); std::copy (expr.begin(), expr.end(), std::back_inserter (mCode)); @@ -121,7 +122,7 @@ namespace Compiler Codes loop2; - Generator::jump (loop2, -static_cast (mCodeBlock.size()-expr.size()-skip.size())); + Generator::jump (loop2, -static_cast (mCodeBlock.size()+expr.size()+skip.size())); if (loop.size()!=loop2.size()) throw std::logic_error ( From 9de2922d22c310ae41a6d80ae5374bc4e6296c2f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 11 Feb 2014 13:56:56 +0100 Subject: [PATCH 790/889] fixed case problem in remote member variable access --- apps/openmw/mwscript/scriptmanagerimp.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index 6862b9f83..11e094a84 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -7,12 +7,15 @@ #include #include -#include "../mwworld/esmstore.hpp" + +#include #include #include #include +#include "../mwworld/esmstore.hpp" + #include "extensions.hpp" namespace MWScript @@ -140,15 +143,17 @@ namespace MWScript Compiler::Locals& ScriptManager::getLocals (const std::string& name) { + std::string name2 = Misc::StringUtils::lowerCase (name); + { - ScriptCollection::iterator iter = mScripts.find (name); + ScriptCollection::iterator iter = mScripts.find (name2); if (iter!=mScripts.end()) return iter->second.second; } { - std::map::iterator iter = mOtherLocals.find (name); + std::map::iterator iter = mOtherLocals.find (name2); if (iter!=mOtherLocals.end()) return iter->second; @@ -156,7 +161,7 @@ namespace MWScript Compiler::Locals locals; - if (const ESM::Script *script = mStore.get().find (name)) + if (const ESM::Script *script = mStore.get().find (name2)) { int index = 0; @@ -170,7 +175,7 @@ namespace MWScript locals.declare ('f', script->mVarNames[index++]); std::map::iterator iter = - mOtherLocals.insert (std::make_pair (name, locals)).first; + mOtherLocals.insert (std::make_pair (name2, locals)).first; return iter->second; } From 6a4820c0f7fa21def9bb90989a57b79acb5893ec Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Tue, 11 Feb 2014 14:51:46 +0100 Subject: [PATCH 791/889] Show a message when the player attempts to cast a disabled spell --- apps/openmw/mwmechanics/spellcasting.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 0dec49f13..fe395e566 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -232,6 +232,24 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->getStore().get().find ( effectIt->mEffectID); + if (!MWBase::Environment::get().getWorld()->isLevitationEnabled() && effectIt->mEffectID == ESM::MagicEffect::Levitate) + { + if (caster.getRefData().getHandle() == "player") + MWBase::Environment::get().getWindowManager()->messageBox("#{sLevitateDisabled}"); + continue; + } + + if (!MWBase::Environment::get().getWorld()->isTeleportingEnabled() && + (effectIt->mEffectID == ESM::MagicEffect::AlmsiviIntervention || + effectIt->mEffectID == ESM::MagicEffect::DivineIntervention || + effectIt->mEffectID == ESM::MagicEffect::Mark || + effectIt->mEffectID == ESM::MagicEffect::Recall)) + { + if (caster.getRefData().getHandle() == "player") + MWBase::Environment::get().getWindowManager()->messageBox("#{sTeleportDisabled}"); + continue; + } + // If player is healing someone, show the target's HP bar if (caster.getRefData().getHandle() == "player" && target != caster && effectIt->mEffectID == ESM::MagicEffect::RestoreHealth From 697bda63719add550ef35d3144592f278b1b86c6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 11 Feb 2014 14:55:31 +0100 Subject: [PATCH 792/889] allow (and discard) explicit reference on instructions that do not accept explicit references --- components/compiler/extensions.cpp | 4 ++-- components/compiler/extensions.hpp | 4 +++- components/compiler/lineparser.cpp | 17 ++++++++++++++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/components/compiler/extensions.cpp b/components/compiler/extensions.cpp index c6a74b234..6f5c2fc89 100644 --- a/components/compiler/extensions.cpp +++ b/components/compiler/extensions.cpp @@ -38,7 +38,7 @@ namespace Compiler } bool Extensions::isInstruction (int keyword, std::string& argumentType, - bool explicitReference) const + bool& explicitReference) const { std::map::const_iterator iter = mInstructions.find (keyword); @@ -46,7 +46,7 @@ namespace Compiler return false; if (explicitReference && iter->second.mCodeExplicit==-1) - return false; + explicitReference = false; argumentType = iter->second.mArguments; return true; diff --git a/components/compiler/extensions.hpp b/components/compiler/extensions.hpp index 53ebfa31b..585c42681 100644 --- a/components/compiler/extensions.hpp +++ b/components/compiler/extensions.hpp @@ -52,8 +52,10 @@ namespace Compiler /// types. bool isInstruction (int keyword, std::string& argumentType, - bool explicitReference) const; + bool& explicitReference) const; ///< Is this keyword registered with a function? If yes, return argument types. + /// \param explicitReference In: has explicit reference; Out: set to false, if + /// explicit reference is not available for this instruction. void registerFunction (const std::string& keyword, char returnType, const std::string& argumentType, int code, int codeExplicit = -1); diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 5987a4803..3b27905d4 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -234,8 +234,15 @@ namespace Compiler { std::string argumentType; - if (extensions->isInstruction (keyword, argumentType, mState==ExplicitState)) + bool hasExplicit = mState==ExplicitState; + if (extensions->isInstruction (keyword, argumentType, hasExplicit)) { + if (!hasExplicit && mState==ExplicitState) + { + getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); + mExplicit.clear(); + } + int optionals = mExprParser.parseArguments (argumentType, scanner, mCode, true); extensions->generateInstructionCode (keyword, mCode, mLiterals, mExplicit, optionals); @@ -271,6 +278,14 @@ namespace Compiler } } + if (mState==ExplicitState) + { + // drop stray explicit reference + getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); + mState = BeginState; + mExplicit.clear(); + } + if (mState==BeginState) { switch (keyword) From 70d35da1160090cd9dd3e338dbf498f49deca356 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Tue, 11 Feb 2014 16:34:23 +0100 Subject: [PATCH 793/889] Unset selected spell when removed --- apps/openmw/mwscript/statsextensions.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 7a59e9689..c4f672dc7 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -17,6 +17,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" @@ -451,6 +452,14 @@ namespace MWScript runtime.pop(); MWWorld::Class::get (ptr).getCreatureStats (ptr).getSpells().remove (id); + + MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager(); + + if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr() && + id == wm->getSelectedSpell()) + { + wm->unsetSelectedSpell(); + } } }; From a3eea4f6b61932caf20e1ab856fa683be2253e5f Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Tue, 11 Feb 2014 16:34:51 +0100 Subject: [PATCH 794/889] Do not allow spellcasting stance without spell selected --- apps/openmw/mwgui/windowmanagerimp.cpp | 5 +++++ apps/openmw/mwinput/inputmanagerimp.cpp | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index bd17cb317..878c2b95c 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1035,6 +1035,11 @@ namespace MWGui { mSelectedSpell = ""; mHud->unsetSelectedSpell(); + + MWWorld::Player* player = &MWBase::Environment::get().getWorld()->getPlayer(); + if (player->getDrawState() == MWMechanics::DrawState_Spell) + player->setDrawState(MWMechanics::DrawState_Nothing); + mSpellWindow->setTitle("#{sNone}"); } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index c4b8d0a89..7ed3007ff 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -646,6 +646,10 @@ namespace MWInput if (!mControlSwitch["playermagic"]) return; + // Not allowed if no spell selected + if (MWBase::Environment::get().getWindowManager()->getSelectedSpell().empty()) + return; + MWMechanics::DrawState_ state = mPlayer->getDrawState(); if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing) mPlayer->setDrawState(MWMechanics::DrawState_Spell); From 8824af30b4d58ef0b34929a96521182c01d5d785 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Tue, 11 Feb 2014 18:26:57 +0100 Subject: [PATCH 795/889] Allow to display message box outside of dialogue window --- apps/openmw/mwbase/windowmanager.hpp | 8 +++++++- apps/openmw/mwgui/windowmanagerimp.cpp | 13 ++++--------- apps/openmw/mwgui/windowmanagerimp.hpp | 2 +- apps/openmw/mwmechanics/npcstats.cpp | 6 ++++-- apps/openmw/mwscript/containerextensions.cpp | 4 ++-- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 069e6311a..4fce19e33 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -57,6 +57,12 @@ namespace MWGui class InventoryWindow; class ContainerWindow; class DialogueWindow; + + enum ShowInDialogueMode { + ShowInDialogueMode_IfPossible, + ShowInDialogueMode_Only, + ShowInDialogueMode_Never + }; } namespace SFO @@ -226,7 +232,7 @@ namespace MWBase virtual void removeDialog(OEngine::GUI::Layout* dialog) = 0; ///< Hides dialog and schedules dialog to be deleted. - virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector(), bool showInDialogueModeOnly = false) = 0; + virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector(), enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible) = 0; virtual void staticMessageBox(const std::string& message) = 0; virtual void removeStaticMessageBox() = 0; virtual int readPressedButton() = 0; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 878c2b95c..5448bc3c4 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -648,19 +648,14 @@ namespace MWGui mGarbageDialogs.push_back(dialog); } - void WindowManager::messageBox (const std::string& message, const std::vector& buttons, bool showInDialogueModeOnly) + void WindowManager::messageBox (const std::string& message, const std::vector& buttons, enum MWGui::ShowInDialogueMode showInDialogueMode) { if (buttons.empty()) { /* If there are no buttons, and there is a dialogue window open, messagebox goes to the dialogue window */ - if (getMode() == GM_Dialogue) { + if (getMode() == GM_Dialogue && showInDialogueMode != MWGui::ShowInDialogueMode_Never) { mDialogueWindow->addMessageBox(MyGUI::LanguageManager::getInstance().replaceTags(message)); - } else { - if (showInDialogueModeOnly) { - if (getMode() == GM_Dialogue) - mMessageBoxManager->createMessageBox(message); - } else { - mMessageBoxManager->createMessageBox(message); - } + } else if (showInDialogueMode != MWGui::ShowInDialogueMode_Only) { + mMessageBoxManager->createMessageBox(message); } } else { mMessageBoxManager->createInteractiveMessageBox(message, buttons); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index db52d9f79..dafb65e47 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -220,7 +220,7 @@ namespace MWGui virtual void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. - virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector(), bool showInDialogueModeOnly = false); + virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector(), enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible); virtual void staticMessageBox(const std::string& message); virtual void removeStaticMessageBox(); virtual int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index e642ffc5a..63b4467f6 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -220,16 +220,18 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas /// \todo check if character is the player, if levelling is ever implemented for NPCs MWBase::Environment::get().getSoundManager ()->playSound ("skillraise", 1, 1); + std::vector noButtons; + std::stringstream message; message << boost::format(MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", "")) % std::string("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}") % static_cast (base); - MWBase::Environment::get().getWindowManager ()->messageBox(message.str()); + MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), noButtons, MWGui::ShowInDialogueMode_Never); if (mLevelProgress >= gmst.find("iLevelUpTotal")->getInt()) { // levelup is possible now - MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}"); + MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}", noButtons, MWGui::ShowInDialogueMode_Never); } getSkill (skillIndex).setBase (base); diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index e06505e86..fc21c5712 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -70,7 +70,7 @@ namespace MWScript msgBox = boost::str(boost::format(msgBox) % count % itemName); } std::vector noButtons; - MWBase::Environment::get().getWindowManager()->messageBox(msgBox, noButtons, /*showInDialogueModeOnly*/ true); + MWBase::Environment::get().getWindowManager()->messageBox(msgBox, noButtons, MWGui::ShowInDialogueMode_Only); } } }; @@ -142,7 +142,7 @@ namespace MWScript msgBox = boost::str (boost::format(msgBox) % itemName); } std::vector noButtons; - MWBase::Environment::get().getWindowManager()->messageBox(msgBox, noButtons, /*showInDialogueModeOnly*/ true); + MWBase::Environment::get().getWindowManager()->messageBox(msgBox, noButtons, MWGui::ShowInDialogueMode_Only); } } }; From 6a5d88b640c0ba26f8431e14e891411dcca505c2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 12 Feb 2014 08:42:42 +0100 Subject: [PATCH 796/889] ignore stray else and endif in scripts --- components/compiler/lineparser.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 3b27905d4..4a5e1cbd0 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -332,6 +332,18 @@ namespace Compiler Generator::stopScript (mCode); mState = EndState; return true; + + case Scanner::K_else: + + getErrorHandler().warning ("stay else (ignoring it)", loc); + mState = EndState; + return true; + + case Scanner::K_endif: + + getErrorHandler().warning ("stay endif (ignoring it)", loc); + mState = EndState; + return true; } } else if (mState==SetLocalVarState && keyword==Scanner::K_to) From 61626e90da98641db5da0e33457977311ec30c6f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 12 Feb 2014 08:53:37 +0100 Subject: [PATCH 797/889] allow additional string argument for stopcombat and addspell (ignored) --- components/compiler/exprparser.cpp | 25 ++++++++++++++----------- components/compiler/exprparser.hpp | 1 + components/compiler/extensions0.cpp | 4 ++-- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 283da854c..4af11ec2b 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -705,11 +705,11 @@ namespace Compiler { optional = true; } - else if (*iter=='S' || *iter=='c') + else if (*iter=='S' || *iter=='c' || *iter=='x') { stringParser.reset(); - if (optional) + if (optional || *iter=='x') stringParser.setOptional (true); if (*iter=='c') stringParser.smashCase(); @@ -718,18 +718,21 @@ namespace Compiler if (optional && stringParser.isEmpty()) break; - if (invert) + if (*iter!='x') { - std::vector tmp; - stringParser.append (tmp); + if (invert) + { + std::vector tmp; + stringParser.append (tmp); - stack.push (tmp); + stack.push (tmp); + } + else + stringParser.append (code); + + if (optional) + ++optionalCount; } - else - stringParser.append (code); - - if (optional) - ++optionalCount; } else { diff --git a/components/compiler/exprparser.hpp b/components/compiler/exprparser.hpp index 8ce5409d2..905f0d454 100644 --- a/components/compiler/exprparser.hpp +++ b/components/compiler/exprparser.hpp @@ -101,6 +101,7 @@ namespace Compiler /// \param arguments Each character represents one arguments ('l': integer, /// 'f': float, 'S': string, 'c': string (case smashed), '/': following arguments are /// optional) + /// 'x': optional string that will be ignored (die in a fire, MW script compiler!) /// \param invert Store arguments in reverted order. /// \return number of optional arguments }; diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index ebe46d282..53f1baaf9 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -62,7 +62,7 @@ namespace Compiler extensions.registerInstruction ("toggleai", "", opcodeToggleAI, opcodeToggleAI); extensions.registerInstruction ("tai", "", opcodeToggleAI, opcodeToggleAI); extensions.registerInstruction("startcombat", "c", opcodeStartCombat, opcodeStartCombatExplicit); - extensions.registerInstruction("stopcombat", "", opcodeStopCombat, opcodeStopCombatExplicit); + extensions.registerInstruction("stopcombat", "x", opcodeStopCombat, opcodeStopCombatExplicit); extensions.registerFunction ("gethello", 'l', "", opcodeGetHello, opcodeGetHelloExplicit); extensions.registerFunction ("getfight", 'l', "", opcodeGetFight, opcodeGetFightExplicit); extensions.registerFunction ("getflee", 'l', "", opcodeGetFlee, opcodeGetFleeExplicit); @@ -398,7 +398,7 @@ namespace Compiler extensions.registerInstruction ("setpccrimelevel", "f", opcodeSetPCCrimeLevel); extensions.registerInstruction ("modpccrimelevel", "f", opcodeModPCCrimeLevel); - extensions.registerInstruction ("addspell", "c", opcodeAddSpell, opcodeAddSpellExplicit); + extensions.registerInstruction ("addspell", "cx", opcodeAddSpell, opcodeAddSpellExplicit); extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell, opcodeRemoveSpellExplicit); extensions.registerInstruction ("removespelleffects", "c", opcodeRemoveSpellEffects, From fc135fbfee090ae37f89b8cfd80d4668dd968624 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 12 Feb 2014 09:04:52 +0100 Subject: [PATCH 798/889] Added new Display types --- apps/opencs/model/world/columnbase.hpp | 65 ++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index 70f38c534..be5a427b8 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -27,6 +27,71 @@ namespace CSMWorld enum Display { Display_String, + + //CONCRETE TYPES STARTS HERE + Display_Globals, + Display_Global, + Display_VerificationResults, + Display_Gmsts, + Display_Gmst, + Display_Skills, + Display_Skill, + Display_Classes, + Display_Class, + Display_Factions, + Display_Faction, + Display_Races, + Display_Race, + Display_Sounds, + Display_Sound, + Display_Scripts, + Display_Script, + Display_Regions, + Display_Region, + Display_Birthsigns, + Display_Birthsign, + Display_Spells, + Display_Spell, + Display_Cells, + Display_Cell, + Display_Referenceables, + Display_Referenceable, + Display_Activator, + Display_Potion, + Display_Apparatus, + Display_Armor, + Display_Book, + Display_Clothing, + Display_Container, + Display_Creature, + Display_Door, + Display_Ingredient, + Display_CreatureLevelledList, + Display_ItemLevelledList, + Display_Light, + Display_Lockpick, + Display_Miscellaneous, + Display_Npc, + Display_Probe, + Display_Repair, + Display_Static, + Display_Weapon, + Display_References, + Display_Reference, + Display_RegionMap, + Display_Filter, + Display_Filters, + Display_Topics, + Display_Topic, + Display_Journals, + Display_Journal, + Display_TopicInfos, + Display_TopicInfo, + Display_JournalInfos, + Display_JournalInfo, + Display_Scene, + //CONCRETE TYPES ENDS HERE + Display_Integer, Display_Float, Display_Var, From bfb0e62c4ab2fc3b6cb391e40ba8a70adc6c3c28 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 12 Feb 2014 09:10:05 +0100 Subject: [PATCH 799/889] ignore additional string argument after enable/disable --- components/compiler/lineparser.cpp | 14 +++++++++++--- components/compiler/lineparser.hpp | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 4a5e1cbd0..3b2b1f11b 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -84,6 +84,13 @@ namespace Compiler bool LineParser::parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner) { + if (mState==PotentialEndState) + { + getErrorHandler().warning ("stay string argument (ignoring it)", loc); + mState = EndState; + return true; + } + if (mState==SetState) { std::string name2 = Misc::StringUtils::lowerCase (name); @@ -219,13 +226,13 @@ namespace Compiler case Scanner::K_enable: Generator::enable (mCode, mLiterals, mExplicit); - mState = EndState; + mState = PotentialEndState; return true; case Scanner::K_disable: Generator::disable (mCode, mLiterals, mExplicit); - mState = EndState; + mState = PotentialEndState; return true; } @@ -406,7 +413,8 @@ namespace Compiler bool LineParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) { - if (code==Scanner::S_newline && (mState==EndState || mState==BeginState)) + if (code==Scanner::S_newline && + (mState==EndState || mState==BeginState || mState==PotentialEndState)) return false; if (code==Scanner::S_comma && mState==MessageState) diff --git a/components/compiler/lineparser.hpp b/components/compiler/lineparser.hpp index 0aba30d4b..c54c764bc 100644 --- a/components/compiler/lineparser.hpp +++ b/components/compiler/lineparser.hpp @@ -23,7 +23,7 @@ namespace Compiler SetState, SetLocalVarState, SetGlobalVarState, SetPotentialMemberVarState, SetMemberVarState, SetMemberVarState2, MessageState, MessageCommaState, MessageButtonState, MessageButtonCommaState, - EndState, + EndState, PotentialEndState /* may have a stray string argument */, PotentialExplicitState, ExplicitState, MemberState }; From b1b0877122a21d48898b46ef49c3062168a5b8fc Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 12 Feb 2014 09:14:32 +0100 Subject: [PATCH 800/889] allow additional numeric argument for AiFollow and ignore it --- components/compiler/extensions0.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 53f1baaf9..5079a6064 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -41,7 +41,7 @@ namespace Compiler opcodeAiEscortCellExplicit); extensions.registerInstruction ("aiwander", "fff/llllllllll", opcodeAiWander, opcodeAiWanderExplicit); - extensions.registerInstruction ("aifollow", "cffff/l", opcodeAiFollow, + extensions.registerInstruction ("aifollow", "cffff/ll", opcodeAiFollow, opcodeAiFollowExplicit); extensions.registerInstruction ("aifollowcell", "ccffff/l", opcodeAiFollowCell, opcodeAiFollowCellExplicit); From c0a6acfe6cb7cf3ea6277c2539de571d6e55ad1e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 12 Feb 2014 09:23:23 +0100 Subject: [PATCH 801/889] allow the use of keywords as variable names in more places --- components/compiler/lineparser.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 3b2b1f11b..3ae24bfb7 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -219,6 +219,12 @@ namespace Compiler bool LineParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { + if (mState==SetState) + { + // allow keywords to be used as variable names when assigning a value to a variable. + return parseName (loc.mLiteral, loc, scanner); + } + if (mState==BeginState || mState==ExplicitState) { switch (keyword) From e1a39b83884cfd9ac45025533cb9af9adfeb0fcd Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 12 Feb 2014 09:24:08 +0100 Subject: [PATCH 802/889] Replaced some display_string with specific type. Damn, i wish this is correct ;-) --- apps/opencs/model/world/columnimp.hpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 18aac9e0b..049042109 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -373,7 +373,7 @@ namespace CSMWorld SkillsColumn (int index, bool typePrefix = false, bool major = false) : Column ((typePrefix ? ( major ? Columns::ColumnId_MajorSkill1 : Columns::ColumnId_MinorSkill1) : - Columns::ColumnId_Skill1) + index, ColumnBase::Display_String), + Columns::ColumnId_Skill1) + index, ColumnBase::Display_Skills), mIndex (index), mMajor (major) {} @@ -598,7 +598,7 @@ namespace CSMWorld struct SoundFileColumn : public Column { SoundFileColumn() - : Column (Columns::ColumnId_SoundFile, ColumnBase::Display_String) + : Column (Columns::ColumnId_SoundFile, ColumnBase::Display_Sound) {} virtual QVariant get (const Record& record) const @@ -811,7 +811,7 @@ namespace CSMWorld template struct CellColumn : public Column { - CellColumn() : Column (Columns::ColumnId_Cell, ColumnBase::Display_String) {} + CellColumn() : Column (Columns::ColumnId_Cell, ColumnBase::Display_Cell) {} virtual QVariant get (const Record& record) const { @@ -890,7 +890,7 @@ namespace CSMWorld template struct OwnerColumn : public Column { - OwnerColumn() : Column (Columns::ColumnId_Owner, ColumnBase::Display_String) {} + OwnerColumn() : Column (Columns::ColumnId_Owner, ColumnBase::Display_Npc) {} virtual QVariant get (const Record& record) const { @@ -915,7 +915,7 @@ namespace CSMWorld template struct SoulColumn : public Column { - SoulColumn() : Column (Columns::ColumnId_Soul, ColumnBase::Display_String) {} + SoulColumn() : Column (Columns::ColumnId_Soul, ColumnBase::Display_Creature) {} virtual QVariant get (const Record& record) const { @@ -940,7 +940,7 @@ namespace CSMWorld template struct FactionColumn : public Column { - FactionColumn() : Column (Columns::ColumnId_Faction, ColumnBase::Display_String) {} + FactionColumn() : Column (Columns::ColumnId_Faction, ColumnBase::Display_Faction) {} virtual QVariant get (const Record& record) const { @@ -1090,7 +1090,7 @@ namespace CSMWorld struct TeleportCellColumn : public Column { TeleportCellColumn() - : Column (Columns::ColumnId_TeleportCell, ColumnBase::Display_String) + : Column (Columns::ColumnId_TeleportCell, ColumnBase::Display_Cell) {} virtual QVariant get (const Record& record) const @@ -1146,7 +1146,7 @@ namespace CSMWorld template struct KeyColumn : public Column { - KeyColumn() : Column (Columns::ColumnId_Key, ColumnBase::Display_String) {} + KeyColumn() : Column (Columns::ColumnId_Key, ColumnBase::Display_Miscellaneous) {} virtual QVariant get (const Record& record) const { @@ -1510,7 +1510,7 @@ namespace CSMWorld template struct ClassColumn : public Column { - ClassColumn() : Column (Columns::ColumnId_Class, ColumnBase::Display_String) {} + ClassColumn() : Column (Columns::ColumnId_Class, ColumnBase::Display_Class) {} virtual QVariant get (const Record& record) const { @@ -1535,7 +1535,7 @@ namespace CSMWorld template struct PcFactionColumn : public Column { - PcFactionColumn() : Column (Columns::ColumnId_PcFaction, ColumnBase::Display_String) {} + PcFactionColumn() : Column (Columns::ColumnId_PcFaction, ColumnBase::Display_Faction) {} virtual QVariant get (const Record& record) const { From 93d47430125be4d766d6937541ba826138a790a2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 12 Feb 2014 09:56:25 +0100 Subject: [PATCH 803/889] interpret instruction keywords as names within expressions --- components/compiler/exprparser.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 4af11ec2b..319bb4498 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -344,6 +344,17 @@ namespace Compiler bool ExprParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { + if (const Extensions *extensions = getContext().getExtensions()) + { + std::string argumentType; // ignored + bool hasExplicit = false; // ignored + if (extensions->isInstruction (keyword, argumentType, hasExplicit)) + { + // pretend this is not a keyword + return parseName (loc.mLiteral, loc, scanner); + } + } + mFirst = false; if (!mExplicit.empty()) From 2e33ab3a13b61e846155e9b8bf889c8725d4f015 Mon Sep 17 00:00:00 2001 From: gus Date: Wed, 12 Feb 2014 11:16:12 +0100 Subject: [PATCH 804/889] droping somewhat works --- apps/opencs/view/world/table.cpp | 5 +++++ apps/opencs/view/world/table.hpp | 3 +++ 2 files changed, 8 insertions(+) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 1a9499f52..15f946f71 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -465,4 +465,9 @@ void CSVWorld::Table::dropEvent(QDropEvent *event) { std::cout << "drop"; event->acceptProposedAction(); +} + +void CSVWorld::Table::dragMoveEvent(QDragMoveEvent *event) +{ + event->accept(); } \ No newline at end of file diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 44d72f7fb..fd2839688 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -56,6 +56,9 @@ namespace CSVWorld void dropEvent(QDropEvent *event); + void dragMoveEvent(QDragMoveEvent *event); + + public: Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, bool sorting); From 6cf561f713d54ede7e4e4d5ffb34da962c097bb0 Mon Sep 17 00:00:00 2001 From: gus Date: Wed, 12 Feb 2014 11:32:01 +0100 Subject: [PATCH 805/889] get index to where we are dropping --- apps/opencs/view/world/table.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 15f946f71..fe79ab2ff 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -465,6 +465,8 @@ void CSVWorld::Table::dropEvent(QDropEvent *event) { std::cout << "drop"; event->acceptProposedAction(); + QModelIndex index = indexAt(event->pos()); + std::cout << index.row(); } void CSVWorld::Table::dragMoveEvent(QDragMoveEvent *event) From 62c9c77ddf734b0f4cbb97842d855e776e4fc0f2 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 12 Feb 2014 11:42:19 +0100 Subject: [PATCH 806/889] Compiles now. --- apps/opencs/model/world/columnbase.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index be5a427b8..d0419289f 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -44,8 +44,6 @@ namespace CSMWorld Display_Race, Display_Sounds, Display_Sound, - Display_Scripts, - Display_Script, Display_Regions, Display_Region, Display_Birthsigns, From c00834a8de88e1042b2f046c9532270dc5da1ddc Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 12 Feb 2014 13:12:58 +0100 Subject: [PATCH 807/889] added some iostream garbage. --- apps/opencs/model/world/universalid.cpp | 3 ++- apps/opencs/view/world/table.cpp | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index e633f4f69..88674c4ce 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace { @@ -186,7 +187,7 @@ CSMWorld::UniversalId::UniversalId (Type type, const std::string& id) mClass = sIdArg[i].mClass; return; } - + std::cout<setMimeData (mime); drag->setPixmap (QString::fromStdString (mime->getIcon())); drag->exec(); - std::cout << "startdrag"; + std::cout << "startdrag\n"; } } @@ -456,14 +456,14 @@ void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) void CSVWorld::Table::dragEnterEvent(QDragEnterEvent *event) { //if (event->mimeData()->hasFormat("text/plain")) - std::cout << "accept drag event"; + std::cout << "accept drag event\n"; event->acceptProposedAction(); } void CSVWorld::Table::dropEvent(QDropEvent *event) { - std::cout << "drop"; + std::cout << "drop\n"; event->acceptProposedAction(); QModelIndex index = indexAt(event->pos()); std::cout << index.row(); From 388735046fdb75b34744f1c292a8a349f4ab4f40 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 12 Feb 2014 13:35:24 +0100 Subject: [PATCH 808/889] fixed broken remote member variable access --- apps/openmw/mwscript/interpretercontext.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 17092b41c..b79808d08 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -66,7 +66,7 @@ namespace MWScript { const MWWorld::Ptr ptr = getReference (id, false); - std::string id = MWWorld::Class::get (ptr).getScript (ptr); + id = MWWorld::Class::get (ptr).getScript (ptr); ptr.getRefData().setLocals ( *MWBase::Environment::get().getWorld()->getStore().get().find (id)); @@ -86,7 +86,7 @@ namespace MWScript { const MWWorld::Ptr ptr = getReference (id, false); - std::string id = MWWorld::Class::get (ptr).getScript (ptr); + id = MWWorld::Class::get (ptr).getScript (ptr); ptr.getRefData().setLocals ( *MWBase::Environment::get().getWorld()->getStore().get().find (id)); From dc433a3c093c1a060e2c71c31a9d09a061834093 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 12 Feb 2014 13:38:16 +0100 Subject: [PATCH 809/889] fixed case handling problem for local variable access --- apps/openmw/mwscript/scriptmanagerimp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index 11e094a84..5e18151da 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -221,8 +221,10 @@ namespace MWScript throw std::runtime_error ("invalid variable type"); } + std::string variable2 = Misc::StringUtils::lowerCase (variable); + for (int i=0; imVarNames.at (i+offset)==variable) + if (Misc::StringUtils::lowerCase (script->mVarNames.at (i+offset))==variable2) return i; throw std::runtime_error ("unable to access local variable " + variable + " of " + scriptId); From 6e2e4d1adf9000a81a49c3f7f1bee2245edf933d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 12 Feb 2014 13:45:01 +0100 Subject: [PATCH 810/889] ignore stray begin --- components/compiler/lineparser.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 3ae24bfb7..1407d0e95 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -357,6 +357,12 @@ namespace Compiler getErrorHandler().warning ("stay endif (ignoring it)", loc); mState = EndState; return true; + + case Scanner::K_begin: + + getErrorHandler().warning ("stay begin (ignoring it)", loc); + mState = EndState; + return true; } } else if (mState==SetLocalVarState && keyword==Scanner::K_to) From a85d3c7dcb52db79ad8ab0314063e134153e6742 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 12 Feb 2014 13:53:59 +0100 Subject: [PATCH 811/889] ignore unused explicit references for functions --- components/compiler/exprparser.cpp | 13 +++++++++++-- components/compiler/extensions.cpp | 4 ++-- components/compiler/extensions.hpp | 4 +++- components/compiler/lineparser.cpp | 11 +++++++++-- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 319bb4498..07f576006 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -397,8 +397,15 @@ namespace Compiler char returnType; std::string argumentType; - if (extensions->isFunction (keyword, returnType, argumentType, true)) + bool hasExplicit = true; + if (extensions->isFunction (keyword, returnType, argumentType, hasExplicit)) { + if (!hasExplicit) + { + getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); + mExplicit.clear(); + } + start(); mTokenLoc = loc; @@ -519,7 +526,9 @@ namespace Compiler char returnType; std::string argumentType; - if (extensions->isFunction (keyword, returnType, argumentType, false)) + bool hasExplicit = false; + + if (extensions->isFunction (keyword, returnType, argumentType, hasExplicit)) { mTokenLoc = loc; int optionals = parseArguments (argumentType, scanner); diff --git a/components/compiler/extensions.cpp b/components/compiler/extensions.cpp index 6f5c2fc89..c09abcbaf 100644 --- a/components/compiler/extensions.cpp +++ b/components/compiler/extensions.cpp @@ -22,7 +22,7 @@ namespace Compiler } bool Extensions::isFunction (int keyword, char& returnType, std::string& argumentType, - bool explicitReference) const + bool& explicitReference) const { std::map::const_iterator iter = mFunctions.find (keyword); @@ -30,7 +30,7 @@ namespace Compiler return false; if (explicitReference && iter->second.mCodeExplicit==-1) - return false; + explicitReference = false; returnType = iter->second.mReturn; argumentType = iter->second.mArguments; diff --git a/components/compiler/extensions.hpp b/components/compiler/extensions.hpp index 585c42681..79cfed9e8 100644 --- a/components/compiler/extensions.hpp +++ b/components/compiler/extensions.hpp @@ -47,9 +47,11 @@ namespace Compiler /// - keyword must be all lower case. bool isFunction (int keyword, char& returnType, std::string& argumentType, - bool explicitReference) const; + bool& explicitReference) const; ///< Is this keyword registered with a function? If yes, return return and argument /// types. + /// \param explicitReference In: has explicit reference; Out: set to false, if + /// explicit reference is not available for this instruction. bool isInstruction (int keyword, std::string& argumentType, bool& explicitReference) const; diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 1407d0e95..e435b936c 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -279,9 +279,16 @@ namespace Compiler char returnType; std::string argumentType; - if (extensions->isFunction (keyword, returnType, argumentType, - !mExplicit.empty())) + bool hasExplicit = !mExplicit.empty(); + + if (extensions->isFunction (keyword, returnType, argumentType, hasExplicit)) { + if (!hasExplicit && !mExplicit.empty()) + { + getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); + mExplicit.clear(); + } + scanner.putbackKeyword (keyword, loc); parseExpression (scanner, loc); mState = EndState; From b3412b7eec1c19ae427201c544d778d18cff7a1a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 12 Feb 2014 15:22:17 +0100 Subject: [PATCH 812/889] another case fix (remote member access again) --- apps/openmw/mwscript/compilercontext.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/compilercontext.cpp b/apps/openmw/mwscript/compilercontext.cpp index 8018b86e0..1b3e769bf 100644 --- a/apps/openmw/mwscript/compilercontext.cpp +++ b/apps/openmw/mwscript/compilercontext.cpp @@ -52,7 +52,8 @@ namespace MWScript char type = ' '; if (!script.empty()) - type = MWBase::Environment::get().getScriptManager()->getLocals (script).getType (name); + type = MWBase::Environment::get().getScriptManager()->getLocals (script).getType ( + Misc::StringUtils::lowerCase (name)); return std::make_pair (type, reference); } From 749136bf3394df4498eb1f672313124b3365c1cc Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 12 Feb 2014 20:23:47 +0100 Subject: [PATCH 813/889] ignore attempts to set non-existing variables --- components/compiler/lineparser.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index e435b936c..495e7e25e 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -219,6 +219,14 @@ namespace Compiler bool LineParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { + if (mState==SetPotentialMemberVarState && keyword==Scanner::K_to) + { + getErrorHandler().warning ("unknown variable (ignoring set instruction)", loc); + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + return false; + } + if (mState==SetState) { // allow keywords to be used as variable names when assigning a value to a variable. From 2eeb0eb4f3fcc60ee206b7fb53786a66a3d2d04c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Feb 2014 08:38:26 +0100 Subject: [PATCH 814/889] allow lines to continue with other instructions after an legit else --- components/compiler/controlparser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/compiler/controlparser.cpp b/components/compiler/controlparser.cpp index ba2db62b6..60d17a821 100644 --- a/components/compiler/controlparser.cpp +++ b/components/compiler/controlparser.cpp @@ -72,7 +72,7 @@ namespace Compiler } else if (keyword==Scanner::K_else) { - mState = IfElseEndState; + mState = IfElseBodyState; /// \todo should be IfElseEndState; add an option for that } return true; From 0313876d88f9eb55c8ed43a9b79787ec17866b05 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Feb 2014 08:49:40 +0100 Subject: [PATCH 815/889] allow leaving out if in a top-level if-statement --- components/compiler/scriptparser.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/components/compiler/scriptparser.cpp b/components/compiler/scriptparser.cpp index 1b613595a..f34313969 100644 --- a/components/compiler/scriptparser.cpp +++ b/components/compiler/scriptparser.cpp @@ -71,6 +71,12 @@ namespace Compiler if (code==Scanner::S_newline) // empty line return true; + if (code==Scanner::S_open) /// \todo Option to switch this off + { + scanner.putbackSpecial (code, loc); + return parseKeyword (Scanner::K_if, loc, scanner); + } + mLineParser.reset(); if (mLineParser.parseSpecial (code, loc, scanner)) scanner.scan (mLineParser); From c03bd8ebb6c3b18869c75350f1c276ed03dfdb0e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Feb 2014 08:59:33 +0100 Subject: [PATCH 816/889] allow [] as aliases for () --- components/compiler/scanner.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 816443c44..46e50a2e9 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -370,9 +370,9 @@ namespace Compiler if (c=='\n') special = S_newline; - else if (c=='(') + else if (c=='(' || c=='[') /// \todo option to disable the use of [ as alias for ( special = S_open; - else if (c==')') + else if (c==')' || c==']') /// \todo option to disable the use of ] as alias for ) special = S_close; else if (c=='.') { From 87b51e47a9d1357d3e1c6968ea3add6ddd58d8d5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Feb 2014 09:40:07 +0100 Subject: [PATCH 817/889] fixed another case issue in remote member access --- components/compiler/controlparser.cpp | 1 - components/compiler/locals.cpp | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/components/compiler/controlparser.cpp b/components/compiler/controlparser.cpp index 60d17a821..499358ca0 100644 --- a/components/compiler/controlparser.cpp +++ b/components/compiler/controlparser.cpp @@ -9,7 +9,6 @@ #include "generator.hpp" #include "errorhandler.hpp" -#include namespace Compiler { bool ControlParser::parseIfBody (int keyword, const TokenLoc& loc, Scanner& scanner) diff --git a/components/compiler/locals.cpp b/components/compiler/locals.cpp index e2b1c5c96..60a5704bf 100644 --- a/components/compiler/locals.cpp +++ b/components/compiler/locals.cpp @@ -7,6 +7,8 @@ #include #include +#include + namespace Compiler { const std::vector& Locals::get (char type) const @@ -97,7 +99,7 @@ namespace Compiler void Locals::declare (char type, const std::string& name) { - get (type).push_back (name); + get (type).push_back (Misc::StringUtils::lowerCase (name)); } void Locals::clear() From dde4fbd8184211700be7d5e5bd1651acafeeb1cb Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Feb 2014 09:52:44 +0100 Subject: [PATCH 818/889] allow one more integer argument in RemoveSoulGem and up to 6 more in AiFollow and then throw them all away --- apps/openmw/mwscript/docs/vmformat.txt | 8 +++++--- apps/openmw/mwscript/miscextensions.cpp | 12 ++++++++---- components/compiler/extensions0.cpp | 4 ++-- components/compiler/opcodes.hpp | 4 ++-- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 2fe4c768b..70186a089 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -52,7 +52,9 @@ op 0x20023: AiFollow, explicit reference op 0x20024: AiFollowCell op 0x20025: AiFollowCell, explicit reference op 0x20026: ModRegion -opcodes 0x20027-0x3ffff unused +op 0x20027: RemoveSoulGem +op 0x20028: RemoveSoulGem, explicit reference +opcodes 0x20029-0x3ffff unused Segment 4: (not implemented yet) @@ -308,8 +310,8 @@ op 0x20001f1: GetDetected op 0x20001f2: GetDetected, explicit reference op 0x20001f3: AddSoulGem op 0x20001f4: AddSoulGem, explicit reference -op 0x20001f5: RemoveSoulGem -op 0x20001f6: RemoveSoulGem, explicit reference +op 0x20001f5: unused +op 0x20001f6: unused op 0x20001f7: PlayBink op 0x20001f8: Drop op 0x20001f9: Drop, explicit reference diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index aa0e775af..0c60e1c94 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -365,17 +365,21 @@ namespace MWScript }; template - class OpRemoveSoulGem : public Interpreter::Opcode0 + class OpRemoveSoulGem : public Interpreter::Opcode1 { public: - virtual void execute (Interpreter::Runtime& runtime) + virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::Ptr ptr = R()(runtime); std::string soul = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); + // throw away additional arguments + for (unsigned int i=0; i); interpreter.installSegment5 (Compiler::Misc::opcodeAddSoulGem, new OpAddSoulGem); interpreter.installSegment5 (Compiler::Misc::opcodeAddSoulGemExplicit, new OpAddSoulGem); - interpreter.installSegment5 (Compiler::Misc::opcodeRemoveSoulGem, new OpRemoveSoulGem); - interpreter.installSegment5 (Compiler::Misc::opcodeRemoveSoulGemExplicit, new OpRemoveSoulGem); + interpreter.installSegment3 (Compiler::Misc::opcodeRemoveSoulGem, new OpRemoveSoulGem); + interpreter.installSegment3 (Compiler::Misc::opcodeRemoveSoulGemExplicit, new OpRemoveSoulGem); interpreter.installSegment5 (Compiler::Misc::opcodeDrop, new OpDrop); interpreter.installSegment5 (Compiler::Misc::opcodeDropExplicit, new OpDrop); interpreter.installSegment5 (Compiler::Misc::opcodeDropSoulGem, new OpDropSoulGem); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 5079a6064..78b6409f2 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -41,7 +41,7 @@ namespace Compiler opcodeAiEscortCellExplicit); extensions.registerInstruction ("aiwander", "fff/llllllllll", opcodeAiWander, opcodeAiWanderExplicit); - extensions.registerInstruction ("aifollow", "cffff/ll", opcodeAiFollow, + extensions.registerInstruction ("aifollow", "cffff/llllllll", opcodeAiFollow, opcodeAiFollowExplicit); extensions.registerInstruction ("aifollowcell", "ccffff/l", opcodeAiFollowCell, opcodeAiFollowCellExplicit); @@ -253,7 +253,7 @@ namespace Compiler extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); extensions.registerFunction ("geteffect", 'l', "S", opcodeGetEffect, opcodeGetEffectExplicit); extensions.registerInstruction ("addsoulgem", "cc", opcodeAddSoulGem, opcodeAddSoulGemExplicit); - extensions.registerInstruction ("removesoulgem", "c", opcodeRemoveSoulGem, opcodeRemoveSoulGemExplicit); + extensions.registerInstruction ("removesoulgem", "c/l", opcodeRemoveSoulGem, opcodeRemoveSoulGemExplicit); extensions.registerInstruction ("drop", "cl", opcodeDrop, opcodeDropExplicit); extensions.registerInstruction ("dropsoulgem", "c", opcodeDropSoulGem, opcodeDropSoulGemExplicit); extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 583cf4eea..1dbdbf7e7 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -203,8 +203,8 @@ namespace Compiler const int opcodeGetEffectExplicit = 0x20001d0; const int opcodeAddSoulGem = 0x20001f3; const int opcodeAddSoulGemExplicit = 0x20001f4; - const int opcodeRemoveSoulGem = 0x20001f5; - const int opcodeRemoveSoulGemExplicit = 0x20001f6; + const int opcodeRemoveSoulGem = 0x20027; + const int opcodeRemoveSoulGemExplicit = 0x20028; const int opcodeDrop = 0x20001f8; const int opcodeDropExplicit = 0x20001f9; const int opcodeDropSoulGem = 0x20001fa; From ac8290c4d3b329073a49369fde82a33d023998a2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Feb 2014 09:59:22 +0100 Subject: [PATCH 819/889] fixed problem with line endings in case of a local variable redeclaration --- components/compiler/declarationparser.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/compiler/declarationparser.cpp b/components/compiler/declarationparser.cpp index 586b28bc2..9c4e4f631 100644 --- a/components/compiler/declarationparser.cpp +++ b/components/compiler/declarationparser.cpp @@ -27,8 +27,7 @@ bool Compiler::DeclarationParser::parseName (const std::string& name, const Toke /// \todo add option to make re-declared local variables an error getErrorHandler().warning ("can't re-declare local variable (ignoring declaration)", loc); - SkipParser skip (getErrorHandler(), getContext()); - scanner.scan (skip); + mState = State_End; return true; } From fb0c5be536de767a14deb5a2a34c7744269db826 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 13 Feb 2014 10:13:03 +0100 Subject: [PATCH 820/889] Don't suppress git error output --- cmake/GetGitRevisionDescription.cmake | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmake/GetGitRevisionDescription.cmake b/cmake/GetGitRevisionDescription.cmake index f70f64261..fecd1654d 100644 --- a/cmake/GetGitRevisionDescription.cmake +++ b/cmake/GetGitRevisionDescription.cmake @@ -122,7 +122,6 @@ function(git_describe _var) res OUTPUT_VARIABLE out - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT res EQUAL 0) @@ -147,7 +146,6 @@ function(get_git_tag_revision _var) res OUTPUT_VARIABLE out - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT res EQUAL 0) set(out "${out}-${res}-NOTFOUND") From c6fb0f2d9bf2540d8c23817174d8066485a7f5f1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 13 Feb 2014 10:13:53 +0100 Subject: [PATCH 821/889] Removed terrain component's dependency on ESM --- apps/openmw/mwrender/terrainstorage.cpp | 514 ++++++++++++++++++++++++ apps/openmw/mwrender/terrainstorage.hpp | 69 +++- components/terrain/chunk.cpp | 6 +- components/terrain/quadtreenode.cpp | 6 +- components/terrain/quadtreenode.hpp | 2 +- components/terrain/storage.cpp | 509 ----------------------- components/terrain/storage.hpp | 37 +- components/terrain/world.cpp | 101 ++--- 8 files changed, 650 insertions(+), 594 deletions(-) diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp index 318627fc7..197572db9 100644 --- a/apps/openmw/mwrender/terrainstorage.cpp +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -1,5 +1,14 @@ #include "terrainstorage.hpp" +#include +#include +#include +#include +#include +#include + +#include + #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/esmstore.hpp" @@ -53,4 +62,509 @@ namespace MWRender return esmStore.get().find(index, plugin); } + bool TerrainStorage::getMinMaxHeights(float size, const Ogre::Vector2 ¢er, float &min, float &max) + { + assert (size <= 1 && "TerrainStorage::getMinMaxHeights, chunk size should be <= 1 cell"); + + /// \todo investigate if min/max heights should be stored at load time in ESM::Land instead + + Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f); + + assert(origin.x == (int) origin.x); + assert(origin.y == (int) origin.y); + + int cellX = origin.x; + int cellY = origin.y; + + const ESM::Land* land = getLand(cellX, cellY); + if (!land) + return false; + + min = std::numeric_limits().max(); + max = -std::numeric_limits().max(); + for (int row=0; rowmLandData->mHeights[col*ESM::Land::LAND_SIZE+row]; + if (h > max) + max = h; + if (h < min) + min = h; + } + } + return true; + } + + void TerrainStorage::fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row) + { + while (col >= ESM::Land::LAND_SIZE-1) + { + ++cellY; + col -= ESM::Land::LAND_SIZE-1; + } + while (row >= ESM::Land::LAND_SIZE-1) + { + ++cellX; + row -= ESM::Land::LAND_SIZE-1; + } + while (col < 0) + { + --cellY; + col += ESM::Land::LAND_SIZE-1; + } + while (row < 0) + { + --cellX; + row += ESM::Land::LAND_SIZE-1; + } + ESM::Land* land = getLand(cellX, cellY); + if (land && land->mHasData) + { + normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; + normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; + normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; + normal.normalise(); + } + else + normal = Ogre::Vector3(0,0,1); + } + + void TerrainStorage::averageNormal(Ogre::Vector3 &normal, int cellX, int cellY, int col, int row) + { + Ogre::Vector3 n1,n2,n3,n4; + fixNormal(n1, cellX, cellY, col+1, row); + fixNormal(n2, cellX, cellY, col-1, row); + fixNormal(n3, cellX, cellY, col, row+1); + fixNormal(n4, cellX, cellY, col, row-1); + normal = (n1+n2+n3+n4); + normal.normalise(); + } + + void TerrainStorage::fixColour (Ogre::ColourValue& color, int cellX, int cellY, int col, int row) + { + if (col == ESM::Land::LAND_SIZE-1) + { + ++cellY; + col = 0; + } + if (row == ESM::Land::LAND_SIZE-1) + { + ++cellX; + row = 0; + } + ESM::Land* land = getLand(cellX, cellY); + if (land && land->mLandData->mUsingColours) + { + color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; + color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; + color.b = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f; + } + else + { + color.r = 1; + color.g = 1; + color.b = 1; + } + + } + + void TerrainStorage::fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, + Ogre::HardwareVertexBufferSharedPtr vertexBuffer, + Ogre::HardwareVertexBufferSharedPtr normalBuffer, + Ogre::HardwareVertexBufferSharedPtr colourBuffer) + { + // LOD level n means every 2^n-th vertex is kept + size_t increment = 1 << lodLevel; + + Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f); + assert(origin.x == (int) origin.x); + assert(origin.y == (int) origin.y); + + int startX = origin.x; + int startY = origin.y; + + size_t numVerts = size*(ESM::Land::LAND_SIZE-1)/increment + 1; + + std::vector colors; + colors.resize(numVerts*numVerts*4); + std::vector positions; + positions.resize(numVerts*numVerts*3); + std::vector normals; + normals.resize(numVerts*numVerts*3); + + Ogre::Vector3 normal; + Ogre::ColourValue color; + + float vertY; + float vertX; + + float vertY_ = 0; // of current cell corner + for (int cellY = startY; cellY < startY + std::ceil(size); ++cellY) + { + float vertX_ = 0; // of current cell corner + for (int cellX = startX; cellX < startX + std::ceil(size); ++cellX) + { + ESM::Land* land = getLand(cellX, cellY); + if (land && !land->mHasData) + land = NULL; + bool hasColors = land && land->mLandData->mUsingColours; + + int rowStart = 0; + int colStart = 0; + // Skip the first row / column unless we're at a chunk edge, + // since this row / column is already contained in a previous cell + if (colStart == 0 && vertY_ != 0) + colStart += increment; + if (rowStart == 0 && vertX_ != 0) + rowStart += increment; + + vertY = vertY_; + for (int col=colStart; colmLandData->mHeights[col*ESM::Land::LAND_SIZE+row]; + else + positions[vertX*numVerts*3 + vertY*3 + 2] = -2048; + + if (land) + { + normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; + normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; + normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; + normal.normalise(); + } + else + normal = Ogre::Vector3(0,0,1); + + // Normals apparently don't connect seamlessly between cells + if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) + fixNormal(normal, cellX, cellY, col, row); + + // some corner normals appear to be complete garbage (z < 0) + if ((row == 0 || row == ESM::Land::LAND_SIZE-1) && (col == 0 || col == ESM::Land::LAND_SIZE-1)) + averageNormal(normal, cellX, cellY, col, row); + + assert(normal.z > 0); + + normals[vertX*numVerts*3 + vertY*3] = normal.x; + normals[vertX*numVerts*3 + vertY*3 + 1] = normal.y; + normals[vertX*numVerts*3 + vertY*3 + 2] = normal.z; + + if (hasColors) + { + color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; + color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; + color.b = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f; + } + else + { + color.r = 1; + color.g = 1; + color.b = 1; + } + + // Unlike normals, colors mostly connect seamlessly between cells, but not always... + if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) + fixColour(color, cellX, cellY, col, row); + + color.a = 1; + Ogre::uint32 rsColor; + Ogre::Root::getSingleton().getRenderSystem()->convertColourValue(color, &rsColor); + memcpy(&colors[vertX*numVerts*4 + vertY*4], &rsColor, sizeof(Ogre::uint32)); + + ++vertX; + } + ++vertY; + } + vertX_ = vertX; + } + vertY_ = vertY; + + assert(vertX_ == numVerts); // Ensure we covered whole area + } + assert(vertY_ == numVerts); // Ensure we covered whole area + + vertexBuffer->writeData(0, vertexBuffer->getSizeInBytes(), &positions[0], true); + normalBuffer->writeData(0, normalBuffer->getSizeInBytes(), &normals[0], true); + colourBuffer->writeData(0, colourBuffer->getSizeInBytes(), &colors[0], true); + } + + TerrainStorage::UniqueTextureId TerrainStorage::getVtexIndexAt(int cellX, int cellY, + int x, int y) + { + // For the first/last row/column, we need to get the texture from the neighbour cell + // to get consistent blending at the borders + --x; + if (x < 0) + { + --cellX; + x += ESM::Land::LAND_TEXTURE_SIZE; + } + if (y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not? + { + ++cellY; + y -= ESM::Land::LAND_TEXTURE_SIZE; + } + + assert(xisDataLoaded(ESM::Land::DATA_VTEX)) + land->loadData(ESM::Land::DATA_VTEX); + + int tex = land->mLandData->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; + if (tex == 0) + return std::make_pair(0,0); // vtex 0 is always the base texture, regardless of plugin + return std::make_pair(tex, land->mPlugin); + } + else + return std::make_pair(0,0); + } + + std::string TerrainStorage::getTextureName(UniqueTextureId id) + { + if (id.first == 0) + return "_land_default.dds"; // Not sure if the default texture floatly is hardcoded? + + // NB: All vtex ids are +1 compared to the ltex ids + const ESM::LandTexture* ltex = getLandTexture(id.first-1, id.second); + + std::string texture = ltex->mTexture; + //TODO this is needed due to MWs messed up texture handling + texture = texture.substr(0, texture.rfind(".")) + ".dds"; + + return texture; + } + + void TerrainStorage::getBlendmaps(float chunkSize, const Ogre::Vector2 &chunkCenter, + bool pack, std::vector &blendmaps, std::vector &layerList) + { + // TODO - blending isn't completely right yet; the blending radius appears to be + // different at a cell transition (2 vertices, not 4), so we may need to create a larger blendmap + // and interpolate the rest of the cell by hand? :/ + + Ogre::Vector2 origin = chunkCenter - Ogre::Vector2(chunkSize/2.f, chunkSize/2.f); + int cellX = origin.x; + int cellY = origin.y; + + // Save the used texture indices so we know the total number of textures + // and number of required blend maps + std::set textureIndices; + // Due to the way the blending works, the base layer will always shine through in between + // blend transitions (eg halfway between two texels, both blend values will be 0.5, so 25% of base layer visible). + // To get a consistent look, we need to make sure to use the same base layer in all cells. + // So we're always adding _land_default.dds as the base layer here, even if it's not referenced in this cell. + textureIndices.insert(std::make_pair(0,0)); + + for (int y=0; y textureIndicesMap; + for (std::set::iterator it = textureIndices.begin(); it != textureIndices.end(); ++it) + { + int size = textureIndicesMap.size(); + textureIndicesMap[*it] = size; + layerList.push_back(getLayerInfo(getTextureName(*it))); + } + + int numTextures = textureIndices.size(); + // numTextures-1 since the base layer doesn't need blending + int numBlendmaps = pack ? std::ceil((numTextures-1) / 4.f) : (numTextures-1); + + int channels = pack ? 4 : 1; + + // Second iteration - create and fill in the blend maps + const int blendmapSize = ESM::Land::LAND_TEXTURE_SIZE+1; + std::vector data; + data.resize(blendmapSize * blendmapSize * channels, 0); + + for (int i=0; isecond; + int blendIndex = (pack ? std::floor((layerIndex-1)/4.f) : layerIndex-1); + int channel = pack ? std::max(0, (layerIndex-1) % 4) : 0; + + if (blendIndex == i) + data[y*blendmapSize*channels + x*channels + channel] = 255; + else + data[y*blendmapSize*channels + x*channels + channel] = 0; + } + } + + // All done, upload to GPU + Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size())); + map->loadRawData(stream, blendmapSize, blendmapSize, format); + blendmaps.push_back(map); + } + } + + float TerrainStorage::getHeightAt(const Ogre::Vector3 &worldPos) + { + int cellX = std::floor(worldPos.x / 8192.f); + int cellY = std::floor(worldPos.y / 8192.f); + + ESM::Land* land = getLand(cellX, cellY); + if (!land) + return -2048; + + // Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition + + // Normalized position in the cell + float nX = (worldPos.x - (cellX * 8192))/8192.f; + float nY = (worldPos.y - (cellY * 8192))/8192.f; + + // get left / bottom points (rounded down) + float factor = ESM::Land::LAND_SIZE - 1.0f; + float invFactor = 1.0f / factor; + + int startX = static_cast(nX * factor); + int startY = static_cast(nY * factor); + int endX = startX + 1; + int endY = startY + 1; + + assert(endX < ESM::Land::LAND_SIZE); + assert(endY < ESM::Land::LAND_SIZE); + + // now get points in terrain space (effectively rounding them to boundaries) + float startXTS = startX * invFactor; + float startYTS = startY * invFactor; + float endXTS = endX * invFactor; + float endYTS = endY * invFactor; + + // get parametric from start coord to next point + float xParam = (nX - startXTS) * factor; + float yParam = (nY - startYTS) * factor; + + /* For even / odd tri strip rows, triangles are this shape: + even odd + 3---2 3---2 + | / | | \ | + 0---1 0---1 + */ + + // Build all 4 positions in normalized cell space, using point-sampled height + Ogre::Vector3 v0 (startXTS, startYTS, getVertexHeight(land, startX, startY) / 8192.f); + Ogre::Vector3 v1 (endXTS, startYTS, getVertexHeight(land, endX, startY) / 8192.f); + Ogre::Vector3 v2 (endXTS, endYTS, getVertexHeight(land, endX, endY) / 8192.f); + Ogre::Vector3 v3 (startXTS, endYTS, getVertexHeight(land, startX, endY) / 8192.f); + // define this plane in terrain space + Ogre::Plane plane; + // (At the moment, all rows have the same triangle alignment) + if (true) + { + // odd row + bool secondTri = ((1.0 - yParam) > xParam); + if (secondTri) + plane.redefine(v0, v1, v3); + else + plane.redefine(v1, v2, v3); + } + else + { + // even row + bool secondTri = (yParam > xParam); + if (secondTri) + plane.redefine(v0, v2, v3); + else + plane.redefine(v0, v1, v2); + } + + // Solve plane equation for z + return (-plane.normal.x * nX + -plane.normal.y * nY + - plane.d) / plane.normal.z * 8192; + + } + + float TerrainStorage::getVertexHeight(const ESM::Land *land, int x, int y) + { + assert(x < ESM::Land::LAND_SIZE); + assert(y < ESM::Land::LAND_SIZE); + return land->mLandData->mHeights[y * ESM::Land::LAND_SIZE + x]; + } + + Terrain::LayerInfo TerrainStorage::getLayerInfo(const std::string& texture) + { + // Already have this cached? + if (mLayerInfoMap.find(texture) != mLayerInfoMap.end()) + return mLayerInfoMap[texture]; + + Terrain::LayerInfo info; + info.mParallax = false; + info.mSpecular = false; + info.mDiffuseMap = "textures\\" + texture; + std::string texture_ = texture; + boost::replace_last(texture_, ".", "_nh."); + if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_)) + { + info.mNormalMap = "textures\\" + texture_; + info.mParallax = true; + } + else + { + texture_ = texture; + boost::replace_last(texture_, ".", "_n."); + if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_)) + info.mNormalMap = "textures\\" + texture_; + } + + texture_ = texture; + boost::replace_last(texture_, ".", "_diffusespec."); + if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_)) + { + info.mDiffuseMap = "textures\\" + texture_; + info.mSpecular = true; + } + + mLayerInfoMap[texture] = info; + + return info; + } + + Terrain::LayerInfo TerrainStorage::getDefaultLayer() + { + Terrain::LayerInfo info; + info.mDiffuseMap = "textures\\_land_default.dds"; + info.mParallax = false; + info.mSpecular = false; + return info; + } + + float TerrainStorage::getCellWorldSize() + { + return ESM::Land::REAL_SIZE; + } + + int TerrainStorage::getCellVertices() + { + return ESM::Land::LAND_SIZE; + } + } diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index ebf5e26ab..5c2035952 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -12,8 +12,75 @@ namespace MWRender virtual ESM::Land* getLand (int cellX, int cellY); virtual const ESM::LandTexture* getLandTexture(int index, short plugin); public: + + /// Get bounds of the whole terrain in cell units virtual Ogre::AxisAlignedBox getBounds(); - ///< Get bounds in cell units + + /// Get the minimum and maximum heights of a terrain chunk. + /// @note Should only be called for chunks <= 1 cell, i.e. leafs of the quad tree. + /// Larger chunks can simply merge AABB of children. + /// @param size size of the chunk in cell units + /// @param center center of the chunk in cell units + /// @param min min height will be stored here + /// @param max max height will be stored here + /// @return true if there was data available for this terrain chunk + virtual bool getMinMaxHeights (float size, const Ogre::Vector2& center, float& min, float& max); + + /// Fill vertex buffers for a terrain chunk. + /// @param lodLevel LOD level, 0 = most detailed + /// @param size size of the terrain chunk in cell units + /// @param center center of the chunk in cell units + /// @param vertexBuffer buffer to write vertices + /// @param normalBuffer buffer to write vertex normals + /// @param colourBuffer buffer to write vertex colours + virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, + Ogre::HardwareVertexBufferSharedPtr vertexBuffer, + Ogre::HardwareVertexBufferSharedPtr normalBuffer, + Ogre::HardwareVertexBufferSharedPtr colourBuffer); + + /// Create textures holding layer blend values for a terrain chunk. + /// @note The terrain chunk shouldn't be larger than one cell since otherwise we might + /// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used. + /// @param chunkSize size of the terrain chunk in cell units + /// @param chunkCenter center of the chunk in cell units + /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) - + /// otherwise, each texture contains blend values for one layer only. Shader-based rendering + /// can utilize packing, FFP can't. + /// @param blendmaps created blendmaps will be written here + /// @param layerList names of the layer textures used will be written here + virtual void getBlendmaps (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack, + std::vector& blendmaps, + std::vector& layerList); + + virtual float getHeightAt (const Ogre::Vector3& worldPos); + + virtual Terrain::LayerInfo getDefaultLayer(); + + /// Get the transformation factor for mapping cell units to world units. + virtual float getCellWorldSize(); + + /// Get the number of vertices on one side for each cell. Should be (power of two)+1 + virtual int getCellVertices(); + + private: + void fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row); + void fixColour (Ogre::ColourValue& colour, int cellX, int cellY, int col, int row); + void averageNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row); + + float getVertexHeight (const ESM::Land* land, int x, int y); + + // Since plugins can define new texture palettes, we need to know the plugin index too + // in order to retrieve the correct texture name. + // pair + typedef std::pair UniqueTextureId; + + UniqueTextureId getVtexIndexAt(int cellX, int cellY, + int x, int y); + std::string getTextureName (UniqueTextureId id); + + std::map mLayerInfoMap; + + Terrain::LayerInfo getLayerInfo(const std::string& texture); }; } diff --git a/components/terrain/chunk.cpp b/components/terrain/chunk.cpp index ce2118cdb..a5c629088 100644 --- a/components/terrain/chunk.cpp +++ b/components/terrain/chunk.cpp @@ -18,11 +18,13 @@ namespace Terrain mVertexData = OGRE_NEW Ogre::VertexData; mVertexData->vertexStart = 0; + unsigned int verts = mNode->getTerrain()->getStorage()->getCellVertices(); + // Set the total number of vertices - size_t numVertsOneSide = mNode->getSize() * (ESM::Land::LAND_SIZE-1); + size_t numVertsOneSide = mNode->getSize() * (verts-1); numVertsOneSide /= 1 << lodLevel; numVertsOneSide += 1; - assert((int)numVertsOneSide == ESM::Land::LAND_SIZE); + assert(numVertsOneSide == verts); mVertexData->vertexCount = numVertsOneSide * numVertsOneSide; // Set up the vertex declaration, which specifies the info for each vertex (normals, colors, UVs, etc) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 7fc452fbf..a4fdd5e13 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -430,11 +430,7 @@ void QuadTreeNode::prepareForCompositeMap(Ogre::TRect area) // TODO - store this default material somewhere instead of creating one for each empty cell MaterialGenerator matGen(mTerrain->getShadersEnabled()); std::vector layer; - LayerInfo info; - info.mDiffuseMap = "textures\\_land_default.dds"; - info.mParallax = false; - info.mSpecular = false; - layer.push_back(info); + layer.push_back(mTerrain->getStorage()->getDefaultLayer()); matGen.setLayerList(layer); makeQuad(sceneMgr, area.left, area.top, area.right, area.bottom, matGen.generateForCompositeMapRTT(Ogre::MaterialPtr())); return; diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 9f6fb5d24..ea299c096 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -108,7 +108,7 @@ namespace Terrain void destroyChunks(bool children); /// Get the effective LOD level if this node was rendered in one chunk - /// with ESM::Land::LAND_SIZE^2 vertices + /// with Storage::getCellVertices^2 vertices size_t getNativeLodLevel() { return mLodLevel; } /// Get the effective current LOD level used by the chunk rendering this node diff --git a/components/terrain/storage.cpp b/components/terrain/storage.cpp index 398ebac01..e69de29bb 100644 --- a/components/terrain/storage.cpp +++ b/components/terrain/storage.cpp @@ -1,509 +0,0 @@ -#include "storage.hpp" - -#include -#include -#include -#include -#include -#include - -#include - -namespace Terrain -{ - - struct VertexElement - { - Ogre::Vector3 pos; - Ogre::Vector3 normal; - Ogre::ColourValue colour; - }; - - bool Storage::getMinMaxHeights(float size, const Ogre::Vector2 ¢er, float &min, float &max) - { - assert (size <= 1 && "Storage::getMinMaxHeights, chunk size should be <= 1 cell"); - - /// \todo investigate if min/max heights should be stored at load time in ESM::Land instead - - Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f); - - assert(origin.x == (int) origin.x); - assert(origin.y == (int) origin.y); - - int cellX = origin.x; - int cellY = origin.y; - - const ESM::Land* land = getLand(cellX, cellY); - if (!land) - return false; - - min = std::numeric_limits().max(); - max = -std::numeric_limits().max(); - for (int row=0; rowmLandData->mHeights[col*ESM::Land::LAND_SIZE+row]; - if (h > max) - max = h; - if (h < min) - min = h; - } - } - return true; - } - - void Storage::fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row) - { - while (col >= ESM::Land::LAND_SIZE-1) - { - ++cellY; - col -= ESM::Land::LAND_SIZE-1; - } - while (row >= ESM::Land::LAND_SIZE-1) - { - ++cellX; - row -= ESM::Land::LAND_SIZE-1; - } - while (col < 0) - { - --cellY; - col += ESM::Land::LAND_SIZE-1; - } - while (row < 0) - { - --cellX; - row += ESM::Land::LAND_SIZE-1; - } - ESM::Land* land = getLand(cellX, cellY); - if (land && land->mHasData) - { - normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; - normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; - normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; - normal.normalise(); - } - else - normal = Ogre::Vector3(0,0,1); - } - - void Storage::averageNormal(Ogre::Vector3 &normal, int cellX, int cellY, int col, int row) - { - Ogre::Vector3 n1,n2,n3,n4; - fixNormal(n1, cellX, cellY, col+1, row); - fixNormal(n2, cellX, cellY, col-1, row); - fixNormal(n3, cellX, cellY, col, row+1); - fixNormal(n4, cellX, cellY, col, row-1); - normal = (n1+n2+n3+n4); - normal.normalise(); - } - - void Storage::fixColour (Ogre::ColourValue& color, int cellX, int cellY, int col, int row) - { - if (col == ESM::Land::LAND_SIZE-1) - { - ++cellY; - col = 0; - } - if (row == ESM::Land::LAND_SIZE-1) - { - ++cellX; - row = 0; - } - ESM::Land* land = getLand(cellX, cellY); - if (land && land->mLandData->mUsingColours) - { - color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; - color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; - color.b = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f; - } - else - { - color.r = 1; - color.g = 1; - color.b = 1; - } - - } - - void Storage::fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, - Ogre::HardwareVertexBufferSharedPtr vertexBuffer, - Ogre::HardwareVertexBufferSharedPtr normalBuffer, - Ogre::HardwareVertexBufferSharedPtr colourBuffer) - { - // LOD level n means every 2^n-th vertex is kept - size_t increment = 1 << lodLevel; - - Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f); - assert(origin.x == (int) origin.x); - assert(origin.y == (int) origin.y); - - int startX = origin.x; - int startY = origin.y; - - size_t numVerts = size*(ESM::Land::LAND_SIZE-1)/increment + 1; - - std::vector colors; - colors.resize(numVerts*numVerts*4); - std::vector positions; - positions.resize(numVerts*numVerts*3); - std::vector normals; - normals.resize(numVerts*numVerts*3); - - Ogre::Vector3 normal; - Ogre::ColourValue color; - - float vertY; - float vertX; - - float vertY_ = 0; // of current cell corner - for (int cellY = startY; cellY < startY + std::ceil(size); ++cellY) - { - float vertX_ = 0; // of current cell corner - for (int cellX = startX; cellX < startX + std::ceil(size); ++cellX) - { - ESM::Land* land = getLand(cellX, cellY); - if (land && !land->mHasData) - land = NULL; - bool hasColors = land && land->mLandData->mUsingColours; - - int rowStart = 0; - int colStart = 0; - // Skip the first row / column unless we're at a chunk edge, - // since this row / column is already contained in a previous cell - if (colStart == 0 && vertY_ != 0) - colStart += increment; - if (rowStart == 0 && vertX_ != 0) - rowStart += increment; - - vertY = vertY_; - for (int col=colStart; colmLandData->mHeights[col*ESM::Land::LAND_SIZE+row]; - else - positions[vertX*numVerts*3 + vertY*3 + 2] = -2048; - - if (land) - { - normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; - normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; - normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; - normal.normalise(); - } - else - normal = Ogre::Vector3(0,0,1); - - // Normals apparently don't connect seamlessly between cells - if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) - fixNormal(normal, cellX, cellY, col, row); - - // some corner normals appear to be complete garbage (z < 0) - if ((row == 0 || row == ESM::Land::LAND_SIZE-1) && (col == 0 || col == ESM::Land::LAND_SIZE-1)) - averageNormal(normal, cellX, cellY, col, row); - - assert(normal.z > 0); - - normals[vertX*numVerts*3 + vertY*3] = normal.x; - normals[vertX*numVerts*3 + vertY*3 + 1] = normal.y; - normals[vertX*numVerts*3 + vertY*3 + 2] = normal.z; - - if (hasColors) - { - color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; - color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; - color.b = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f; - } - else - { - color.r = 1; - color.g = 1; - color.b = 1; - } - - // Unlike normals, colors mostly connect seamlessly between cells, but not always... - if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) - fixColour(color, cellX, cellY, col, row); - - color.a = 1; - Ogre::uint32 rsColor; - Ogre::Root::getSingleton().getRenderSystem()->convertColourValue(color, &rsColor); - memcpy(&colors[vertX*numVerts*4 + vertY*4], &rsColor, sizeof(Ogre::uint32)); - - ++vertX; - } - ++vertY; - } - vertX_ = vertX; - } - vertY_ = vertY; - - assert(vertX_ == numVerts); // Ensure we covered whole area - } - assert(vertY_ == numVerts); // Ensure we covered whole area - - vertexBuffer->writeData(0, vertexBuffer->getSizeInBytes(), &positions[0], true); - normalBuffer->writeData(0, normalBuffer->getSizeInBytes(), &normals[0], true); - colourBuffer->writeData(0, colourBuffer->getSizeInBytes(), &colors[0], true); - } - - Storage::UniqueTextureId Storage::getVtexIndexAt(int cellX, int cellY, - int x, int y) - { - // For the first/last row/column, we need to get the texture from the neighbour cell - // to get consistent blending at the borders - --x; - if (x < 0) - { - --cellX; - x += ESM::Land::LAND_TEXTURE_SIZE; - } - if (y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not? - { - ++cellY; - y -= ESM::Land::LAND_TEXTURE_SIZE; - } - - assert(xisDataLoaded(ESM::Land::DATA_VTEX)) - land->loadData(ESM::Land::DATA_VTEX); - - int tex = land->mLandData->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; - if (tex == 0) - return std::make_pair(0,0); // vtex 0 is always the base texture, regardless of plugin - return std::make_pair(tex, land->mPlugin); - } - else - return std::make_pair(0,0); - } - - std::string Storage::getTextureName(UniqueTextureId id) - { - if (id.first == 0) - return "_land_default.dds"; // Not sure if the default texture floatly is hardcoded? - - // NB: All vtex ids are +1 compared to the ltex ids - const ESM::LandTexture* ltex = getLandTexture(id.first-1, id.second); - - std::string texture = ltex->mTexture; - //TODO this is needed due to MWs messed up texture handling - texture = texture.substr(0, texture.rfind(".")) + ".dds"; - - return texture; - } - - void Storage::getBlendmaps(float chunkSize, const Ogre::Vector2 &chunkCenter, - bool pack, std::vector &blendmaps, std::vector &layerList) - { - // TODO - blending isn't completely right yet; the blending radius appears to be - // different at a cell transition (2 vertices, not 4), so we may need to create a larger blendmap - // and interpolate the rest of the cell by hand? :/ - - Ogre::Vector2 origin = chunkCenter - Ogre::Vector2(chunkSize/2.f, chunkSize/2.f); - int cellX = origin.x; - int cellY = origin.y; - - // Save the used texture indices so we know the total number of textures - // and number of required blend maps - std::set textureIndices; - // Due to the way the blending works, the base layer will always shine through in between - // blend transitions (eg halfway between two texels, both blend values will be 0.5, so 25% of base layer visible). - // To get a consistent look, we need to make sure to use the same base layer in all cells. - // So we're always adding _land_default.dds as the base layer here, even if it's not referenced in this cell. - textureIndices.insert(std::make_pair(0,0)); - - for (int y=0; y textureIndicesMap; - for (std::set::iterator it = textureIndices.begin(); it != textureIndices.end(); ++it) - { - int size = textureIndicesMap.size(); - textureIndicesMap[*it] = size; - layerList.push_back(getLayerInfo(getTextureName(*it))); - } - - int numTextures = textureIndices.size(); - // numTextures-1 since the base layer doesn't need blending - int numBlendmaps = pack ? std::ceil((numTextures-1) / 4.f) : (numTextures-1); - - int channels = pack ? 4 : 1; - - // Second iteration - create and fill in the blend maps - const int blendmapSize = ESM::Land::LAND_TEXTURE_SIZE+1; - std::vector data; - data.resize(blendmapSize * blendmapSize * channels, 0); - - for (int i=0; isecond; - int blendIndex = (pack ? std::floor((layerIndex-1)/4.f) : layerIndex-1); - int channel = pack ? std::max(0, (layerIndex-1) % 4) : 0; - - if (blendIndex == i) - data[y*blendmapSize*channels + x*channels + channel] = 255; - else - data[y*blendmapSize*channels + x*channels + channel] = 0; - } - } - - // All done, upload to GPU - Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size())); - map->loadRawData(stream, blendmapSize, blendmapSize, format); - blendmaps.push_back(map); - } - } - - float Storage::getHeightAt(const Ogre::Vector3 &worldPos) - { - int cellX = std::floor(worldPos.x / 8192.f); - int cellY = std::floor(worldPos.y / 8192.f); - - ESM::Land* land = getLand(cellX, cellY); - if (!land) - return -2048; - - // Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition - - // Normalized position in the cell - float nX = (worldPos.x - (cellX * 8192))/8192.f; - float nY = (worldPos.y - (cellY * 8192))/8192.f; - - // get left / bottom points (rounded down) - float factor = ESM::Land::LAND_SIZE - 1.0f; - float invFactor = 1.0f / factor; - - int startX = static_cast(nX * factor); - int startY = static_cast(nY * factor); - int endX = startX + 1; - int endY = startY + 1; - - assert(endX < ESM::Land::LAND_SIZE); - assert(endY < ESM::Land::LAND_SIZE); - - // now get points in terrain space (effectively rounding them to boundaries) - float startXTS = startX * invFactor; - float startYTS = startY * invFactor; - float endXTS = endX * invFactor; - float endYTS = endY * invFactor; - - // get parametric from start coord to next point - float xParam = (nX - startXTS) * factor; - float yParam = (nY - startYTS) * factor; - - /* For even / odd tri strip rows, triangles are this shape: - even odd - 3---2 3---2 - | / | | \ | - 0---1 0---1 - */ - - // Build all 4 positions in normalized cell space, using point-sampled height - Ogre::Vector3 v0 (startXTS, startYTS, getVertexHeight(land, startX, startY) / 8192.f); - Ogre::Vector3 v1 (endXTS, startYTS, getVertexHeight(land, endX, startY) / 8192.f); - Ogre::Vector3 v2 (endXTS, endYTS, getVertexHeight(land, endX, endY) / 8192.f); - Ogre::Vector3 v3 (startXTS, endYTS, getVertexHeight(land, startX, endY) / 8192.f); - // define this plane in terrain space - Ogre::Plane plane; - // (At the moment, all rows have the same triangle alignment) - if (true) - { - // odd row - bool secondTri = ((1.0 - yParam) > xParam); - if (secondTri) - plane.redefine(v0, v1, v3); - else - plane.redefine(v1, v2, v3); - } - else - { - // even row - bool secondTri = (yParam > xParam); - if (secondTri) - plane.redefine(v0, v2, v3); - else - plane.redefine(v0, v1, v2); - } - - // Solve plane equation for z - return (-plane.normal.x * nX - -plane.normal.y * nY - - plane.d) / plane.normal.z * 8192; - - } - - float Storage::getVertexHeight(const ESM::Land *land, int x, int y) - { - assert(x < ESM::Land::LAND_SIZE); - assert(y < ESM::Land::LAND_SIZE); - return land->mLandData->mHeights[y * ESM::Land::LAND_SIZE + x]; - } - - LayerInfo Storage::getLayerInfo(const std::string& texture) - { - // Already have this cached? - if (mLayerInfoMap.find(texture) != mLayerInfoMap.end()) - return mLayerInfoMap[texture]; - - LayerInfo info; - info.mParallax = false; - info.mSpecular = false; - info.mDiffuseMap = "textures\\" + texture; - std::string texture_ = texture; - boost::replace_last(texture_, ".", "_nh."); - if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_)) - { - info.mNormalMap = "textures\\" + texture_; - info.mParallax = true; - } - else - { - texture_ = texture; - boost::replace_last(texture_, ".", "_n."); - if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_)) - info.mNormalMap = "textures\\" + texture_; - } - - texture_ = texture; - boost::replace_last(texture_, ".", "_diffusespec."); - if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_)) - { - info.mDiffuseMap = "textures\\" + texture_; - info.mSpecular = true; - } - - mLayerInfoMap[texture] = info; - - return info; - } - - -} diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index 18d05b100..021e01c7e 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -24,9 +24,6 @@ namespace Terrain { public: virtual ~Storage() {} - private: - virtual ESM::Land* getLand (int cellX, int cellY) = 0; - virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0; public: /// Get bounds of the whole terrain in cell units @@ -40,7 +37,7 @@ namespace Terrain /// @param min min height will be stored here /// @param max max height will be stored here /// @return true if there was data available for this terrain chunk - bool getMinMaxHeights (float size, const Ogre::Vector2& center, float& min, float& max); + virtual bool getMinMaxHeights (float size, const Ogre::Vector2& center, float& min, float& max) = 0; /// Fill vertex buffers for a terrain chunk. /// @param lodLevel LOD level, 0 = most detailed @@ -49,10 +46,10 @@ namespace Terrain /// @param vertexBuffer buffer to write vertices /// @param normalBuffer buffer to write vertex normals /// @param colourBuffer buffer to write vertex colours - void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, + virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Ogre::HardwareVertexBufferSharedPtr vertexBuffer, Ogre::HardwareVertexBufferSharedPtr normalBuffer, - Ogre::HardwareVertexBufferSharedPtr colourBuffer); + Ogre::HardwareVertexBufferSharedPtr colourBuffer) = 0; /// Create textures holding layer blend values for a terrain chunk. /// @note The terrain chunk shouldn't be larger than one cell since otherwise we might @@ -64,31 +61,19 @@ namespace Terrain /// can utilize packing, FFP can't. /// @param blendmaps created blendmaps will be written here /// @param layerList names of the layer textures used will be written here - void getBlendmaps (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack, + virtual void getBlendmaps (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack, std::vector& blendmaps, - std::vector& layerList); + std::vector& layerList) = 0; - float getHeightAt (const Ogre::Vector3& worldPos); + virtual float getHeightAt (const Ogre::Vector3& worldPos) = 0; - private: - void fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row); - void fixColour (Ogre::ColourValue& colour, int cellX, int cellY, int col, int row); - void averageNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row); + virtual LayerInfo getDefaultLayer() = 0; - float getVertexHeight (const ESM::Land* land, int x, int y); + /// Get the transformation factor for mapping cell units to world units. + virtual float getCellWorldSize() = 0; - // Since plugins can define new texture palettes, we need to know the plugin index too - // in order to retrieve the correct texture name. - // pair - typedef std::pair UniqueTextureId; - - UniqueTextureId getVtexIndexAt(int cellX, int cellY, - int x, int y); - std::string getTextureName (UniqueTextureId id); - - std::map mLayerInfoMap; - - LayerInfo getLayerInfo(const std::string& texture); + /// Get the number of vertices on one side for each cell. Should be (power of two)+1 + virtual int getCellVertices() = 0; }; } diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 711ebbc8f..dac960fbb 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include "storage.hpp" @@ -208,6 +207,8 @@ namespace Terrain Ogre::HardwareIndexBufferSharedPtr World::getIndexBuffer(int flags, size_t& numIndices) { + unsigned int verts = mStorage->getCellVertices(); + if (mIndexBufferMap.find(flags) != mIndexBufferMap.end()) { numIndices = mIndexBufferMap[flags]->getNumIndexes(); @@ -224,11 +225,11 @@ namespace Terrain bool anyDeltas = (lodDeltas[North] || lodDeltas[South] || lodDeltas[West] || lodDeltas[East]); size_t increment = 1 << lodLevel; - assert((int)increment < ESM::Land::LAND_SIZE); + assert(increment < verts); std::vector indices; - indices.reserve((ESM::Land::LAND_SIZE-1)*(ESM::Land::LAND_SIZE-1)*2*3 / increment); + indices.reserve((verts-1)*(verts-1)*2*3 / increment); - size_t rowStart = 0, colStart = 0, rowEnd = ESM::Land::LAND_SIZE-1, colEnd = ESM::Land::LAND_SIZE-1; + size_t rowStart = 0, colStart = 0, rowEnd = verts-1, colEnd = verts-1; // If any edge needs stitching we'll skip all edges at this point, // mainly because stitching one edge would have an effect on corners and on the adjacent edges if (anyDeltas) @@ -242,13 +243,13 @@ namespace Terrain { for (size_t col = colStart; col < colEnd; col += increment) { - indices.push_back(ESM::Land::LAND_SIZE*col+row); - indices.push_back(ESM::Land::LAND_SIZE*(col+increment)+row+increment); - indices.push_back(ESM::Land::LAND_SIZE*col+row+increment); + indices.push_back(verts*col+row); + indices.push_back(verts*(col+increment)+row+increment); + indices.push_back(verts*col+row+increment); - indices.push_back(ESM::Land::LAND_SIZE*col+row); - indices.push_back(ESM::Land::LAND_SIZE*(col+increment)+row); - indices.push_back(ESM::Land::LAND_SIZE*(col+increment)+row+increment); + indices.push_back(verts*col+row); + indices.push_back(verts*(col+increment)+row); + indices.push_back(verts*(col+increment)+row+increment); } } @@ -261,96 +262,96 @@ namespace Terrain // South size_t row = 0; size_t outerStep = 1 << (lodDeltas[South] + lodLevel); - for (size_t col = 0; col < ESM::Land::LAND_SIZE-1; col += outerStep) + for (size_t col = 0; col < verts-1; col += outerStep) { - indices.push_back(ESM::Land::LAND_SIZE*col+row); - indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep)+row); + indices.push_back(verts*col+row); + indices.push_back(verts*(col+outerStep)+row); // Make sure not to touch the right edge - if (col+outerStep == ESM::Land::LAND_SIZE-1) - indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep-innerStep)+row+innerStep); + if (col+outerStep == verts-1) + indices.push_back(verts*(col+outerStep-innerStep)+row+innerStep); else - indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep)+row+innerStep); + indices.push_back(verts*(col+outerStep)+row+innerStep); for (size_t i = 0; i < outerStep; i += innerStep) { // Make sure not to touch the left or right edges - if (col+i == 0 || col+i == ESM::Land::LAND_SIZE-1-innerStep) + if (col+i == 0 || col+i == verts-1-innerStep) continue; - indices.push_back(ESM::Land::LAND_SIZE*(col)+row); - indices.push_back(ESM::Land::LAND_SIZE*(col+i+innerStep)+row+innerStep); - indices.push_back(ESM::Land::LAND_SIZE*(col+i)+row+innerStep); + indices.push_back(verts*(col)+row); + indices.push_back(verts*(col+i+innerStep)+row+innerStep); + indices.push_back(verts*(col+i)+row+innerStep); } } // North - row = ESM::Land::LAND_SIZE-1; + row = verts-1; outerStep = 1 << (lodDeltas[North] + lodLevel); - for (size_t col = 0; col < ESM::Land::LAND_SIZE-1; col += outerStep) + for (size_t col = 0; col < verts-1; col += outerStep) { - indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep)+row); - indices.push_back(ESM::Land::LAND_SIZE*col+row); + indices.push_back(verts*(col+outerStep)+row); + indices.push_back(verts*col+row); // Make sure not to touch the left edge if (col == 0) - indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row-innerStep); + indices.push_back(verts*(col+innerStep)+row-innerStep); else - indices.push_back(ESM::Land::LAND_SIZE*col+row-innerStep); + indices.push_back(verts*col+row-innerStep); for (size_t i = 0; i < outerStep; i += innerStep) { // Make sure not to touch the left or right edges - if (col+i == 0 || col+i == ESM::Land::LAND_SIZE-1-innerStep) + if (col+i == 0 || col+i == verts-1-innerStep) continue; - indices.push_back(ESM::Land::LAND_SIZE*(col+i)+row-innerStep); - indices.push_back(ESM::Land::LAND_SIZE*(col+i+innerStep)+row-innerStep); - indices.push_back(ESM::Land::LAND_SIZE*(col+outerStep)+row); + indices.push_back(verts*(col+i)+row-innerStep); + indices.push_back(verts*(col+i+innerStep)+row-innerStep); + indices.push_back(verts*(col+outerStep)+row); } } // West size_t col = 0; outerStep = 1 << (lodDeltas[West] + lodLevel); - for (size_t row = 0; row < ESM::Land::LAND_SIZE-1; row += outerStep) + for (size_t row = 0; row < verts-1; row += outerStep) { - indices.push_back(ESM::Land::LAND_SIZE*col+row+outerStep); - indices.push_back(ESM::Land::LAND_SIZE*col+row); + indices.push_back(verts*col+row+outerStep); + indices.push_back(verts*col+row); // Make sure not to touch the top edge - if (row+outerStep == ESM::Land::LAND_SIZE-1) - indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+outerStep-innerStep); + if (row+outerStep == verts-1) + indices.push_back(verts*(col+innerStep)+row+outerStep-innerStep); else - indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+outerStep); + indices.push_back(verts*(col+innerStep)+row+outerStep); for (size_t i = 0; i < outerStep; i += innerStep) { // Make sure not to touch the top or bottom edges - if (row+i == 0 || row+i == ESM::Land::LAND_SIZE-1-innerStep) + if (row+i == 0 || row+i == verts-1-innerStep) continue; - indices.push_back(ESM::Land::LAND_SIZE*col+row); - indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+i); - indices.push_back(ESM::Land::LAND_SIZE*(col+innerStep)+row+i+innerStep); + indices.push_back(verts*col+row); + indices.push_back(verts*(col+innerStep)+row+i); + indices.push_back(verts*(col+innerStep)+row+i+innerStep); } } // East - col = ESM::Land::LAND_SIZE-1; + col = verts-1; outerStep = 1 << (lodDeltas[East] + lodLevel); - for (size_t row = 0; row < ESM::Land::LAND_SIZE-1; row += outerStep) + for (size_t row = 0; row < verts-1; row += outerStep) { - indices.push_back(ESM::Land::LAND_SIZE*col+row); - indices.push_back(ESM::Land::LAND_SIZE*col+row+outerStep); + indices.push_back(verts*col+row); + indices.push_back(verts*col+row+outerStep); // Make sure not to touch the bottom edge if (row == 0) - indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row+innerStep); + indices.push_back(verts*(col-innerStep)+row+innerStep); else - indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row); + indices.push_back(verts*(col-innerStep)+row); for (size_t i = 0; i < outerStep; i += innerStep) { // Make sure not to touch the top or bottom edges - if (row+i == 0 || row+i == ESM::Land::LAND_SIZE-1-innerStep) + if (row+i == 0 || row+i == verts-1-innerStep) continue; - indices.push_back(ESM::Land::LAND_SIZE*col+row+outerStep); - indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row+i+innerStep); - indices.push_back(ESM::Land::LAND_SIZE*(col-innerStep)+row+i); + indices.push_back(verts*col+row+outerStep); + indices.push_back(verts*(col-innerStep)+row+i+innerStep); + indices.push_back(verts*(col-innerStep)+row+i); } } } From 5f3f867a10cde649afd4d749680e12aba180da83 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 13 Feb 2014 10:21:49 +0100 Subject: [PATCH 822/889] Implemented convertEnums function --- apps/opencs/model/world/columnbase.hpp | 22 -- apps/opencs/model/world/columnimp.hpp | 2 +- apps/opencs/model/world/tablemimedata.cpp | 234 +++++++++++++++++++++- apps/opencs/model/world/tablemimedata.hpp | 9 +- apps/opencs/view/world/table.cpp | 6 - 5 files changed, 234 insertions(+), 39 deletions(-) diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index d0419289f..e689c6a3f 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -29,30 +29,15 @@ namespace CSMWorld Display_String, //CONCRETE TYPES STARTS HERE - Display_Globals, - Display_Global, - Display_VerificationResults, - Display_Gmsts, - Display_Gmst, - Display_Skills, Display_Skill, - Display_Classes, Display_Class, - Display_Factions, Display_Faction, - Display_Races, Display_Race, - Display_Sounds, Display_Sound, - Display_Regions, Display_Region, - Display_Birthsigns, Display_Birthsign, - Display_Spells, Display_Spell, - Display_Cells, Display_Cell, - Display_Referenceables, Display_Referenceable, Display_Activator, Display_Potion, @@ -74,18 +59,11 @@ namespace CSMWorld Display_Repair, Display_Static, Display_Weapon, - Display_References, Display_Reference, - Display_RegionMap, Display_Filter, - Display_Filters, - Display_Topics, Display_Topic, - Display_Journals, Display_Journal, - Display_TopicInfos, Display_TopicInfo, - Display_JournalInfos, Display_JournalInfo, Display_Scene, //CONCRETE TYPES ENDS HERE diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 049042109..9e9bedcf8 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -373,7 +373,7 @@ namespace CSMWorld SkillsColumn (int index, bool typePrefix = false, bool major = false) : Column ((typePrefix ? ( major ? Columns::ColumnId_MajorSkill1 : Columns::ColumnId_MinorSkill1) : - Columns::ColumnId_Skill1) + index, ColumnBase::Display_Skills), + Columns::ColumnId_Skill1) + index, ColumnBase::Display_Skill), mIndex (index), mMajor (major) {} diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp index 0cdcdba3a..45305431e 100644 --- a/apps/opencs/model/world/tablemimedata.cpp +++ b/apps/opencs/model/world/tablemimedata.cpp @@ -1,19 +1,21 @@ #include "tablemimedata.hpp" -#include "universalid.hpp" #include +#include "universalid.hpp" +#include "columnbase.hpp" + CSMWorld::TableMimeData::TableMimeData (UniversalId id) { - mUniversalId.push_back(id); - mObjectsFormats << QString::fromStdString("tabledata/" + id.getTypeName()); + mUniversalId.push_back (id); + mObjectsFormats << QString::fromStdString ("tabledata/" + id.getTypeName()); } -CSMWorld::TableMimeData::TableMimeData (std::vector< CSMWorld::UniversalId >& id) +CSMWorld::TableMimeData::TableMimeData (std::vector< CSMWorld::UniversalId >& id) : + mUniversalId (id) { - mUniversalId = id; - for (std::vector::iterator it(mUniversalId.begin()); it != mUniversalId.end(); ++it) + for (std::vector::iterator it (mUniversalId.begin()); it != mUniversalId.end(); ++it) { - mObjectsFormats << QString::fromStdString("tabledata/" + it->getTypeName()); + mObjectsFormats << QString::fromStdString ("tabledata/" + it->getTypeName()); } } @@ -30,11 +32,12 @@ std::string CSMWorld::TableMimeData::getIcon() const { if (mUniversalId.empty()) { - throw("TableMimeData holds no UniversalId"); + throw ("TableMimeData holds no UniversalId"); } std::string tmpIcon; bool firstIteration = true; + for (unsigned i = 0; i < mUniversalId.size(); ++i) { if (firstIteration) @@ -51,10 +54,223 @@ std::string CSMWorld::TableMimeData::getIcon() const tmpIcon = mUniversalId[i].getIcon(); } + return mUniversalId.begin()->getIcon(); //All objects are of the same type; } std::vector< CSMWorld::UniversalId > CSMWorld::TableMimeData::getData() const { return mUniversalId; -} \ No newline at end of file +} + +bool CSMWorld::TableMimeData::holdsType (CSMWorld::UniversalId::Type type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == type) + { + return true; + } + } + + return false; +} + +bool CSMWorld::TableMimeData::holdsType (CSMWorld::ColumnBase::Display type) +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == convertEnums (type)) + { + return true; + } + } + + return false; +} + +CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::UniversalId::Type type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == type) + { + return *it; + } + } + + throw ("TableMimeData object does not hold object of the seeked type"); +} + +CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnBase::Display type) +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == convertEnums (type)) + { + return *it; + } + } + + throw ("TableMimeData object does not hold object of the seeked type"); +} + +CSMWorld::UniversalId::Type CSMWorld::TableMimeData::convertEnums (CSMWorld::ColumnBase::Display type) +{ + switch (type) + { + default: + throw ("This type is not handled"); + return CSMWorld::UniversalId::Type_None; + break; + + case CSMWorld::ColumnBase::Display_Race: + return CSMWorld::UniversalId::Type_Race; + break; + + case CSMWorld::ColumnBase::Display_Skill: + return CSMWorld::UniversalId::Type_Skill; + break; + + case CSMWorld::ColumnBase::Display_Class: + return CSMWorld::UniversalId::Type_Class; + break; + + case CSMWorld::ColumnBase::Display_Faction: + return CSMWorld::UniversalId::Type_Faction; + break; + + case CSMWorld::ColumnBase::Display_Sound: + return CSMWorld::UniversalId::Type_Sound; + break; + + case CSMWorld::ColumnBase::Display_Region: + return CSMWorld::UniversalId::Type_Region; + break; + + case CSMWorld::ColumnBase::Display_Birthsign: + return CSMWorld::UniversalId::Type_Birthsign; + break; + + case CSMWorld::ColumnBase::Display_Spell: + return CSMWorld::UniversalId::Type_Spell; + break; + + case CSMWorld::ColumnBase::Display_Cell: + return CSMWorld::UniversalId::Type_Cell; + break; + + case CSMWorld::ColumnBase::Display_Referenceable: + return CSMWorld::UniversalId::Type_Referenceable; + break; + + case CSMWorld::ColumnBase::Display_Activator: + return CSMWorld::UniversalId::Type_Activator; + break; + + case CSMWorld::ColumnBase::Display_Potion: + return CSMWorld::UniversalId::Type_Potion; + break; + + case CSMWorld::ColumnBase::Display_Apparatus: + return CSMWorld::UniversalId::Type_Apparatus; + break; + + case CSMWorld::ColumnBase::Display_Armor: + return CSMWorld::UniversalId::Type_Armor; + break; + + case CSMWorld::ColumnBase::Display_Book: + return CSMWorld::UniversalId::Type_Book; + break; + + case CSMWorld::ColumnBase::Display_Clothing: + return CSMWorld::UniversalId::Type_Clothing; + break; + + case CSMWorld::ColumnBase::Display_Container: + return CSMWorld::UniversalId::Type_Container; + break; + + case CSMWorld::ColumnBase::Display_Creature: + return CSMWorld::UniversalId::Type_Creature; + break; + + case CSMWorld::ColumnBase::Display_Door: + return CSMWorld::UniversalId::Type_Door; + break; + + case CSMWorld::ColumnBase::Display_Ingredient: + return CSMWorld::UniversalId::Type_Ingredient; + break; + + case CSMWorld::ColumnBase::Display_CreatureLevelledList: + return CSMWorld::UniversalId::Type_CreatureLevelledList; + break; + + case CSMWorld::ColumnBase::Display_ItemLevelledList: + return CSMWorld::UniversalId::Type_ItemLevelledList; + break; + + case CSMWorld::ColumnBase::Display_Light: + return CSMWorld::UniversalId::Type_Light; + break; + + case CSMWorld::ColumnBase::Display_Lockpick: + return CSMWorld::UniversalId::Type_Lockpick; + break; + + case CSMWorld::ColumnBase::Display_Miscellaneous: + return CSMWorld::UniversalId::Type_Miscellaneous; + break; + + case CSMWorld::ColumnBase::Display_Npc: + return CSMWorld::UniversalId::Type_Npc; + break; + + case CSMWorld::ColumnBase::Display_Probe: + return CSMWorld::UniversalId::Type_Probe; + break; + + case CSMWorld::ColumnBase::Display_Repair: + return CSMWorld::UniversalId::Type_Repair; + break; + + case CSMWorld::ColumnBase::Display_Static: + return CSMWorld::UniversalId::Type_Static; + break; + + case CSMWorld::ColumnBase::Display_Weapon: + return CSMWorld::UniversalId::Type_Weapon; + break; + + case CSMWorld::ColumnBase::Display_Reference: + return CSMWorld::UniversalId::Type_Reference; + break; + + case CSMWorld::ColumnBase::Display_Filter: + return CSMWorld::UniversalId::Type_Filter; + break; + + case CSMWorld::ColumnBase::Display_Topic: + return CSMWorld::UniversalId::Type_Topic; + break; + + case CSMWorld::ColumnBase::Display_Journal: + return CSMWorld::UniversalId::Type_Journal; + break; + + case CSMWorld::ColumnBase::Display_TopicInfo: + return CSMWorld::UniversalId::Type_TopicInfo; + break; + + case CSMWorld::ColumnBase::Display_JournalInfo: + return CSMWorld::UniversalId::Type_JournalInfo; + break; + + case CSMWorld::ColumnBase::Display_Scene: + return CSMWorld::UniversalId::Type_Scene; + break; + } +} +// kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/apps/opencs/model/world/tablemimedata.hpp b/apps/opencs/model/world/tablemimedata.hpp index 7509bd905..d2e5d3ecb 100644 --- a/apps/opencs/model/world/tablemimedata.hpp +++ b/apps/opencs/model/world/tablemimedata.hpp @@ -10,6 +10,7 @@ #include #include "universalid.hpp" +#include "columnbase.hpp" namespace CSMWorld @@ -23,10 +24,16 @@ namespace CSMWorld virtual QStringList formats() const; std::string getIcon() const; std::vector getData() const; + bool holdsType(UniversalId::Type type) const; + bool holdsType(CSMWorld::ColumnBase::Display type); + UniversalId returnMatching(UniversalId::Type type) const; + UniversalId returnMatching(CSMWorld::ColumnBase::Display type); private: std::vector mUniversalId; QStringList mObjectsFormats; + + CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type); }; } -#endif // TABLEMIMEDATA_H \ No newline at end of file +#endif // TABLEMIMEDATA_H diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 7ac5946c8..c39ed3469 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -469,25 +469,19 @@ void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) drag->setMimeData (mime); drag->setPixmap (QString::fromStdString (mime->getIcon())); drag->exec(); - std::cout << "startdrag\n"; } } void CSVWorld::Table::dragEnterEvent(QDragEnterEvent *event) { - //if (event->mimeData()->hasFormat("text/plain")) - std::cout << "accept drag event\n"; event->acceptProposedAction(); - } void CSVWorld::Table::dropEvent(QDropEvent *event) { - std::cout << "drop\n"; event->acceptProposedAction(); QModelIndex index = indexAt(event->pos()); - std::cout << index.row(); } void CSVWorld::Table::dragMoveEvent(QDragMoveEvent *event) From 71b2fc1c7024242209022e646b0cfe1ac9be65da Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 13 Feb 2014 10:54:39 +0100 Subject: [PATCH 823/889] Introduced method to access display type. --- apps/opencs/model/world/idtable.cpp | 5 +++++ apps/opencs/model/world/idtable.hpp | 3 +++ 2 files changed, 8 insertions(+) diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index bea307aa2..b509f6b9d 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -188,4 +188,9 @@ void CSMWorld::IdTable::reorderRows (int baseIndex, const std::vector& newO CSMWorld::IdTable::Reordering CSMWorld::IdTable::getReordering() const { return mReordering; +} + +CSMWorld::ColumnBase::Display CSMWorld::IdTable::getColumnDisplay (int index) const +{ + return mIdCollection->getColumn(index).mDisplayType; } \ No newline at end of file diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index 74923867d..aee49a961 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -11,6 +11,7 @@ namespace CSMWorld { class CollectionBase; + class ColumnsBase; class RecordBase; class IdTable : public QAbstractItemModel @@ -86,6 +87,8 @@ namespace CSMWorld /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). Reordering getReordering() const; + + CSMWorld::ColumnBase::Display getColumnDisplay(int index) const; }; } From d4a755d1aa53bc37661bc41ac390ce682e821346 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 13 Feb 2014 11:15:55 +0100 Subject: [PATCH 824/889] Fix some hardcoded literals --- components/terrain/quadtreenode.cpp | 12 +++++++----- components/terrain/world.cpp | 10 ++++++---- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index a4fdd5e13..82ccc7c89 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -168,7 +168,8 @@ QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const if (mParent) pos = mParent->getCenter(); pos = mCenter - pos; - mSceneNode->setPosition(Ogre::Vector3(pos.x*8192, pos.y*8192, 0)); + float cellWorldSize = mTerrain->getStorage()->getCellWorldSize(); + mSceneNode->setPosition(Ogre::Vector3(pos.x*cellWorldSize, pos.y*cellWorldSize, 0)); mMaterialGenerator = new MaterialGenerator(mTerrain->getShadersEnabled()); } @@ -203,6 +204,7 @@ void QuadTreeNode::initNeighbours() void QuadTreeNode::initAabb() { + float cellWorldSize = mTerrain->getStorage()->getCellWorldSize(); if (hasChildren()) { for (int i=0; i<4; ++i) @@ -210,11 +212,11 @@ void QuadTreeNode::initAabb() mChildren[i]->initAabb(); mBounds.merge(mChildren[i]->getBoundingBox()); } - mBounds = Ogre::AxisAlignedBox (Ogre::Vector3(-mSize/2*8192, -mSize/2*8192, mBounds.getMinimum().z), - Ogre::Vector3(mSize/2*8192, mSize/2*8192, mBounds.getMaximum().z)); + mBounds = Ogre::AxisAlignedBox (Ogre::Vector3(-mSize/2*cellWorldSize, -mSize/2*cellWorldSize, mBounds.getMinimum().z), + Ogre::Vector3(mSize/2*cellWorldSize, mSize/2*cellWorldSize, mBounds.getMaximum().z)); } - mWorldBounds = Ogre::AxisAlignedBox(mBounds.getMinimum() + Ogre::Vector3(mCenter.x*8192, mCenter.y*8192, 0), - mBounds.getMaximum() + Ogre::Vector3(mCenter.x*8192, mCenter.y*8192, 0)); + mWorldBounds = Ogre::AxisAlignedBox(mBounds.getMinimum() + Ogre::Vector3(mCenter.x*cellWorldSize, mCenter.y*cellWorldSize, 0), + mBounds.getMaximum() + Ogre::Vector3(mCenter.x*cellWorldSize, mCenter.y*cellWorldSize, 0)); } void QuadTreeNode::setBoundingBox(const Ogre::AxisAlignedBox &box) diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index dac960fbb..f4070393d 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -113,9 +113,10 @@ namespace Terrain // We arrived at a leaf float minZ,maxZ; Ogre::Vector2 center = node->getCenter(); + float cellWorldSize = getStorage()->getCellWorldSize(); if (mStorage->getMinMaxHeights(node->getSize(), center, minZ, maxZ)) - node->setBoundingBox(Ogre::AxisAlignedBox(Ogre::Vector3(-halfSize*8192, -halfSize*8192, minZ), - Ogre::Vector3(halfSize*8192, halfSize*8192, maxZ))); + node->setBoundingBox(Ogre::AxisAlignedBox(Ogre::Vector3(-halfSize*cellWorldSize, -halfSize*cellWorldSize, minZ), + Ogre::Vector3(halfSize*cellWorldSize, halfSize*cellWorldSize, maxZ))); else node->markAsDummy(); // no data available for this node, skip it return; @@ -168,8 +169,9 @@ namespace Terrain return Ogre::AxisAlignedBox::BOX_NULL; QuadTreeNode* node = findNode(center, mRootNode); Ogre::AxisAlignedBox box = node->getBoundingBox(); - box.setExtents(box.getMinimum() + Ogre::Vector3(center.x, center.y, 0) * 8192, - box.getMaximum() + Ogre::Vector3(center.x, center.y, 0) * 8192); + float cellWorldSize = getStorage()->getCellWorldSize(); + box.setExtents(box.getMinimum() + Ogre::Vector3(center.x, center.y, 0) * cellWorldSize, + box.getMaximum() + Ogre::Vector3(center.x, center.y, 0) * cellWorldSize); return box; } From 4e6507248bf58e552f331e26fb3c5cc03fa55745 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 13 Feb 2014 12:19:21 +0100 Subject: [PATCH 825/889] Fix travis build --- .travis.yml | 2 +- CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 04d019c0d..693db6f11 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ branches: - next before_install: - pwd - - git submodule update --init --recursive + - git fetch --tags - echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse" - echo "yes" | sudo apt-add-repository ppa:openmw/openmw - sudo apt-get update -qq diff --git a/CMakeLists.txt b/CMakeLists.txt index 01ac9b23f..6f209552c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ include(OpenMWMacros) include(GetGitRevisionDescription) -get_git_tag_revision(TAGHASH --tags --max-count=1) +get_git_tag_revision(TAGHASH --tags --max-count=1 "HEAD...") get_git_head_revision(REFSPEC COMMITHASH) git_describe(VERSION --tags ${TAGHASH}) From 0fcec24c9b02b9a5924907891f5fa34296f16c12 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 13 Feb 2014 12:24:15 +0100 Subject: [PATCH 826/889] Remove some bogus dependencies for travis build --- .travis.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 693db6f11..bc60f3ca6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,17 +4,16 @@ compiler: branches: only: - master - - next before_install: - pwd - git fetch --tags - echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse" - echo "yes" | sudo apt-add-repository ppa:openmw/openmw - sudo apt-get update -qq - - sudo apt-get install -qq libboost-all-dev libgtest-dev google-mock libzzip-dev uuid-dev - - sudo apt-get install -qq libqt4-dev libxaw7-dev libxrandr-dev libfreeimage-dev libpng-dev - - sudo apt-get install -qq libopenal-dev libmpg123-dev libsndfile1-dev - - sudo apt-get install -qq libavcodec-dev libavformat-dev libavdevice-dev libavutil-dev libswscale-dev libpostproc-dev + - sudo apt-get install -qq libboost-all-dev libgtest-dev google-mock + - sudo apt-get install -qq libqt4-dev + - sudo apt-get install -qq libopenal-dev + - sudo apt-get install -qq libavcodec-dev libavformat-dev libavutil-dev libswscale-dev - sudo apt-get install -qq libbullet-dev libogre-1.9-dev libmygui-dev libsdl2-dev libunshield-dev - sudo mkdir /usr/src/gtest/build - cd /usr/src/gtest/build From 1ed1b432961223122346d4ea0e4304d28a5d1578 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 13 Feb 2014 12:30:53 +0100 Subject: [PATCH 827/889] Enable release branches in Travis CI --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index bc60f3ca6..b7a5d61ad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ compiler: branches: only: - master + - /openmw-.*$/ before_install: - pwd - git fetch --tags From ea90035eca1892a503666ab988eaba2eac0a3fb7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 13 Feb 2014 12:35:33 +0100 Subject: [PATCH 828/889] Enable irc notification for travis --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.travis.yml b/.travis.yml index b7a5d61ad..39a02de63 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,3 +37,9 @@ notifications: email: on_success: change on_failure: always + irc: + channels: + - "chat.freenode.net#openmw" + on_success: change + on_failure: always + From 09d3c7a44659a4ca466d88124628791227287074 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 13 Feb 2014 13:54:09 +0100 Subject: [PATCH 829/889] Attempt to match types. Does not work at the moment. --- apps/opencs/model/world/columnimp.hpp | 2 +- apps/opencs/model/world/idtable.cpp | 5 ----- apps/opencs/model/world/idtable.hpp | 3 --- apps/opencs/model/world/tablemimedata.cpp | 21 +++++++++++++-------- apps/opencs/model/world/tablemimedata.hpp | 6 +++--- apps/opencs/view/world/table.cpp | 19 ++++++++++++++++--- 6 files changed, 33 insertions(+), 23 deletions(-) diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 9e9bedcf8..def225018 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -1485,7 +1485,7 @@ namespace CSMWorld template struct RaceColumn : public Column { - RaceColumn() : Column (Columns::ColumnId_Race, ColumnBase::Display_String) {} + RaceColumn() : Column (Columns::ColumnId_Race, ColumnBase::Display_Race) {} virtual QVariant get (const Record& record) const { diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index b509f6b9d..bea307aa2 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -188,9 +188,4 @@ void CSMWorld::IdTable::reorderRows (int baseIndex, const std::vector& newO CSMWorld::IdTable::Reordering CSMWorld::IdTable::getReordering() const { return mReordering; -} - -CSMWorld::ColumnBase::Display CSMWorld::IdTable::getColumnDisplay (int index) const -{ - return mIdCollection->getColumn(index).mDisplayType; } \ No newline at end of file diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index aee49a961..74923867d 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -11,7 +11,6 @@ namespace CSMWorld { class CollectionBase; - class ColumnsBase; class RecordBase; class IdTable : public QAbstractItemModel @@ -87,8 +86,6 @@ namespace CSMWorld /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). Reordering getReordering() const; - - CSMWorld::ColumnBase::Display getColumnDisplay(int index) const; }; } diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp index 45305431e..07c9999b9 100644 --- a/apps/opencs/model/world/tablemimedata.cpp +++ b/apps/opencs/model/world/tablemimedata.cpp @@ -3,6 +3,7 @@ #include "universalid.hpp" #include "columnbase.hpp" +#include CSMWorld::TableMimeData::TableMimeData (UniversalId id) { @@ -76,8 +77,9 @@ bool CSMWorld::TableMimeData::holdsType (CSMWorld::UniversalId::Type type) const return false; } -bool CSMWorld::TableMimeData::holdsType (CSMWorld::ColumnBase::Display type) +bool CSMWorld::TableMimeData::holdsType (CSMWorld::ColumnBase::Display type) const { + std::cout<::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) { if (it->getType() == convertEnums (type)) @@ -102,7 +104,7 @@ CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::Univers throw ("TableMimeData object does not hold object of the seeked type"); } -CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnBase::Display type) +CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnBase::Display type) const { for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) { @@ -115,15 +117,10 @@ CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnB throw ("TableMimeData object does not hold object of the seeked type"); } -CSMWorld::UniversalId::Type CSMWorld::TableMimeData::convertEnums (CSMWorld::ColumnBase::Display type) +CSMWorld::UniversalId::Type CSMWorld::TableMimeData::convertEnums (CSMWorld::ColumnBase::Display type) const { switch (type) { - default: - throw ("This type is not handled"); - return CSMWorld::UniversalId::Type_None; - break; - case CSMWorld::ColumnBase::Display_Race: return CSMWorld::UniversalId::Type_Race; break; @@ -271,6 +268,14 @@ CSMWorld::UniversalId::Type CSMWorld::TableMimeData::convertEnums (CSMWorld::Col case CSMWorld::ColumnBase::Display_Scene: return CSMWorld::UniversalId::Type_Scene; break; + + case CSMWorld::ColumnBase::Display_Script: + return CSMWorld::UniversalId::Type_Script; + break; + + default: + return CSMWorld::UniversalId::Type_None; + break; } } // kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/apps/opencs/model/world/tablemimedata.hpp b/apps/opencs/model/world/tablemimedata.hpp index d2e5d3ecb..1b6c5e635 100644 --- a/apps/opencs/model/world/tablemimedata.hpp +++ b/apps/opencs/model/world/tablemimedata.hpp @@ -25,15 +25,15 @@ namespace CSMWorld std::string getIcon() const; std::vector getData() const; bool holdsType(UniversalId::Type type) const; - bool holdsType(CSMWorld::ColumnBase::Display type); + bool holdsType(CSMWorld::ColumnBase::Display type) const; UniversalId returnMatching(UniversalId::Type type) const; - UniversalId returnMatching(CSMWorld::ColumnBase::Display type); + UniversalId returnMatching(CSMWorld::ColumnBase::Display type) const; private: std::vector mUniversalId; QStringList mObjectsFormats; - CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type); + CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type) const; }; } #endif // TABLEMIMEDATA_H diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index c39ed3469..bfd58fb86 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -17,6 +17,8 @@ #include "recordstatusdelegate.hpp" #include "util.hpp" +#include +#include "../../model/world/tablemimedata.hpp" void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) { @@ -475,13 +477,24 @@ void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) void CSVWorld::Table::dragEnterEvent(QDragEnterEvent *event) { - event->acceptProposedAction(); +// QModelIndex index = indexAt (event->pos()); + +// if (dynamic_cast (event->mimeData())->holdsType (mModel->getColumnDisplay (index.column()))) +// { + event->acceptProposedAction(); +// } } void CSVWorld::Table::dropEvent(QDropEvent *event) { - event->acceptProposedAction(); - QModelIndex index = indexAt(event->pos()); + QModelIndex index = indexAt (event->pos()); + + CSMWorld::ColumnBase::Display display = static_cast(mModel->headerData(index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + if (dynamic_cast(event->mimeData())->holdsType(display)) + { + event->acceptProposedAction(); + std::cout<<"Dropped/n"; + } else {std::cout<<"Not Dropped\n";} } void CSVWorld::Table::dragMoveEvent(QDragMoveEvent *event) From 309573a3ac01f38fe27ce3a3d00b2e0e90187814 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Feb 2014 14:15:48 +0100 Subject: [PATCH 830/889] allow the use of the keyword end as a variable name in an expression --- components/compiler/exprparser.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 07f576006..474de856a 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -355,6 +355,11 @@ namespace Compiler } } + if (keyword==Scanner::K_end) + { + return parseName (loc.mLiteral, loc, scanner); + } + mFirst = false; if (!mExplicit.empty()) From e9238b456d8e830421b140f0bdb60552144ab2a2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Feb 2014 15:01:10 +0100 Subject: [PATCH 831/889] use case-insensitive sorting in columns --- apps/opencs/model/world/idtableproxymodel.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/world/idtableproxymodel.cpp b/apps/opencs/model/world/idtableproxymodel.cpp index 2b757adfe..f51b7f818 100644 --- a/apps/opencs/model/world/idtableproxymodel.cpp +++ b/apps/opencs/model/world/idtableproxymodel.cpp @@ -33,7 +33,9 @@ bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelI CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent) : QSortFilterProxyModel (parent) -{} +{ + setSortCaseSensitivity (Qt::CaseInsensitive); +} QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const { From f7ff4fbd51178825a1199ae4b5a5b7cabc765e16 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Feb 2014 15:31:07 +0100 Subject: [PATCH 832/889] allow disable as an alias for getDisabled (in most cases) --- components/compiler/exprparser.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 474de856a..6b451292a 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -366,7 +366,8 @@ namespace Compiler { if (mRefOp && mNextOperand) { - if (keyword==Scanner::K_getdisabled) + if (keyword==Scanner::K_getdisabled || + keyword==Scanner::K_disable) /// \todo add option to disable this { start(); @@ -509,7 +510,8 @@ namespace Compiler mNextOperand = false; return true; } - else if (keyword==Scanner::K_getdisabled) + else if (keyword==Scanner::K_getdisabled || + keyword==Scanner::K_disable) /// \todo add option to disable this { start(); From 6662560cbca7d892dd774da29f5fcf067222a379 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 13 Feb 2014 15:43:19 +0100 Subject: [PATCH 833/889] new displays for referencable table columns --- apps/opencs/model/world/refidcollection.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 8a1af3509..89a917139 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -59,7 +59,7 @@ CSMWorld::RefIdCollection::RefIdCollection() mColumns.push_back (RefIdColumn (Columns::ColumnId_Name, ColumnBase::Display_String)); nameColumns.mName = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Script, ColumnBase::Display_String)); + mColumns.push_back (RefIdColumn (Columns::ColumnId_Script, ColumnBase::Display_Script)); nameColumns.mScript = &mColumns.back(); InventoryColumns inventoryColumns (nameColumns); @@ -214,10 +214,10 @@ CSMWorld::RefIdCollection::RefIdCollection() creatureColumns.mFlags.insert (std::make_pair (respawn, ESM::Creature::Respawn)); - mColumns.push_back (RefIdColumn (Columns::ColumnId_OpenSound, ColumnBase::Display_String)); + mColumns.push_back (RefIdColumn (Columns::ColumnId_OpenSound, ColumnBase::Display_Sound)); const RefIdColumn *openSound = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_CloseSound, ColumnBase::Display_String)); + mColumns.push_back (RefIdColumn (Columns::ColumnId_CloseSound, ColumnBase::Display_Sound)); const RefIdColumn *closeSound = &mColumns.back(); LightColumns lightColumns (inventoryColumns); @@ -231,7 +231,7 @@ CSMWorld::RefIdCollection::RefIdCollection() mColumns.push_back (RefIdColumn (Columns::ColumnId_Colour, ColumnBase::Display_Integer)); lightColumns.mColor = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Sound, ColumnBase::Display_String)); + mColumns.push_back (RefIdColumn (Columns::ColumnId_Sound, ColumnBase::Display_Sound)); lightColumns.mSound = &mColumns.back(); static const struct @@ -263,13 +263,13 @@ CSMWorld::RefIdCollection::RefIdCollection() NpcColumns npcColumns (actorsColumns); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Race, ColumnBase::Display_String)); + mColumns.push_back (RefIdColumn (Columns::ColumnId_Race, ColumnBase::Display_Race)); npcColumns.mRace = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Class, ColumnBase::Display_String)); + mColumns.push_back (RefIdColumn (Columns::ColumnId_Class, ColumnBase::Display_Class)); npcColumns.mClass = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Faction, ColumnBase::Display_String)); + mColumns.push_back (RefIdColumn (Columns::ColumnId_Faction, ColumnBase::Display_Faction)); npcColumns.mFaction = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::Columnid_Hair, ColumnBase::Display_String)); From 2afe3f3e5735bb369826e2ad8542dc661f47ae24 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 13 Feb 2014 19:00:35 +0100 Subject: [PATCH 834/889] Introduced multitype icon. --- apps/opencs/model/world/tablemimedata.cpp | 2 -- apps/opencs/view/world/genericcreator.cpp | 4 ++-- apps/opencs/view/world/table.cpp | 22 ++++++++++++---------- files/opencs/multitype.png | Bin 0 -> 1708 bytes files/opencs/resources.qrc | 1 + 5 files changed, 15 insertions(+), 14 deletions(-) create mode 100644 files/opencs/multitype.png diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp index 07c9999b9..87bd67ce9 100644 --- a/apps/opencs/model/world/tablemimedata.cpp +++ b/apps/opencs/model/world/tablemimedata.cpp @@ -3,7 +3,6 @@ #include "universalid.hpp" #include "columnbase.hpp" -#include CSMWorld::TableMimeData::TableMimeData (UniversalId id) { @@ -79,7 +78,6 @@ bool CSMWorld::TableMimeData::holdsType (CSMWorld::UniversalId::Type type) const bool CSMWorld::TableMimeData::holdsType (CSMWorld::ColumnBase::Display type) const { - std::cout<::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) { if (it->getType() == convertEnums (type)) diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index cd7a5fa18..31c216e2c 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -133,9 +133,9 @@ void CSVWorld::GenericCreator::create() std::string id = getId(); std::auto_ptr command (new CSMWorld::CloneCommand ( dynamic_cast (*mData.getTableModel(mListId)), mClonedId, id, mClonedType)); - + mUndoStack.push(command.release()); - + emit done(); emit requestFocus(id); } else { diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index bfd58fb86..48b533992 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "../../model/world/data.hpp" #include "../../model/world/commands.hpp" @@ -477,27 +478,28 @@ void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) void CSVWorld::Table::dragEnterEvent(QDragEnterEvent *event) { -// QModelIndex index = indexAt (event->pos()); - -// if (dynamic_cast (event->mimeData())->holdsType (mModel->getColumnDisplay (index.column()))) -// { event->acceptProposedAction(); -// } } void CSVWorld::Table::dropEvent(QDropEvent *event) { QModelIndex index = indexAt (event->pos()); - CSMWorld::ColumnBase::Display display = static_cast(mModel->headerData(index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + CSMWorld::ColumnBase::Display display = static_cast + (mModel->headerData(index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + if (dynamic_cast(event->mimeData())->holdsType(display)) { - event->acceptProposedAction(); - std::cout<<"Dropped/n"; - } else {std::cout<<"Not Dropped\n";} + const CSMWorld::TableMimeData* mime = dynamic_cast(event->mimeData()); + CSMWorld::UniversalId record(mime->returnMatching(display)); + mUndoStack.push (new CSMWorld::ModifyCommand ( *mModel, + index, + QVariant(record.getId().c_str()) + )); + } } void CSVWorld::Table::dragMoveEvent(QDragMoveEvent *event) { - event->accept(); + event->accept(); } diff --git a/files/opencs/multitype.png b/files/opencs/multitype.png new file mode 100644 index 0000000000000000000000000000000000000000..05676e2de06f52ed563842c0d8d5fe888e771a70 GIT binary patch literal 1708 zcmV;d22=ToP)Ekc{Xm)1$i(h%(W|y+oEr<7?dfwtd#j#`W=aP>d z{Klc7H5<=7{e+;hy*X@yKVZUq?i4 zwO3e@!G|P^!(or}?!k~`kEAO{$F}cz#3sg{3&0P1(d=|*h1f=?&#>o%AFaH$e$%&( zCcpYbeo!8L>X@CoEdYlP$VMk_e_E;SmyycuyWV|&ckAwbwN+#9acONB=QAe%`g85g zH&E@iod6;bn-{oqZ9N@%@ZheOe*Kfv-#ZvS|D|L3b4wE8w?86b)mZh-T_4^*G%~g$ zYpfX~Bq?T~8c|g$Bb#~Y@B^Is*HscP?7XYa=6&BGojHTWoS3(ZU(D&M1C7<|2DaS${;V-n#adHzn{ShMW>FI)Q~}3ey~f9%Ji_B&dzPWK zTZmWA5(YOQJ|NB^;t?Qdi|sq#n|cZRmn6b8Ngv;K--l|2#|kb1pCO@A7*DDgAeu8f z^-tQD|IXUGKfvTGFYw0rPl>*cq_qNsbBH(^!-K(v{_7~09e%z5%qC6DP1+f;|v)6^AAy?KhWT4UYl2y54^&(gnT2X6_$JOAlM zqY+M>_!B2zyF^81@e)xSleP&p2O_Mh3{a5_!=VIBk|G(47@`O&fp%EUwdr>_e(D?# z@7+z_?e2c&A<3S4!P*Pd0b5n8X576Y<({$4xFkh`+!8k(t3f2FBiuD-Sav3;*upcBMI}oUzpb00mn@`^GyL18u-r*S=7+t zfR-MD|0e#6fK%<<`a~om0t!CNqd(3QWSnrDo zP6WmEW+KjEIE)goD41YkD2k~s!)Il&fMy3LB4TD1ZuB09h&Z1i&f_DP12I9=VNSs! z4#Pw+bAY1iu7|DY;g~nX6y}ImPym_jTxUMa^FCmRgcK2nx&pB?K}$ZwT;Yi*Vg{y& z7>Xh0vEp!69?}fM3h|lt1O85n(BpJIfPSKi^GIpb6uei=9EO#Ki_Iy|1me zH!ISwq&|qlK(4k(04E{^fRcUzQbPpI5pg&dzzp$L9K}S6%?yfuXGn>mXT+h!6tf~6 zW_`su?=eHJG^vyc5a%-Bfr=Rp%jshhK?Iz+UZcPSyjmZSi2yoBt%{j|7;~`Uo$qNN zEA8cBUP>nIs1`d-sv}!5F)YSDomea@RV+iHn3maMiii?b3bCnTQnW}ZQ#y89#2Ed= zH0@pq2sidYy1#ohKjVM!-5uA7TfQL}uU_XFlKfBGWrvVStfP3p1E6w$lx?ICV zh*z6j0Zs!`K;HjjA|kE?k(A6VKLv%?InV_nVE+cordT0F3*6fP0000spell.png static.png weapon.png + multitype.png raster/startup/big/create-addon.png raster/startup/big/new-game.png raster/startup/big/edit-content.png From 1bcc6d6918e323834552d68c8e4023908e4566d2 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 13 Feb 2014 19:14:17 +0100 Subject: [PATCH 835/889] using new icon. --- apps/opencs/model/world/tablemimedata.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp index 87bd67ce9..b0cf0abcc 100644 --- a/apps/opencs/model/world/tablemimedata.cpp +++ b/apps/opencs/model/world/tablemimedata.cpp @@ -49,7 +49,7 @@ std::string CSMWorld::TableMimeData::getIcon() const if (tmpIcon != mUniversalId[i].getIcon()) { - return ""; //should return multiple types icon, but at the moment we don't have one + return ":/multitype.png"; //icon stolen from gnome } tmpIcon = mUniversalId[i].getIcon(); From 40cc108e54e09ddee979c9296637c592d6c800a7 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 13 Feb 2014 20:19:51 +0100 Subject: [PATCH 836/889] drag and drop works. --- apps/opencs/view/world/table.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 48b533992..4d5554d86 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -490,12 +490,11 @@ void CSVWorld::Table::dropEvent(QDropEvent *event) if (dynamic_cast(event->mimeData())->holdsType(display)) { - const CSMWorld::TableMimeData* mime = dynamic_cast(event->mimeData()); - CSMWorld::UniversalId record(mime->returnMatching(display)); - mUndoStack.push (new CSMWorld::ModifyCommand ( *mModel, - index, - QVariant(record.getId().c_str()) - )); + const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + CSMWorld::UniversalId record (mime->returnMatching (display)); + std::auto_ptr command (new CSMWorld::ModifyCommand + (*mProxyModel, index, QVariant (QString::fromStdString (record.getId())))); + mUndoStack.push (command.release()); } } From e597328b6b6ae483f69adcc4878ca3ff92c0f50f Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Thu, 13 Feb 2014 20:24:27 +0100 Subject: [PATCH 837/889] Make enable/disable a no-op for items in containers --- apps/openmw/mwworld/worldimp.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 4e240195a..95bf65d08 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -610,6 +610,10 @@ namespace MWWorld void World::enable (const Ptr& reference) { + // enable is a no-op for items in containers + if (!reference.isInCell()) + return; + if (!reference.getRefData().isEnabled()) { reference.getRefData().enable(); @@ -640,6 +644,10 @@ namespace MWWorld void World::disable (const Ptr& reference) { + // disable is a no-op for items in containers + if (!reference.isInCell()) + return; + if (reference.getRefData().isEnabled()) { reference.getRefData().disable(); From 7c981587fca5b2671dd59e9cf9b6b2ccdcf83627 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Thu, 13 Feb 2014 22:58:12 +0100 Subject: [PATCH 838/889] When searching object by id, search in active cells before searching in the player's inventory --- apps/openmw/mwworld/worldimp.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 95bf65d08..be6c0b338 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -529,12 +529,6 @@ namespace MWWorld return mPlayer->getPlayer(); } - Ptr ptr = Class::get (mPlayer->getPlayer()). - getContainerStore (mPlayer->getPlayer()).search (name); - - if (!ptr.isEmpty()) - return ptr; - std::string lowerCaseName = Misc::StringUtils::lowerCase(name); // active cells @@ -548,6 +542,12 @@ namespace MWWorld return ptr; } + Ptr ptr = Class::get (mPlayer->getPlayer()). + getContainerStore (mPlayer->getPlayer()).search (lowerCaseName); + + if (!ptr.isEmpty()) + return ptr; + if (!activeOnly) { ret = mCells.getPtr (lowerCaseName); From b01c6dad375375864f5b667b9d31ce8b310ff482 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 14 Feb 2014 08:29:18 +0100 Subject: [PATCH 839/889] Revert "allow disable as an alias for getDisabled (in most cases)" This reverts commit f7ff4fbd51178825a1199ae4b5a5b7cabc765e16. --- components/compiler/exprparser.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 6b451292a..474de856a 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -366,8 +366,7 @@ namespace Compiler { if (mRefOp && mNextOperand) { - if (keyword==Scanner::K_getdisabled || - keyword==Scanner::K_disable) /// \todo add option to disable this + if (keyword==Scanner::K_getdisabled) { start(); @@ -510,8 +509,7 @@ namespace Compiler mNextOperand = false; return true; } - else if (keyword==Scanner::K_getdisabled || - keyword==Scanner::K_disable) /// \todo add option to disable this + else if (keyword==Scanner::K_getdisabled) { start(); From 2086ebe4104a00d00ede72c174138640a43948fa Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 14 Feb 2014 08:48:26 +0100 Subject: [PATCH 840/889] fix for inappropriate disable (2nd attempt) --- components/compiler/exprparser.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 474de856a..7d310617c 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -355,7 +355,16 @@ namespace Compiler } } - if (keyword==Scanner::K_end) + if (keyword==Scanner::K_end || keyword==Scanner::K_begin || + keyword==Scanner::K_short || keyword==Scanner::K_long || + keyword==Scanner::K_float || keyword==Scanner::K_if || + keyword==Scanner::K_endif || keyword==Scanner::K_else || + keyword==Scanner::K_elseif || keyword==Scanner::K_while || + keyword==Scanner::K_endwhile || keyword==Scanner::K_return || + keyword==Scanner::K_messagebox || keyword==Scanner::K_set || + keyword==Scanner::K_to || keyword==Scanner::K_startscript || + keyword==Scanner::K_stopscript || keyword==Scanner::K_enable || + keyword==Scanner::K_disable) { return parseName (loc.mLiteral, loc, scanner); } From e76ef9266995a83fad40f3ebc2b74a54d5d6ff17 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 14 Feb 2014 09:06:06 +0100 Subject: [PATCH 841/889] also allow the use of keywords as remote local variables in set statements --- components/compiler/lineparser.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 495e7e25e..b1956e628 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -219,6 +219,20 @@ namespace Compiler bool LineParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { + if (mState==SetMemberVarState) + { + mMemberName = loc.mLiteral; + std::pair type = getContext().getMemberType (mMemberName, mName); + + if (type.first!=' ') + { + mState = SetMemberVarState2; + mType = type.first; + mReferenceMember = type.second; + return true; + } + } + if (mState==SetPotentialMemberVarState && keyword==Scanner::K_to) { getErrorHandler().warning ("unknown variable (ignoring set instruction)", loc); From 451e1f413ba33170ba01e96788443a51b1c5a113 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 14 Feb 2014 11:15:16 +0100 Subject: [PATCH 842/889] instead of using pre-compiled variable lists for remote member access get the variable list from the remote script on the fly --- apps/openmw/mwscript/scriptmanagerimp.cpp | 17 +++----- components/CMakeLists.txt | 1 + components/compiler/declarationparser.cpp | 5 +++ components/compiler/declarationparser.hpp | 2 + components/compiler/quickfileparser.cpp | 52 +++++++++++++++++++++++ components/compiler/quickfileparser.hpp | 39 +++++++++++++++++ 6 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 components/compiler/quickfileparser.cpp create mode 100644 components/compiler/quickfileparser.hpp diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index 5e18151da..74c85dbbf 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include "../mwworld/esmstore.hpp" @@ -159,20 +160,14 @@ namespace MWScript return iter->second; } - Compiler::Locals locals; - if (const ESM::Script *script = mStore.get().find (name2)) { - int index = 0; + Compiler::Locals locals; - for (int i=0; imData.mNumShorts; ++i) - locals.declare ('s', script->mVarNames[index++]); - - for (int i=0; imData.mNumLongs; ++i) - locals.declare ('l', script->mVarNames[index++]); - - for (int i=0; imData.mNumFloats; ++i) - locals.declare ('f', script->mVarNames[index++]); + std::istringstream stream (script->mScriptText); + Compiler::QuickFileParser parser (mErrorHandler, mCompilerContext, locals); + Compiler::Scanner scanner (mErrorHandler, stream, mCompilerContext.getExtensions()); + scanner.scan (parser); std::map::iterator iter = mOtherLocals.insert (std::make_pair (name2, locals)).first; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index fbe9ad5bd..45a91f368 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -60,6 +60,7 @@ add_component_dir (compiler context controlparser errorhandler exception exprparser extensions fileparser generator lineparser literals locals output parser scanner scriptparser skipparser streamerrorhandler stringparser tokenloc nullerrorhandler opcodes extensions0 declarationparser + quickfileparser ) add_component_dir (interpreter diff --git a/components/compiler/declarationparser.cpp b/components/compiler/declarationparser.cpp index 9c4e4f631..357f0259b 100644 --- a/components/compiler/declarationparser.cpp +++ b/components/compiler/declarationparser.cpp @@ -75,4 +75,9 @@ bool Compiler::DeclarationParser::parseSpecial (int code, const TokenLoc& loc, S return false; return Parser::parseSpecial (code, loc, scanner); +} + +void Compiler::DeclarationParser::reset() +{ + mState = State_Begin; } \ No newline at end of file diff --git a/components/compiler/declarationparser.hpp b/components/compiler/declarationparser.hpp index 3e0ac57c3..f85b31ba1 100644 --- a/components/compiler/declarationparser.hpp +++ b/components/compiler/declarationparser.hpp @@ -35,6 +35,8 @@ namespace Compiler ///< Handle a special character token. /// \return fetch another token? + void reset(); + }; } diff --git a/components/compiler/quickfileparser.cpp b/components/compiler/quickfileparser.cpp new file mode 100644 index 000000000..157e15249 --- /dev/null +++ b/components/compiler/quickfileparser.cpp @@ -0,0 +1,52 @@ + +#include "quickfileparser.hpp" + +#include "skipparser.hpp" +#include "scanner.hpp" + +Compiler::QuickFileParser::QuickFileParser (ErrorHandler& errorHandler, Context& context, + Locals& locals) +: Parser (errorHandler, context), mDeclarationParser (errorHandler, context, locals) +{} + +bool Compiler::QuickFileParser::parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner) +{ + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + return true; +} + +bool Compiler::QuickFileParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) +{ + if (keyword==Scanner::K_end) + return false; + + if (keyword==Scanner::K_short || keyword==Scanner::K_long || keyword==Scanner::K_float) + { + mDeclarationParser.reset(); + scanner.putbackKeyword (keyword, loc); + scanner.scan (mDeclarationParser); + return true; + } + + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + return true; +} + +bool Compiler::QuickFileParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) +{ + if (code!=Scanner::S_newline) + { + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + } + + return true; +} + +void Compiler::QuickFileParser::parseEOF (Scanner& scanner) +{ + +} \ No newline at end of file diff --git a/components/compiler/quickfileparser.hpp b/components/compiler/quickfileparser.hpp new file mode 100644 index 000000000..d2dfafb34 --- /dev/null +++ b/components/compiler/quickfileparser.hpp @@ -0,0 +1,39 @@ +#ifndef COMPILER_QUICKFILEPARSER_H_INCLUDED +#define COMPILER_QUICKFILEPARSER_H_INCLUDED + +#include "parser.hpp" +#include "declarationparser.hpp" + +namespace Compiler +{ + class Locals; + + /// \brief File parser variant that ignores everything but variable declarations + class QuickFileParser : public Parser + { + DeclarationParser mDeclarationParser; + + public: + + QuickFileParser (ErrorHandler& errorHandler, Context& context, Locals& locals); + + virtual bool parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner); + ///< Handle a name token. + /// \return fetch another token? + + virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner); + ///< Handle a keyword token. + /// \return fetch another token? + + virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); + ///< Handle a special character token. + /// \return fetch another token? + + virtual void parseEOF (Scanner& scanner); + ///< Handle EOF token. + }; +} + +#endif + From 910d62e4b84e701e4da58ac83e0d882804eb6c6f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 14 Feb 2014 11:59:33 +0100 Subject: [PATCH 843/889] added missing implementation for CSMWorld::ScriptContext::getGlobalType --- apps/opencs/model/world/scriptcontext.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/apps/opencs/model/world/scriptcontext.cpp b/apps/opencs/model/world/scriptcontext.cpp index 1b3028159..2627c49f3 100644 --- a/apps/opencs/model/world/scriptcontext.cpp +++ b/apps/opencs/model/world/scriptcontext.cpp @@ -16,6 +16,20 @@ bool CSMWorld::ScriptContext::canDeclareLocals() const char CSMWorld::ScriptContext::getGlobalType (const std::string& name) const { + int index = mData.getGlobals().searchId (name); + + if (index!=-1) + { + switch (mData.getGlobals().getRecord (index).get().mValue.getType()) + { + case ESM::VT_Short: return 's'; + case ESM::VT_Long: return 'l'; + case ESM::VT_Float: return 'f'; + + default: return ' '; + } + } + return ' '; } From d213c6c36a48f3435e1b42aa9d4101eb0618ebc0 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 14 Feb 2014 12:23:00 +0100 Subject: [PATCH 844/889] fixed a constness-issue --- components/compiler/controlparser.cpp | 2 +- components/compiler/controlparser.hpp | 2 +- components/compiler/declarationparser.cpp | 2 +- components/compiler/declarationparser.hpp | 2 +- components/compiler/exprparser.cpp | 2 +- components/compiler/exprparser.hpp | 2 +- components/compiler/lineparser.cpp | 2 +- components/compiler/lineparser.hpp | 2 +- components/compiler/parser.cpp | 4 ++-- components/compiler/parser.hpp | 6 +++--- components/compiler/quickfileparser.cpp | 2 +- components/compiler/quickfileparser.hpp | 2 +- components/compiler/scriptparser.cpp | 2 +- components/compiler/scriptparser.hpp | 14 +++++++------- components/compiler/skipparser.cpp | 4 ++-- components/compiler/skipparser.hpp | 8 ++++---- components/compiler/stringparser.cpp | 2 +- components/compiler/stringparser.hpp | 18 +++++++++--------- 18 files changed, 39 insertions(+), 39 deletions(-) diff --git a/components/compiler/controlparser.cpp b/components/compiler/controlparser.cpp index 499358ca0..58542bd73 100644 --- a/components/compiler/controlparser.cpp +++ b/components/compiler/controlparser.cpp @@ -154,7 +154,7 @@ namespace Compiler } } - ControlParser::ControlParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + ControlParser::ControlParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals) : Parser (errorHandler, context), mLocals (locals), mLiterals (literals), mLineParser (errorHandler, context, locals, literals, mCodeBlock), diff --git a/components/compiler/controlparser.hpp b/components/compiler/controlparser.hpp index 50fd2d1f9..24bc7bec7 100644 --- a/components/compiler/controlparser.hpp +++ b/components/compiler/controlparser.hpp @@ -47,7 +47,7 @@ namespace Compiler public: - ControlParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + ControlParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals); void appendCode (std::vector& code) const; diff --git a/components/compiler/declarationparser.cpp b/components/compiler/declarationparser.cpp index 357f0259b..d17f49caf 100644 --- a/components/compiler/declarationparser.cpp +++ b/components/compiler/declarationparser.cpp @@ -8,7 +8,7 @@ #include "skipparser.hpp" #include "locals.hpp" -Compiler::DeclarationParser::DeclarationParser (ErrorHandler& errorHandler, Context& context, +Compiler::DeclarationParser::DeclarationParser (ErrorHandler& errorHandler, const Context& context, Locals& locals) : Parser (errorHandler, context), mLocals (locals), mState (State_Begin), mType (0) {} diff --git a/components/compiler/declarationparser.hpp b/components/compiler/declarationparser.hpp index f85b31ba1..43dd83570 100644 --- a/components/compiler/declarationparser.hpp +++ b/components/compiler/declarationparser.hpp @@ -20,7 +20,7 @@ namespace Compiler public: - DeclarationParser (ErrorHandler& errorHandler, Context& context, Locals& locals); + DeclarationParser (ErrorHandler& errorHandler, const Context& context, Locals& locals); virtual bool parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner); diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 7d310617c..6d26ef403 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -219,7 +219,7 @@ namespace Compiler return false; } - ExprParser::ExprParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + ExprParser::ExprParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals, bool argument) : Parser (errorHandler, context), mLocals (locals), mLiterals (literals), mNextOperand (true), mFirst (true), mArgument (argument), mRefOp (false), mMemberOp (false) diff --git a/components/compiler/exprparser.hpp b/components/compiler/exprparser.hpp index 905f0d454..6a4e1be2f 100644 --- a/components/compiler/exprparser.hpp +++ b/components/compiler/exprparser.hpp @@ -58,7 +58,7 @@ namespace Compiler public: - ExprParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + ExprParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals, bool argument = false); ///< constructor /// \param argument Parser is used to parse function- or instruction- diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index b1956e628..5457d7625 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -50,7 +50,7 @@ namespace Compiler } } - LineParser::LineParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + LineParser::LineParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals, std::vector& code, bool allowExpression) : Parser (errorHandler, context), mLocals (locals), mLiterals (literals), mCode (code), mState (BeginState), mExprParser (errorHandler, context, locals, literals), diff --git a/components/compiler/lineparser.hpp b/components/compiler/lineparser.hpp index c54c764bc..4f2d33bde 100644 --- a/components/compiler/lineparser.hpp +++ b/components/compiler/lineparser.hpp @@ -44,7 +44,7 @@ namespace Compiler public: - LineParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + LineParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals, std::vector& code, bool allowExpression = false); ///< \param allowExpression Allow lines consisting of a naked expression diff --git a/components/compiler/parser.cpp b/components/compiler/parser.cpp index 8d11c4086..781fbad8c 100644 --- a/components/compiler/parser.cpp +++ b/components/compiler/parser.cpp @@ -52,7 +52,7 @@ namespace Compiler // Return context - Context& Parser::getContext() + const Context& Parser::getContext() const { return mContext; } @@ -64,7 +64,7 @@ namespace Compiler return lowerCase; } - Parser::Parser (ErrorHandler& errorHandler, Context& context) + Parser::Parser (ErrorHandler& errorHandler, const Context& context) : mErrorHandler (errorHandler), mContext (context), mOptional (false), mEmpty (true) {} diff --git a/components/compiler/parser.hpp b/components/compiler/parser.hpp index 4fec570e9..54e913b20 100644 --- a/components/compiler/parser.hpp +++ b/components/compiler/parser.hpp @@ -17,7 +17,7 @@ namespace Compiler class Parser { ErrorHandler& mErrorHandler; - Context& mContext; + const Context& mContext; bool mOptional; bool mEmpty; @@ -38,14 +38,14 @@ namespace Compiler ErrorHandler& getErrorHandler(); ///< Return error handler - Context& getContext(); + const Context& getContext() const; ///< Return context static std::string toLower (const std::string& name); public: - Parser (ErrorHandler& errorHandler, Context& context); + Parser (ErrorHandler& errorHandler, const Context& context); ///< constructor virtual ~Parser(); diff --git a/components/compiler/quickfileparser.cpp b/components/compiler/quickfileparser.cpp index 157e15249..f3d8063b2 100644 --- a/components/compiler/quickfileparser.cpp +++ b/components/compiler/quickfileparser.cpp @@ -4,7 +4,7 @@ #include "skipparser.hpp" #include "scanner.hpp" -Compiler::QuickFileParser::QuickFileParser (ErrorHandler& errorHandler, Context& context, +Compiler::QuickFileParser::QuickFileParser (ErrorHandler& errorHandler, const Context& context, Locals& locals) : Parser (errorHandler, context), mDeclarationParser (errorHandler, context, locals) {} diff --git a/components/compiler/quickfileparser.hpp b/components/compiler/quickfileparser.hpp index d2dfafb34..440d91038 100644 --- a/components/compiler/quickfileparser.hpp +++ b/components/compiler/quickfileparser.hpp @@ -15,7 +15,7 @@ namespace Compiler public: - QuickFileParser (ErrorHandler& errorHandler, Context& context, Locals& locals); + QuickFileParser (ErrorHandler& errorHandler, const Context& context, Locals& locals); virtual bool parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner); diff --git a/components/compiler/scriptparser.cpp b/components/compiler/scriptparser.cpp index f34313969..ea11be5f0 100644 --- a/components/compiler/scriptparser.cpp +++ b/components/compiler/scriptparser.cpp @@ -7,7 +7,7 @@ namespace Compiler { - ScriptParser::ScriptParser (ErrorHandler& errorHandler, Context& context, + ScriptParser::ScriptParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, bool end) : Parser (errorHandler, context), mOutput (locals), mLineParser (errorHandler, context, locals, mOutput.getLiterals(), mOutput.getCode()), diff --git a/components/compiler/scriptparser.hpp b/components/compiler/scriptparser.hpp index bb4809dab..000244c79 100644 --- a/components/compiler/scriptparser.hpp +++ b/components/compiler/scriptparser.hpp @@ -12,7 +12,7 @@ namespace Compiler class Locals; // Script parser, to be used in dialogue scripts and as part of FileParser - + class ScriptParser : public Parser { Output mOutput; @@ -21,14 +21,14 @@ namespace Compiler bool mEnd; public: - + /// \param end of script is marked by end keyword. - ScriptParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + ScriptParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, bool end = false); - + void getCode (std::vector& code) const; ///< store generated code in \æ code. - + virtual bool parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner); ///< Handle a name token. @@ -43,8 +43,8 @@ namespace Compiler /// \return fetch another token? virtual void parseEOF (Scanner& scanner); - ///< Handle EOF token. - + ///< Handle EOF token. + void reset(); ///< Reset parser to clean state. }; diff --git a/components/compiler/skipparser.cpp b/components/compiler/skipparser.cpp index 2d09b63ba..c7cb31f58 100644 --- a/components/compiler/skipparser.cpp +++ b/components/compiler/skipparser.cpp @@ -5,7 +5,7 @@ namespace Compiler { - SkipParser::SkipParser (ErrorHandler& errorHandler, Context& context) + SkipParser::SkipParser (ErrorHandler& errorHandler, const Context& context) : Parser (errorHandler, context) {} @@ -34,7 +34,7 @@ namespace Compiler { if (code==Scanner::S_newline) return false; - + return true; } } diff --git a/components/compiler/skipparser.hpp b/components/compiler/skipparser.hpp index 17f1c8d98..239c8bb02 100644 --- a/components/compiler/skipparser.hpp +++ b/components/compiler/skipparser.hpp @@ -8,13 +8,13 @@ namespace Compiler // \brief Skip parser for skipping a line // // This parser is mainly intended for skipping the rest of a faulty line. - + class SkipParser : public Parser { public: - - SkipParser (ErrorHandler& errorHandler, Context& context); - + + SkipParser (ErrorHandler& errorHandler, const Context& context); + virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner); ///< Handle an int token. /// \return fetch another token? diff --git a/components/compiler/stringparser.cpp b/components/compiler/stringparser.cpp index 09c902131..a86c15794 100644 --- a/components/compiler/stringparser.cpp +++ b/components/compiler/stringparser.cpp @@ -10,7 +10,7 @@ namespace Compiler { - StringParser::StringParser (ErrorHandler& errorHandler, Context& context, Literals& literals) + StringParser::StringParser (ErrorHandler& errorHandler, const Context& context, Literals& literals) : Parser (errorHandler, context), mLiterals (literals), mState (StartState), mSmashCase (false) { diff --git a/components/compiler/stringparser.hpp b/components/compiler/stringparser.hpp index f692c650b..3859a2496 100644 --- a/components/compiler/stringparser.hpp +++ b/components/compiler/stringparser.hpp @@ -10,22 +10,22 @@ namespace Compiler { class Literals; - + class StringParser : public Parser { enum State { StartState, CommaState }; - + Literals& mLiterals; State mState; std::vector mCode; bool mSmashCase; - + public: - - StringParser (ErrorHandler& errorHandler, Context& context, Literals& literals); + + StringParser (ErrorHandler& errorHandler, const Context& context, Literals& literals); virtual bool parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner); @@ -35,15 +35,15 @@ namespace Compiler virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); ///< Handle a special character token. /// \return fetch another token? - + void append (std::vector& code); ///< Append code for parsed string. - + void smashCase(); ///< Transform all scanned strings to lower case - + void reset(); - ///< Reset parser to clean state (this includes the smashCase function). + ///< Reset parser to clean state (this includes the smashCase function). }; } From ace42520138b4d8280fab3a0f421b4d8d806bf57 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 14 Feb 2014 12:46:15 +0100 Subject: [PATCH 845/889] nicer formatting --- apps/opencs/view/world/table.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 4d5554d86..32d692be6 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -490,10 +490,14 @@ void CSVWorld::Table::dropEvent(QDropEvent *event) if (dynamic_cast(event->mimeData())->holdsType(display)) { - const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + const CSMWorld::TableMimeData* mime = dynamic_cast + (event->mimeData()); + CSMWorld::UniversalId record (mime->returnMatching (display)); + std::auto_ptr command (new CSMWorld::ModifyCommand (*mProxyModel, index, QVariant (QString::fromStdString (record.getId())))); + mUndoStack.push (command.release()); } } From 0a8ffbfb1d67080b1e22b0f7756794fa6134b94c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 14 Feb 2014 12:46:54 +0100 Subject: [PATCH 846/889] added missing implementation for CSMWorld::ScriptContext::getMemberType --- apps/opencs/model/world/scriptcontext.cpp | 56 ++++++++++++++++++++++- apps/opencs/model/world/scriptcontext.hpp | 3 ++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/world/scriptcontext.cpp b/apps/opencs/model/world/scriptcontext.cpp index 2627c49f3..ee95f06c8 100644 --- a/apps/opencs/model/world/scriptcontext.cpp +++ b/apps/opencs/model/world/scriptcontext.cpp @@ -5,6 +5,10 @@ #include +#include +#include +#include + #include "data.hpp" CSMWorld::ScriptContext::ScriptContext (const Data& data) : mData (data), mIdsUpdated (false) {} @@ -36,7 +40,57 @@ char CSMWorld::ScriptContext::getGlobalType (const std::string& name) const std::pair CSMWorld::ScriptContext::getMemberType (const std::string& name, const std::string& id) const { - return std::make_pair (' ', false); + /// \todo invalidate locals cache on change to scripts + + std::string id2 = Misc::StringUtils::lowerCase (id); + + int index = mData.getScripts().searchId (id2); + bool reference = false; + + if (index!=-1) + { + // ID is not a script ID. Search for a matching referenceable instead. + index = mData.getReferenceables().searchId (id2); + + if (index!=-1) + { + // Referenceable found. + int columnIndex = mData.getReferenceables().searchColumnIndex (Columns::ColumnId_Script); + + if (columnIndex!=-1) + { + id2 = Misc::StringUtils::lowerCase (mData.getReferenceables(). + getData (index, columnIndex).toString().toUtf8().constData()); + + if (!id2.empty()) + { + // Referenceable has a script -> use it. + index = mData.getScripts().searchId (id2); + reference = true; + } + } + } + } + + if (index==-1) + return std::make_pair (' ', false); + + std::map::iterator iter = mLocals.find (id2); + + if (iter==mLocals.end()) + { + Compiler::Locals locals; + + Compiler::NullErrorHandler errorHandler; + std::istringstream stream (mData.getScripts().getRecord (index).get().mScriptText); + Compiler::QuickFileParser parser (errorHandler, *this, locals); + Compiler::Scanner scanner (errorHandler, stream, getExtensions()); + scanner.scan (parser); + + iter = mLocals.insert (std::make_pair (id2, locals)).first; + } + + return std::make_pair (iter->second.getType (Misc::StringUtils::lowerCase (name)), reference); } bool CSMWorld::ScriptContext::isId (const std::string& name) const diff --git a/apps/opencs/model/world/scriptcontext.hpp b/apps/opencs/model/world/scriptcontext.hpp index 3baca99b2..400748e5f 100644 --- a/apps/opencs/model/world/scriptcontext.hpp +++ b/apps/opencs/model/world/scriptcontext.hpp @@ -3,8 +3,10 @@ #include #include +#include #include +#include namespace CSMWorld { @@ -15,6 +17,7 @@ namespace CSMWorld const Data& mData; mutable std::vector mIds; mutable bool mIdsUpdated; + mutable std::map mLocals; public: From ae418f2538435484f852da126baaf21c85667a1d Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 14 Feb 2014 12:46:15 +0100 Subject: [PATCH 847/889] nicer formatting --- apps/opencs/view/world/table.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 4d5554d86..7819a75c0 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -410,10 +410,10 @@ void CSVWorld::Table::tableSizeUpdate() switch (state) { - case CSMWorld::RecordBase::State_BaseOnly: ++size; break; - case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break; - case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break; - case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break; + case CSMWorld::RecordBase::State_BaseOnly: ++size; break; + case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break; + case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break; + case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break; } } } @@ -490,10 +490,14 @@ void CSVWorld::Table::dropEvent(QDropEvent *event) if (dynamic_cast(event->mimeData())->holdsType(display)) { - const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + const CSMWorld::TableMimeData* mime = dynamic_cast + (event->mimeData()); + CSMWorld::UniversalId record (mime->returnMatching (display)); + std::auto_ptr command (new CSMWorld::ModifyCommand (*mProxyModel, index, QVariant (QString::fromStdString (record.getId())))); + mUndoStack.push (command.release()); } } From a315d5cc2b501d7c8b766ad7a603f80ce2366507 Mon Sep 17 00:00:00 2001 From: gus Date: Fri, 14 Feb 2014 12:55:14 +0100 Subject: [PATCH 848/889] aiactivate works. Bug when you try to use it on a reference that doesn't exist. Need to clran up door.cpp --- apps/openmw/mwclass/door.cpp | 8 ++++---- apps/openmw/mwmechanics/aiactivate.cpp | 19 +++++++------------ 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index f99cffe04..a846a8492 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -117,19 +117,19 @@ namespace MWClass { // teleport door /// \todo remove this if clause once ActionTeleport can also support other actors - if (MWBase::Environment::get().getWorld()->getPlayerPtr()==actor) - { + //if (MWBase::Environment::get().getWorld()->getPlayerPtr()==actor) + //{ boost::shared_ptr action(new MWWorld::ActionTeleport (ref->mRef.mDestCell, ref->mRef.mDoorDest)); action->setSound(openSound); return action; - } + /*} else { // another NPC or a creature is using the door return boost::shared_ptr (new MWWorld::FailedAction); - } + }*/ } else { diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index e751140c0..667ebea88 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -6,6 +6,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/action.hpp" namespace { @@ -64,19 +65,11 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) { mCellX = cell->mData.mX; mCellY = cell->mData.mY; - float xCell = 0; - float yCell = 0; - - if(cell->isExterior()) - { - xCell = cell->mData.mX * ESM::Land::REAL_SIZE; - yCell = cell->mData.mY * ESM::Land::REAL_SIZE; - } ESM::Pathgrid::Point dest; dest.mX = targetPos.pos[0]; - dest.mY = targetPos.pos[0]; - dest.mZ = targetPos.pos[0]; + dest.mY = targetPos.pos[1]; + dest.mZ = targetPos.pos[2]; ESM::Pathgrid::Point start; start.mX = pos.pos[0]; @@ -92,7 +85,8 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) { movement.mPosition[1] = 0; MWWorld::Ptr target = world->getPtr(mObjectId,false); - MWWorld::Class::get(target).activate(target,actor); + MWWorld::Class::get(target).activate(target,actor).get()->execute(actor); + std::cout << "activated"; return true; } @@ -100,7 +94,8 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) { movement.mPosition[1] = 0; MWWorld::Ptr target = world->getPtr(mObjectId,false); - MWWorld::Class::get(target).activate(target,actor); + MWWorld::Class::get(target).activate(target,actor).get()->execute(actor); + std::cout << "activated"; return true; } From 0ff744c2ff801aee8bd635924d4eaaeca5f25b03 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 14 Feb 2014 12:56:05 +0100 Subject: [PATCH 849/889] fixed CSMWorld::ScriptContext::isJournalId --- apps/opencs/model/world/scriptcontext.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/opencs/model/world/scriptcontext.cpp b/apps/opencs/model/world/scriptcontext.cpp index ee95f06c8..688c33d30 100644 --- a/apps/opencs/model/world/scriptcontext.cpp +++ b/apps/opencs/model/world/scriptcontext.cpp @@ -109,8 +109,7 @@ bool CSMWorld::ScriptContext::isId (const std::string& name) const bool CSMWorld::ScriptContext::isJournalId (const std::string& name) const { - /// \todo fix this after isId is fixed - return isId (name); + return mData.getJournals().searchId (name)!=-1; } void CSMWorld::ScriptContext::invalidateIds() From e17af4231a9e16eb554a7b2fa56d3ca18fb99ead Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 14 Feb 2014 13:38:30 +0100 Subject: [PATCH 850/889] added script verifier --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/tools/scriptcheck.cpp | 98 +++++++++++++++++++++++ apps/opencs/model/tools/scriptcheck.hpp | 40 +++++++++ apps/opencs/model/tools/tools.cpp | 3 + apps/opencs/model/world/scriptcontext.cpp | 9 ++- apps/opencs/model/world/scriptcontext.hpp | 3 + 6 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 apps/opencs/model/tools/scriptcheck.cpp create mode 100644 apps/opencs/model/tools/scriptcheck.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 5c3cd0dcc..82313b7ed 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -38,7 +38,7 @@ opencs_units (model/tools opencs_units_noqt (model/tools mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck - birthsigncheck spellcheck referenceablecheck + birthsigncheck spellcheck referenceablecheck scriptcheck ) diff --git a/apps/opencs/model/tools/scriptcheck.cpp b/apps/opencs/model/tools/scriptcheck.cpp new file mode 100644 index 000000000..8ed41ec02 --- /dev/null +++ b/apps/opencs/model/tools/scriptcheck.cpp @@ -0,0 +1,98 @@ + +#include "scriptcheck.hpp" + +#include +#include +#include +#include +#include + +#include "../world/data.hpp" + +void CSMTools::ScriptCheckStage::report (const std::string& message, const Compiler::TokenLoc& loc, + Type type) +{ + std::ostringstream stream; + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); + + stream << id.toString() << "|"; + + if (type==ErrorMessage) + stream << "error "; + else + stream << "warning "; + + stream + << "line " << loc.mLine << ", column " << loc.mColumn + << " (" << loc.mLiteral << "): " << message; + + mMessages->push_back (stream.str()); +} + +void CSMTools::ScriptCheckStage::report (const std::string& message, Type type) +{ + std::ostringstream stream; + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); + + stream << id.toString() << "|"; + + if (type==ErrorMessage) + stream << "error: "; + else + stream << "warning: "; + + stream << message; + + mMessages->push_back (stream.str()); +} + +CSMTools::ScriptCheckStage::ScriptCheckStage (const CSMWorld::Data& data) +: mData (data), mContext (data), mMessages (0) +{ + Compiler::registerExtensions (mExtensions); + mContext.setExtensions (&mExtensions); +} + +int CSMTools::ScriptCheckStage::setup() +{ + mContext.clear(); + mMessages = 0; + mId.clear(); + + return mData.getScripts().getSize(); +} + +void CSMTools::ScriptCheckStage::perform (int stage, std::vector& messages) +{ + mMessages = &messages; + mId = mData.getScripts().getId (stage); + + try + { + std::istringstream input (mData.getScripts().getRecord (stage).get().mScriptText); + + Compiler::Scanner scanner (*this, input, mContext.getExtensions()); + + Compiler::FileParser parser (*this, mContext); + + scanner.scan (parser); + } + catch (const Compiler::SourceException&) + { + // error has already been reported via error handler + } + catch (const std::exception& error) + { + std::ostringstream stream; + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); + + stream << id.toString() << "|Critical compile error: " << error.what(); + + messages.push_back (stream.str()); + } + + mMessages = 0; +} \ No newline at end of file diff --git a/apps/opencs/model/tools/scriptcheck.hpp b/apps/opencs/model/tools/scriptcheck.hpp new file mode 100644 index 000000000..325280d7f --- /dev/null +++ b/apps/opencs/model/tools/scriptcheck.hpp @@ -0,0 +1,40 @@ +#ifndef CSM_TOOLS_SCRIPTCHECK_H +#define CSM_TOOLS_SCRIPTCHECK_H + +#include +#include + +#include "../doc/stage.hpp" + +#include "../world/scriptcontext.hpp" + +namespace CSMTools +{ + /// \brief VerifyStage: make sure that scripts compile + class ScriptCheckStage : public CSMDoc::Stage, private Compiler::ErrorHandler + { + const CSMWorld::Data& mData; + Compiler::Extensions mExtensions; + CSMWorld::ScriptContext mContext; + std::string mId; + std::vector *mMessages; + + virtual void report (const std::string& message, const Compiler::TokenLoc& loc, Type type); + ///< Report error to the user. + + virtual void report (const std::string& message, Type type); + ///< Report a file related error + + public: + + ScriptCheckStage (const CSMWorld::Data& data); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this tage will be appended to \a messages. + }; +} + +#endif diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 64e39ad2f..79a09dcb4 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -20,6 +20,7 @@ #include "birthsigncheck.hpp" #include "spellcheck.hpp" #include "referenceablecheck.hpp" +#include "scriptcheck.hpp" CSMDoc::Operation *CSMTools::Tools::get (int type) { @@ -77,6 +78,8 @@ CSMDoc::Operation *CSMTools::Tools::getVerifier() mVerifier->appendStage (new SpellCheckStage (mData.getSpells())); mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions())); + + mVerifier->appendStage (new ScriptCheckStage (mData)); } return mVerifier; diff --git a/apps/opencs/model/world/scriptcontext.cpp b/apps/opencs/model/world/scriptcontext.cpp index 688c33d30..8a4670ed2 100644 --- a/apps/opencs/model/world/scriptcontext.cpp +++ b/apps/opencs/model/world/scriptcontext.cpp @@ -15,7 +15,7 @@ CSMWorld::ScriptContext::ScriptContext (const Data& data) : mData (data), mIdsUp bool CSMWorld::ScriptContext::canDeclareLocals() const { - return false; + return true; } char CSMWorld::ScriptContext::getGlobalType (const std::string& name) const @@ -115,4 +115,11 @@ bool CSMWorld::ScriptContext::isJournalId (const std::string& name) const void CSMWorld::ScriptContext::invalidateIds() { mIdsUpdated = false; +} + +void CSMWorld::ScriptContext::clear() +{ + mIds.clear(); + mIdsUpdated = false; + mLocals.clear(); } \ No newline at end of file diff --git a/apps/opencs/model/world/scriptcontext.hpp b/apps/opencs/model/world/scriptcontext.hpp index 400748e5f..29ee42645 100644 --- a/apps/opencs/model/world/scriptcontext.hpp +++ b/apps/opencs/model/world/scriptcontext.hpp @@ -43,6 +43,9 @@ namespace CSMWorld ///< Does \a name match a journal ID? void invalidateIds(); + + void clear(); + ///< Remove all cached data. }; } From aafde926d34008e0413c2c16178f4c144b8c7f73 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 14 Feb 2014 14:04:36 +0100 Subject: [PATCH 851/889] Documentation, corrections. --- apps/opencs/model/world/tablemimedata.hpp | 10 ++++++++-- apps/opencs/model/world/universalid.cpp | 1 - apps/opencs/view/world/table.cpp | 4 ++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/apps/opencs/model/world/tablemimedata.hpp b/apps/opencs/model/world/tablemimedata.hpp index 1b6c5e635..2829a0754 100644 --- a/apps/opencs/model/world/tablemimedata.hpp +++ b/apps/opencs/model/world/tablemimedata.hpp @@ -1,5 +1,3 @@ -/*This class provides way to construct mimedata object holding the reference to the -* universalid. universalid is used in the majority of the tables to store type, id, argument types*/ #ifndef TABLEMIMEDATA_H #define TABLEMIMEDATA_H @@ -15,6 +13,14 @@ namespace CSMWorld { + +/// \brief Subclass of QmimeData, augmented to contain and transport UniversalIds. +/// +/// This class provides way to construct mimedata object holding the universalid copy +/// Universalid is used in the majority of the tables to store type, id, argument types. +/// This way universalid grants a way to retrive record from the concrete table. +/// Please note, that tablemimedata object can hold multiple universalIds in the vector. + class TableMimeData : public QMimeData { public: diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 88674c4ce..8301ebfd3 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -187,7 +187,6 @@ CSMWorld::UniversalId::UniversalId (Type type, const std::string& id) mClass = sIdArg[i].mClass; return; } - std::cout< #include #include +#include #include "../../model/world/data.hpp" #include "../../model/world/commands.hpp" @@ -15,11 +16,10 @@ #include "../../model/world/record.hpp" #include "../../model/world/columns.hpp" #include "../../model/world/tablemimedata.hpp" +#include "../../model/world/tablemimedata.hpp" #include "recordstatusdelegate.hpp" #include "util.hpp" -#include -#include "../../model/world/tablemimedata.hpp" void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) { From 37d262b1477f6da0cb4e624bfd3fe1bebf62e211 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 14 Feb 2014 15:12:34 +0100 Subject: [PATCH 852/889] Splited very long line. --- apps/opencs/model/doc/document.hpp | 5 ++++- apps/opencs/model/world/tablemimedata.hpp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 437b0c513..201fb4342 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -71,7 +71,10 @@ namespace CSMDoc public: - Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_); + Document (const Files::ConfigurationManager& configuration, + const std::vector< boost::filesystem::path >& files, + const boost::filesystem::path& savePath, + const boost::filesystem::path& resDir, bool new_); ~Document(); diff --git a/apps/opencs/model/world/tablemimedata.hpp b/apps/opencs/model/world/tablemimedata.hpp index 2829a0754..51785714e 100644 --- a/apps/opencs/model/world/tablemimedata.hpp +++ b/apps/opencs/model/world/tablemimedata.hpp @@ -42,4 +42,4 @@ namespace CSMWorld CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type) const; }; } -#endif // TABLEMIMEDATA_H +#endif // TABLEMIMEDATA_H \ No newline at end of file From f3dc45f1ec735159be0b3e387b4dafde46b1db25 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 14 Feb 2014 15:29:10 +0100 Subject: [PATCH 853/889] long line split. --- apps/opencs/model/doc/documentmanager.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/doc/documentmanager.hpp b/apps/opencs/model/doc/documentmanager.hpp index b80a18642..b969862e9 100644 --- a/apps/opencs/model/doc/documentmanager.hpp +++ b/apps/opencs/model/doc/documentmanager.hpp @@ -29,7 +29,9 @@ namespace CSMDoc ~DocumentManager(); - Document *addDocument (const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, bool new_); + Document *addDocument (const std::vector< boost::filesystem::path >& files, + const boost::filesystem::path& savePath, + bool new_); ///< The ownership of the returned document is not transferred to the caller. /// /// \param new_ Do not load the last content file in \a files and instead create in an From d6820b977ead88e15b838d168e9324261f4f58ee Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 15 Feb 2014 12:22:32 +0100 Subject: [PATCH 854/889] store const ref to the document in the table. --- apps/opencs/view/world/table.cpp | 4 ++-- apps/opencs/view/world/table.hpp | 12 +++++++++++- apps/opencs/view/world/tablesubview.cpp | 2 +- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index c077d5f7f..419935991 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -162,8 +162,8 @@ std::vector CSVWorld::Table::listDeletableSelectedIds() const } CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, - bool createAndDelete, bool sorting) - : mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0) + bool createAndDelete, bool sorting, const CSMDoc::Document& document) + : mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0), mDocument(document) { mModel = &dynamic_cast (*data.getTableModel (id)); diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 6102a133a..ff08db06f 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -9,6 +9,10 @@ #include "../../model/filter/node.hpp" +namespace CSMDoc { + class Document; +} + class QUndoStack; class QAction; @@ -43,6 +47,10 @@ namespace CSVWorld bool mEditLock; int mRecordStatusDisplay; + /// \brief This variable is used exclusivly for checking if dropEvents came from the same document. Most likely you + /// should NOT use it for anything else. + const CSMDoc::Document& mDocument; + private: void contextMenuEvent (QContextMenuEvent *event); @@ -62,7 +70,9 @@ namespace CSVWorld public: - Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, bool sorting); + Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, + bool sorting, const CSMDoc::Document& document); + ///< \param createAndDelete Allow creation and deletion of records. /// \param sorting Allow changing order of rows in the view via column headers. diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 981faaf59..d379db51a 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -22,7 +22,7 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0); layout->insertWidget (0, mTable = - new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete(), sorting), 2); + new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete(), sorting, document), 2); CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); From b1f63947e8a74d0fdce58b511cb588401dd0c4b4 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 15 Feb 2014 12:40:07 +0100 Subject: [PATCH 855/889] Checking if the drop comes from same document. --- apps/opencs/model/world/tablemimedata.cpp | 12 +++++++--- apps/opencs/model/world/tablemimedata.hpp | 20 ++++++++++++++-- apps/opencs/view/world/table.cpp | 29 +++++++++++++---------- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp index b0cf0abcc..ee37dfce6 100644 --- a/apps/opencs/model/world/tablemimedata.cpp +++ b/apps/opencs/model/world/tablemimedata.cpp @@ -4,14 +4,15 @@ #include "universalid.hpp" #include "columnbase.hpp" -CSMWorld::TableMimeData::TableMimeData (UniversalId id) +CSMWorld::TableMimeData::TableMimeData (UniversalId id, const CSMDoc::Document& document) : +mDocument(document) { mUniversalId.push_back (id); mObjectsFormats << QString::fromStdString ("tabledata/" + id.getTypeName()); } -CSMWorld::TableMimeData::TableMimeData (std::vector< CSMWorld::UniversalId >& id) : - mUniversalId (id) +CSMWorld::TableMimeData::TableMimeData (std::vector< CSMWorld::UniversalId >& id, const CSMDoc::Document& document) : + mUniversalId (id), mDocument(document) { for (std::vector::iterator it (mUniversalId.begin()); it != mUniversalId.end(); ++it) { @@ -115,6 +116,11 @@ CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnB throw ("TableMimeData object does not hold object of the seeked type"); } +bool CSMWorld::TableMimeData::fromDocument (const CSMDoc::Document& document) const +{ + return &document == &mDocument; +} + CSMWorld::UniversalId::Type CSMWorld::TableMimeData::convertEnums (CSMWorld::ColumnBase::Display type) const { switch (type) diff --git a/apps/opencs/model/world/tablemimedata.hpp b/apps/opencs/model/world/tablemimedata.hpp index 51785714e..7c05386fb 100644 --- a/apps/opencs/model/world/tablemimedata.hpp +++ b/apps/opencs/model/world/tablemimedata.hpp @@ -10,6 +10,10 @@ #include "universalid.hpp" #include "columnbase.hpp" +namespace CSMDoc +{ + class Document; +} namespace CSMWorld { @@ -24,21 +28,33 @@ namespace CSMWorld class TableMimeData : public QMimeData { public: - TableMimeData(UniversalId id); - TableMimeData(std::vector& id); + TableMimeData(UniversalId id, const CSMDoc::Document& document); + + TableMimeData(std::vector& id, const CSMDoc::Document& document); + ~TableMimeData(); + virtual QStringList formats() const; + std::string getIcon() const; + std::vector getData() const; + bool holdsType(UniversalId::Type type) const; + bool holdsType(CSMWorld::ColumnBase::Display type) const; + + bool fromDocument(const CSMDoc::Document& document) const; + UniversalId returnMatching(UniversalId::Type type) const; + UniversalId returnMatching(CSMWorld::ColumnBase::Display type) const; private: std::vector mUniversalId; QStringList mObjectsFormats; + const CSMDoc::Document& mDocument; CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type) const; }; } diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 419935991..79700458d 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -455,7 +455,7 @@ void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) if (selectedRows.size() == 1) { - mime = new CSMWorld::TableMimeData (getUniversalId (selectedRows.begin()->row())); + mime = new CSMWorld::TableMimeData (getUniversalId (selectedRows.begin()->row()), mDocument); } else { @@ -466,7 +466,7 @@ void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) idToDrag.push_back (getUniversalId (it.row())); } - mime = new CSMWorld::TableMimeData (idToDrag); + mime = new CSMWorld::TableMimeData (idToDrag, mDocument); } drag->setMimeData (mime); @@ -485,21 +485,24 @@ void CSVWorld::Table::dropEvent(QDropEvent *event) { QModelIndex index = indexAt (event->pos()); - CSMWorld::ColumnBase::Display display = static_cast - (mModel->headerData(index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - - if (dynamic_cast(event->mimeData())->holdsType(display)) + if (dynamic_cast (event->mimeData())->fromDocument (mDocument)) { - const CSMWorld::TableMimeData* mime = dynamic_cast - (event->mimeData()); + CSMWorld::ColumnBase::Display display = static_cast + (mModel->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - CSMWorld::UniversalId record (mime->returnMatching (display)); + if (dynamic_cast (event->mimeData())->holdsType (display)) + { + const CSMWorld::TableMimeData* mime = dynamic_cast + (event->mimeData()); - std::auto_ptr command (new CSMWorld::ModifyCommand - (*mProxyModel, index, QVariant (QString::fromStdString (record.getId())))); + CSMWorld::UniversalId record (mime->returnMatching (display)); - mUndoStack.push (command.release()); - } + std::auto_ptr command (new CSMWorld::ModifyCommand + (*mProxyModel, index, QVariant (QString::fromStdString (record.getId())))); + + mUndoStack.push (command.release()); + } + } //TODO handle drops from different document } void CSVWorld::Table::dragMoveEvent(QDragMoveEvent *event) From fd665a19941d072b6bdc3a44eab9aa26b74e1f40 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 15 Feb 2014 12:45:50 +0100 Subject: [PATCH 856/889] ignore conditions after an else (only works if condition is put in parentheses) --- components/compiler/controlparser.cpp | 15 ++++++++++++--- components/compiler/controlparser.hpp | 3 ++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/components/compiler/controlparser.cpp b/components/compiler/controlparser.cpp index 58542bd73..aefe6d16d 100644 --- a/components/compiler/controlparser.cpp +++ b/components/compiler/controlparser.cpp @@ -8,6 +8,7 @@ #include "scanner.hpp" #include "generator.hpp" #include "errorhandler.hpp" +#include "skipparser.hpp" namespace Compiler { @@ -71,7 +72,7 @@ namespace Compiler } else if (keyword==Scanner::K_else) { - mState = IfElseBodyState; /// \todo should be IfElseEndState; add an option for that + mState = IfElseJunkState; /// \todo should be IfElseEndState; add an option for that } return true; @@ -207,7 +208,8 @@ namespace Compiler return true; } } - else if (mState==IfBodyState || mState==IfElseifBodyState || mState==IfElseBodyState) + else if (mState==IfBodyState || mState==IfElseifBodyState || mState==IfElseBodyState || + mState==IfElseJunkState) { if (parseIfBody (keyword, loc, scanner)) return true; @@ -230,6 +232,7 @@ namespace Compiler case IfEndState: mState = IfBodyState; return true; case IfElseifEndState: mState = IfElseifBodyState; return true; case IfElseEndState: mState = IfElseBodyState; return true; + case IfElseJunkState: mState = IfElseBodyState; return true; case WhileEndState: mState = WhileBodyState; return true; @@ -247,7 +250,13 @@ namespace Compiler default: ; } - + } + else if (code==Scanner::S_open && mState==IfElseJunkState) + { + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + mState = IfElseBodyState; + return true; } return Parser::parseSpecial (code, loc, scanner); diff --git a/components/compiler/controlparser.hpp b/components/compiler/controlparser.hpp index 24bc7bec7..1175a0ed5 100644 --- a/components/compiler/controlparser.hpp +++ b/components/compiler/controlparser.hpp @@ -26,7 +26,8 @@ namespace Compiler IfElseEndState, IfElseBodyState, IfEndifState, WhileEndState, WhileBodyState, - WhileEndwhileState + WhileEndwhileState, + IfElseJunkState }; typedef std::vector Codes; From 0d84adb2c601345bb9a782624a9caaa88d0fedbb Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 15 Feb 2014 12:50:40 +0100 Subject: [PATCH 857/889] allow x->(y) instead of (x->y) --- components/compiler/exprparser.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 6d26ef403..0c013b18f 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -570,6 +570,14 @@ namespace Compiler { if (!mExplicit.empty()) { + if (mRefOp && code==Scanner::S_open) + { + /// \todo add option to disable this workaround + mOperators.push_back ('('); + mTokenLoc = loc; + return true; + } + if (!mRefOp && code==Scanner::S_ref) { mRefOp = true; From 2130ec39d698ad92994b216185cf9fbc238ef944 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 15 Feb 2014 12:58:34 +0100 Subject: [PATCH 858/889] disable warnings by default in script verifier --- apps/opencs/model/tools/scriptcheck.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/opencs/model/tools/scriptcheck.cpp b/apps/opencs/model/tools/scriptcheck.cpp index 8ed41ec02..2ed8fe657 100644 --- a/apps/opencs/model/tools/scriptcheck.cpp +++ b/apps/opencs/model/tools/scriptcheck.cpp @@ -51,6 +51,9 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, Type type) CSMTools::ScriptCheckStage::ScriptCheckStage (const CSMWorld::Data& data) : mData (data), mContext (data), mMessages (0) { + /// \todo add an option to configure warning mode + setWarningsMode (0); + Compiler::registerExtensions (mExtensions); mContext.setExtensions (&mExtensions); } From 4e041319578a310e2d6be5be4c118d0e83155b27 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 15 Feb 2014 13:09:43 +0100 Subject: [PATCH 859/889] fixing case broke sorting in script context --- apps/opencs/model/world/scriptcontext.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/opencs/model/world/scriptcontext.cpp b/apps/opencs/model/world/scriptcontext.cpp index 8a4670ed2..9da49defe 100644 --- a/apps/opencs/model/world/scriptcontext.cpp +++ b/apps/opencs/model/world/scriptcontext.cpp @@ -100,6 +100,7 @@ bool CSMWorld::ScriptContext::isId (const std::string& name) const mIds = mData.getIds(); std::for_each (mIds.begin(), mIds.end(), &Misc::StringUtils::lowerCase); + std::sort (mIds.begin(), mIds.end()); mIdsUpdated = true; } From 4ee90c24e58778ab73262af8b099c0ced4f5de83 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 15 Feb 2014 13:22:14 +0100 Subject: [PATCH 860/889] more readable code. --- apps/opencs/view/world/table.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 79700458d..73b1fd226 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -485,16 +485,14 @@ void CSVWorld::Table::dropEvent(QDropEvent *event) { QModelIndex index = indexAt (event->pos()); - if (dynamic_cast (event->mimeData())->fromDocument (mDocument)) + const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + if (mime->fromDocument (mDocument)) { CSMWorld::ColumnBase::Display display = static_cast (mModel->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - if (dynamic_cast (event->mimeData())->holdsType (display)) + if (mime->holdsType (display)) { - const CSMWorld::TableMimeData* mime = dynamic_cast - (event->mimeData()); - CSMWorld::UniversalId record (mime->returnMatching (display)); std::auto_ptr command (new CSMWorld::ModifyCommand From 56be62b95644789d24568618098bf451aec51667 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 15 Feb 2014 13:23:51 +0100 Subject: [PATCH 861/889] small reformatting --- apps/opencs/model/world/tablemimedata.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/world/tablemimedata.hpp b/apps/opencs/model/world/tablemimedata.hpp index 7c05386fb..010a18acb 100644 --- a/apps/opencs/model/world/tablemimedata.hpp +++ b/apps/opencs/model/world/tablemimedata.hpp @@ -53,8 +53,8 @@ namespace CSMWorld private: std::vector mUniversalId; QStringList mObjectsFormats; - const CSMDoc::Document& mDocument; + CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type) const; }; } From 2007a3e9027c640f1dc6d5ad4259ed632f04d1b8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 15 Feb 2014 13:25:38 +0100 Subject: [PATCH 862/889] improved script check error messages --- apps/opencs/model/tools/scriptcheck.cpp | 4 +++- apps/opencs/model/tools/scriptcheck.hpp | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/scriptcheck.cpp b/apps/opencs/model/tools/scriptcheck.cpp index 2ed8fe657..a5154d292 100644 --- a/apps/opencs/model/tools/scriptcheck.cpp +++ b/apps/opencs/model/tools/scriptcheck.cpp @@ -24,7 +24,8 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, const Compi stream << "warning "; stream - << "line " << loc.mLine << ", column " << loc.mColumn + << "script " << mFile + << ", line " << loc.mLine << ", column " << loc.mColumn << " (" << loc.mLiteral << "): " << message; mMessages->push_back (stream.str()); @@ -74,6 +75,7 @@ void CSMTools::ScriptCheckStage::perform (int stage, std::vector& m try { + mFile = mData.getScripts().getRecord (stage).get().mId; std::istringstream input (mData.getScripts().getRecord (stage).get().mScriptText); Compiler::Scanner scanner (*this, input, mContext.getExtensions()); diff --git a/apps/opencs/model/tools/scriptcheck.hpp b/apps/opencs/model/tools/scriptcheck.hpp index 325280d7f..8de8e1a66 100644 --- a/apps/opencs/model/tools/scriptcheck.hpp +++ b/apps/opencs/model/tools/scriptcheck.hpp @@ -17,6 +17,7 @@ namespace CSMTools Compiler::Extensions mExtensions; CSMWorld::ScriptContext mContext; std::string mId; + std::string mFile; std::vector *mMessages; virtual void report (const std::string& message, const Compiler::TokenLoc& loc, Type type); From 0afa61eed56264fea4af81fcfe78502700e2711c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 15 Feb 2014 15:50:17 +0100 Subject: [PATCH 863/889] fixed referenceable-loading in case of more than one content file --- apps/opencs/model/world/refidcollection.cpp | 8 +++---- apps/opencs/model/world/refiddata.cpp | 4 ++-- apps/opencs/model/world/refiddata.hpp | 23 ++++++++++++--------- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 89a917139..f515e34d8 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -432,7 +432,7 @@ void CSMWorld::RefIdCollection::removeRows (int index, int count) void CSMWorld::RefIdCollection::appendBlankRecord (const std::string& id, UniversalId::Type type) { - mData.appendRecord (type, id); + mData.appendRecord (type, id, false); } int CSMWorld::RefIdCollection::searchId (const std::string& id) const @@ -450,7 +450,7 @@ void CSMWorld::RefIdCollection::replace (int index, const RecordBase& record) mData.getRecord (mData.globalToLocalIndex (index)).assign (record); } -void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin, +void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin, const std::string& destination, const CSMWorld::UniversalId::Type type) { @@ -467,7 +467,7 @@ void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, int index = mData.getAppendIndex (type); - mData.appendRecord (type, id); + mData.appendRecord (type, id, false); mData.getRecord (mData.globalToLocalIndex (index)).assign (record); } @@ -515,7 +515,7 @@ void CSMWorld::RefIdCollection::load (ESM::ESMReader& reader, bool base, Univers { // new record int index = mData.getAppendIndex (type); - mData.appendRecord (type, id); + mData.appendRecord (type, id, base); RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 9d980a99c..f67ab2152 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -131,7 +131,7 @@ CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord (const LocalIndex& index) return iter->second->getRecord (index.first); } -void CSMWorld::RefIdData::appendRecord (UniversalId::Type type, const std::string& id) +void CSMWorld::RefIdData::appendRecord (UniversalId::Type type, const std::string& id, bool base) { std::map::iterator iter = mRecordContainers.find (type); @@ -139,7 +139,7 @@ void CSMWorld::RefIdData::appendRecord (UniversalId::Type type, const std::strin if (iter==mRecordContainers.end()) throw std::logic_error ("invalid local index type"); - iter->second->appendRecord (id); + iter->second->appendRecord (id, base); mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), LocalIndex (iter->second->getSize()-1, type))); diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index cf98fee21..1b600364c 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -45,8 +45,8 @@ namespace CSMWorld virtual RecordBase& getRecord (int index)= 0; - virtual void appendRecord (const std::string& id) = 0; - + virtual void appendRecord (const std::string& id, bool base) = 0; + virtual void insertRecord (RecordBase& record) = 0; virtual void load (int index, ESM::ESMReader& reader, bool base) = 0; @@ -69,8 +69,8 @@ namespace CSMWorld virtual RecordBase& getRecord (int index); - virtual void appendRecord (const std::string& id); - + virtual void appendRecord (const std::string& id, bool base); + virtual void insertRecord (RecordBase& record); virtual void load (int index, ESM::ESMReader& reader, bool base); @@ -88,7 +88,7 @@ namespace CSMWorld Record& newRecord = dynamic_cast& >(record); mContainer.push_back(newRecord); } - + template int RefIdDataContainer::getSize() const { @@ -108,12 +108,15 @@ namespace CSMWorld } template - void RefIdDataContainer::appendRecord (const std::string& id) + void RefIdDataContainer::appendRecord (const std::string& id, bool base) { Record record; + + record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; + + record.mBase.mId = id; record.mModified.mId = id; - record.mModified.blank(); - record.mState = RecordBase::State_ModifiedOnly; + (base ? record.mBase : record.mModified).blank(); mContainer.push_back (record); } @@ -206,14 +209,14 @@ namespace CSMWorld LocalIndex searchId (const std::string& id) const; void erase (int index, int count); - + void insertRecord(CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id); const RecordBase& getRecord (const LocalIndex& index) const; RecordBase& getRecord (const LocalIndex& index); - void appendRecord (UniversalId::Type type, const std::string& id); + void appendRecord (UniversalId::Type type, const std::string& id, bool base); int getAppendIndex (UniversalId::Type type) const; From 3d8ca02479d42d9bd856d31254512dfb70f2428c Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Sat, 15 Feb 2014 17:39:11 +0100 Subject: [PATCH 864/889] Fix breaking of enchanted items casting --- apps/openmw/mwinput/inputmanagerimp.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 7ed3007ff..66d93469c 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -17,6 +17,7 @@ #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" @@ -647,7 +648,9 @@ namespace MWInput return; // Not allowed if no spell selected - if (MWBase::Environment::get().getWindowManager()->getSelectedSpell().empty()) + MWWorld::InventoryStore& inventory = MWWorld::Class::get(mPlayer->getPlayer()).getInventoryStore(mPlayer->getPlayer()); + if (MWBase::Environment::get().getWindowManager()->getSelectedSpell().empty() && + inventory.getSelectedEnchantItem() == inventory.end()) return; MWMechanics::DrawState_ state = mPlayer->getDrawState(); From 97fe5465bf4bf6ec2c72a622eb5c226f1b7a547d Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 15 Feb 2014 17:55:18 +0100 Subject: [PATCH 865/889] created new files --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/world/scriptedit.cpp | 39 ++++++++++++++++++++++++++ apps/opencs/view/world/scriptedit.hpp | 40 +++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 apps/opencs/view/world/scriptedit.cpp create mode 100644 apps/opencs/view/world/scriptedit.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 318cc4912..cd89240c2 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -60,7 +60,7 @@ opencs_hdrs_noqt (view/doc opencs_units (view/world table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator cellcreator referenceablecreator referencecreator scenesubview scenetoolbar scenetool - scenetoolmode infocreator + scenetoolmode infocreator scriptedit ) opencs_units (view/render diff --git a/apps/opencs/view/world/scriptedit.cpp b/apps/opencs/view/world/scriptedit.cpp new file mode 100644 index 000000000..3a71d8f0c --- /dev/null +++ b/apps/opencs/view/world/scriptedit.cpp @@ -0,0 +1,39 @@ +/* + * + * Copyright 2014 Marek Kochanowicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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 . + * + */ + +#include "scriptedit.hpp" + +void CSVWorld::ScriptEdit::dragEnterEvent (QDragEnterEvent* event) +{ + event->acceptProposedAction(); +} + +void CSVWorld::ScriptEdit::dragMoveEvent (QDragMoveEvent* event) +{ + event->accept(); +} + +void CSVWorld::ScriptEdit::dropEvent (QDropEvent* event) +{ + +} +// kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/apps/opencs/view/world/scriptedit.hpp b/apps/opencs/view/world/scriptedit.hpp new file mode 100644 index 000000000..74e15ecbf --- /dev/null +++ b/apps/opencs/view/world/scriptedit.hpp @@ -0,0 +1,40 @@ +/* + * + * Copyright 2014 Marek Kochanowicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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 . + * + */ + +#ifndef SCRIPTEDIT_H +#define SCRIPTEDIT_H + +#include + +namespace CSVWorld +{ + class ScriptEdit : public QTextEdit + { + void dragEnterEvent (QDragEnterEvent* event); + + void dropEvent (QDropEvent* event); + + void dragMoveEvent (QDragMoveEvent* event); + }; +} +#endif // SCRIPTEDIT_H +// kate: indent-mode cstyle; indent-width 4; replace-tabs on; \ No newline at end of file From 930b77e60a1d7afb00a6a5766e009d0e49d28e56 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 15 Feb 2014 19:52:40 +0100 Subject: [PATCH 866/889] allow drag and drop into the script edit window. --- apps/opencs/view/world/scriptedit.cpp | 42 +++++++++++------------- apps/opencs/view/world/scriptedit.hpp | 33 +++++-------------- apps/opencs/view/world/scriptsubview.cpp | 3 +- 3 files changed, 31 insertions(+), 47 deletions(-) diff --git a/apps/opencs/view/world/scriptedit.cpp b/apps/opencs/view/world/scriptedit.cpp index 3a71d8f0c..4eb652add 100644 --- a/apps/opencs/view/world/scriptedit.cpp +++ b/apps/opencs/view/world/scriptedit.cpp @@ -1,27 +1,18 @@ -/* - * - * Copyright 2014 Marek Kochanowicz - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License or (at your option) version 3 or any later version - * accepted by the membership of KDE e.V. (or its successor approved - * by the membership of KDE e.V.), which shall act as a proxy - * defined in Section 14 of version 3 of the license. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * 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 . - * - */ - #include "scriptedit.hpp" +#include + +#include + +#include "../../model/world/universalid.hpp" +#include "../../model/world/tablemimedata.hpp" + +CSVWorld::ScriptEdit::ScriptEdit (QWidget* parent) : +QTextEdit(parent) +{ + +} + void CSVWorld::ScriptEdit::dragEnterEvent (QDragEnterEvent* event) { event->acceptProposedAction(); @@ -34,6 +25,13 @@ void CSVWorld::ScriptEdit::dragMoveEvent (QDragMoveEvent* event) void CSVWorld::ScriptEdit::dropEvent (QDropEvent* event) { + const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + std::vector records (mime->getData()); + + for (std::vector::iterator it = records.begin(); it != records.end(); ++it) + { + insertPlainText (QString::fromStdString (it->getId())); + } } // kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/apps/opencs/view/world/scriptedit.hpp b/apps/opencs/view/world/scriptedit.hpp index 74e15ecbf..6df4acc5f 100644 --- a/apps/opencs/view/world/scriptedit.hpp +++ b/apps/opencs/view/world/scriptedit.hpp @@ -1,34 +1,19 @@ -/* - * - * Copyright 2014 Marek Kochanowicz - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License or (at your option) version 3 or any later version - * accepted by the membership of KDE e.V. (or its successor approved - * by the membership of KDE e.V.), which shall act as a proxy - * defined in Section 14 of version 3 of the license. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * 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 . - * - */ - #ifndef SCRIPTEDIT_H #define SCRIPTEDIT_H -#include +#include + +class QWidget; namespace CSVWorld { class ScriptEdit : public QTextEdit { + Q_OBJECT + public: + ScriptEdit (QWidget* parent); + + private: void dragEnterEvent (QDragEnterEvent* event); void dropEvent (QDropEvent* event); @@ -37,4 +22,4 @@ namespace CSVWorld }; } #endif // SCRIPTEDIT_H -// kate: indent-mode cstyle; indent-width 4; replace-tabs on; \ No newline at end of file +// kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/apps/opencs/view/world/scriptsubview.cpp b/apps/opencs/view/world/scriptsubview.cpp index 446c34e5f..4fe5aafac 100644 --- a/apps/opencs/view/world/scriptsubview.cpp +++ b/apps/opencs/view/world/scriptsubview.cpp @@ -13,6 +13,7 @@ #include "../../model/world/idtable.hpp" #include "scripthighlighter.hpp" +#include "scriptedit.hpp" CSVWorld::ScriptSubView::ChangeLock::ChangeLock (ScriptSubView& view) : mView (view) { @@ -27,7 +28,7 @@ CSVWorld::ScriptSubView::ChangeLock::~ChangeLock() CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : SubView (id), mDocument (document), mColumn (-1), mChangeLocked (0) { - setWidget (mEditor = new QTextEdit (this)); + setWidget (mEditor = new ScriptEdit (this)); mEditor->setAcceptRichText (false); mEditor->setLineWrapMode (QTextEdit::NoWrap); From 41606a67e4bad4184841ac2f492f309fa2f233a5 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 15 Feb 2014 20:49:09 +0100 Subject: [PATCH 867/889] implemented whitelist of allowed types in the scripts --- apps/opencs/view/world/scriptedit.cpp | 41 +++++++++++++++++++++++---- apps/opencs/view/world/scriptedit.hpp | 5 ++++ 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/apps/opencs/view/world/scriptedit.cpp b/apps/opencs/view/world/scriptedit.cpp index 4eb652add..20b76ad72 100644 --- a/apps/opencs/view/world/scriptedit.cpp +++ b/apps/opencs/view/world/scriptedit.cpp @@ -1,25 +1,51 @@ #include "scriptedit.hpp" -#include - #include #include "../../model/world/universalid.hpp" #include "../../model/world/tablemimedata.hpp" CSVWorld::ScriptEdit::ScriptEdit (QWidget* parent) : -QTextEdit(parent) + QTextEdit (parent) { - + mAllowedTypes <pos())); event->acceptProposedAction(); } void CSVWorld::ScriptEdit::dragMoveEvent (QDragMoveEvent* event) { + setTextCursor (cursorForPosition (event->pos())); event->accept(); } @@ -27,11 +53,16 @@ void CSVWorld::ScriptEdit::dropEvent (QDropEvent* event) { const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + setTextCursor (cursorForPosition (event->pos())); + std::vector records (mime->getData()); for (std::vector::iterator it = records.begin(); it != records.end(); ++it) { - insertPlainText (QString::fromStdString (it->getId())); + if (mAllowedTypes.contains (it->getType())) + { + insertPlainText (QString::fromStdString (it->getId() + " ")); + } } } // kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/apps/opencs/view/world/scriptedit.hpp b/apps/opencs/view/world/scriptedit.hpp index 6df4acc5f..5355b57ce 100644 --- a/apps/opencs/view/world/scriptedit.hpp +++ b/apps/opencs/view/world/scriptedit.hpp @@ -2,6 +2,9 @@ #define SCRIPTEDIT_H #include +#include + +#include "../../model/world/universalid.hpp" class QWidget; @@ -14,6 +17,8 @@ namespace CSVWorld ScriptEdit (QWidget* parent); private: + QVector mAllowedTypes; + void dragEnterEvent (QDragEnterEvent* event); void dropEvent (QDropEvent* event); From ec8c8a9d88292eee2c34ed50d680b7632b750f30 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 15 Feb 2014 22:05:42 +0100 Subject: [PATCH 868/889] -journalinfo +topic --- apps/opencs/view/world/scriptedit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/world/scriptedit.cpp b/apps/opencs/view/world/scriptedit.cpp index 20b76ad72..c3e38052c 100644 --- a/apps/opencs/view/world/scriptedit.cpp +++ b/apps/opencs/view/world/scriptedit.cpp @@ -9,8 +9,8 @@ CSVWorld::ScriptEdit::ScriptEdit (QWidget* parent) : QTextEdit (parent) { mAllowedTypes < Date: Sat, 15 Feb 2014 22:14:27 +0100 Subject: [PATCH 869/889] Put the id in the quote. --- apps/opencs/view/world/scriptedit.cpp | 5 ++--- apps/opencs/view/world/scriptedit.hpp | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/opencs/view/world/scriptedit.cpp b/apps/opencs/view/world/scriptedit.cpp index c3e38052c..88e990c10 100644 --- a/apps/opencs/view/world/scriptedit.cpp +++ b/apps/opencs/view/world/scriptedit.cpp @@ -61,8 +61,7 @@ void CSVWorld::ScriptEdit::dropEvent (QDropEvent* event) { if (mAllowedTypes.contains (it->getType())) { - insertPlainText (QString::fromStdString (it->getId() + " ")); + QString::fromStdString ('"' + it->getId() + '"')); } } -} -// kate: indent-mode cstyle; indent-width 4; replace-tabs on; +} \ No newline at end of file diff --git a/apps/opencs/view/world/scriptedit.hpp b/apps/opencs/view/world/scriptedit.hpp index 5355b57ce..a110f58c8 100644 --- a/apps/opencs/view/world/scriptedit.hpp +++ b/apps/opencs/view/world/scriptedit.hpp @@ -26,5 +26,4 @@ namespace CSVWorld void dragMoveEvent (QDragMoveEvent* event); }; } -#endif // SCRIPTEDIT_H -// kate: indent-mode cstyle; indent-width 4; replace-tabs on; +#endif // SCRIPTEDIT_H \ No newline at end of file From cbe1fe2f9581b1a458b59360c7902b0d6ebd923c Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 15 Feb 2014 22:38:59 +0100 Subject: [PATCH 870/889] removed misplaced bracket --- apps/opencs/view/world/scriptedit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/world/scriptedit.cpp b/apps/opencs/view/world/scriptedit.cpp index 88e990c10..f7f82c978 100644 --- a/apps/opencs/view/world/scriptedit.cpp +++ b/apps/opencs/view/world/scriptedit.cpp @@ -61,7 +61,7 @@ void CSVWorld::ScriptEdit::dropEvent (QDropEvent* event) { if (mAllowedTypes.contains (it->getType())) { - QString::fromStdString ('"' + it->getId() + '"')); + QString::fromStdString ('"' + it->getId() + '"'); } } } \ No newline at end of file From 17af865a9fcbb6ebf88f39e6accb4a8fa4840833 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 16 Feb 2014 09:51:33 +0100 Subject: [PATCH 871/889] checking for the source of the drop, reject those from other files for now --- apps/opencs/view/world/scriptedit.cpp | 18 +++++++++++------- apps/opencs/view/world/scriptedit.hpp | 8 +++++++- apps/opencs/view/world/scriptsubview.cpp | 2 +- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/apps/opencs/view/world/scriptedit.cpp b/apps/opencs/view/world/scriptedit.cpp index f7f82c978..ecfaa3ba8 100644 --- a/apps/opencs/view/world/scriptedit.cpp +++ b/apps/opencs/view/world/scriptedit.cpp @@ -5,8 +5,9 @@ #include "../../model/world/universalid.hpp" #include "../../model/world/tablemimedata.hpp" -CSVWorld::ScriptEdit::ScriptEdit (QWidget* parent) : - QTextEdit (parent) +CSVWorld::ScriptEdit::ScriptEdit (QWidget* parent, const CSMDoc::Document& document) : + QTextEdit (parent), + mDocument (document) { mAllowedTypes <pos())); - std::vector records (mime->getData()); - - for (std::vector::iterator it = records.begin(); it != records.end(); ++it) + if (mime->fromDocument (mDocument)) { - if (mAllowedTypes.contains (it->getType())) + std::vector records (mime->getData()); + + for (std::vector::iterator it = records.begin(); it != records.end(); ++it) { - QString::fromStdString ('"' + it->getId() + '"'); + if (mAllowedTypes.contains (it->getType())) + { + QString::fromStdString ('"' + it->getId() + '"'); + } } } } \ No newline at end of file diff --git a/apps/opencs/view/world/scriptedit.hpp b/apps/opencs/view/world/scriptedit.hpp index a110f58c8..dc97382b3 100644 --- a/apps/opencs/view/world/scriptedit.hpp +++ b/apps/opencs/view/world/scriptedit.hpp @@ -8,16 +8,22 @@ class QWidget; +namespace CSMDoc +{ + class Document; +} + namespace CSVWorld { class ScriptEdit : public QTextEdit { Q_OBJECT public: - ScriptEdit (QWidget* parent); + ScriptEdit (QWidget* parent, const CSMDoc::Document& document); private: QVector mAllowedTypes; + const CSMDoc::Document& mDocument; void dragEnterEvent (QDragEnterEvent* event); diff --git a/apps/opencs/view/world/scriptsubview.cpp b/apps/opencs/view/world/scriptsubview.cpp index 4fe5aafac..fa41151ca 100644 --- a/apps/opencs/view/world/scriptsubview.cpp +++ b/apps/opencs/view/world/scriptsubview.cpp @@ -28,7 +28,7 @@ CSVWorld::ScriptSubView::ChangeLock::~ChangeLock() CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : SubView (id), mDocument (document), mColumn (-1), mChangeLocked (0) { - setWidget (mEditor = new ScriptEdit (this)); + setWidget (mEditor = new ScriptEdit (this, mDocument)); mEditor->setAcceptRichText (false); mEditor->setLineWrapMode (QTextEdit::NoWrap); From 3c281c6a62e0fd000245956b89737ae1997472b3 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sun, 16 Feb 2014 14:12:45 +0400 Subject: [PATCH 872/889] OS X: specify path to framework binary, not to framework directory --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f9d532910..e27910958 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -680,7 +680,8 @@ if (APPLE) foreach (PLUGIN ${ABSOLUTE_PLUGINS}) get_filename_component(PLUGIN_RELATIVE ${PLUGIN} NAME) - set(PLUGINS ${PLUGINS} "${PLUGIN_INSTALL_BASE}/${PLUGIN_RELATIVE}") + get_filename_component(PLUGIN_RELATIVE_WE ${PLUGIN} NAME_WE) + set(PLUGINS ${PLUGINS} "${PLUGIN_INSTALL_BASE}/${PLUGIN_RELATIVE}/${PLUGIN_RELATIVE_WE}") endforeach () set(${plugins_var} ${PLUGINS} PARENT_SCOPE) From 8f99da084b11036793dd4154802792fd98f29fc2 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sun, 16 Feb 2014 14:12:59 +0400 Subject: [PATCH 873/889] OS X: plugins are now in Frameworks dir, not in Plugins --- components/ogreinit/ogreinit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ogreinit/ogreinit.cpp b/components/ogreinit/ogreinit.cpp index 46424a29a..840cf4bb0 100644 --- a/components/ogreinit/ogreinit.cpp +++ b/components/ogreinit/ogreinit.cpp @@ -133,7 +133,7 @@ namespace OgreInit pluginDir = OGRE_PLUGIN_DIR; // if path is not specified try to find plugins inside the app bundle if (pluginDir.empty()) - pluginDir = Ogre::macPluginPath(); + pluginDir = Ogre::macFrameworksPath(); #endif #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX pluginDir = OGRE_PLUGIN_DIR_REL; From e4017b8bfe706f881b5aa9c41edea2225491b880 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sun, 16 Feb 2014 14:16:45 +0400 Subject: [PATCH 874/889] It seems that OctreeSceneManager is not required anymore --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e27910958..024527ae5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -252,7 +252,6 @@ link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS} ${OGRE_LIB_DIR} ${MY if (APPLE) # List used Ogre plugins SET(USED_OGRE_PLUGINS ${OGRE_RenderSystem_GL_LIBRARY_REL} - ${OGRE_Plugin_OctreeSceneManager_LIBRARY_REL} ${OGRE_Plugin_ParticleFX_LIBRARY_REL}) # Actually we must use OGRE_Plugin_CgProgramManager_FOUND but it's From 9f721fd14463bf849133514b60217b4411d736d8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 16 Feb 2014 12:54:27 +0100 Subject: [PATCH 875/889] store list of known topics in saved game files --- apps/openmw/mwbase/dialoguemanager.hpp | 14 ++++++++ apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 36 +++++++++++++++++++ apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 6 ++++ apps/openmw/mwstate/statemanagerimp.cpp | 9 ++++- components/CMakeLists.txt | 2 +- components/esm/defs.hpp | 1 + components/esm/dialoguestate.cpp | 21 +++++++++++ components/esm/dialoguestate.hpp | 23 ++++++++++++ 8 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 components/esm/dialoguestate.cpp create mode 100644 components/esm/dialoguestate.hpp diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index 971bc3b4e..33bba07e1 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -3,6 +3,14 @@ #include +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; +} + namespace MWWorld { class Ptr; @@ -52,6 +60,12 @@ namespace MWBase virtual void persuade (int type) = 0; virtual int getTemporaryDispositionChange () const = 0; virtual void applyDispositionChange (int delta) = 0; + + virtual int countSavedGameRecords() const = 0; + + virtual void write (ESM::ESMWriter& writer) const = 0; + + virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0; }; } diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 845c3c07b..c9e8ad955 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -591,6 +592,41 @@ namespace MWDialogue } } + int DialogueManager::countSavedGameRecords() const + { + return 1; // known topics + } + + void DialogueManager::write (ESM::ESMWriter& writer) const + { + ESM::DialogueState state; + + for (std::map::const_iterator iter (mKnownTopics.begin()); + iter!=mKnownTopics.end(); ++iter) + if (iter->second) + state.mKnownTopics.push_back (iter->first); + + writer.startRecord (ESM::REC_DIAS); + state.save (writer); + writer.endRecord (ESM::REC_DIAS); + } + + void DialogueManager::readRecord (ESM::ESMReader& reader, int32_t type) + { + if (type==ESM::REC_DIAS) + { + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + + ESM::DialogueState state; + state.load (reader); + + for (std::vector::const_iterator iter (state.mKnownTopics.begin()); + iter!=state.mKnownTopics.end(); ++iter) + if (store.get().search (*iter)) + mKnownTopics.insert (std::make_pair (*iter, true)); + } + } + std::vector ParseHyperText(const std::string& text) { diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index c32a5dbd8..cf8ea1176 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -78,6 +78,12 @@ namespace MWDialogue virtual void persuade (int type); virtual int getTemporaryDispositionChange () const; virtual void applyDispositionChange (int delta); + + virtual int countSavedGameRecords() const; + + virtual void write (ESM::ESMWriter& writer) const; + + virtual void readRecord (ESM::ESMReader& reader, int32_t type); }; diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index f68a01bf4..ba0e1d056 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -193,7 +193,8 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot +MWBase::Environment::get().getJournal()->countSavedGameRecords() +MWBase::Environment::get().getWorld()->countSavedGameRecords() +MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords() - + 1 // global map + +MWBase::Environment::get().getDialogueManager()->countSavedGameRecords() + +1 // global map ); writer.save (stream); @@ -203,6 +204,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot writer.endRecord (ESM::REC_SAVE); MWBase::Environment::get().getJournal()->write (writer); + MWBase::Environment::get().getDialogueManager()->write (writer); MWBase::Environment::get().getWorld()->write (writer); MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer); MWBase::Environment::get().getWindowManager()->write(writer); @@ -245,6 +247,11 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl MWBase::Environment::get().getJournal()->readRecord (reader, n.val); break; + case ESM::REC_DIAS: + + MWBase::Environment::get().getDialogueManager()->readRecord (reader, n.val); + break; + case ESM::REC_ALCH: case ESM::REC_ARMO: case ESM::REC_BOOK: diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 45a91f368..e6b89c08e 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -44,7 +44,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate + savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate ) add_component_dir (misc diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 1b0125e78..c1f167992 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -91,6 +91,7 @@ enum RecNameInts REC_PLAY = 0x59414c50, REC_CSTA = 0x41545343, REC_GMAP = 0x50414d47, + REC_DIAS = 0x53414944, // format 1 REC_FILT = 0x544C4946 diff --git a/components/esm/dialoguestate.cpp b/components/esm/dialoguestate.cpp new file mode 100644 index 000000000..b3544c85c --- /dev/null +++ b/components/esm/dialoguestate.cpp @@ -0,0 +1,21 @@ + +#include "dialoguestate.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::DialogueState::load (ESMReader &esm) +{ + while (esm.isNextSub ("TOPI")) + mKnownTopics.push_back (esm.getHString()); +} + +void ESM::DialogueState::save (ESMWriter &esm) const +{ + for (std::vector::const_iterator iter (mKnownTopics.begin()); + iter!=mKnownTopics.end(); ++iter) + { + esm.writeHNString ("TOPI", *iter); + + } +} \ No newline at end of file diff --git a/components/esm/dialoguestate.hpp b/components/esm/dialoguestate.hpp new file mode 100644 index 000000000..9aa9eaefd --- /dev/null +++ b/components/esm/dialoguestate.hpp @@ -0,0 +1,23 @@ +#ifndef OPENMW_ESM_DIALOGUESTATE_H +#define OPENMW_ESM_DIALOGUESTATE_H + +#include +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + struct DialogueState + { + std::vector mKnownTopics; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif \ No newline at end of file From b0532e0c8528828bcaf6d3af5ffdaccf00dc509b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 16 Feb 2014 15:06:34 +0100 Subject: [PATCH 876/889] store NPC state in saved game files --- apps/openmw/mwclass/npc.cpp | 12 ++- apps/openmw/mwmechanics/npcstats.cpp | 77 ++++++++++++++++ apps/openmw/mwmechanics/npcstats.hpp | 5 + apps/openmw/mwmechanics/stat.cpp | 29 ++++++ apps/openmw/mwmechanics/stat.hpp | 10 ++ components/CMakeLists.txt | 3 +- components/esm/npcstate.cpp | 8 ++ components/esm/npcstate.hpp | 4 + components/esm/npcstats.cpp | 133 +++++++++++++++++++++++++++ components/esm/npcstats.hpp | 54 +++++++++++ components/esm/statstate.hpp | 53 +++++++++++ 11 files changed, 383 insertions(+), 5 deletions(-) create mode 100644 apps/openmw/mwmechanics/stat.cpp create mode 100644 components/esm/npcstats.cpp create mode 100644 components/esm/npcstats.hpp create mode 100644 components/esm/statstate.hpp diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 34ea515ba..7e67738d4 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1278,8 +1278,10 @@ namespace MWClass ensureCustomData (ptr); - dynamic_cast (*ptr.getRefData().getCustomData()).mInventoryStore. - readState (state2.mInventory); + CustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + + customData.mInventoryStore.readState (state2.mInventory); + customData.mNpcStats.readState (state2.mNpcStats); } void Npc::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) @@ -1289,8 +1291,10 @@ namespace MWClass ensureCustomData (ptr); - dynamic_cast (*ptr.getRefData().getCustomData()).mInventoryStore. - writeState (state2.mInventory); + CustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + + customData.mInventoryStore.writeState (state2.mInventory); + customData.mNpcStats.writeState (state2.mNpcStats); } const ESM::GameSetting *Npc::fMinWalkSpeed; diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 63b4467f6..786d35b9c 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" @@ -423,3 +424,79 @@ void MWMechanics::NpcStats::setTimeToStartDrowning(float time) assert(time>=0 && time<=20); mTimeToStartDrowning=time; } + +void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const +{ + for (std::map::const_iterator iter (mFactionRank.begin()); + iter!=mFactionRank.end(); ++iter) + state.mFactions[iter->first].mRank = iter->second; + + state.mDisposition = mDisposition; + + for (int i=0; i<27; ++i) + { + mSkill[i].writeState (state.mSkills[i].mRegular); + mWerewolfSkill[i].writeState (state.mSkills[i].mWerewolf); + state.mSkills[i].mIncrease = mSkillIncreases[i]; + } + + state.mBounty = mBounty; + + for (std::set::const_iterator iter (mExpelled.begin()); + iter!=mExpelled.end(); ++iter) + state.mFactions[*iter].mExpelled = true; + + for (std::map::const_iterator iter (mFactionReputation.begin()); + iter!=mFactionReputation.end(); ++iter) + state.mFactions[iter->first].mReputation = iter->second; + + state.mReputation = mReputation; + state.mWerewolfKills = mWerewolfKills; + state.mProfit = mProfit; + state.mAttackStrength = mAttackStrength; + state.mLevelProgress = mLevelProgress; + + std::copy (mUsedIds.begin(), mUsedIds.end(), std::back_inserter (state.mUsedIds)); + + state.mTimeToStartDrowning = mTimeToStartDrowning; + state.mLastDrowningHit = mLastDrowningHit; + state.mLevelHealthBonus = mLevelHealthBonus; +} + +void MWMechanics::NpcStats::readState (const ESM::NpcStats& state) +{ + for (std::map::const_iterator iter (state.mFactions.begin()); + iter!=state.mFactions.end(); ++iter) + { + if (iter->second.mExpelled) + mExpelled.insert (iter->first); + + if (iter->second.mRank) + mFactionRank.insert (std::make_pair (iter->first, iter->second.mRank)); + + if (iter->second.mReputation) + mFactionReputation.insert (std::make_pair (iter->first, iter->second.mReputation)); + } + + mDisposition = state.mDisposition; + + for (int i=0; i<27; ++i) + { + mSkill[i].readState (state.mSkills[i].mRegular); + mWerewolfSkill[i].readState (state.mSkills[i].mWerewolf); + mSkillIncreases[i] = state.mSkills[i].mIncrease; + } + + mBounty = state.mBounty; + mReputation = state.mReputation; + mWerewolfKills = state.mWerewolfKills; + mProfit = state.mProfit; + mAttackStrength = state.mAttackStrength; + mLevelProgress = state.mLevelProgress; + + std::copy (state.mUsedIds.begin(), state.mUsedIds.end(), std::inserter (mUsedIds, mUsedIds.begin())); + + mTimeToStartDrowning = state.mTimeToStartDrowning; + mLastDrowningHit = state.mLastDrowningHit; + mLevelHealthBonus = state.mLevelHealthBonus; +} \ No newline at end of file diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index d7db999e4..ad493be3c 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -13,6 +13,7 @@ namespace ESM { struct Class; + struct NpcStats; } namespace MWMechanics @@ -128,6 +129,10 @@ namespace MWMechanics /// Sets time left for the creature to drown if it stays underwater. /// @param time value from [0,20] void setTimeToStartDrowning(float time); + + void writeState (ESM::NpcStats& state) const; + + void readState (const ESM::NpcStats& state); }; } diff --git a/apps/openmw/mwmechanics/stat.cpp b/apps/openmw/mwmechanics/stat.cpp new file mode 100644 index 000000000..61b6d60ad --- /dev/null +++ b/apps/openmw/mwmechanics/stat.cpp @@ -0,0 +1,29 @@ + +#include "stat.hpp" + +void MWMechanics::AttributeValue::writeState (ESM::StatState& state) const +{ + state.mBase = mBase; + state.mMod = mModifier; + state.mDamage = mDamage; +} + +void MWMechanics::AttributeValue::readState (const ESM::StatState& state) +{ + mBase = state.mBase; + mModifier = state.mMod; + mDamage = state.mDamage; +} + + +void MWMechanics::SkillValue::writeState (ESM::StatState& state) const +{ + AttributeValue::writeState (state); + state.mProgress = mProgress; +} + +void MWMechanics::SkillValue::readState (const ESM::StatState& state) +{ + AttributeValue::readState (state); + mProgress = state.mProgress; +} \ No newline at end of file diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index 75ac6939a..28005966a 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -6,6 +6,8 @@ #include +#include + namespace MWMechanics { template @@ -225,6 +227,10 @@ namespace MWMechanics void damage(int damage) { mDamage += damage; } void restore(int amount) { mDamage -= std::min(mDamage, amount); } int getDamage() const { return mDamage; } + + void writeState (ESM::StatState& state) const; + + void readState (const ESM::StatState& state); }; class SkillValue : public AttributeValue @@ -234,6 +240,10 @@ namespace MWMechanics SkillValue() : mProgress(0) {} float getProgress() const { return mProgress; } void setProgress(float progress) { mProgress = progress; } + + void writeState (ESM::StatState& state) const; + + void readState (const ESM::StatState& state); }; inline bool operator== (const AttributeValue& left, const AttributeValue& right) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index e6b89c08e..866593863 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -44,7 +44,8 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate + savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate + npcstats ) add_component_dir (misc diff --git a/components/esm/npcstate.cpp b/components/esm/npcstate.cpp index c452611a0..e59ec3e26 100644 --- a/components/esm/npcstate.cpp +++ b/components/esm/npcstate.cpp @@ -6,6 +6,10 @@ void ESM::NpcState::load (ESMReader &esm) ObjectState::load (esm); mInventory.load (esm); + + mNpcStats.load (esm); + + mCreatureStats.load (esm); } void ESM::NpcState::save (ESMWriter &esm, bool inInventory) const @@ -13,4 +17,8 @@ void ESM::NpcState::save (ESMWriter &esm, bool inInventory) const ObjectState::save (esm, inInventory); mInventory.save (esm); + + mNpcStats.save (esm); + + mCreatureStats.save (esm); } \ No newline at end of file diff --git a/components/esm/npcstate.hpp b/components/esm/npcstate.hpp index ceb18b88b..39858d553 100644 --- a/components/esm/npcstate.hpp +++ b/components/esm/npcstate.hpp @@ -3,6 +3,8 @@ #include "objectstate.hpp" #include "inventorystate.hpp" +#include "npcstats.hpp" +#include "creaturestats.hpp" namespace ESM { @@ -11,6 +13,8 @@ namespace ESM struct NpcState : public ObjectState { InventoryState mInventory; + NpcStats mNpcStats; + CreatureStats mCreatureStats; virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; diff --git a/components/esm/npcstats.cpp b/components/esm/npcstats.cpp new file mode 100644 index 000000000..531424ab2 --- /dev/null +++ b/components/esm/npcstats.cpp @@ -0,0 +1,133 @@ + +#include "npcstats.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +ESM::NpcStats::Faction::Faction() : mExpelled (false), mRank (0), mReputation (0) {} + +void ESM::NpcStats::load (ESMReader &esm) +{ + while (esm.isNextSub ("FACT")) + { + std::string id = esm.getHString(); + + Faction faction; + + int expelled = 0; + esm.getHNOT (expelled, "FAEX"); + + if (expelled) + faction.mExpelled = true; + + esm.getHNOT (faction.mRank, "FARA"); + + esm.getHNOT (faction.mReputation, "FARE"); + + mFactions.insert (std::make_pair (id, faction)); + } + + mDisposition = 0; + esm.getHNOT (mDisposition, "DISP"); + + for (int i=0; i<27; ++i) + { + mSkills[i].mRegular.load (esm); + mSkills[i].mWerewolf.load (esm); + } + + mBounty = 0; + esm.getHNOT (mBounty, "BOUN"); + + mReputation = 0; + esm.getHNOT (mReputation, "REPU"); + + mWerewolfKills = 0; + esm.getHNOT (mWerewolfKills, "WKIL"); + + mProfit = 0; + esm.getHNOT (mProfit, "PROF"); + + mAttackStrength = 0; + esm.getHNOT (mAttackStrength, "ASTR"); + + mLevelProgress = 0; + esm.getHNOT (mLevelProgress, "LPRO"); + + esm.getHNT (mSkillIncrease, "INCR"); + + while (esm.isNextSub ("USED")) + mUsedIds.push_back (esm.getHString()); + + mTimeToStartDrowning = 0; + esm.getHNOT (mTimeToStartDrowning, "DRTI"); + + mLastDrowningHit = 0; + esm.getHNOT (mLastDrowningHit, "DRLH"); + + mLevelHealthBonus = 0; + esm.getHNOT (mLevelHealthBonus, "LVLH"); +} + +void ESM::NpcStats::save (ESMWriter &esm) const +{ + for (std::map::const_iterator iter (mFactions.begin()); + iter!=mFactions.end(); ++iter) + { + esm.writeHNString ("FACT", iter->first); + + if (iter->second.mExpelled) + { + int expelled = 1; + esm.writeHNT ("FAEX", expelled); + } + + if (iter->second.mRank) + esm.writeHNT ("FARA", iter->second.mRank); + + if (iter->second.mReputation) + esm.writeHNT ("FARE", iter->second.mReputation); + } + + if (mDisposition) + esm.writeHNT ("DISP", mDisposition); + + for (int i=0; i<27; ++i) + { + mSkills[i].mRegular.save (esm); + mSkills[i].mWerewolf.save (esm); + } + + if (mBounty) + esm.writeHNT ("BOUN", mBounty); + + if (mReputation) + esm.writeHNT ("REPU", mReputation); + + if (mWerewolfKills) + esm.writeHNT ("WKIL", mWerewolfKills); + + if (mProfit) + esm.writeHNT ("PROF", mProfit); + + if (mAttackStrength) + esm.writeHNT ("ASTR", mAttackStrength); + + if (mLevelProgress) + esm.writeHNT ("LPRO", mLevelProgress); + + esm.writeHNT ("INCR", mSkillIncrease); + + for (std::vector::const_iterator iter (mUsedIds.begin()); iter!=mUsedIds.end(); + ++iter) + esm.writeHNT ("USED", *iter); + + if (mTimeToStartDrowning) + esm.writeHNT ("DRTI", mTimeToStartDrowning); + + if (mLastDrowningHit) + esm.writeHNT ("DRLH", mLastDrowningHit); + + if (mLevelHealthBonus) + esm.writeHNT ("LVLH", mLevelHealthBonus); +} \ No newline at end of file diff --git a/components/esm/npcstats.hpp b/components/esm/npcstats.hpp new file mode 100644 index 000000000..b3f70db25 --- /dev/null +++ b/components/esm/npcstats.hpp @@ -0,0 +1,54 @@ +#ifndef OPENMW_ESM_NPCSTATS_H +#define OPENMW_ESM_NPCSTATS_H + +#include +#include +#include + +#include "statstate.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + struct NpcStats + { + struct Skill + { + StatState mRegular; + StatState mWerewolf; + }; + + struct Faction + { + bool mExpelled; + int mRank; + int mReputation; + + Faction(); + }; + + std::map mFactions; + int mDisposition; + Skill mSkills[27]; + int mBounty; + int mReputation; + int mWerewolfKills; + int mProfit; + float mAttackStrength; + int mLevelProgress; + int mSkillIncrease[8]; + std::vector mUsedIds; + float mTimeToStartDrowning; + float mLastDrowningHit; + float mLevelHealthBonus; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif \ No newline at end of file diff --git a/components/esm/statstate.hpp b/components/esm/statstate.hpp new file mode 100644 index 000000000..c8b0d6a4d --- /dev/null +++ b/components/esm/statstate.hpp @@ -0,0 +1,53 @@ +#ifndef OPENMW_ESM_STATSTATE_H +#define OPENMW_ESM_STATSTATE_H + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +namespace ESM +{ + // format 0, saved games only + + template + struct StatState + { + T mBase; + T mMod; + T mDamage; + float mProgress; + + StatState(); + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; + + template + StatState::StatState() : mBase (0), mMod (0), mDamage (0), mProgress (0) {} + + template + void StatState::load (ESMReader &esm) + { + esm.getHNT (mBase, "STBA"); + esm.getHNT (mMod, "STMO"); + mDamage = 0; + esm.getHNOT (mDamage, "STDA"); + mProgress = 0; + esm.getHNOT (mProgress, "STPR"); + } + + template + void StatState::save (ESMWriter &esm) const + { + esm.writeHNT ("STBA", mBase); + esm.writeHNT ("STMO", mMod); + + if (mDamage) + esm.writeHNT ("STDA", mDamage); + + if (mProgress) + esm.writeHNT ("STPR", mProgress); + } +} + +#endif \ No newline at end of file From 9788bbcab962b8d72f91c3b762ed6d90035e2bec Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 16 Feb 2014 15:56:36 +0100 Subject: [PATCH 877/889] partially store creature state in saved game files (only attributes and dynamics for now) --- apps/openmw/mwclass/creature.cpp | 13 +++++++---- apps/openmw/mwclass/npc.cpp | 2 ++ apps/openmw/mwmechanics/creaturestats.cpp | 19 ++++++++++++++++ apps/openmw/mwmechanics/creaturestats.hpp | 9 ++++++++ apps/openmw/mwmechanics/npcstats.cpp | 8 +++++-- apps/openmw/mwmechanics/stat.hpp | 24 ++++++++++++++++++++ components/CMakeLists.txt | 2 +- components/esm/creaturestate.cpp | 4 ++++ components/esm/creaturestate.hpp | 2 ++ components/esm/creaturestats.cpp | 20 +++++++++++++++++ components/esm/creaturestats.hpp | 27 +++++++++++++++++++++++ components/esm/statstate.hpp | 8 ++++++- 12 files changed, 130 insertions(+), 8 deletions(-) create mode 100644 components/esm/creaturestats.cpp create mode 100644 components/esm/creaturestats.hpp diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 6af8373c5..81ca0ce2b 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -766,8 +766,11 @@ namespace MWClass ensureCustomData (ptr); - dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore-> - readState (state2.mInventory); + CustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + + customData.mContainerStore->readState (state2.mInventory); + customData.mCreatureStats.readState (state2.mCreatureStats); + } void Creature::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) @@ -777,8 +780,10 @@ namespace MWClass ensureCustomData (ptr); - dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore-> - writeState (state2.mInventory); + CustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + + customData.mContainerStore->writeState (state2.mInventory); + customData.mCreatureStats.writeState (state2.mCreatureStats); } const ESM::GameSetting* Creature::fMinWalkSpeedCreature; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 7e67738d4..3a95f3c29 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1282,6 +1282,7 @@ namespace MWClass customData.mInventoryStore.readState (state2.mInventory); customData.mNpcStats.readState (state2.mNpcStats); + static_cast (customData.mNpcStats).readState (state2.mCreatureStats); } void Npc::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) @@ -1295,6 +1296,7 @@ namespace MWClass customData.mInventoryStore.writeState (state2.mInventory); customData.mNpcStats.writeState (state2.mNpcStats); + static_cast (customData.mNpcStats).writeState (state2.mCreatureStats); } const ESM::GameSetting *Npc::fMinWalkSpeed; diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 8f890befb..d61b96739 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -2,6 +2,8 @@ #include +#include + #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" @@ -457,4 +459,21 @@ namespace MWMechanics mAttackStrength = value; } + void CreatureStats::writeState (ESM::CreatureStats& state) const + { + for (int i=0; i<8; ++i) + mAttributes[i].writeState (state.mAttributes[i]); + + for (int i=0; i<3; ++i) + mDynamic[i].writeState (state.mDynamic[i]); + } + + void CreatureStats::readState (const ESM::CreatureStats& state) + { + for (int i=0; i<8; ++i) + mAttributes[i].readState (state.mAttributes[i]); + + for (int i=0; i<3; ++i) + mDynamic[i].readState (state.mDynamic[i]); + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index bb9583301..94e506fc4 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -12,6 +12,11 @@ #include "aisequence.hpp" #include "drawstate.hpp" +namespace ESM +{ + struct CreatureStats; +} + namespace MWMechanics { /// \brief Common creature stats @@ -212,6 +217,10 @@ namespace MWMechanics std::set mBoundItems; // Same as above std::map mSummonedCreatures; + + void writeState (ESM::CreatureStats& state) const; + + void readState (const ESM::CreatureStats& state); }; } diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 786d35b9c..eee27272a 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -437,7 +437,6 @@ void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const { mSkill[i].writeState (state.mSkills[i].mRegular); mWerewolfSkill[i].writeState (state.mSkills[i].mWerewolf); - state.mSkills[i].mIncrease = mSkillIncreases[i]; } state.mBounty = mBounty; @@ -456,6 +455,9 @@ void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const state.mAttackStrength = mAttackStrength; state.mLevelProgress = mLevelProgress; + for (int i=0; i<8; ++i) + state.mSkillIncrease[i] = mSkillIncreases[i]; + std::copy (mUsedIds.begin(), mUsedIds.end(), std::back_inserter (state.mUsedIds)); state.mTimeToStartDrowning = mTimeToStartDrowning; @@ -484,7 +486,6 @@ void MWMechanics::NpcStats::readState (const ESM::NpcStats& state) { mSkill[i].readState (state.mSkills[i].mRegular); mWerewolfSkill[i].readState (state.mSkills[i].mWerewolf); - mSkillIncreases[i] = state.mSkills[i].mIncrease; } mBounty = state.mBounty; @@ -494,6 +495,9 @@ void MWMechanics::NpcStats::readState (const ESM::NpcStats& state) mAttackStrength = state.mAttackStrength; mLevelProgress = state.mLevelProgress; + for (int i=0; i<8; ++i) + mSkillIncreases[i] = state.mSkillIncrease[i]; + std::copy (state.mUsedIds.begin(), state.mUsedIds.end(), std::inserter (mUsedIds, mUsedIds.begin())); mTimeToStartDrowning = state.mTimeToStartDrowning; diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index 28005966a..0fb4c5732 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -88,6 +88,18 @@ namespace MWMechanics { mModified = mBase + modifier; } + + void writeState (ESM::StatState& state) const + { + state.mBase = mBase; + state.mMod = mModified; + } + + void readState (const ESM::StatState& state) + { + mBase = state.mBase; + mModified = state.mMod; + } }; template @@ -192,6 +204,18 @@ namespace MWMechanics mStatic.setModifier (modifier); setCurrent (getCurrent()+diff); } + + void writeState (ESM::StatState& state) const + { + mStatic.writeState (state); + state.mCurrent = mCurrent; + } + + void readState (const ESM::StatState& state) + { + mStatic.readState (state); + mCurrent = state.mCurrent; + } }; template diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 866593863..831b14057 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -45,7 +45,7 @@ add_component_dir (esm loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate - npcstats + npcstats creaturestats ) add_component_dir (misc diff --git a/components/esm/creaturestate.cpp b/components/esm/creaturestate.cpp index 43cde3025..9e9b56102 100644 --- a/components/esm/creaturestate.cpp +++ b/components/esm/creaturestate.cpp @@ -6,6 +6,8 @@ void ESM::CreatureState::load (ESMReader &esm) ObjectState::load (esm); mInventory.load (esm); + + mCreatureStats.load (esm); } void ESM::CreatureState::save (ESMWriter &esm, bool inInventory) const @@ -13,4 +15,6 @@ void ESM::CreatureState::save (ESMWriter &esm, bool inInventory) const ObjectState::save (esm, inInventory); mInventory.save (esm); + + mCreatureStats.save (esm); } \ No newline at end of file diff --git a/components/esm/creaturestate.hpp b/components/esm/creaturestate.hpp index f7f9b8038..604c2f3a7 100644 --- a/components/esm/creaturestate.hpp +++ b/components/esm/creaturestate.hpp @@ -3,6 +3,7 @@ #include "objectstate.hpp" #include "inventorystate.hpp" +#include "creaturestats.hpp" namespace ESM { @@ -11,6 +12,7 @@ namespace ESM struct CreatureState : public ObjectState { InventoryState mInventory; + CreatureStats mCreatureStats; virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp new file mode 100644 index 000000000..fe250089a --- /dev/null +++ b/components/esm/creaturestats.cpp @@ -0,0 +1,20 @@ + +#include "creaturestats.hpp" + +void ESM::CreatureStats::load (ESMReader &esm) +{ + for (int i=0; i<8; ++i) + mAttributes[i].load (esm); + + for (int i=0; i<3; ++i) + mDynamic[i].load (esm); +} + +void ESM::CreatureStats::save (ESMWriter &esm) const +{ + for (int i=0; i<8; ++i) + mAttributes[i].save (esm); + + for (int i=0; i<3; ++i) + mDynamic[i].save (esm); +} \ No newline at end of file diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp new file mode 100644 index 000000000..540044f38 --- /dev/null +++ b/components/esm/creaturestats.hpp @@ -0,0 +1,27 @@ +#ifndef OPENMW_ESM_CREATURESTATS_H +#define OPENMW_ESM_CREATURESTATS_H + +#include +#include +#include + +#include "statstate.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + struct CreatureStats + { + StatState mAttributes[8]; + StatState mDynamic[3]; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif \ No newline at end of file diff --git a/components/esm/statstate.hpp b/components/esm/statstate.hpp index c8b0d6a4d..4b4023bc2 100644 --- a/components/esm/statstate.hpp +++ b/components/esm/statstate.hpp @@ -13,6 +13,7 @@ namespace ESM { T mBase; T mMod; + T mCurrent; T mDamage; float mProgress; @@ -23,13 +24,15 @@ namespace ESM }; template - StatState::StatState() : mBase (0), mMod (0), mDamage (0), mProgress (0) {} + StatState::StatState() : mBase (0), mMod (0), mCurrent (0), mDamage (0), mProgress (0) {} template void StatState::load (ESMReader &esm) { esm.getHNT (mBase, "STBA"); esm.getHNT (mMod, "STMO"); + mCurrent = 0; + esm.getHNOT (mCurrent, "STCU"); mDamage = 0; esm.getHNOT (mDamage, "STDA"); mProgress = 0; @@ -42,6 +45,9 @@ namespace ESM esm.writeHNT ("STBA", mBase); esm.writeHNT ("STMO", mMod); + if (mCurrent) + esm.writeHNT ("STCU", mCurrent); + if (mDamage) esm.writeHNT ("STDA", mDamage); From 4a6186852129332ad34dbb79118a10729d6e8b4f Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sun, 16 Feb 2014 19:01:28 +0400 Subject: [PATCH 878/889] OS X: fixed Ogre plugin packaging --- CMakeLists.txt | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 024527ae5..a9d1f2017 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -668,11 +668,15 @@ if (APPLE) set(ABSOLUTE_PLUGINS ${PLUGIN_ABS} ${ABSOLUTE_PLUGINS}) endforeach () + install(CODE " + set(BU_CHMOD_BUNDLE_ITEMS ON) + include(BundleUtilities) + " COMPONENT Runtime) + # installs used plugins in bundle at given path (bundle_path must be relative to ${CMAKE_INSTALL_PREFIX}) # and returns list of install paths for all installed plugins function (install_plugins_for_bundle bundle_path plugins_var) set(RELATIVE_PLUGIN_INSTALL_BASE "${bundle_path}/Contents/Frameworks") - install(DIRECTORY ${ABSOLUTE_PLUGINS} DESTINATION ${RELATIVE_PLUGIN_INSTALL_BASE} COMPONENT Runtime) set(PLUGINS "") set(PLUGIN_INSTALL_BASE "\${CMAKE_INSTALL_PREFIX}/${RELATIVE_PLUGIN_INSTALL_BASE}") @@ -680,7 +684,13 @@ if (APPLE) foreach (PLUGIN ${ABSOLUTE_PLUGINS}) get_filename_component(PLUGIN_RELATIVE ${PLUGIN} NAME) get_filename_component(PLUGIN_RELATIVE_WE ${PLUGIN} NAME_WE) - set(PLUGINS ${PLUGINS} "${PLUGIN_INSTALL_BASE}/${PLUGIN_RELATIVE}/${PLUGIN_RELATIVE_WE}") + + set(PLUGIN_DYLIB_IN_BUNDLE "${PLUGIN_INSTALL_BASE}/${PLUGIN_RELATIVE}/${PLUGIN_RELATIVE_WE}") + set(PLUGINS ${PLUGINS} "${PLUGIN_DYLIB_IN_BUNDLE}") + + install(CODE " + copy_resolved_framework_into_bundle(\"${PLUGIN}/${PLUGIN_RELATIVE_WE}\" \"${PLUGIN_DYLIB_IN_BUNDLE}\") + " COMPONENT Runtime) endforeach () set(${plugins_var} ${PLUGINS} PARENT_SCOPE) @@ -700,6 +710,7 @@ if (APPLE) # Current limitations: # 1. Handles only frameworks, not simple libs INSTALL(CODE " + cmake_policy(SET CMP0009 OLD) set(CMAKE_FIND_LIBRARY_PREFIXES ${CMAKE_FIND_LIBRARY_PREFIXES}) set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) set(CMAKE_SYSTEM_FRAMEWORK_PATH ${CMAKE_SYSTEM_FRAMEWORK_PATH}) @@ -731,9 +742,6 @@ if (APPLE) endif() endfunction(gp_resolve_item_override) - cmake_policy(SET CMP0009 OLD) - set(BU_CHMOD_BUNDLE_ITEMS ON) - include(BundleUtilities) fixup_bundle(\"${OPENMW_APP}\" \"${PLUGINS}\" \"${DIRS}\") fixup_bundle(\"${OPENCS_APP}\" \"${OPENCS_PLUGINS}\" \"${DIRS}\") " COMPONENT Runtime) From c18c3e51ee81e50247fdfa5e8a945a3b0660dc90 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 16 Feb 2014 16:22:09 +0100 Subject: [PATCH 879/889] handle IDs that don't exist anymore after loading --- apps/openmw/mwmechanics/npcstats.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index eee27272a..8918bfbe7 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -467,18 +467,21 @@ void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const void MWMechanics::NpcStats::readState (const ESM::NpcStats& state) { + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + for (std::map::const_iterator iter (state.mFactions.begin()); iter!=state.mFactions.end(); ++iter) - { - if (iter->second.mExpelled) - mExpelled.insert (iter->first); + if (store.get().search (iter->first)) + { + if (iter->second.mExpelled) + mExpelled.insert (iter->first); - if (iter->second.mRank) - mFactionRank.insert (std::make_pair (iter->first, iter->second.mRank)); + if (iter->second.mRank) + mFactionRank.insert (std::make_pair (iter->first, iter->second.mRank)); - if (iter->second.mReputation) - mFactionReputation.insert (std::make_pair (iter->first, iter->second.mReputation)); - } + if (iter->second.mReputation) + mFactionReputation.insert (std::make_pair (iter->first, iter->second.mReputation)); + } mDisposition = state.mDisposition; @@ -498,7 +501,10 @@ void MWMechanics::NpcStats::readState (const ESM::NpcStats& state) for (int i=0; i<8; ++i) mSkillIncreases[i] = state.mSkillIncrease[i]; - std::copy (state.mUsedIds.begin(), state.mUsedIds.end(), std::inserter (mUsedIds, mUsedIds.begin())); + for (std::vector::const_iterator iter (state.mUsedIds.begin()); + iter!=state.mUsedIds.end(); ++iter) + if (store.find (*iter)) + mUsedIds.insert (*iter); mTimeToStartDrowning = state.mTimeToStartDrowning; mLastDrowningHit = state.mLastDrowningHit; From 4e28fd85a39782f2c220e3d5804148b2ca072296 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sun, 16 Feb 2014 20:22:46 +0400 Subject: [PATCH 880/889] Fixed build on OS X --- apps/opencs/view/world/table.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index c077d5f7f..3ce90d3a7 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include "../../model/world/data.hpp" #include "../../model/world/commands.hpp" From 3cc23a9cb3660347f55008d9c801dcfef3240c7a Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 16 Feb 2014 18:41:42 +0100 Subject: [PATCH 881/889] very basic, but safe putting down " " --- apps/opencs/view/world/scriptedit.cpp | 21 +++++++++++++++++++-- apps/opencs/view/world/scriptedit.hpp | 2 ++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/world/scriptedit.cpp b/apps/opencs/view/world/scriptedit.cpp index ecfaa3ba8..5d80d2904 100644 --- a/apps/opencs/view/world/scriptedit.cpp +++ b/apps/opencs/view/world/scriptedit.cpp @@ -1,6 +1,10 @@ #include "scriptedit.hpp" +#include + #include +#include +#include #include "../../model/world/universalid.hpp" #include "../../model/world/tablemimedata.hpp" @@ -64,8 +68,21 @@ void CSVWorld::ScriptEdit::dropEvent (QDropEvent* event) { if (mAllowedTypes.contains (it->getType())) { - QString::fromStdString ('"' + it->getId() + '"'); + if (stringNeedsQuote(it->getId())) + { + insertPlainText(QString::fromStdString ('"' + it->getId() + '"')); + } else { + insertPlainText(QString::fromStdString (it->getId())); + } } } } -} \ No newline at end of file +} + +bool CSVWorld::ScriptEdit::stringNeedsQuote (const std::string& id) +{ + QString string(QString::fromStdString(id)); // is only for c++11, so let's use qregexp for now. + //I'm not quite sure when do we need to put quotes. To be safe we will use quotes for anything other than… + QRegExp regexp("^[a-z]{1}[a-z|0-9]{0,}$", Qt::CaseInsensitive); + return !(string.contains(regexp)); +} diff --git a/apps/opencs/view/world/scriptedit.hpp b/apps/opencs/view/world/scriptedit.hpp index dc97382b3..afad12048 100644 --- a/apps/opencs/view/world/scriptedit.hpp +++ b/apps/opencs/view/world/scriptedit.hpp @@ -30,6 +30,8 @@ namespace CSVWorld void dropEvent (QDropEvent* event); void dragMoveEvent (QDragMoveEvent* event); + + bool stringNeedsQuote(const std::string& id); }; } #endif // SCRIPTEDIT_H \ No newline at end of file From b83817e05e7f2dd9e519edb1f71be9c0ae7ca602 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 16 Feb 2014 20:18:28 +0100 Subject: [PATCH 882/889] May only include alphanumeric characters and underscores and may not start with a number. --- apps/opencs/view/world/scriptedit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/world/scriptedit.cpp b/apps/opencs/view/world/scriptedit.cpp index 5d80d2904..1e1bb0e2e 100644 --- a/apps/opencs/view/world/scriptedit.cpp +++ b/apps/opencs/view/world/scriptedit.cpp @@ -83,6 +83,6 @@ bool CSVWorld::ScriptEdit::stringNeedsQuote (const std::string& id) { QString string(QString::fromStdString(id)); // is only for c++11, so let's use qregexp for now. //I'm not quite sure when do we need to put quotes. To be safe we will use quotes for anything other than… - QRegExp regexp("^[a-z]{1}[a-z|0-9]{0,}$", Qt::CaseInsensitive); + QRegExp regexp("^[a-z]{1}[a-z|0-9|_]{0,}$", Qt::CaseInsensitive); return !(string.contains(regexp)); } From e1a0f60041f5ff0d2f7a8970cab33a3c551e904c Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 16 Feb 2014 20:26:22 +0100 Subject: [PATCH 883/889] allow _foo ids without qoute --- apps/opencs/view/world/scriptedit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/world/scriptedit.cpp b/apps/opencs/view/world/scriptedit.cpp index 1e1bb0e2e..79b123ee4 100644 --- a/apps/opencs/view/world/scriptedit.cpp +++ b/apps/opencs/view/world/scriptedit.cpp @@ -83,6 +83,6 @@ bool CSVWorld::ScriptEdit::stringNeedsQuote (const std::string& id) { QString string(QString::fromStdString(id)); // is only for c++11, so let's use qregexp for now. //I'm not quite sure when do we need to put quotes. To be safe we will use quotes for anything other than… - QRegExp regexp("^[a-z]{1}[a-z|0-9|_]{0,}$", Qt::CaseInsensitive); + QRegExp regexp("^[a-z|_]{1}[a-z|0-9|_]{0,}$", Qt::CaseInsensitive); return !(string.contains(regexp)); } From 5e8cb2e4667abe4dfded326897347b7a124623f6 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Mon, 17 Feb 2014 02:35:13 +0400 Subject: [PATCH 884/889] Another attempt to fix #1041. This time I'm sending real data from decoder once after playback started. --- apps/openmw/mwsound/openal_output.cpp | 33 +++++++++++++++++++-------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 9dc0b8c5d..7563ad015 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -172,6 +172,7 @@ class OpenAL_SoundStream : public Sound DecoderPtr mDecoder; volatile bool mIsFinished; + volatile bool mIsInitialBatchEnqueued; void updateAll(bool local); @@ -264,7 +265,7 @@ private: OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder, float basevol, float pitch, int flags) : Sound(Ogre::Vector3(0.0f), 1.0f, basevol, pitch, 1.0f, 1000.0f, flags) - , mOutput(output), mSource(src), mSamplesQueued(0), mDecoder(decoder), mIsFinished(true) + , mOutput(output), mSource(src), mSamplesQueued(0), mDecoder(decoder), mIsFinished(true), mIsInitialBatchEnqueued(false) { throwALerror(); @@ -315,16 +316,8 @@ void OpenAL_SoundStream::play() alSourcei(mSource, AL_BUFFER, 0); throwALerror(); mSamplesQueued = 0; - - for(ALuint i = 0;i < sNumBuffers;i++) - alBufferData(mBuffers[i], mFormat, this, 0, mSampleRate); - throwALerror(); - - alSourceQueueBuffers(mSource, sNumBuffers, mBuffers); - alSourcePlay(mSource); - throwALerror(); - mIsFinished = false; + mIsInitialBatchEnqueued = false; mOutput.mStreamThread->add(this); } @@ -332,6 +325,7 @@ void OpenAL_SoundStream::stop() { mOutput.mStreamThread->remove(this); mIsFinished = true; + mIsInitialBatchEnqueued = false; alSourceStop(mSource); alSourcei(mSource, AL_BUFFER, 0); @@ -444,6 +438,24 @@ bool OpenAL_SoundStream::process() } while(processed > 0); throwALerror(); } + else if (!mIsInitialBatchEnqueued) { // nothing enqueued yet + std::vector data(mBufferSize); + + for(ALuint i = 0;i < sNumBuffers && !finished;i++) + { + size_t got = mDecoder->read(&data[0], data.size()); + finished = (got < data.size()); + if(got > 0) + { + ALuint bufid = mBuffers[i]; + alBufferData(bufid, mFormat, &data[0], got, mSampleRate); + alSourceQueueBuffers(mSource, 1, &bufid); + throwALerror(); + mSamplesQueued += getBufferSampleCount(bufid); + } + } + mIsInitialBatchEnqueued = true; + } if(state != AL_PLAYING && state != AL_PAUSED) { @@ -461,6 +473,7 @@ bool OpenAL_SoundStream::process() std::cout<< "Error updating stream \""<getName()<<"\"" < Date: Mon, 17 Feb 2014 02:59:23 +0400 Subject: [PATCH 885/889] #1041 in progress: decode first sample batch right in OpenAL_SoundStream::play() --- apps/openmw/mwsound/openal_output.cpp | 45 +++++++++++++-------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 7563ad015..58566225b 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -172,7 +172,6 @@ class OpenAL_SoundStream : public Sound DecoderPtr mDecoder; volatile bool mIsFinished; - volatile bool mIsInitialBatchEnqueued; void updateAll(bool local); @@ -265,7 +264,7 @@ private: OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder, float basevol, float pitch, int flags) : Sound(Ogre::Vector3(0.0f), 1.0f, basevol, pitch, 1.0f, 1000.0f, flags) - , mOutput(output), mSource(src), mSamplesQueued(0), mDecoder(decoder), mIsFinished(true), mIsInitialBatchEnqueued(false) + , mOutput(output), mSource(src), mSamplesQueued(0), mDecoder(decoder), mIsFinished(true) { throwALerror(); @@ -316,8 +315,26 @@ void OpenAL_SoundStream::play() alSourcei(mSource, AL_BUFFER, 0); throwALerror(); mSamplesQueued = 0; - mIsFinished = false; - mIsInitialBatchEnqueued = false; + + std::vector data(mBufferSize); + + bool finished = false; + for(ALuint i = 0;i < sNumBuffers && !finished;i++) + { + size_t got = mDecoder->read(&data[0], data.size()); + finished = (got < data.size()); + if(got > 0) + { + ALuint bufid = mBuffers[i]; + alBufferData(bufid, mFormat, &data[0], got, mSampleRate); + alSourceQueueBuffers(mSource, 1, &bufid); + throwALerror(); + mSamplesQueued += getBufferSampleCount(bufid); + } + } + + mIsFinished = finished; + alSourcePlay(mSource); mOutput.mStreamThread->add(this); } @@ -325,7 +342,6 @@ void OpenAL_SoundStream::stop() { mOutput.mStreamThread->remove(this); mIsFinished = true; - mIsInitialBatchEnqueued = false; alSourceStop(mSource); alSourcei(mSource, AL_BUFFER, 0); @@ -438,24 +454,6 @@ bool OpenAL_SoundStream::process() } while(processed > 0); throwALerror(); } - else if (!mIsInitialBatchEnqueued) { // nothing enqueued yet - std::vector data(mBufferSize); - - for(ALuint i = 0;i < sNumBuffers && !finished;i++) - { - size_t got = mDecoder->read(&data[0], data.size()); - finished = (got < data.size()); - if(got > 0) - { - ALuint bufid = mBuffers[i]; - alBufferData(bufid, mFormat, &data[0], got, mSampleRate); - alSourceQueueBuffers(mSource, 1, &bufid); - throwALerror(); - mSamplesQueued += getBufferSampleCount(bufid); - } - } - mIsInitialBatchEnqueued = true; - } if(state != AL_PLAYING && state != AL_PAUSED) { @@ -473,7 +471,6 @@ bool OpenAL_SoundStream::process() std::cout<< "Error updating stream \""<getName()<<"\"" < Date: Mon, 17 Feb 2014 08:58:55 +0100 Subject: [PATCH 886/889] Improvements. --- apps/opencs/view/world/scriptedit.cpp | 10 +++++----- apps/opencs/view/world/scriptedit.hpp | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/world/scriptedit.cpp b/apps/opencs/view/world/scriptedit.cpp index 79b123ee4..fccac75b4 100644 --- a/apps/opencs/view/world/scriptedit.cpp +++ b/apps/opencs/view/world/scriptedit.cpp @@ -11,7 +11,8 @@ CSVWorld::ScriptEdit::ScriptEdit (QWidget* parent, const CSMDoc::Document& document) : QTextEdit (parent), - mDocument (document) + mDocument (document), + mWhiteListQoutes("^[a-z|_]{1}[a-z|0-9|_]{0,}$", Qt::CaseInsensitive) { mAllowedTypes < is only for c++11, so let's use qregexp for now. + const QString string(QString::fromStdString(id)); // is only for c++11, so let's use qregexp for now. //I'm not quite sure when do we need to put quotes. To be safe we will use quotes for anything other than… - QRegExp regexp("^[a-z|_]{1}[a-z|0-9|_]{0,}$", Qt::CaseInsensitive); - return !(string.contains(regexp)); + return !(string.contains(mWhiteListQoutes)); } diff --git a/apps/opencs/view/world/scriptedit.hpp b/apps/opencs/view/world/scriptedit.hpp index afad12048..b4627c2fe 100644 --- a/apps/opencs/view/world/scriptedit.hpp +++ b/apps/opencs/view/world/scriptedit.hpp @@ -7,6 +7,7 @@ #include "../../model/world/universalid.hpp" class QWidget; +class QRegExp; namespace CSMDoc { @@ -24,6 +25,7 @@ namespace CSVWorld private: QVector mAllowedTypes; const CSMDoc::Document& mDocument; + const QRegExp mWhiteListQoutes; void dragEnterEvent (QDragEnterEvent* event); @@ -31,7 +33,7 @@ namespace CSVWorld void dragMoveEvent (QDragMoveEvent* event); - bool stringNeedsQuote(const std::string& id); + bool stringNeedsQuote(const std::string& id) const; }; } #endif // SCRIPTEDIT_H \ No newline at end of file From e5b19cf3c696b5c4b392112c2b785799e2b9a66a Mon Sep 17 00:00:00 2001 From: gus Date: Mon, 17 Feb 2014 10:37:11 +0100 Subject: [PATCH 887/889] clean up --- apps/openmw/mwclass/door.cpp | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index a846a8492..e9ac39f1d 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -115,21 +115,11 @@ namespace MWClass if (ref->mRef.mTeleport) { - // teleport door - /// \todo remove this if clause once ActionTeleport can also support other actors - //if (MWBase::Environment::get().getWorld()->getPlayerPtr()==actor) - //{ - boost::shared_ptr action(new MWWorld::ActionTeleport (ref->mRef.mDestCell, ref->mRef.mDoorDest)); + boost::shared_ptr action(new MWWorld::ActionTeleport (ref->mRef.mDestCell, ref->mRef.mDoorDest)); - action->setSound(openSound); + action->setSound(openSound); - return action; - /*} - else - { - // another NPC or a creature is using the door - return boost::shared_ptr (new MWWorld::FailedAction); - }*/ + return action; } else { From 6e1425321bba487587d7c4ba7e6e90154e085b36 Mon Sep 17 00:00:00 2001 From: gus Date: Mon, 17 Feb 2014 10:43:09 +0100 Subject: [PATCH 888/889] remove cout spam --- apps/openmw/mwmechanics/aiactivate.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index 667ebea88..a22c2b9ba 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -86,7 +86,6 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) movement.mPosition[1] = 0; MWWorld::Ptr target = world->getPtr(mObjectId,false); MWWorld::Class::get(target).activate(target,actor).get()->execute(actor); - std::cout << "activated"; return true; } @@ -95,7 +94,6 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) movement.mPosition[1] = 0; MWWorld::Ptr target = world->getPtr(mObjectId,false); MWWorld::Class::get(target).activate(target,actor).get()->execute(actor); - std::cout << "activated"; return true; } From 84959eea288057e48374862b86452b70eab9e027 Mon Sep 17 00:00:00 2001 From: gus Date: Mon, 17 Feb 2014 10:50:10 +0100 Subject: [PATCH 889/889] woops, thanks scrawl --- apps/openmw/mwmechanics/aiactivate.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index a22c2b9ba..1f3c58521 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -8,6 +8,8 @@ #include "../mwworld/class.hpp" #include "../mwworld/action.hpp" +#include "steering.hpp" + namespace { float sgn(float a) @@ -98,8 +100,8 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) } float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - // TODO: use movement settings instead of rotating directly - world->rotateObject(actor, 0, 0, zAngle, false); + zTurn(actor, Ogre::Degree(zAngle)); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; movement.mPosition[1] = 1; return false;