diff --git a/.gitignore b/.gitignore index 9734ac35c..b757c53f1 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,9 @@ makefile data *.kdev4 CMakeLists.txt.user +*.swp +*.swo +*.kate-swp +.cproject +.project +.settings/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..374b38ce0 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,40 @@ +language: cpp +compiler: + - gcc +branches: + only: + - master + - next +before_install: + - pwd + - git submodule update --init --recursive + - 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/deps + - sudo apt-get update -qq + - sudo apt-get install -qq libboost-all-dev libgtest-dev google-mock libzzip-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 libcg nvidia-cg-toolkit + - sudo apt-get install -qq libavcodec-dev libavformat-dev libavdevice-dev libavutil-dev libswscale-dev libpostproc-dev + - sudo apt-get install -qq libois-dev libbullet-dev libogre-static-dev libmygui-static-dev + - sudo mkdir /usr/src/gtest/build + - cd /usr/src/gtest/build + - sudo cmake .. -DBUILD_SHARED_LIBS=1 + - sudo make -j4 + - sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so + - sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so +before_script: + - cd - + - mkdir build + - cd build + - cmake .. -DOGRE_STATIC=1 -DMYGUI_STATIC=1 -DBUILD_WITH_CODE_COVERAGE=1 -DBUILD_UNITTESTS=1 +script: + - make -j4 +after_script: + - ./openmw_test_suite +notifications: + recipients: + - lgromanowski+travis.ci@gmail.com + email: + on_success: change + on_failure: always diff --git a/Bitstream Vera License.txt b/Bitstream Vera License.txt deleted file mode 100644 index 2b37cc1df..000000000 --- a/Bitstream Vera License.txt +++ /dev/null @@ -1,123 +0,0 @@ -Bitstream Vera Fonts Copyright - -The fonts have a generous copyright, allowing derivative works (as -long as "Bitstream" or "Vera" are not in the names), and full -redistribution (so long as they are not *sold* by themselves). They -can be be bundled, redistributed and sold with any software. - -The fonts are distributed under the following copyright: - -Copyright -========= - -Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream -Vera is a trademark of Bitstream, Inc. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of the fonts accompanying this license ("Fonts") and associated -documentation files (the "Font Software"), to reproduce and distribute -the Font Software, including without limitation the rights to use, -copy, merge, publish, distribute, and/or sell copies of the Font -Software, and to permit persons to whom the Font Software is furnished -to do so, subject to the following conditions: - -The above copyright and trademark notices and this permission notice -shall be included in all copies of one or more of the Font Software -typefaces. - -The Font Software may be modified, altered, or added to, and in -particular the designs of glyphs or characters in the Fonts may be -modified and additional glyphs or characters may be added to the -Fonts, only if the fonts are renamed to names not containing either -the words "Bitstream" or the word "Vera". - -This License becomes null and void to the extent applicable to Fonts -or Font Software that has been modified and is distributed under the -"Bitstream Vera" names. - -The Font Software may be sold as part of a larger software package but -no copy of one or more of the Font Software typefaces may be sold by -itself. - -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL -BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, -OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT -SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. - -Except as contained in this notice, the names of Gnome, the Gnome -Foundation, and Bitstream Inc., shall not be used in advertising or -otherwise to promote the sale, use or other dealings in this Font -Software without prior written authorization from the Gnome Foundation -or Bitstream Inc., respectively. For further information, contact: -fonts at gnome dot org. - -Copyright FAQ -============= - - 1. I don't understand the resale restriction... What gives? - - Bitstream is giving away these fonts, but wishes to ensure its - competitors can't just drop the fonts as is into a font sale system - and sell them as is. It seems fair that if Bitstream can't make money - from the Bitstream Vera fonts, their competitors should not be able to - do so either. You can sell the fonts as part of any software package, - however. - - 2. I want to package these fonts separately for distribution and - sale as part of a larger software package or system. Can I do so? - - Yes. A RPM or Debian package is a "larger software package" to begin - with, and you aren't selling them independently by themselves. - See 1. above. - - 3. Are derivative works allowed? - Yes! - - 4. Can I change or add to the font(s)? - Yes, but you must change the name(s) of the font(s). - - 5. Under what terms are derivative works allowed? - - You must change the name(s) of the fonts. This is to ensure the - quality of the fonts, both to protect Bitstream and Gnome. We want to - ensure that if an application has opened a font specifically of these - names, it gets what it expects (though of course, using fontconfig, - substitutions could still could have occurred during font - opening). You must include the Bitstream copyright. Additional - copyrights can be added, as per copyright law. Happy Font Hacking! - - 6. If I have improvements for Bitstream Vera, is it possible they might get - adopted in future versions? - - Yes. The contract between the Gnome Foundation and Bitstream has - provisions for working with Bitstream to ensure quality additions to - the Bitstream Vera font family. Please contact us if you have such - additions. Note, that in general, we will want such additions for the - entire family, not just a single font, and that you'll have to keep - both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add - glyphs to the font, they must be stylistically in keeping with Vera's - design. Vera cannot become a "ransom note" font. Jim Lyles will be - providing a document describing the design elements used in Vera, as a - guide and aid for people interested in contributing to Vera. - - 7. I want to sell a software package that uses these fonts: Can I do so? - - Sure. Bundle the fonts with your software and sell your software - with the fonts. That is the intent of the copyright. - - 8. If applications have built the names "Bitstream Vera" into them, - can I override this somehow to use fonts of my choosing? - - This depends on exact details of the software. Most open source - systems and software (e.g., Gnome, KDE, etc.) are now converting to - use fontconfig (see www.fontconfig.org) to handle font configuration, - selection and substitution; it has provisions for overriding font - names and subsituting alternatives. An example is provided by the - supplied local.conf file, which chooses the family Bitstream Vera for - "sans", "serif" and "monospace". Other software (e.g., the XFree86 - core server) has other mechanisms for font substitution. diff --git a/CMakeLists.txt b/CMakeLists.txt index 37023571b..1c2fbdb9b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ include (OpenMWMacros) # Version set (OPENMW_VERSION_MAJOR 0) -set (OPENMW_VERSION_MINOR 20) +set (OPENMW_VERSION_MINOR 23) set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") @@ -29,6 +29,7 @@ option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binarie option(BOOST_STATIC "Link static build of Boost into the binaries" FALSE) # Apps and tools +option(BUILD_BSATOOL "build BSA extractor" OFF) option(BUILD_ESMTOOL "build ESM inspector" ON) option(BUILD_LAUNCHER "build Launcher" ON) option(BUILD_MWINIIMPORTER "build MWiniImporter" ON) @@ -67,35 +68,6 @@ endif() # We probably support older versions than this. cmake_minimum_required(VERSION 2.6) -# -# Pre-built binaries being used? -# -IF(EXISTS "${CMAKE_SOURCE_DIR}/prebuilt/vc100-mt-gd/ogre_1_7_1") - set(PREBUILT_DIR "${CMAKE_SOURCE_DIR}/prebuilt/vc100-mt-gd") - message (STATUS "OpenMW pre-built binaries found at ${PREBUILT_DIR}.") - - SET(ENV{OGRE_HOME} "${PREBUILT_DIR}/ogre_1_7_1") - - SET(ENV{BOOST_ROOT} "${PREBUILT_DIR}/boost_1_42_0") - set(Boost_USE_STATIC_LIBS ON) - set(Boost_USE_MULTITHREADED ON) - set(ENV{BOOST_INCLUDEDIR} "${BOOST_ROOT}/include") - set(ENV{BOOST_LIBRARYDIR} "${BOOST_ROOT}/lib") - - set(ENV{FREETYPE_DIR} "${PREBUILT_DIR}/freetype-2.3.5-1") - - set(USE_MPG123 OFF) - set(USE_AUDIERE ON) - set(AUDIERE_INCLUDE_DIR "${PREBUILT_DIR}/audiere-1.9.4/include") - set(AUDIERE_LIBRARY "${PREBUILT_DIR}/audiere-1.9.4/lib/audiere.lib") - - set(ENV{OPENALDIR} "${PREBUILT_DIR}/OpenAL 1.1 SDK") - - set(BULLET_ROOT "${PREBUILT_DIR}/bullet") -ELSE() - message (STATUS "OpenMW pre-built binaries not found. Using standard locations.") -ENDIF() - # source directory: libs set(LIBDIR ${CMAKE_SOURCE_DIR}/libs) @@ -103,7 +75,7 @@ set(LIBDIR ${CMAKE_SOURCE_DIR}/libs) set(OENGINE_OGRE ${LIBDIR}/openengine/ogre/renderer.cpp ${LIBDIR}/openengine/ogre/fader.cpp - ${LIBDIR}/openengine/ogre/imagerotate.cpp + ${LIBDIR}/openengine/ogre/particles.cpp ${LIBDIR}/openengine/ogre/selectionbuffer.cpp ) set(OENGINE_GUI @@ -123,8 +95,6 @@ set(OENGINE_BULLET ${LIBDIR}/openengine/bullet/physic.hpp ${LIBDIR}/openengine/bullet/BulletShapeLoader.cpp ${LIBDIR}/openengine/bullet/BulletShapeLoader.h - ${LIBDIR}/openengine/bullet/pmove.cpp - ${LIBDIR}/openengine/bullet/pmove.h ${LIBDIR}/openengine/bullet/trace.cpp ${LIBDIR}/openengine/bullet/trace.h @@ -211,15 +181,14 @@ if (UNIX AND NOT APPLE) find_package (Threads) endif() -# find boost without components so we can use Boost_VERSION -find_package(Boost REQUIRED) +include (CheckIncludeFileCXX) +check_include_file_cxx(unordered_map HAVE_UNORDERED_MAP) +if (HAVE_UNORDERED_MAP) + add_definitions(-DHAVE_UNORDERED_MAP) +endif () -set(BOOST_COMPONENTS system filesystem program_options thread) -if (Boost_VERSION LESS 104900) - set(SHINY_USE_WAVE_SYSTEM_INSTALL "TRUE") - set(BOOST_COMPONENTS ${BOOST_COMPONENTS} wave) -endif() +set(BOOST_COMPONENTS system filesystem program_options thread date_time wave) IF(BOOST_STATIC) set(Boost_USE_STATIC_LIBS ON) @@ -289,10 +258,10 @@ set_directory_properties(PROPERTIES COMPILE_DEFINITIONS_DEBUG DEBUG=1) # Set up Ogre plugin folder & debug suffix if (APPLE) - # Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt) - add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="") + # Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt) + add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="") else () - add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") + add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") endif() add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}") @@ -310,9 +279,9 @@ add_subdirectory(files/mygui) # Specify build paths if (APPLE) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${APP_BUNDLE_DIR}/Contents/MacOS") + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${APP_BUNDLE_DIR}/Contents/MacOS") else (APPLE) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}") + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}") endif (APPLE) # Other files @@ -329,14 +298,16 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg "${OpenMW_BINARY_DIR}/openmw.cfg.install") -if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") +if (NOT WIN32 AND NOT APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop "${OpenMW_BINARY_DIR}/openmw.desktop") + configure_file(${OpenMW_SOURCE_DIR}/files/opencs.desktop + "${OpenMW_BINARY_DIR}/opencs.desktop") endif() # Compiler settings if (CMAKE_COMPILER_IS_GNUCC) - add_definitions (-Wall -Wextra -Wno-unused-parameter -Wno-reorder -std=c++0x -pedantic) + add_definitions (-Wall -Wextra -Wno-unused-parameter -Wno-reorder -std=c++98 -pedantic -Wno-long-long) # Silence warnings in OGRE headers. Remove once OGRE got fixed! add_definitions (-Wno-ignored-qualifiers) @@ -364,7 +335,7 @@ if(DPKG_PROGRAM) #Install icon and desktop file INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "share/applications/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") - INSTALL(FILES "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.png" DESTINATION "share/pixmaps/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") + INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "share/pixmaps/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") #Install global configuration files INSTALL(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "../etc/openmw/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") @@ -385,8 +356,7 @@ if(DPKG_PROGRAM) 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 esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter") - #TODO: should SDL2 be mentioned in here somewhere? + SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW 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), libois-1.3.0 (>= 1.3.0), 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_SECTION "Games") @@ -411,11 +381,11 @@ if(WIN32) "${OpenMW_SOURCE_DIR}/readme.txt" "${OpenMW_SOURCE_DIR}/GPL3.txt" "${OpenMW_SOURCE_DIR}/OFL.txt" - "${OpenMW_SOURCE_DIR}/Bitstream Vera License.txt" + "${OpenMW_SOURCE_DIR}/DejaVu Font License.txt" "${OpenMW_SOURCE_DIR}/Daedric Font License.txt" - "${OpenMW_BINARY_DIR}/launcher.qss" "${OpenMW_BINARY_DIR}/settings-default.cfg" "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" + "${OpenMW_BINARY_DIR}/Release/mwiniimport.exe" "${OpenMW_BINARY_DIR}/Release/omwlauncher.exe" "${OpenMW_BINARY_DIR}/Release/openmw.exe" DESTINATION ".") @@ -426,7 +396,7 @@ if(WIN32) SET(CPACK_PACKAGE_VENDOR "OpenMW.org") 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(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW;omwlauncher;OpenMW Launcher") SET(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\readme.txt'") @@ -437,12 +407,12 @@ if(WIN32) SET(CPACK_RESOURCE_FILE_README "${OpenMW_SOURCE_DIR}/readme.txt") SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/readme.txt") SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") - SET(CPACK_NSIS_DISPLAY_NAME "OpenMW") + SET(CPACK_NSIS_DISPLAY_NAME "OpenMW ${OPENMW_VERSION}") SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.openmw.org") SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.openmw.org") SET(CPACK_NSIS_INSTALLED_ICON_NAME "omwlauncher.exe") - SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.ico") - SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.ico") + SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.ico") + SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.ico") SET(CPACK_PACKAGE_ICON "${OpenMW_SOURCE_DIR}\\\\files\\\\openmw.bmp") SET(VCREDIST32 "${OpenMW_BINARY_DIR}/vcredist_x86.exe") @@ -482,6 +452,10 @@ add_subdirectory (components) # Apps and tools add_subdirectory( apps/openmw ) +if (BUILD_BSATOOL) + add_subdirectory( apps/bsatool ) +endif() + if (BUILD_ESMTOOL) add_subdirectory( apps/esmtool ) endif() @@ -528,7 +502,7 @@ if (WIN32) # Warnings that aren't enabled normally and don't need to be enabled # They're unneeded and sometimes completely retarded warnings that /Wall enables # Not going to bother commenting them as they tend to warn on every standard library files - 4061 4263 4264 4266 4350 4514 4548 4571 4610 4619 4623 4625 4626 4628 4640 4668 4710 4711 4820 4826 4917 4946 + 4061 4263 4264 4266 4350 4371 4514 4548 4571 4610 4619 4623 4625 4626 4628 4640 4668 4710 4711 4820 4826 4917 4946 # Warnings that are thrown on standard libraries and not OpenMW 4347 # Non-template function with same name and parameter count as template function @@ -539,6 +513,11 @@ if (WIN32) 4986 # Undocumented warning that occurs in the crtdbg.h file 4996 # Function was declared deprecated + # cause by ogre extensivly + 4193 # #pragma warning(pop) : no matching '#pragma warning(push)' + 4251 # class 'XXXX' needs to have dll-interface to be used by clients of class 'YYYY' + 4275 # non dll-interface struct 'XXXX' used as base for dll-interface class 'YYYY' + # OpenMW specific warnings 4099 # Type mismatch, declared class or struct is defined with other type 4100 # Unreferenced formal parameter (-Wunused-parameter) @@ -549,18 +528,23 @@ if (WIN32) 4309 # Variable overflow, trying to store 128 in a signed char for example 4355 # Using 'this' in member initialization list 4701 # Potentially uninitialized local variable used - 4800 # Boolean optimization warning, e.g. myBool = (myInt != 0) instead of myBool = myInt + 4800 # Boolean optimization warning, e.g. myBool = (myInt != 0) instead of myBool = myInt ) foreach(d ${WARNINGS_DISABLE}) set(WARNINGS "${WARNINGS} /wd${d}") endforeach(d) + set_target_properties(shiny PROPERTIES COMPILE_FLAGS ${WARNINGS}) + set_target_properties(shiny.OgrePlatform PROPERTIES COMPILE_FLAGS ${WARNINGS}) set_target_properties(components PROPERTIES COMPILE_FLAGS ${WARNINGS}) if (BUILD_LAUNCHER) - set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS}) - endif (BUILD_LAUNCHER) + set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS}) + endif (BUILD_LAUNCHER) set_target_properties(openmw PROPERTIES COMPILE_FLAGS ${WARNINGS}) + if (BUILD_BSATOOL) + set_target_properties(bsatool PROPERTIES COMPILE_FLAGS ${WARNINGS}) + endif (BUILD_BSATOOL) if (BUILD_ESMTOOL) set_target_properties(esmtool PROPERTIES COMPILE_FLAGS ${WARNINGS}) endif (BUILD_ESMTOOL) @@ -594,8 +578,6 @@ if (APPLE) install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) - install(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) - install(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) @@ -674,23 +656,39 @@ endif (APPLE) if (NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE) ## Non Debian based Linux building # paths - set(BINDIR "${CMAKE_INSTALL_PREFIX}/usr/bin" CACHE PATH "Where to install binaries") + set(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries") set(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location") - set(DATADIR "${DATAROOTDIR}/openmw" CACHE PATH "Sets the openmw data directories to a non-default location") + set(DATADIR "${DATAROOTDIR}/games/openmw" CACHE PATH "Sets the openmw data directories to a non-default location") set(DOCDIR "${DATAROOTDIR}/doc/openmw" CACHE PATH "Sets the doc directory to a non-default location.") set(MANDIR "${DATAROOTDIR}/man" CACHE PATH "Where to install manpages") - set(SYSCONFDIR "${CMAKE_INSTALL_PREFIX}/etc/openmw" CACHE PATH "Set config dir") + set(SYSCONFDIR "/etc/openmw" CACHE PATH "Set config dir") set(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir") # Install binaries INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" ) - INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" ) - INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" ) - INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" ) + IF(BUILD_LAUNCHER) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" ) + ENDIF(BUILD_LAUNCHER) + IF(BUILD_BSATOOL) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" ) + ENDIF(BUILD_BSATOOL) + IF(BUILD_ESMTOOL) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" ) + ENDIF(BUILD_ESMTOOL) + IF(BUILD_MWINIIMPORTER) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" ) + ENDIF(BUILD_MWINIIMPORTER) + IF(BUILD_OPENCS) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" ) + ENDIF(BUILD_OPENCS) # Install icon and .desktop - INSTALL(FILES "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.png" DESTINATION "${ICONDIR}") + INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications") + IF(BUILD_OPENCS) + INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/opencs/opencs.png" DESTINATION "${ICONDIR}") + INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.desktop" DESTINATION "${DATAROOTDIR}/applications") + ENDIF(BUILD_OPENCS) # Install global configuration files INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" ) @@ -700,5 +698,4 @@ if (NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE) # Install resources INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" ) - INSTALL(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${DATADIR}/resources" ) endif(NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE) diff --git a/DejaVu Font License.txt b/DejaVu Font License.txt new file mode 100644 index 000000000..254e2cc42 --- /dev/null +++ b/DejaVu Font License.txt @@ -0,0 +1,99 @@ +Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. +Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) + +Bitstream Vera Fonts Copyright +------------------------------ + +Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is +a trademark of Bitstream, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of the fonts accompanying this license ("Fonts") and associated +documentation files (the "Font Software"), to reproduce and distribute the +Font Software, including without limitation the rights to use, copy, merge, +publish, distribute, and/or sell copies of the Font Software, and to permit +persons to whom the Font Software is furnished to do so, subject to the +following conditions: + +The above copyright and trademark notices and this permission notice shall +be included in all copies of one or more of the Font Software typefaces. + +The Font Software may be modified, altered, or added to, and in particular +the designs of glyphs or characters in the Fonts may be modified and +additional glyphs or characters may be added to the Fonts, only if the fonts +are renamed to names not containing either the words "Bitstream" or the word +"Vera". + +This License becomes null and void to the extent applicable to Fonts or Font +Software that has been modified and is distributed under the "Bitstream +Vera" names. + +The Font Software may be sold as part of a larger software package but no +copy of one or more of the Font Software typefaces may be sold by itself. + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, +TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME +FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING +ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE +FONT SOFTWARE. + +Except as contained in this notice, the names of Gnome, the Gnome +Foundation, and Bitstream Inc., shall not be used in advertising or +otherwise to promote the sale, use or other dealings in this Font Software +without prior written authorization from the Gnome Foundation or Bitstream +Inc., respectively. For further information, contact: fonts at gnome dot +org. + +Arev Fonts Copyright +------------------------------ + +Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the fonts accompanying this license ("Fonts") and +associated documentation files (the "Font Software"), to reproduce +and distribute the modifications to the Bitstream Vera Font Software, +including without limitation the rights to use, copy, merge, publish, +distribute, and/or sell copies of the Font Software, and to permit +persons to whom the Font Software is furnished to do so, subject to +the following conditions: + +The above copyright and trademark notices and this permission notice +shall be included in all copies of one or more of the Font Software +typefaces. + +The Font Software may be modified, altered, or added to, and in +particular the designs of glyphs or characters in the Fonts may be +modified and additional glyphs or characters may be added to the +Fonts, only if the fonts are renamed to names not containing either +the words "Tavmjong Bah" or the word "Arev". + +This License becomes null and void to the extent applicable to Fonts +or Font Software that has been modified and is distributed under the +"Tavmjong Bah Arev" names. + +The Font Software may be sold as part of a larger software package but +no copy of one or more of the Font Software typefaces may be sold by +itself. + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL +TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +Except as contained in this notice, the name of Tavmjong Bah shall not +be used in advertising or otherwise to promote the sale, use or other +dealings in this Font Software without prior written authorization +from Tavmjong Bah. For further information, contact: tavmjong @ free +. fr. + +$Id: LICENSE 2133 2007-11-28 02:46:28Z lechimp $ diff --git a/apps/bsatool/CMakeLists.txt b/apps/bsatool/CMakeLists.txt new file mode 100644 index 000000000..3f1988a70 --- /dev/null +++ b/apps/bsatool/CMakeLists.txt @@ -0,0 +1,19 @@ +set(BSATOOL + bsatool.cpp +) +source_group(apps\\bsatool FILES ${BSATOOL}) + +# Main executable +add_executable(bsatool + ${BSATOOL} +) + +target_link_libraries(bsatool + ${Boost_LIBRARIES} + components +) + +if (BUILD_WITH_CODE_COVERAGE) + add_definitions (--coverage) + target_link_libraries(bsatool gcov) +endif() diff --git a/apps/bsatool/bsatool.cpp b/apps/bsatool/bsatool.cpp new file mode 100644 index 000000000..e6fcc2567 --- /dev/null +++ b/apps/bsatool/bsatool.cpp @@ -0,0 +1,288 @@ +#include +#include +#include + +#include +#include +#include + +#include + +#define BSATOOL_VERSION 1.1 + +// Create local aliases for brevity +namespace bpo = boost::program_options; +namespace bfs = boost::filesystem; + +struct Arguments +{ + std::string mode; + std::string filename; + std::string extractfile; + std::string outdir; + + bool longformat; + bool fullpath; +}; + +void replaceAll(std::string& str, const std::string& needle, const std::string& substitute) +{ + int pos = str.find(needle); + while(pos != -1) + { + str.replace(pos, needle.size(), substitute); + pos = str.find(needle); + } +} + +bool parseOptions (int argc, char** argv, Arguments &info) +{ + bpo::options_description desc("Inspect and extract files from Bethesda BSA archives\n\n" + "Usages:\n" + " bsatool list [-l] archivefile\n" + " List the files presents in the input archive.\n\n" + " bsatool extract [-f] archivefile [file_to_extract] [output_directory]\n" + " Extract a file from the input archive.\n\n" + " bsatool extractall archivefile [output_directory]\n" + " Extract all files from the input archive.\n\n" + "Allowed options"); + + desc.add_options() + ("help,h", "print help message.") + ("version,v", "print version information and quit.") + ("long,l", "Include extra information in archive listing.") + ("full-path,f", "Create diretory hierarchy on file extraction " + "(always true for extractall).") + ; + + // input-file is hidden and used as a positional argument + bpo::options_description hidden("Hidden Options"); + + hidden.add_options() + ( "mode,m", bpo::value(), "bsatool mode") + ( "input-file,i", bpo::value< std::vector >(), "input file") + ; + + bpo::positional_options_description p; + p.add("mode", 1).add("input-file", 3); + + // there might be a better way to do this + bpo::options_description all; + all.add(desc).add(hidden); + + bpo::variables_map variables; + try + { + bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv) + .options(all).positional(p).run(); + bpo::store(valid_opts, variables); + } + catch(std::exception &e) + { + std::cout << "ERROR parsing arguments: " << e.what() << "\n\n" + << desc << std::endl; + return false; + } + + bpo::notify(variables); + + if (variables.count ("help")) + { + std::cout << desc << std::endl; + return false; + } + if (variables.count ("version")) + { + std::cout << "BSATool version " << BSATOOL_VERSION << std::endl; + return false; + } + if (!variables.count("mode")) + { + std::cout << "ERROR: no mode specified!\n\n" + << desc << std::endl; + return false; + } + + info.mode = variables["mode"].as(); + if (!(info.mode == "list" || info.mode == "extract" || info.mode == "extractall")) + { + std::cout << std::endl << "ERROR: invalid mode \"" << info.mode << "\"\n\n" + << desc << std::endl; + return false; + } + + if (!variables.count("input-file")) + { + std::cout << "\nERROR: missing BSA archive\n\n" + << desc << std::endl; + return false; + } + info.filename = variables["input-file"].as< std::vector >()[0]; + + // Default output to the working directory + info.outdir = "."; + + if (info.mode == "extract") + { + if (variables["input-file"].as< std::vector >().size() < 2) + { + std::cout << "\nERROR: file to extract unspecified\n\n" + << desc << std::endl; + return false; + } + if (variables["input-file"].as< std::vector >().size() > 1) + info.extractfile = variables["input-file"].as< std::vector >()[1]; + if (variables["input-file"].as< std::vector >().size() > 2) + info.outdir = variables["input-file"].as< std::vector >()[2]; + } + else if (variables["input-file"].as< std::vector >().size() > 1) + info.outdir = variables["input-file"].as< std::vector >()[1]; + + info.longformat = variables.count("long"); + info.fullpath = variables.count("full-path"); + + return true; +} + +int list(Bsa::BSAFile& bsa, Arguments& info); +int extract(Bsa::BSAFile& bsa, Arguments& info); +int extractAll(Bsa::BSAFile& bsa, Arguments& info); + +int main(int argc, char** argv) +{ + Arguments info; + if(!parseOptions (argc, argv, info)) + return 1; + + // Open file + Bsa::BSAFile bsa; + try + { + bsa.open(info.filename); + } + catch(std::exception &e) + { + std::cout << "ERROR reading BSA archive '" << info.filename + << "'\nDetails:\n" << e.what() << std::endl; + return 2; + } + + if (info.mode == "list") + return list(bsa, info); + else if (info.mode == "extract") + return extract(bsa, info); + else if (info.mode == "extractall") + return extractAll(bsa, info); + else + { + std::cout << "Unsupported mode. That is not supposed to happen." << std::endl; + return 1; + } +} + +int list(Bsa::BSAFile& bsa, Arguments& info) +{ + // List all files + const Bsa::BSAFile::FileList &files = bsa.getList(); + for(int i=0; igetAsString().c_str(), data->size()); + out.close(); + + return 0; +} + +int extractAll(Bsa::BSAFile& bsa, Arguments& info) +{ + // Get the list of files present in the archive + Bsa::BSAFile::FileList list = bsa.getList(); + + // Iter on the list + for(Bsa::BSAFile::FileList::iterator it = list.begin(); it != list.end(); ++it) { + const char* archivePath = it->name; + + std::string extractPath (archivePath); + replaceAll(extractPath, "\\", "/"); + + // Get the target path (the path the file will be extracted to) + bfs::path target (info.outdir); + target /= extractPath; + + // Create the directory hierarchy + bfs::create_directories(target.parent_path()); + + bfs::file_status s = bfs::status(target.parent_path()); + if (!bfs::is_directory(s)) + { + std::cout << "ERROR: " << target.parent_path() << " is not a directory." << std::endl; + return 3; + } + + // Get a stream for the file to extract + // (inefficient because getFile iter on the list again) + Ogre::DataStreamPtr data = bsa.getFile(archivePath); + bfs::ofstream out(target, std::ios::binary); + + // Write the file to disk + std::cout << "Extracting " << target << std::endl; + out.write(data->getAsString().c_str(), data->size()); + out.close(); + } + + return 0; +} diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index fbfc884d7..8e0900ba0 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -23,8 +23,7 @@ struct ESMData std::string author; std::string description; int version; - int type; - ESM::ESMReader::MasterList masters; + std::vector masters; std::deque mRecords; std::map > mCellRefs; @@ -52,6 +51,7 @@ struct Arguments unsigned int raw_given; unsigned int quiet_given; unsigned int loadcells_given; + bool plain_given; std::string mode; std::string encoding; @@ -78,6 +78,9 @@ bool parseOptions (int argc, char** argv, Arguments &info) ("type,t", bpo::value< std::vector >(), "Show only records of this type (four character record code). May " "be specified multiple times. Only affects dump mode.") + ("plain,p", "Print contents of dialogs, books and scripts. " + "(skipped by default)" + "Only affects dump mode.") ("quiet,q", "Supress all record information. Useful for speed tests.") ("loadcells,C", "Browse through contents of all cells.") @@ -162,6 +165,7 @@ bool parseOptions (int argc, char** argv, Arguments &info) info.raw_given = variables.count ("raw"); info.quiet_given = variables.count ("quiet"); info.loadcells_given = variables.count ("loadcells"); + info.plain_given = (variables.count("plain") > 0); // Font encoding settings info.encoding = variables["encoding"].as(); @@ -209,7 +213,10 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info) bool save = (info.mode == "clone"); // Skip back to the beginning of the reference list - cell.restore(esm); + // FIXME: Changes to the references backend required to support multiple plugins have + // almost certainly broken this following line. I'll leave it as is for now, so that + // the compiler does not complain. + cell.restore(esm, 0); // Loop through all the references ESM::CellRef ref; @@ -225,7 +232,10 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info) std::cout << " Refnum: " << ref.mRefnum << std::endl; std::cout << " ID: '" << ref.mRefID << "'\n"; std::cout << " Owner: '" << ref.mOwner << "'\n"; - std::cout << " INTV: " << ref.mIntv << " NAM9: " << ref.mIntv << std::endl; + std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n"; + std::cout << " Uses/health: '" << ref.mCharge << "'\n"; + std::cout << " Gold value: '" << ref.mGoldValue << "'\n"; + std::cout << " Blocked: '" << static_cast(ref.mReferenceBlocked) << "'" << std::endl; } } @@ -281,16 +291,13 @@ int load(Arguments& info) info.data.author = esm.getAuthor(); info.data.description = esm.getDesc(); info.data.masters = esm.getMasters(); - info.data.version = esm.getVer(); - info.data.type = esm.getType(); if (!quiet) { std::cout << "Author: " << esm.getAuthor() << std::endl << "Description: " << esm.getDesc() << std::endl - << "File format version: " << esm.getFVer() << std::endl - << "Special flag: " << esm.getSpecial() << std::endl; - ESM::ESMReader::MasterList m = esm.getMasters(); + << "File format version: " << esm.getFVer() << std::endl; + std::vector m = esm.getMasters(); if (!m.empty()) { std::cout << "Masters:" << std::endl; @@ -341,6 +348,7 @@ int load(Arguments& info) } record->setId(id); record->setFlags((int) flags); + record->setPrintPlain(info.plain_given); record->load(esm); if (!quiet && interested) record->print(); @@ -427,9 +435,9 @@ int clone(Arguments& info) esm.setAuthor(info.data.author); esm.setDescription(info.data.description); esm.setVersion(info.data.version); - esm.setType(info.data.type); + esm.setRecordCount (recordCount); - for (ESM::ESMReader::MasterList::iterator it = info.data.masters.begin(); it != info.data.masters.end(); ++it) + for (std::vector::iterator it = info.data.masters.begin(); it != info.data.masters.end(); ++it) esm.addMaster(it->name, it->size); std::fstream save(info.outname.c_str(), std::fstream::out | std::fstream::binary); diff --git a/apps/esmtool/labels.cpp b/apps/esmtool/labels.cpp index 9fb233237..3fb1166e8 100644 --- a/apps/esmtool/labels.cpp +++ b/apps/esmtool/labels.cpp @@ -16,7 +16,7 @@ #include #include -std::string bodyPartLabel(char idx) +std::string bodyPartLabel(int idx) { const char *bodyPartLabels[] = { "Head", @@ -47,14 +47,14 @@ std::string bodyPartLabel(char idx) "Weapon", "Tail" }; - - if ((int)idx >= 0 && (int)(idx) <= 26) - return bodyPartLabels[(int)(idx)]; + + if (idx >= 0 && idx <= 26) + return bodyPartLabels[idx]; else return "Invalid"; } -std::string meshPartLabel(char idx) +std::string meshPartLabel(int idx) { const char *meshPartLabels[] = { "Head", @@ -73,25 +73,25 @@ std::string meshPartLabel(char idx) "Clavicle", "Tail" }; - - if ((int)(idx) >= 0 && (int)(idx) <= ESM::BodyPart::MP_Tail) - return meshPartLabels[(int)(idx)]; + + if (idx >= 0 && idx <= ESM::BodyPart::MP_Tail) + return meshPartLabels[idx]; else return "Invalid"; } -std::string meshTypeLabel(char idx) +std::string meshTypeLabel(int idx) { const char *meshTypeLabels[] = { "Skin", "Clothing", "Armor" }; - - if ((int)(idx) >= 0 && (int)(idx) <= ESM::BodyPart::MT_Armor) - return meshTypeLabels[(int)(idx)]; + + if (idx >= 0 && idx <= ESM::BodyPart::MT_Armor) + return meshTypeLabels[idx]; else - return "Invalid"; + return "Invalid"; } std::string clothingTypeLabel(int idx) @@ -108,7 +108,7 @@ std::string clothingTypeLabel(int idx) "Ring", "Amulet" }; - + if (idx >= 0 && idx <= 9) return clothingTypeLabels[idx]; else @@ -627,10 +627,10 @@ std::string bodyPartFlags(int flags) std::string properties = ""; if (flags == 0) properties += "[None] "; if (flags & ESM::BodyPart::BPF_Female) properties += "Female "; - if (flags & ESM::BodyPart::BPF_Playable) properties += "Playable "; + if (flags & ESM::BodyPart::BPF_NotPlayable) properties += "NotPlayable "; int unused = (0xFFFFFFFF ^ (ESM::BodyPart::BPF_Female| - ESM::BodyPart::BPF_Playable)); + ESM::BodyPart::BPF_NotPlayable)); 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 7f1ff7986..48d7b249b 100644 --- a/apps/esmtool/labels.hpp +++ b/apps/esmtool/labels.hpp @@ -3,9 +3,9 @@ #include -std::string bodyPartLabel(char idx); -std::string meshPartLabel(char idx); -std::string meshTypeLabel(char idx); +std::string bodyPartLabel(int idx); +std::string meshPartLabel(int idx); +std::string meshTypeLabel(int idx); std::string clothingTypeLabel(int idx); std::string armorTypeLabel(int idx); std::string dialogTypeLabel(int idx); diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index a732f1938..8e2de6494 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -112,14 +112,11 @@ std::string ruleString(ESM::DialInfo::SelectStruct ss) case '5': oper_str = ">="; break; } - std::string value_str = "??"; - if (ss.mType == ESM::VT_Int) - value_str = str(boost::format("%d") % ss.mI); - else if (ss.mType == ESM::VT_Float) - value_str = str(boost::format("%f") % ss.mF); + std::ostringstream stream; + stream << ss.mValue; std::string result = str(boost::format("%-12s %-32s %2s %s") - % type_str % func_str % oper_str % value_str); + % type_str % func_str % oper_str % stream.str()); return result; } @@ -278,7 +275,7 @@ RecordBase::create(ESM::NAME type) } case ESM::REC_LOCK: { - record = new EsmTool::Record; + record = new EsmTool::Record; break; } case ESM::REC_LTEX: @@ -442,7 +439,7 @@ void Record::print() template<> void Record::print() { - std::cout << " Name: " << mData.mName << std::endl; + std::cout << " Race: " << mData.mRace << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Type: " << meshTypeLabel(mData.mData.mType) << " (" << (int)mData.mData.mType << ")" << std::endl; @@ -467,12 +464,17 @@ void Record::print() std::cout << " IsScroll: " << mData.mData.mIsScroll << std::endl; std::cout << " SkillID: " << mData.mData.mSkillID << std::endl; std::cout << " Enchantment Points: " << mData.mData.mEnchant << std::endl; - std::cout << " Text: [skipped]" << std::endl; - // Skip until multi-line fields is controllable by a command line option. - // Mildly problematic because there are no parameter to print() currently. - // std::cout << "-------------------------------------------" << std::endl; - // std::cout << mData.mText << std::endl; - // std::cout << "-------------------------------------------" << std::endl; + if (mPrintPlain) + { + std::cout << " Text:" << std::endl; + std::cout << "START--------------------------------------" << std::endl; + std::cout << mData.mText << std::endl; + std::cout << "END----------------------------------------" << std::endl; + } + else + { + std::cout << " Text: [skipped]" << std::endl; + } } template<> @@ -682,14 +684,14 @@ void Record::print() std::cout << " Hidden: " << mData.mData.mIsHidden << std::endl; if (mData.mData.mUnknown != -1) std::cout << " Unknown: " << mData.mData.mUnknown << std::endl; - std::cout << " Attribute1: " << attributeLabel(mData.mData.mAttribute1) - << " (" << mData.mData.mAttribute1 << ")" << std::endl; - std::cout << " Attribute2: " << attributeLabel(mData.mData.mAttribute2) - << " (" << mData.mData.mAttribute2 << ")" << std::endl; + std::cout << " Attribute1: " << attributeLabel(mData.mData.mAttribute[0]) + << " (" << mData.mData.mAttribute[0] << ")" << std::endl; + std::cout << " Attribute2: " << attributeLabel(mData.mData.mAttribute[1]) + << " (" << mData.mData.mAttribute[1] << ")" << std::endl; for (int i = 0; i != 6; i++) - if (mData.mData.mSkillID[i] != -1) - std::cout << " Skill: " << skillLabel(mData.mData.mSkillID[i]) - << " (" << mData.mData.mSkillID[i] << ")" << std::endl; + if (mData.mData.mSkills[i] != -1) + std::cout << " Skill: " << skillLabel(mData.mData.mSkills[i]) + << " (" << mData.mData.mSkills[i] << ")" << std::endl; for (int i = 0; i != 10; i++) if (mData.mRanks[i] != "") { @@ -713,31 +715,13 @@ void Record::print() template<> void Record::print() { - // nothing to print (well, nothing that's correct anyway) - std::cout << " Type: " << mData.mType << std::endl; - std::cout << " Value: " << mData.mValue << std::endl; + std::cout << " " << mData.mValue << std::endl; } template<> void Record::print() { - std::cout << " Value: "; - switch (mData.mType) { - case ESM::VT_String: - std::cout << "'" << mData.mStr << "' (std::string)"; - break; - - case ESM::VT_Float: - std::cout << mData.mF << " (float)"; - break; - - case ESM::VT_Int: - std::cout << mData.mI << " (int)"; - break; - - default: - std::cout << "unknown type"; - } + std::cout << " " << mData.mValue << std::endl; } template<> @@ -774,15 +758,6 @@ void Record::print() if (mData.mSound != "") std::cout << " Sound File: " << mData.mSound << std::endl; - if (mData.mResultScript != "") - { - std::cout << " Result Script: [skipped]" << std::endl; - // Skip until multi-line fields is controllable by a command line option. - // Mildly problematic because there are no parameter to print() currently. - // std::cout << "-------------------------------------------" << std::endl; - // std::cout << mData.mResultScript << std::endl; - // std::cout << "-------------------------------------------" << std::endl; - } std::cout << " Quest Status: " << questStatusLabel(mData.mQuestStatus) << " (" << mData.mQuestStatus << ")" << std::endl; @@ -792,6 +767,21 @@ void Record::print() std::vector::iterator sit; for (sit = mData.mSelects.begin(); sit != mData.mSelects.end(); sit++) std::cout << " Select Rule: " << ruleString(*sit) << std::endl; + + if (mData.mResultScript != "") + { + if (mPrintPlain) + { + std::cout << " Result Script:" << std::endl; + std::cout << "START--------------------------------------" << std::endl; + std::cout << mData.mResultScript << std::endl; + std::cout << "END----------------------------------------" << std::endl; + } + else + { + std::cout << " Result Script: [skipped]" << std::endl; + } + } } template<> @@ -885,14 +875,13 @@ void Record::print() } template<> -void Record::print() +void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Icon: " << mData.mIcon << std::endl; if (mData.mScript != "") std::cout << " Script: " << mData.mScript << std::endl; - std::cout << " Type: " << mData.mType << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl; @@ -907,8 +896,6 @@ void Record::print() std::cout << " Icon: " << mData.mIcon << std::endl; if (mData.mScript != "") std::cout << " Script: " << mData.mScript << std::endl; - // BUG? No Type Label? - std::cout << " Type: " << mData.mType << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl; @@ -923,7 +910,6 @@ void Record::print() std::cout << " Icon: " << mData.mIcon << std::endl; if (mData.mScript != "") std::cout << " Script: " << mData.mScript << std::endl; - std::cout << " Type: " << mData.mType << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Quality: " << mData.mData.mQuality << std::endl; @@ -1124,53 +1110,29 @@ void Record::print() template<> void Record::print() { + static const char *sAttributeNames[8] = + { + "Strength", "Intelligence", "Willpower", "Agility", + "Speed", "Endurance", "Personality", "Luck" + }; + std::cout << " Name: " << mData.mName << std::endl; std::cout << " Description: " << mData.mDescription << std::endl; std::cout << " Flags: " << raceFlags(mData.mData.mFlags) << std::endl; - std::cout << " Male:" << std::endl; - std::cout << " Strength: " - << mData.mData.mStrength.mMale << std::endl; - std::cout << " Intelligence: " - << mData.mData.mIntelligence.mMale << std::endl; - std::cout << " Willpower: " - << mData.mData.mWillpower.mMale << std::endl; - std::cout << " Agility: " - << mData.mData.mAgility.mMale << std::endl; - std::cout << " Speed: " - << mData.mData.mSpeed.mMale << std::endl; - std::cout << " Endurance: " - << mData.mData.mEndurance.mMale << std::endl; - std::cout << " Personality: " - << mData.mData.mPersonality.mMale << std::endl; - std::cout << " Luck: " - << mData.mData.mLuck.mMale << std::endl; - std::cout << " Height: " - << mData.mData.mHeight.mMale << std::endl; - std::cout << " Weight: " - << mData.mData.mWeight.mMale << std::endl; - - std::cout << " Female:" << std::endl; - std::cout << " Strength: " - << mData.mData.mStrength.mFemale << std::endl; - std::cout << " Intelligence: " - << mData.mData.mIntelligence.mFemale << std::endl; - std::cout << " Willpower: " - << mData.mData.mWillpower.mFemale << std::endl; - std::cout << " Agility: " - << mData.mData.mAgility.mFemale << std::endl; - std::cout << " Speed: " - << mData.mData.mSpeed.mFemale << std::endl; - std::cout << " Endurance: " - << mData.mData.mEndurance.mFemale << std::endl; - std::cout << " Personality: " - << mData.mData.mPersonality.mFemale << std::endl; - std::cout << " Luck: " - << mData.mData.mLuck.mFemale << std::endl; - std::cout << " Height: " - << mData.mData.mHeight.mFemale << std::endl; - std::cout << " Weight: " - << mData.mData.mWeight.mFemale << std::endl; + for (int i=0; i<2; ++i) + { + bool male = i==0; + + std::cout << (male ? " Male:" : " Female:") << std::endl; + + for (int i=0; i<8; ++i) + std::cout << " " << sAttributeNames[i] << ": " + << mData.mData.mAttributeValues[i].getValue (male) << std::endl; + + std::cout << " Height: " << mData.mData.mHeight.getValue (male) << std::endl; + std::cout << " Weight: " << mData.mData.mWeight.getValue (male) << std::endl; + } for (int i = 0; i != 7; i++) // Not all races have 7 skills. @@ -1220,21 +1182,28 @@ void Record::print() std::cout << " Script Data Size: " << mData.mData.mScriptDataSize << std::endl; std::cout << " Table Size: " << mData.mData.mStringTableSize << std::endl; - std::cout << " Script: [skipped]" << std::endl; - // Skip until multi-line fields is controllable by a command line option. - // Mildly problematic because there are no parameter to print() currently. - // std::cout << "-------------------------------------------" << std::endl; - // std::cout << s->scriptText << std::endl; - // std::cout << "-------------------------------------------" << std::endl; + std::vector::iterator vit; for (vit = mData.mVarNames.begin(); vit != mData.mVarNames.end(); vit++) std::cout << " Variable: " << *vit << std::endl; std::cout << " ByteCode: "; - std::vector::iterator cit; + std::vector::iterator cit; for (cit = mData.mScriptData.begin(); cit != mData.mScriptData.end(); cit++) std::cout << boost::format("%02X") % (int)(*cit); std::cout << std::endl; + + if (mPrintPlain) + { + std::cout << " Script:" << std::endl; + std::cout << "START--------------------------------------" << std::endl; + std::cout << mData.mScriptText << std::endl; + std::cout << "END----------------------------------------" << std::endl; + } + else + { + std::cout << " Script: [skipped]" << std::endl; + } } template<> diff --git a/apps/esmtool/record.hpp b/apps/esmtool/record.hpp index c3daa9d0c..78cf5d436 100644 --- a/apps/esmtool/record.hpp +++ b/apps/esmtool/record.hpp @@ -21,9 +21,10 @@ namespace EsmTool std::string mId; int mFlags; ESM::NAME mType; + bool mPrintPlain; public: - RecordBase () {} + RecordBase () { mPrintPlain = false; } virtual ~RecordBase() {} const std::string &getId() const { @@ -46,6 +47,14 @@ namespace EsmTool return mType; } + bool getPrintPlain() const { + return mPrintPlain; + } + + void setPrintPlain(bool plain) { + mPrintPlain = plain; + } + virtual void load(ESM::ESMReader &esm) = 0; virtual void save(ESM::ESMWriter &esm) = 0; virtual void print() = 0; @@ -104,7 +113,7 @@ namespace EsmTool template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); - template<> void Record::print(); + template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); template<> void Record::print(); diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index 09beaf59d..0c93474da 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -5,17 +5,14 @@ set(LAUNCHER maindialog.cpp playpage.cpp - model/datafilesmodel.cpp - model/modelitem.cpp - model/esm/esmfile.cpp - - utils/filedialog.cpp - utils/naturalsort.cpp - utils/lineedit.cpp - utils/profilescombobox.cpp + settings/gamesettings.cpp + settings/graphicssettings.cpp + settings/launchersettings.cpp + + utils/checkablemessagebox.cpp utils/textinputdialog.cpp - launcher.rc + ${CMAKE_SOURCE_DIR}/files/launcher/launcher.rc ) set(LAUNCHER_HEADER @@ -24,14 +21,12 @@ set(LAUNCHER_HEADER maindialog.hpp playpage.hpp - model/datafilesmodel.hpp - model/modelitem.hpp - model/esm/esmfile.hpp + settings/gamesettings.hpp + settings/graphicssettings.hpp + settings/launchersettings.hpp + settings/settingsbase.hpp - utils/lineedit.hpp - utils/filedialog.hpp - utils/naturalsort.hpp - utils/profilescombobox.hpp + utils/checkablemessagebox.hpp utils/textinputdialog.hpp ) @@ -43,17 +38,18 @@ set(LAUNCHER_HEADER_MOC maindialog.hpp playpage.hpp - model/datafilesmodel.hpp - model/modelitem.hpp - model/esm/esmfile.hpp - - utils/lineedit.hpp - utils/filedialog.hpp - utils/profilescombobox.hpp + utils/checkablemessagebox.hpp utils/textinputdialog.hpp ) -source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER} ${LAUNCHER_HEADER_MOC}) +set(LAUNCHER_UI + ${CMAKE_SOURCE_DIR}/files/ui/datafilespage.ui + ${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui + ${CMAKE_SOURCE_DIR}/files/ui/mainwindow.ui + ${CMAKE_SOURCE_DIR}/files/ui/playpage.ui +) + +source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER}) find_package(Qt4 REQUIRED) set(QT_USE_QTGUI 1) @@ -64,10 +60,12 @@ if(WIN32) set(QT_USE_QTMAIN TRUE) endif(WIN32) -QT4_ADD_RESOURCES(RCC_SRCS resources.qrc) +QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc) QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC}) +QT4_WRAP_UI(UI_HDRS ${LAUNCHER_UI}) include(${QT_USE_FILE}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) # Main executable IF(OGRE_STATIC) @@ -82,8 +80,10 @@ ENDIF(OGRE_STATIC) add_executable(omwlauncher ${GUI_TYPE} ${LAUNCHER} + ${LAUNCHER_HEADER} ${RCC_SRCS} ${MOC_SRCS} + ${UI_HDRS} ) target_link_libraries(omwlauncher @@ -98,19 +98,13 @@ if(DPKG_PROGRAM) INSTALL(TARGETS omwlauncher RUNTIME DESTINATION games COMPONENT omwlauncher) endif() -if (APPLE) - configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss - "${APP_BUNDLE_DIR}/../launcher.qss") -else() - configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss - "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/resources/launcher.qss") - - # Fallback in case getGlobalDataPath does not point to resources - configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss - "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.qss") -endif() - if (BUILD_WITH_CODE_COVERAGE) add_definitions (--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/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 6b0539c1d..add3dea40 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -1,641 +1,311 @@ -#include - -#include -#include - -#include "model/datafilesmodel.hpp" -#include "model/esm/esmfile.hpp" - -#include "utils/profilescombobox.hpp" -#include "utils/filedialog.hpp" -#include "utils/lineedit.hpp" -#include "utils/naturalsort.hpp" -#include "utils/textinputdialog.hpp" - #include "datafilespage.hpp" -#include -/** - * Workaround for problems with whitespaces in paths in older versions of Boost library - */ -#if (BOOST_VERSION <= 104600) -namespace boost -{ +#include +#include +#include +#include - template<> - inline boost::filesystem::path lexical_cast(const std::string& arg) - { - return boost::filesystem::path(arg); - } +#include -} /* namespace boost */ -#endif /* (BOOST_VERSION <= 104600) */ +#include +#include +#include -using namespace ESM; -using namespace std; +#include +#include +#include -//sort QModelIndexList ascending -bool rowGreaterThan(const QModelIndex &index1, const QModelIndex &index2) -{ - return index1.row() >= index2.row(); -} +#include "settings/gamesettings.hpp" +#include "settings/launchersettings.hpp" -//sort QModelIndexList descending -bool rowSmallerThan(const QModelIndex &index1, const QModelIndex &index2) -{ - return index1.row() <= index2.row(); -} +#include "utils/textinputdialog.hpp" -DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, QWidget *parent) - : QWidget(parent) - , mCfgMgr(cfg) +DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent) + : mCfgMgr(cfg) + , mGameSettings(gameSettings) + , mLauncherSettings(launcherSettings) + , QWidget(parent) { - // Models - mMastersModel = new DataFilesModel(this); - mPluginsModel = new DataFilesModel(this); - - mPluginsProxyModel = new QSortFilterProxyModel(); - mPluginsProxyModel->setDynamicSortFilter(true); - mPluginsProxyModel->setSourceModel(mPluginsModel); - - // Filter toolbar - QLabel *filterLabel = new QLabel(tr("&Filter:"), this); - LineEdit *filterLineEdit = new LineEdit(this); - filterLabel->setBuddy(filterLineEdit); + setupUi(this); - QToolBar *filterToolBar = new QToolBar(this); - filterToolBar->setMovable(false); + // Models + mDataFilesModel = new DataFilesModel(this); - // Create a container widget and a layout to get the spacer to work - QWidget *filterWidget = new QWidget(this); - QHBoxLayout *filterLayout = new QHBoxLayout(filterWidget); - QSpacerItem *hSpacer1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + mMastersProxyModel = new QSortFilterProxyModel(); + mMastersProxyModel->setFilterRegExp(QString("^.*\\.esm")); + mMastersProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + mMastersProxyModel->setSourceModel(mDataFilesModel); - filterLayout->addItem(hSpacer1); - filterLayout->addWidget(filterLabel); - filterLayout->addWidget(filterLineEdit); + mPluginsProxyModel = new PluginsProxyModel(); + mPluginsProxyModel->setFilterRegExp(QString("^.*\\.esp")); + mPluginsProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + mPluginsProxyModel->setSourceModel(mDataFilesModel); - filterToolBar->addWidget(filterWidget); + mFilterProxyModel = new QSortFilterProxyModel(); + mFilterProxyModel->setDynamicSortFilter(true); + mFilterProxyModel->setSourceModel(mPluginsProxyModel); QCheckBox checkBox; unsigned int height = checkBox.sizeHint().height() + 4; - mMastersTable = new QTableView(this); - mMastersTable->setModel(mMastersModel); - mMastersTable->setObjectName("MastersTable"); - mMastersTable->setSelectionBehavior(QAbstractItemView::SelectRows); - mMastersTable->setSelectionMode(QAbstractItemView::SingleSelection); - mMastersTable->setEditTriggers(QAbstractItemView::NoEditTriggers); - mMastersTable->setAlternatingRowColors(true); - mMastersTable->horizontalHeader()->setStretchLastSection(true); - mMastersTable->horizontalHeader()->hide(); + mastersTable->setModel(mMastersProxyModel); + mastersTable->setObjectName("MastersTable"); + mastersTable->setContextMenuPolicy(Qt::CustomContextMenu); + mastersTable->setSortingEnabled(false); + mastersTable->setSelectionBehavior(QAbstractItemView::SelectRows); + mastersTable->setSelectionMode(QAbstractItemView::ExtendedSelection); + mastersTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + mastersTable->setAlternatingRowColors(true); + mastersTable->horizontalHeader()->setStretchLastSection(true); + mastersTable->horizontalHeader()->hide(); // Set the row height to the size of the checkboxes - mMastersTable->verticalHeader()->setDefaultSectionSize(height); - mMastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); - mMastersTable->verticalHeader()->hide(); - mMastersTable->setColumnHidden(1, true); - mMastersTable->setColumnHidden(2, true); - mMastersTable->setColumnHidden(3, true); - mMastersTable->setColumnHidden(4, true); - mMastersTable->setColumnHidden(5, true); - mMastersTable->setColumnHidden(6, true); - mMastersTable->setColumnHidden(7, true); - mMastersTable->setColumnHidden(8, true); - - mPluginsTable = new QTableView(this); - mPluginsTable->setModel(mPluginsProxyModel); - mPluginsTable->setObjectName("PluginsTable"); - mPluginsTable->setContextMenuPolicy(Qt::CustomContextMenu); - mPluginsTable->setSelectionBehavior(QAbstractItemView::SelectRows); - mPluginsTable->setSelectionMode(QAbstractItemView::SingleSelection); - mPluginsTable->setEditTriggers(QAbstractItemView::NoEditTriggers); - mPluginsTable->setAlternatingRowColors(true); - mPluginsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem); - mPluginsTable->horizontalHeader()->setStretchLastSection(true); - mPluginsTable->horizontalHeader()->hide(); - - mPluginsTable->verticalHeader()->setDefaultSectionSize(height); - mPluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); - mPluginsTable->setColumnHidden(1, true); - mPluginsTable->setColumnHidden(2, true); - mPluginsTable->setColumnHidden(3, true); - mPluginsTable->setColumnHidden(4, true); - mPluginsTable->setColumnHidden(5, true); - mPluginsTable->setColumnHidden(6, true); - mPluginsTable->setColumnHidden(7, true); - mPluginsTable->setColumnHidden(8, true); - - // Add both tables to a splitter - QSplitter *splitter = new QSplitter(this); - splitter->setOrientation(Qt::Horizontal); - splitter->addWidget(mMastersTable); - splitter->addWidget(mPluginsTable); - - // Adjust the default widget widths inside the splitter + mastersTable->verticalHeader()->setDefaultSectionSize(height); + mastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); + mastersTable->verticalHeader()->hide(); + + pluginsTable->setModel(mFilterProxyModel); + pluginsTable->setObjectName("PluginsTable"); + pluginsTable->setContextMenuPolicy(Qt::CustomContextMenu); + pluginsTable->setSortingEnabled(false); + pluginsTable->setSelectionBehavior(QAbstractItemView::SelectRows); + pluginsTable->setSelectionMode(QAbstractItemView::ExtendedSelection); + pluginsTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + pluginsTable->setAlternatingRowColors(true); + pluginsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem); + pluginsTable->horizontalHeader()->setStretchLastSection(true); + pluginsTable->horizontalHeader()->hide(); + + pluginsTable->verticalHeader()->setDefaultSectionSize(height); + pluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); + + // Adjust the tableview widths inside the splitter QList sizeList; - sizeList << 175 << 200; - splitter->setSizes(sizeList); - - // Bottom part with profile options - QLabel *profileLabel = new QLabel(tr("Current Profile: "), this); + sizeList << mLauncherSettings.value(QString("General/MastersTable/width"), QString("200")).toInt(); + sizeList << mLauncherSettings.value(QString("General/PluginTable/width"), QString("340")).toInt(); - mProfilesComboBox = new ProfilesComboBox(this); - mProfilesComboBox->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); - mProfilesComboBox->setInsertPolicy(QComboBox::NoInsert); - mProfilesComboBox->setDuplicatesEnabled(false); - mProfilesComboBox->setEditEnabled(false); - - mProfileToolBar = new QToolBar(this); - mProfileToolBar->setMovable(false); - mProfileToolBar->setIconSize(QSize(16, 16)); - - mProfileToolBar->addWidget(profileLabel); - mProfileToolBar->addWidget(mProfilesComboBox); - - QVBoxLayout *pageLayout = new QVBoxLayout(this); - - pageLayout->addWidget(filterToolBar); - pageLayout->addWidget(splitter); - pageLayout->addWidget(mProfileToolBar); + splitter->setSizes(sizeList); // Create a dialog for the new profile name input mNewProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this); + connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentIndexChanged(int))); + connect(mNewProfileDialog->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(updateOkButton(QString))); - connect(mPluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); - connect(mMastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); + connect(pluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); + connect(mastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); - connect(mMastersModel, SIGNAL(checkedItemsChanged(QStringList,QStringList)), mPluginsModel, SLOT(slotcheckedItemsChanged(QStringList,QStringList))); + connect(pluginsTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); + connect(mastersTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); - connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); + connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); - connect(mPluginsTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); + connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); - connect(mProfilesComboBox, SIGNAL(profileRenamed(QString,QString)), this, SLOT(profileRenamed(QString,QString))); - connect(mProfilesComboBox, SIGNAL(profileChanged(QString,QString)), this, SLOT(profileChanged(QString,QString))); + connect(splitter, SIGNAL(splitterMoved(int,int)), this, SLOT(updateSplitter())); createActions(); - setupConfig(); + setupDataFiles(); } void DataFilesPage::createActions() { - // Refresh the plugins - QAction *refreshAction = new QAction(tr("Refresh"), this); - refreshAction->setShortcut(QKeySequence(tr("F5"))); - connect(refreshAction, SIGNAL(triggered()), this, SLOT(refresh())); - - // Profile actions - mNewProfileAction = new QAction(QIcon::fromTheme("document-new"), tr("&New Profile"), this); - mNewProfileAction->setToolTip(tr("New Profile")); - mNewProfileAction->setShortcut(QKeySequence(tr("Ctrl+N"))); - connect(mNewProfileAction, SIGNAL(triggered()), this, SLOT(newProfile())); - - mDeleteProfileAction = new QAction(QIcon::fromTheme("edit-delete"), tr("Delete Profile"), this); - mDeleteProfileAction->setToolTip(tr("Delete Profile")); - mDeleteProfileAction->setShortcut(QKeySequence(tr("Delete"))); - connect(mDeleteProfileAction, SIGNAL(triggered()), this, SLOT(deleteProfile())); - - // Add the newly created actions to the toolbar - mProfileToolBar->addSeparator(); - mProfileToolBar->addAction(mNewProfileAction); - mProfileToolBar->addAction(mDeleteProfileAction); - - // Context menu actions - mCheckAction = new QAction(tr("Check selected"), this); - connect(mCheckAction, SIGNAL(triggered()), this, SLOT(check())); - mUncheckAction = new QAction(tr("Uncheck selected"), this); - connect(mUncheckAction, SIGNAL(triggered()), this, SLOT(uncheck())); + // Add the actions to the toolbuttons + newProfileButton->setDefaultAction(newProfileAction); + deleteProfileButton->setDefaultAction(deleteProfileAction); - // Context menu for the plugins table + // Context menu actions mContextMenu = new QMenu(this); - - mContextMenu->addAction(mCheckAction); - mContextMenu->addAction(mUncheckAction); - + mContextMenu->addAction(checkAction); + mContextMenu->addAction(uncheckAction); } -void DataFilesPage::setupConfig() +void DataFilesPage::setupDataFiles() { - // Open our config file - QString config = QString::fromStdString((mCfgMgr.getUserPath() / "launcher.cfg").string()); - mLauncherConfig = new QSettings(config, QSettings::IniFormat); - - // Make sure we have no groups open - while (!mLauncherConfig->group().isEmpty()) { - mLauncherConfig->endGroup(); - } - - mLauncherConfig->beginGroup("Profiles"); - QStringList profiles = mLauncherConfig->childGroups(); - - // Add the profiles to the combobox - foreach (const QString &profile, profiles) { - - if (profile.contains(QRegExp("[^a-zA-Z0-9_]"))) - continue; // Profile name contains garbage + // Set the encoding to the one found in openmw.cfg or the default + mDataFilesModel->setEncoding(mGameSettings.value(QString("encoding"), QString("win1252"))); + QStringList paths = mGameSettings.getDataDirs(); - qDebug() << "adding " << profile; - mProfilesComboBox->addItem(profile); + foreach (const QString &path, paths) { + mDataFilesModel->addFiles(path); } - // Add a default profile - if (mProfilesComboBox->findText(QString("Default")) == -1) { - mProfilesComboBox->addItem(QString("Default")); - } + QString dataLocal = mGameSettings.getDataLocal(); + if (!dataLocal.isEmpty()) + mDataFilesModel->addFiles(dataLocal); - QString currentProfile = mLauncherConfig->value("CurrentProfile").toString(); + // Sort by date accessed for now + mDataFilesModel->sort(3); - if (currentProfile.isEmpty()) { - // No current profile selected - currentProfile = "Default"; - } + QStringList profiles = mLauncherSettings.subKeys(QString("Profiles/")); + QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); - const int currentIndex = mProfilesComboBox->findText(currentProfile); - if (currentIndex != -1) { - // Profile is found - mProfilesComboBox->setCurrentIndex(currentIndex); - } - - mLauncherConfig->endGroup(); -} + if (!profiles.isEmpty()) + profilesComboBox->addItems(profiles); + // Add the current profile if empty + if (profilesComboBox->findText(profile) == -1 && !profile.isEmpty()) + profilesComboBox->addItem(profile); -void DataFilesPage::readConfig() -{ - // Don't read the config if no masters are found - if (mMastersModel->rowCount() < 1) - return; - - QString profile = mProfilesComboBox->currentText(); - - // Make sure we have no groups open - while (!mLauncherConfig->group().isEmpty()) { - mLauncherConfig->endGroup(); - } + if (profilesComboBox->findText(QString("Default")) == -1) + profilesComboBox->addItem(QString("Default")); - mLauncherConfig->beginGroup("Profiles"); - mLauncherConfig->beginGroup(profile); - - QStringList childKeys = mLauncherConfig->childKeys(); - QStringList plugins; - - // Sort the child keys numerical instead of alphabetically - // i.e. Plugin1, Plugin2 instead of Plugin1, Plugin10 - qSort(childKeys.begin(), childKeys.end(), naturalSortLessThanCI); - - foreach (const QString &key, childKeys) { - const QString keyValue = mLauncherConfig->value(key).toString(); - - if (key.startsWith("Plugin")) { - //QStringList checked = mPluginsModel->checkedItems(); - EsmFile *file = mPluginsModel->findItem(keyValue); - QModelIndex index = mPluginsModel->indexFromItem(file); - - mPluginsModel->setCheckState(index, Qt::Checked); - // Move the row to the top of te view - //mPluginsModel->moveRow(index.row(), checked.count()); - plugins << keyValue; - } - - if (key.startsWith("Master")) { - EsmFile *file = mMastersModel->findItem(keyValue); - mMastersModel->setCheckState(mMastersModel->indexFromItem(file), Qt::Checked); - } + if (profile.isEmpty() || profile == QLatin1String("Default")) { + deleteProfileAction->setEnabled(false); + profilesComboBox->setEditEnabled(false); + profilesComboBox->setCurrentIndex(profilesComboBox->findText(QString("Default"))); + } else { + profilesComboBox->setEditEnabled(true); + profilesComboBox->setCurrentIndex(profilesComboBox->findText(profile)); } - qDebug() << plugins; - - -// // Set the checked item positions -// const QStringList checked = mPluginsModel->checkedItems(); -// for (int i = 0; i < plugins.size(); ++i) { -// EsmFile *file = mPluginsModel->findItem(plugins.at(i)); -// QModelIndex index = mPluginsModel->indexFromItem(file); -// mPluginsModel->moveRow(index.row(), i); -// qDebug() << "Moving: " << plugins.at(i) << " from: " << index.row() << " to: " << i << " count: " << checked.count(); - -// } - - // Iterate over the plugins to set their checkstate and position -// for (int i = 0; i < plugins.size(); ++i) { -// const QString plugin = plugins.at(i); + // We do this here to prevent deletion of profiles when initializing the combobox + connect(profilesComboBox, SIGNAL(profileRenamed(QString,QString)), this, SLOT(profileRenamed(QString,QString))); + connect(profilesComboBox, SIGNAL(profileChanged(QString,QString)), this, SLOT(profileChanged(QString,QString))); -// const QList pluginList = mPluginsModel->findItems(plugin); - -// if (!pluginList.isEmpty()) -// { -// foreach (const QStandardItem *currentPlugin, pluginList) { -// mPluginsModel->setData(currentPlugin->index(), Qt::Checked, Qt::CheckStateRole); - -// // Move the plugin to the position specified in the config file -// mPluginsModel->insertRow(i, mPluginsModel->takeRow(currentPlugin->row())); -// } -// } -// } + loadSettings(); } -bool DataFilesPage::showDataFilesWarning() +void DataFilesPage::loadSettings() { + QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); - QMessageBox msgBox; - msgBox.setWindowTitle("Error detecting Morrowind installation"); - msgBox.setIcon(QMessageBox::Warning); - msgBox.setStandardButtons(QMessageBox::Cancel); - msgBox.setText(tr("
Could not find the Data Files location

\ - The directory containing the data files was not found.

\ - Press \"Browse...\" to specify the location manually.
")); - - QAbstractButton *dirSelectButton = - msgBox.addButton(tr("B&rowse..."), QMessageBox::ActionRole); - - msgBox.exec(); - - if (msgBox.clickedButton() == dirSelectButton) { - - // Show a custom dir selection dialog which only accepts valid dirs - QString selectedDir = FileDialog::getExistingDirectory( - this, tr("Select Data Files Directory"), - QDir::currentPath(), - QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); - - // Add the user selected data directory - if (!selectedDir.isEmpty()) { - mDataDirs.push_back(Files::PathContainer::value_type(selectedDir.toStdString())); - mCfgMgr.processPaths(mDataDirs); - } else { - // Cancel from within the dir selection dialog - return false; - } - - } else { - // Cancel - return false; - } - - return true; -} - -bool DataFilesPage::setupDataFiles() -{ - // We use the Configuration Manager to retrieve the configuration values - boost::program_options::variables_map variables; - boost::program_options::options_description desc; - - desc.add_options() - ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()) - ("data-local", boost::program_options::value()->default_value("")) - ("fs-strict", boost::program_options::value()->implicit_value(true)->default_value(false)) - ("encoding", boost::program_options::value()->default_value("win1252")); - - boost::program_options::notify(variables); - - mCfgMgr.readConfiguration(variables, desc); - - if (variables["data"].empty()) { - if (!showDataFilesWarning()) - return false; - } else { - mDataDirs = Files::PathContainer(variables["data"].as()); - } - - std::string local = variables["data-local"].as(); - if (!local.empty()) { - mDataLocal.push_back(Files::PathContainer::value_type(local)); - } - - mCfgMgr.processPaths(mDataDirs); - mCfgMgr.processPaths(mDataLocal); + if (profile.isEmpty()) + return; - // Second chance to display the warning, the data= entries are invalid - while (mDataDirs.empty()) { - if (!showDataFilesWarning()) - return false; - } + mDataFilesModel->uncheckAll(); - // Set the charset for reading the esm/esp files - QString encoding = QString::fromStdString(variables["encoding"].as()); - if (!encoding.isEmpty() && encoding != QLatin1String("win1252")) { - mMastersModel->setEncoding(encoding); - mPluginsModel->setEncoding(encoding); - } + QStringList masters = mLauncherSettings.values(QString("Profiles/") + profile + QString("/master"), Qt::MatchExactly); + QStringList plugins = mLauncherSettings.values(QString("Profiles/") + profile + QString("/plugin"), Qt::MatchExactly); - // Add the paths to the respective models - for (Files::PathContainer::iterator it = mDataDirs.begin(); it != mDataDirs.end(); ++it) { - QString path = QString::fromStdString(it->string()); - path.remove(QChar('\"')); - mMastersModel->addMasters(path); - mPluginsModel->addPlugins(path); + foreach (const QString &master, masters) { + QModelIndex index = mDataFilesModel->indexFromItem(mDataFilesModel->findItem(master)); + if (index.isValid()) + mDataFilesModel->setCheckState(index, Qt::Checked); } - // Same for the data-local paths - for (Files::PathContainer::iterator it = mDataLocal.begin(); it != mDataLocal.end(); ++it) { - QString path = QString::fromStdString(it->string()); - path.remove(QChar('\"')); - mMastersModel->addMasters(path); - mPluginsModel->addPlugins(path); + foreach (const QString &plugin, plugins) { + QModelIndex index = mDataFilesModel->indexFromItem(mDataFilesModel->findItem(plugin)); + if (index.isValid()) + mDataFilesModel->setCheckState(index, Qt::Checked); } - - mMastersModel->sort(0); - mPluginsModel->sort(0); -// mMastersTable->sortByColumn(3, Qt::AscendingOrder); -// mPluginsTable->sortByColumn(3, Qt::AscendingOrder); - - - readConfig(); - return true; } -void DataFilesPage::writeConfig(QString profile) +void DataFilesPage::saveSettings() { - // Don't overwrite the config if no masters are found - if (mMastersModel->rowCount() < 1) + if (mDataFilesModel->rowCount() < 1) return; - QString pathStr = QString::fromStdString(mCfgMgr.getUserPath().string()); - QDir userPath(pathStr); - - if (!userPath.exists()) { - if (!userPath.mkpath(pathStr)) { - QMessageBox msgBox; - msgBox.setWindowTitle("Error creating OpenMW configuration directory"); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not create %0

\ - Please make sure you have the right permissions and try again.
").arg(pathStr)); - msgBox.exec(); - - qApp->quit(); - return; - } - } - // Open the OpenMW config as a QFile - QFile file(pathStr.append("openmw.cfg")); - - if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { - // File cannot be opened or created - QMessageBox msgBox; - msgBox.setWindowTitle("Error writing OpenMW configuration file"); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not open or create %0

\ - Please make sure you have the right permissions and try again.
").arg(file.fileName())); - msgBox.exec(); - - qApp->quit(); - return; - } - - QTextStream in(&file); - QByteArray buffer; - - // Remove all previous entries from config - while (!in.atEnd()) { - QString line = in.readLine(); - if (!line.startsWith("master") && - !line.startsWith("plugin") && - !line.startsWith("data") && - !line.startsWith("data-local")) - { - buffer += line += "\n"; - } - } - - file.close(); - - // Now we write back the other config entries - if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { - QMessageBox msgBox; - msgBox.setWindowTitle("Error writing OpenMW configuration file"); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not write to %0

\ - Please make sure you have the right permissions and try again.
").arg(file.fileName())); - msgBox.exec(); - - qApp->quit(); - return; - } + QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); - if (!buffer.isEmpty()) { - file.write(buffer); + if (profile.isEmpty()) { + profile = profilesComboBox->currentText(); + mLauncherSettings.setValue(QString("Profiles/currentprofile"), profile); } - QTextStream gameConfig(&file); - - // First write the list of data dirs - mCfgMgr.processPaths(mDataDirs); - mCfgMgr.processPaths(mDataLocal); + mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master")); + mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin")); - QString path; + mGameSettings.remove(QString("master")); + mGameSettings.remove(QString("plugin")); - // data= directories - for (Files::PathContainer::iterator it = mDataDirs.begin(); it != mDataDirs.end(); ++it) { - path = QString::fromStdString(it->string()); - path.remove(QChar('\"')); + QStringList items = mDataFilesModel->checkedItems(); - // Make sure the string is quoted when it contains spaces - if (path.contains(" ")) { - gameConfig << "data=\"" << path << "\"" << endl; - } else { - gameConfig << "data=" << path << endl; - } - } + foreach(const QString &item, items) { - // data-local directory - if (!mDataLocal.empty()) { - path = QString::fromStdString(mDataLocal.front().string()); - path.remove(QChar('\"')); + if (item.endsWith(QString(".esm"), Qt::CaseInsensitive)) { + mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/master"), item); + mGameSettings.setMultiValue(QString("master"), item); - if (path.contains(" ")) { - gameConfig << "data-local=\"" << path << "\"" << endl; - } else { - gameConfig << "data-local=" << path << endl; + } else if (item.endsWith(QString(".esp"), Qt::CaseInsensitive)) { + mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/plugin"), item); + mGameSettings.setMultiValue(QString("plugin"), item); } } +} - if (profile.isEmpty()) - profile = mProfilesComboBox->currentText(); - - if (profile.isEmpty()) - return; - - // Make sure we have no groups open - while (!mLauncherConfig->group().isEmpty()) { - mLauncherConfig->endGroup(); - } - - mLauncherConfig->beginGroup("Profiles"); - mLauncherConfig->setValue("CurrentProfile", profile); - - // Open the profile-name subgroup - mLauncherConfig->beginGroup(profile); - mLauncherConfig->remove(""); // Clear the subgroup - - // Now write the masters to the configs - const QStringList masters = mMastersModel->checkedItems(); - - // We don't use foreach because we need i - for (int i = 0; i < masters.size(); ++i) { - const QString currentMaster = masters.at(i); - - mLauncherConfig->setValue(QString("Master%0").arg(i), currentMaster); - gameConfig << "master=" << currentMaster << endl; - +void DataFilesPage::updateOkButton(const QString &text) +{ + // We do this here because we need the profiles combobox text + if (text.isEmpty()) { + mNewProfileDialog->setOkButtonEnabled(false); + return; } - // And finally write all checked plugins - const QStringList plugins = mPluginsModel->checkedItems(); + (profilesComboBox->findText(text) == -1) + ? mNewProfileDialog->setOkButtonEnabled(true) + : mNewProfileDialog->setOkButtonEnabled(false); +} - for (int i = 0; i < plugins.size(); ++i) { - const QString currentPlugin = plugins.at(i); - mLauncherConfig->setValue(QString("Plugin%1").arg(i), currentPlugin); - gameConfig << "plugin=" << currentPlugin << endl; - } +void DataFilesPage::updateSplitter() +{ + // Sigh, update the saved splitter size in settings only when moved + // Since getting mSplitter->sizes() if page is hidden returns invalid values + QList sizes = splitter->sizes(); - file.close(); - mLauncherConfig->endGroup(); - mLauncherConfig->endGroup(); - mLauncherConfig->sync(); + mLauncherSettings.setValue(QString("General/MastersTable/width"), QString::number(sizes.at(0))); + mLauncherSettings.setValue(QString("General/PluginsTable/width"), QString::number(sizes.at(1))); } +void DataFilesPage::updateViews() +{ + // Ensure the columns are hidden because sort() re-enables them + mastersTable->setColumnHidden(1, true); + mastersTable->setColumnHidden(2, true); + mastersTable->setColumnHidden(3, true); + mastersTable->setColumnHidden(4, true); + mastersTable->setColumnHidden(5, true); + mastersTable->setColumnHidden(6, true); + mastersTable->setColumnHidden(7, true); + mastersTable->setColumnHidden(8, true); + + pluginsTable->setColumnHidden(1, true); + pluginsTable->setColumnHidden(2, true); + pluginsTable->setColumnHidden(3, true); + pluginsTable->setColumnHidden(4, true); + pluginsTable->setColumnHidden(5, true); + pluginsTable->setColumnHidden(6, true); + pluginsTable->setColumnHidden(7, true); + pluginsTable->setColumnHidden(8, true); +} -void DataFilesPage::newProfile() +void DataFilesPage::setProfilesComboBoxIndex(int index) { - if (mNewProfileDialog->exec() == QDialog::Accepted) { + profilesComboBox->setCurrentIndex(index); +} - const QString text = mNewProfileDialog->lineEdit()->text(); - mProfilesComboBox->addItem(text); +void DataFilesPage::slotCurrentIndexChanged(int index) +{ + emit profileChanged(index); +} - // Copy the currently checked items to cfg - writeConfig(text); - mLauncherConfig->sync(); +QAbstractItemModel* DataFilesPage::profilesComboBoxModel() +{ + return profilesComboBox->model(); +} - mProfilesComboBox->setCurrentIndex(mProfilesComboBox->findText(text)); - } +int DataFilesPage::profilesComboBoxIndex() +{ + return profilesComboBox->currentIndex(); } -void DataFilesPage::updateOkButton(const QString &text) +void DataFilesPage::on_newProfileAction_triggered() { - if (text.isEmpty()) { - mNewProfileDialog->setOkButtonEnabled(false); - return; + if (mNewProfileDialog->exec() == QDialog::Accepted) { + QString profile = mNewProfileDialog->lineEdit()->text(); + profilesComboBox->addItem(profile); + profilesComboBox->setCurrentIndex(profilesComboBox->findText(profile)); } - - (mProfilesComboBox->findText(text) == -1) - ? mNewProfileDialog->setOkButtonEnabled(true) - : mNewProfileDialog->setOkButtonEnabled(false); } -void DataFilesPage::deleteProfile() +void DataFilesPage::on_deleteProfileAction_triggered() { - QString profile = mProfilesComboBox->currentText(); + QString profile = profilesComboBox->currentText(); if (profile.isEmpty()) return; @@ -652,75 +322,77 @@ void DataFilesPage::deleteProfile() msgBox.exec(); if (msgBox.clickedButton() == deleteButton) { - // Make sure we have no groups open - while (!mLauncherConfig->group().isEmpty()) { - mLauncherConfig->endGroup(); - } - - mLauncherConfig->beginGroup("Profiles"); - - // Open the profile-name subgroup - mLauncherConfig->beginGroup(profile); - mLauncherConfig->remove(""); // Clear the subgroup - mLauncherConfig->endGroup(); - mLauncherConfig->endGroup(); + mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master")); + mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin")); // Remove the profile from the combobox - mProfilesComboBox->removeItem(mProfilesComboBox->findText(profile)); + profilesComboBox->removeItem(profilesComboBox->findText(profile)); } } -void DataFilesPage::check() +void DataFilesPage::on_checkAction_triggered() { - // Check the current selection - if (!mPluginsTable->selectionModel()->hasSelection()) { - return; - } + if (pluginsTable->hasFocus()) + setPluginsCheckstates(Qt::Checked); - QModelIndexList indexes = mPluginsTable->selectionModel()->selectedIndexes(); + if (mastersTable->hasFocus()) + setMastersCheckstates(Qt::Checked); - //sort selection ascending because selectedIndexes returns an unsorted list - //qSort(indexes.begin(), indexes.end(), rowSmallerThan); +} - foreach (const QModelIndex &index, indexes) { - if (!index.isValid()) - return; +void DataFilesPage::on_uncheckAction_triggered() +{ + if (pluginsTable->hasFocus()) + setPluginsCheckstates(Qt::Unchecked); - mPluginsModel->setCheckState(index, Qt::Checked); - } + if (mastersTable->hasFocus()) + setMastersCheckstates(Qt::Unchecked); } -void DataFilesPage::uncheck() +void DataFilesPage::setMastersCheckstates(Qt::CheckState state) { - // uncheck the current selection - if (!mPluginsTable->selectionModel()->hasSelection()) { + if (!mastersTable->selectionModel()->hasSelection()) { return; } - QModelIndexList indexes = mPluginsTable->selectionModel()->selectedIndexes(); + QModelIndexList indexes = mastersTable->selectionModel()->selectedIndexes(); - //sort selection ascending because selectedIndexes returns an unsorted list - //qSort(indexes.begin(), indexes.end(), rowSmallerThan); - - foreach (const QModelIndex &index, indexes) { + foreach (const QModelIndex &index, indexes) + { if (!index.isValid()) return; - mPluginsModel->setCheckState(index, Qt::Unchecked); + QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index); + + if (!sourceIndex.isValid()) + return; + + mDataFilesModel->setCheckState(sourceIndex, state); } } -void DataFilesPage::refresh() +void DataFilesPage::setPluginsCheckstates(Qt::CheckState state) { - mPluginsModel->sort(0); + if (!pluginsTable->selectionModel()->hasSelection()) { + return; + } + QModelIndexList indexes = pluginsTable->selectionModel()->selectedIndexes(); - // Refresh the plugins table - mPluginsTable->scrollToTop(); - writeConfig(); - readConfig(); -} + foreach (const QModelIndex &index, indexes) + { + if (!index.isValid()) + return; + QModelIndex sourceIndex = mPluginsProxyModel->mapToSource( + mFilterProxyModel->mapToSource(index)); + + if (!sourceIndex.isValid()) + return; + + mDataFilesModel->setCheckState(sourceIndex, state); + } +} void DataFilesPage::setCheckState(QModelIndex index) { @@ -733,56 +405,60 @@ void DataFilesPage::setCheckState(QModelIndex index) if (!object) return; + if (object->objectName() == QLatin1String("PluginsTable")) { - QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(index); + QModelIndex sourceIndex = mPluginsProxyModel->mapToSource( + mFilterProxyModel->mapToSource(index)); - (mPluginsModel->checkState(sourceIndex) == Qt::Checked) - ? mPluginsModel->setCheckState(sourceIndex, Qt::Unchecked) - : mPluginsModel->setCheckState(sourceIndex, Qt::Checked); + if (sourceIndex.isValid()) { + (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) + ? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked) + : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); + } } if (object->objectName() == QLatin1String("MastersTable")) { - (mMastersModel->checkState(index) == Qt::Checked) - ? mMastersModel->setCheckState(index, Qt::Unchecked) - : mMastersModel->setCheckState(index, Qt::Checked); + QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index); + + if (sourceIndex.isValid()) { + (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) + ? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked) + : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); + } } return; - } void DataFilesPage::filterChanged(const QString filter) { QRegExp regExp(filter, Qt::CaseInsensitive, QRegExp::FixedString); - mPluginsProxyModel->setFilterRegExp(regExp); + mFilterProxyModel->setFilterRegExp(regExp); } void DataFilesPage::profileChanged(const QString &previous, const QString ¤t) { - qDebug() << "Profile is changed from: " << previous << " to " << current; // Prevent the deletion of the default profile if (current == QLatin1String("Default")) { - mDeleteProfileAction->setEnabled(false); - mProfilesComboBox->setEditEnabled(false); + deleteProfileAction->setEnabled(false); + profilesComboBox->setEditEnabled(false); } else { - mDeleteProfileAction->setEnabled(true); - mProfilesComboBox->setEditEnabled(true); + deleteProfileAction->setEnabled(true); + profilesComboBox->setEditEnabled(true); } - if (!previous.isEmpty()) { - writeConfig(previous); - mLauncherConfig->sync(); + if (previous.isEmpty()) + return; - if (mProfilesComboBox->currentIndex() == -1) - return; + if (profilesComboBox->findText(previous) == -1) + return; // Profile was deleted - } else { - return; - } + // Store the previous profile + mLauncherSettings.setValue(QString("Profiles/currentprofile"), previous); + saveSettings(); + mLauncherSettings.setValue(QString("Profiles/currentprofile"), current); - mMastersModel->uncheckAll(); - mPluginsModel->uncheckAll(); - readConfig(); + loadSettings(); } void DataFilesPage::profileRenamed(const QString &previous, const QString ¤t) @@ -791,54 +467,85 @@ void DataFilesPage::profileRenamed(const QString &previous, const QString &curre return; // Save the new profile name - writeConfig(current); + mLauncherSettings.setValue(QString("Profiles/currentprofile"), current); + saveSettings(); - // Make sure we have no groups open - while (!mLauncherConfig->group().isEmpty()) { - mLauncherConfig->endGroup(); - } + // Remove the old one + mLauncherSettings.remove(QString("Profiles/") + previous + QString("/master")); + mLauncherSettings.remove(QString("Profiles/") + previous + QString("/plugin")); - mLauncherConfig->beginGroup("Profiles"); + // Remove the profile from the combobox + profilesComboBox->removeItem(profilesComboBox->findText(previous)); - // Open the profile-name subgroup - mLauncherConfig->beginGroup(previous); - mLauncherConfig->remove(""); // Clear the subgroup - mLauncherConfig->endGroup(); - mLauncherConfig->endGroup(); - mLauncherConfig->sync(); + loadSettings(); - // Remove the profile from the combobox - mProfilesComboBox->removeItem(mProfilesComboBox->findText(previous)); - - mMastersModel->uncheckAll(); - mPluginsModel->uncheckAll(); - readConfig(); } void DataFilesPage::showContextMenu(const QPoint &point) { - // Make sure there are plugins in the view - if (!mPluginsTable->selectionModel()->hasSelection()) { + QObject *object = QObject::sender(); + + // Not a signal-slot call + if (!object) return; - } - QPoint globalPos = mPluginsTable->mapToGlobal(point); + if (object->objectName() == QLatin1String("PluginsTable")) { + if (!pluginsTable->selectionModel()->hasSelection()) + return; + + QPoint globalPos = pluginsTable->mapToGlobal(point); + QModelIndexList indexes = pluginsTable->selectionModel()->selectedIndexes(); - QModelIndexList indexes = mPluginsTable->selectionModel()->selectedIndexes(); + // Show the check/uncheck actions depending on the state of the selected items + uncheckAction->setEnabled(false); + checkAction->setEnabled(false); - // Show the check/uncheck actions depending on the state of the selected items - mUncheckAction->setEnabled(false); - mCheckAction->setEnabled(false); + foreach (const QModelIndex &index, indexes) + { + if (!index.isValid()) + return; - foreach (const QModelIndex &index, indexes) { - if (!index.isValid()) - return; + QModelIndex sourceIndex = mPluginsProxyModel->mapToSource( + mFilterProxyModel->mapToSource(index)); + + if (!sourceIndex.isValid()) + return; - (mPluginsModel->checkState(index) == Qt::Checked) - ? mUncheckAction->setEnabled(true) - : mCheckAction->setEnabled(true); + (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) + ? uncheckAction->setEnabled(true) + : checkAction->setEnabled(true); + } + + // Show menu + mContextMenu->exec(globalPos); } - // Show menu - mContextMenu->exec(globalPos); + if (object->objectName() == QLatin1String("MastersTable")) { + if (!mastersTable->selectionModel()->hasSelection()) + return; + + QPoint globalPos = mastersTable->mapToGlobal(point); + QModelIndexList indexes = mastersTable->selectionModel()->selectedIndexes(); + + // Show the check/uncheck actions depending on the state of the selected items + uncheckAction->setEnabled(false); + checkAction->setEnabled(false); + + foreach (const QModelIndex &index, indexes) + { + if (!index.isValid()) + return; + + QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index); + + if (!sourceIndex.isValid()) + return; + + (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) + ? uncheckAction->setEnabled(true) + : checkAction->setEnabled(true); + } + + mContextMenu->exec(globalPos); + } } diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index 13668ec30..a0b029330 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -3,93 +3,86 @@ #include #include -#include "utils/profilescombobox.hpp" -#include +#include "ui_datafilespage.h" -class QTableView; class QSortFilterProxyModel; -class QSettings; +class QAbstractItemModel; class QAction; -class QToolBar; class QMenu; -class ProfilesComboBox; -class DataFilesModel; +class DataFilesModel; class TextInputDialog; +class GameSettings; +class LauncherSettings; +class PluginsProxyModel; namespace Files { struct ConfigurationManager; } -class DataFilesPage : public QWidget +class DataFilesPage : public QWidget, private Ui::DataFilesPage { Q_OBJECT public: - DataFilesPage(Files::ConfigurationManager& cfg, QWidget *parent = 0); + DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent = 0); - ProfilesComboBox *mProfilesComboBox; + QAbstractItemModel* profilesComboBoxModel(); + int profilesComboBoxIndex(); void writeConfig(QString profile = QString()); - bool showDataFilesWarning(); - bool setupDataFiles(); + void saveSettings(); + +signals: + void profileChanged(int index); public slots: void setCheckState(QModelIndex index); + void setProfilesComboBoxIndex(int index); void filterChanged(const QString filter); void showContextMenu(const QPoint &point); void profileChanged(const QString &previous, const QString ¤t); void profileRenamed(const QString &previous, const QString ¤t); void updateOkButton(const QString &text); + void updateSplitter(); + void updateViews(); // Action slots - void newProfile(); - void deleteProfile(); -// void moveUp(); -// void moveDown(); -// void moveTop(); -// void moveBottom(); - void check(); - void uncheck(); - void refresh(); + void on_newProfileAction_triggered(); + void on_deleteProfileAction_triggered(); + void on_checkAction_triggered(); + void on_uncheckAction_triggered(); + +private slots: + void slotCurrentIndexChanged(int index); private: - DataFilesModel *mMastersModel; - DataFilesModel *mPluginsModel; + DataFilesModel *mDataFilesModel; - QSortFilterProxyModel *mPluginsProxyModel; + PluginsProxyModel *mPluginsProxyModel; + QSortFilterProxyModel *mMastersProxyModel; - QTableView *mMastersTable; - QTableView *mPluginsTable; + QSortFilterProxyModel *mFilterProxyModel; - QToolBar *mProfileToolBar; QMenu *mContextMenu; - QAction *mNewProfileAction; - QAction *mDeleteProfileAction; - -// QAction *mMoveUpAction; -// QAction *mMoveDownAction; -// QAction *mMoveTopAction; -// QAction *mMoveBottomAction; - QAction *mCheckAction; - QAction *mUncheckAction; - Files::ConfigurationManager &mCfgMgr; - Files::PathContainer mDataDirs; - Files::PathContainer mDataLocal; - QSettings *mLauncherConfig; + GameSettings &mGameSettings; + LauncherSettings &mLauncherSettings; TextInputDialog *mNewProfileDialog; -// const QStringList checkedPlugins(); -// const QStringList selectedMasters(); + void setMastersCheckstates(Qt::CheckState state); + void setPluginsCheckstates(Qt::CheckState state); void createActions(); + void setupDataFiles(); void setupConfig(); void readConfig(); + void loadSettings(); + }; #endif diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 2c4f3430c..700ba3db9 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -1,17 +1,19 @@ -#include +#include "graphicspage.hpp" + +#include +#include +#include #include #include -#include #include #include -#include -#include "utils/naturalsort.hpp" +#include -#include "graphicspage.hpp" +#include "settings/graphicssettings.hpp" QString getAspect(int x, int y) { @@ -25,52 +27,22 @@ QString getAspect(int x, int y) return QString(QString::number(xaspect) + ":" + QString::number(yaspect)); } -GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, QWidget *parent) - : QWidget(parent) - , mCfgMgr(cfg) +GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSetting, QWidget *parent) + : mCfgMgr(cfg) + , mGraphicsSettings(graphicsSetting) + , QWidget(parent) { - QGroupBox *rendererGroup = new QGroupBox(tr("Renderer"), this); - - QLabel *rendererLabel = new QLabel(tr("Rendering Subsystem:"), rendererGroup); - mRendererComboBox = new QComboBox(rendererGroup); - - // Layout for the combobox and label - QGridLayout *renderSystemLayout = new QGridLayout(); - renderSystemLayout->addWidget(rendererLabel, 0, 0, 1, 1); - renderSystemLayout->addWidget(mRendererComboBox, 0, 1, 1, 1); - - // Display - QGroupBox *displayGroup = new QGroupBox(tr("Display"), this); - - mVSyncCheckBox = new QCheckBox(tr("Vertical Sync"), displayGroup); - mFullScreenCheckBox = new QCheckBox(tr("Full Screen"), displayGroup); - - QLabel *antiAliasingLabel = new QLabel(tr("Antialiasing:"), displayGroup); - QLabel *resolutionLabel = new QLabel(tr("Resolution:"), displayGroup); - - mResolutionComboBox = new QComboBox(displayGroup); - mAntiAliasingComboBox = new QComboBox(displayGroup); - - QVBoxLayout *rendererGroupLayout = new QVBoxLayout(rendererGroup); - rendererGroupLayout->addLayout(renderSystemLayout); - - QGridLayout *displayGroupLayout = new QGridLayout(displayGroup); - displayGroupLayout->addWidget(mVSyncCheckBox, 0, 0, 1, 1); - displayGroupLayout->addWidget(mFullScreenCheckBox, 1, 0, 1, 1); - displayGroupLayout->addWidget(antiAliasingLabel, 2, 0, 1, 1); - displayGroupLayout->addWidget(mAntiAliasingComboBox, 2, 1, 1, 1); - displayGroupLayout->addWidget(resolutionLabel, 3, 0, 1, 1); - displayGroupLayout->addWidget(mResolutionComboBox, 3, 1, 1, 1); + setupUi(this); - // Layout for the whole page - QVBoxLayout *pageLayout = new QVBoxLayout(this); - QSpacerItem *vSpacer1 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Expanding); + // Set the maximum res we can set in windowed mode + QRect res = QApplication::desktop()->screenGeometry(); + customWidthSpinBox->setMaximum(res.width()); + customHeightSpinBox->setMaximum(res.height()); - pageLayout->addWidget(rendererGroup); - pageLayout->addWidget(displayGroup); - pageLayout->addItem(vSpacer1); + connect(rendererComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(rendererChanged(const QString&))); + connect(fullScreenCheckBox, SIGNAL(stateChanged(int)), this, SLOT(slotFullScreenChanged(int))); + connect(standardRadioButton, SIGNAL(toggled(bool)), this, SLOT(slotStandardToggled(bool))); - connect(mRendererComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(rendererChanged(const QString&))); } bool GraphicsPage::setupOgre() @@ -117,11 +89,11 @@ bool GraphicsPage::setupOgre() #endif } - boost::filesystem::path absPluginPath = boost::filesystem::absolute(boost::filesystem::path(pluginDir)); - - pluginDir = absPluginPath.string(); + QDir dir(QString::fromStdString(pluginDir)); + pluginDir = dir.absolutePath().toStdString(); Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mOgre); + Files::loadOgrePlugin(pluginDir, "RenderSystem_GL3Plus", *mOgre); Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mOgre); #ifdef ENABLE_PLUGIN_GL @@ -138,7 +110,7 @@ bool GraphicsPage::setupOgre() for (Ogre::RenderSystemList::const_iterator r = renderers.begin(); r != renderers.end(); ++r) { mSelectedRenderSystem = *r; - mRendererComboBox->addItem((*r)->getName().c_str()); + rendererComboBox->addItem((*r)->getName().c_str()); } QString openGLName = QString("OpenGL Rendering Subsystem"); @@ -154,71 +126,85 @@ bool GraphicsPage::setupOgre() msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("
Could not select a valid render system

\ - Please make sure the plugins.cfg file exists and contains a valid rendering plugin.
")); + Please make sure the plugins.cfg file exists and contains a valid rendering plugin.
")); msgBox.exec(); - return false; } // Now fill the GUI elements - int index = mRendererComboBox->findText(QString::fromStdString(Settings::Manager::getString("render system", "Video"))); - + int index = rendererComboBox->findText(mGraphicsSettings.value(QString("Video/render system"))); if ( index != -1) { - mRendererComboBox->setCurrentIndex(index); - } - else - { + rendererComboBox->setCurrentIndex(index); + } else { #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 - mRendererComboBox->setCurrentIndex(mRendererComboBox->findText(direct3DName)); + rendererComboBox->setCurrentIndex(rendererComboBox->findText(direct3DName)); #else - mRendererComboBox->setCurrentIndex(mRendererComboBox->findText(openGLName)); + rendererComboBox->setCurrentIndex(rendererComboBox->findText(openGLName)); #endif } - mAntiAliasingComboBox->clear(); - mResolutionComboBox->clear(); - mAntiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem)); - mResolutionComboBox->addItems(getAvailableResolutions(mSelectedRenderSystem)); + antiAliasingComboBox->clear(); + resolutionComboBox->clear(); + antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem)); + resolutionComboBox->addItems(getAvailableResolutions(mSelectedRenderSystem)); - readConfig(); + // Load the rest of the values + loadSettings(); return true; } -void GraphicsPage::readConfig() +void GraphicsPage::loadSettings() { - if (Settings::Manager::getBool("vsync", "Video")) - mVSyncCheckBox->setCheckState(Qt::Checked); + if (mGraphicsSettings.value(QString("Video/vsync")) == QLatin1String("true")) + vSyncCheckBox->setCheckState(Qt::Checked); - if (Settings::Manager::getBool("fullscreen", "Video")) - mFullScreenCheckBox->setCheckState(Qt::Checked); + if (mGraphicsSettings.value(QString("Video/fullscreen")) == QLatin1String("true")) + fullScreenCheckBox->setCheckState(Qt::Checked); - int aaIndex = mAntiAliasingComboBox->findText(QString::fromStdString(Settings::Manager::getString("antialiasing", "Video"))); + int aaIndex = antiAliasingComboBox->findText(mGraphicsSettings.value(QString("Video/antialiasing"))); if (aaIndex != -1) - mAntiAliasingComboBox->setCurrentIndex(aaIndex); + antiAliasingComboBox->setCurrentIndex(aaIndex); + + QString width = mGraphicsSettings.value(QString("Video/resolution x")); + QString height = mGraphicsSettings.value(QString("Video/resolution y")); + QString resolution = width + QString(" x ") + height; + + int resIndex = resolutionComboBox->findText(resolution, Qt::MatchStartsWith); - QString resolution = QString::number(Settings::Manager::getInt("resolution x", "Video")); - resolution.append(" x " + QString::number(Settings::Manager::getInt("resolution y", "Video"))); + if (resIndex != -1) { + standardRadioButton->toggle(); + resolutionComboBox->setCurrentIndex(resIndex); + } else { + customRadioButton->toggle(); + customWidthSpinBox->setValue(width.toInt()); + customHeightSpinBox->setValue(height.toInt()); - int resIndex = mResolutionComboBox->findText(resolution, Qt::MatchStartsWith); - if (resIndex != -1) - mResolutionComboBox->setCurrentIndex(resIndex); + } } -void GraphicsPage::writeConfig() +void GraphicsPage::saveSettings() { - Settings::Manager::setBool("vsync", "Video", mVSyncCheckBox->checkState()); - Settings::Manager::setBool("fullscreen", "Video", mFullScreenCheckBox->checkState()); - Settings::Manager::setString("antialiasing", "Video", mAntiAliasingComboBox->currentText().toStdString()); - Settings::Manager::setString("render system", "Video", mRendererComboBox->currentText().toStdString()); - - // Get the current resolution, but with the tabs replaced with a single space - QString resolution = mResolutionComboBox->currentText().simplified(); - QStringList tokens = resolution.split(" ", QString::SkipEmptyParts); - - int resX = tokens.at(0).toInt(); - int resY = tokens.at(2).toInt(); - Settings::Manager::setInt("resolution x", "Video", resX); - Settings::Manager::setInt("resolution y", "Video", resY); + vSyncCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/vsync"), QString("true")) + : mGraphicsSettings.setValue(QString("Video/vsync"), QString("false")); + + fullScreenCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/fullscreen"), QString("true")) + : mGraphicsSettings.setValue(QString("Video/fullscreen"), QString("false")); + + mGraphicsSettings.setValue(QString("Video/antialiasing"), antiAliasingComboBox->currentText()); + mGraphicsSettings.setValue(QString("Video/render system"), rendererComboBox->currentText()); + + + if (standardRadioButton->isChecked()) { + QRegExp resolutionRe(QString("(\\d+) x (\\d+).*")); + + if (resolutionRe.exactMatch(resolutionComboBox->currentText().simplified())) { + mGraphicsSettings.setValue(QString("Video/resolution x"), resolutionRe.cap(1)); + mGraphicsSettings.setValue(QString("Video/resolution y"), resolutionRe.cap(2)); + } + } else { + mGraphicsSettings.setValue(QString("Video/resolution x"), QString::number(customWidthSpinBox->value())); + mGraphicsSettings.setValue(QString("Video/resolution y"), QString::number(customHeightSpinBox->value())); + } } QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer) @@ -232,16 +218,14 @@ QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSy { Ogre::StringVector::iterator opt_it; uint idx = 0; - for (opt_it = i->second.possibleValues.begin (); - opt_it != i->second.possibleValues.end (); opt_it++, idx++) - { - if (strcmp (key.toStdString().c_str(), i->first.c_str()) == 0) - { + for (opt_it = i->second.possibleValues.begin(); + opt_it != i->second.possibleValues.end(); opt_it++, idx++) + { + if (strcmp (key.toStdString().c_str(), i->first.c_str()) == 0) { result << ((key == "FSAA") ? QString("MSAA ") : QString("")) + QString::fromStdString((*opt_it).c_str()).simplified(); } } - } // Sort ascending @@ -258,7 +242,7 @@ QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSy QStringList GraphicsPage::getAvailableResolutions(Ogre::RenderSystem *renderer) { - QString key ("Video Mode"); + QString key("Video Mode"); QStringList result; uint row = 0; @@ -275,24 +259,27 @@ QStringList GraphicsPage::getAvailableResolutions(Ogre::RenderSystem *renderer) for (opt_it = i->second.possibleValues.begin (); opt_it != i->second.possibleValues.end (); opt_it++, idx++) { - QString qval = QString::fromStdString(*opt_it).simplified(); - // remove extra tokens after the resolution (for example bpp, can be there or not depending on rendersystem) - QStringList tokens = qval.split(" ", QString::SkipEmptyParts); - assert (tokens.size() >= 3); - QString resolutionStr = tokens.at(0) + QString(" x ") + tokens.at(2); + QRegExp resolutionRe(QString("(\\d+) x (\\d+).*")); + QString resolution = QString::fromStdString(*opt_it).simplified(); - QString aspect = getAspect(tokens.at(0).toInt(),tokens.at(2).toInt()); + if (resolutionRe.exactMatch(resolution)) { - if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10")) { - resolutionStr.append(tr("\t(Widescreen ") + aspect + ")"); + int width = resolutionRe.cap(1).toInt(); + int height = resolutionRe.cap(2).toInt(); - } else if (aspect == QLatin1String("4:3")) { - resolutionStr.append(tr("\t(Standard 4:3)")); - } + QString aspect = getAspect(width, height); + QString cleanRes = resolutionRe.cap(1) + QString(" x ") + resolutionRe.cap(2); - // do not add duplicate resolutions - if (!result.contains(resolutionStr)) - result << resolutionStr; + if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10")) { + cleanRes.append(tr("\t(Wide ") + aspect + ")"); + + } else if (aspect == QLatin1String("4:3")) { + cleanRes.append(tr("\t(Standard 4:3)")); + } + // do not add duplicate resolutions + if (!result.contains(cleanRes)) + result.append(cleanRes); + } } } @@ -306,9 +293,36 @@ void GraphicsPage::rendererChanged(const QString &renderer) { mSelectedRenderSystem = mOgre->getRenderSystemByName(renderer.toStdString()); - mAntiAliasingComboBox->clear(); - mResolutionComboBox->clear(); + antiAliasingComboBox->clear(); + resolutionComboBox->clear(); + + antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem)); + resolutionComboBox->addItems(getAvailableResolutions(mSelectedRenderSystem)); +} + +void GraphicsPage::slotFullScreenChanged(int state) +{ + if (state == Qt::Checked) { + standardRadioButton->toggle(); + customRadioButton->setEnabled(false); + customWidthSpinBox->setEnabled(false); + customHeightSpinBox->setEnabled(false); + } else { + customRadioButton->setEnabled(true); + customWidthSpinBox->setEnabled(true); + customHeightSpinBox->setEnabled(true); + } +} - mAntiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem)); - mResolutionComboBox->addItems(getAvailableResolutions(mSelectedRenderSystem)); +void GraphicsPage::slotStandardToggled(bool checked) +{ + if (checked) { + resolutionComboBox->setEnabled(true); + customWidthSpinBox->setEnabled(false); + customHeightSpinBox->setEnabled(false); + } else { + resolutionComboBox->setEnabled(false); + customWidthSpinBox->setEnabled(true); + customHeightSpinBox->setEnabled(true); + } } diff --git a/apps/launcher/graphicspage.hpp b/apps/launcher/graphicspage.hpp index b8166f672..21039af43 100644 --- a/apps/launcher/graphicspage.hpp +++ b/apps/launcher/graphicspage.hpp @@ -5,8 +5,8 @@ #include #include -#include -#include +//#include +//#include // Static plugin headers #ifdef ENABLE_PLUGIN_GL @@ -16,26 +16,29 @@ # include "OgreD3D9Plugin.h" #endif -class QComboBox; -class QCheckBox; -class QStackedWidget; -class QSettings; +#include "ui_graphicspage.h" + +class GraphicsSettings; namespace Files { struct ConfigurationManager; } -class GraphicsPage : public QWidget +class GraphicsPage : public QWidget, private Ui::GraphicsPage { Q_OBJECT public: - GraphicsPage(Files::ConfigurationManager &cfg, QWidget *parent = 0); + GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0); + void saveSettings(); bool setupOgre(); - void writeConfig(); public slots: void rendererChanged(const QString &renderer); +private slots: + void slotFullScreenChanged(int state); + void slotStandardToggled(bool checked); + private: Ogre::Root *mOgre; Ogre::RenderSystem *mSelectedRenderSystem; @@ -48,22 +51,14 @@ private: Ogre::D3D9Plugin* mD3D9Plugin; #endif - QComboBox *mRendererComboBox; - - QStackedWidget *mDisplayStackedWidget; - - QComboBox *mAntiAliasingComboBox; - QComboBox *mResolutionComboBox; - QCheckBox *mVSyncCheckBox; - QCheckBox *mFullScreenCheckBox; - Files::ConfigurationManager &mCfgMgr; + GraphicsSettings &mGraphicsSettings; QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer); QStringList getAvailableResolutions(Ogre::RenderSystem *renderer); - void createPages(); - void readConfig(); + void loadSettings(); + }; #endif diff --git a/apps/launcher/launcher.rc b/apps/launcher/launcher.rc deleted file mode 100644 index efe86e4da..000000000 --- a/apps/launcher/launcher.rc +++ /dev/null @@ -1 +0,0 @@ -IDI_ICON1 ICON DISCARDABLE "resources/images/openmw.ico" diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index 7c4cb5f7e..09da1d615 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -1,6 +1,6 @@ #include +#include #include -#include #include "maindialog.hpp" @@ -30,14 +30,17 @@ int main(int argc, char *argv[]) QDir::setCurrent(dir.absolutePath()); + // Support non-latin characters + QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); + MainDialog mainWin; if (mainWin.setup()) { - mainWin.show(); - return app.exec(); + } else { + return 0; } - return 0; + return app.exec(); } diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 674ccdf67..e5da3431a 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -1,52 +1,25 @@ -#include - #include "maindialog.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "utils/checkablemessagebox.hpp" + #include "playpage.hpp" #include "graphicspage.hpp" #include "datafilespage.hpp" MainDialog::MainDialog() + : mGameSettings(mCfgMgr) { - QWidget *centralWidget = new QWidget(this); - setCentralWidget(centralWidget); - - mIconWidget = new QListWidget(centralWidget); - mIconWidget->setObjectName("IconWidget"); - mIconWidget->setViewMode(QListView::IconMode); - mIconWidget->setWrapping(false); - mIconWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Just to be sure - mIconWidget->setIconSize(QSize(48, 48)); - mIconWidget->setMovement(QListView::Static); - - mIconWidget->setMinimumWidth(400); - mIconWidget->setFixedHeight(80); - mIconWidget->setSpacing(4); - mIconWidget->setCurrentRow(0); - mIconWidget->setFlow(QListView::LeftToRight); - - QGroupBox *groupBox = new QGroupBox(centralWidget); - QVBoxLayout *groupLayout = new QVBoxLayout(groupBox); - - mPagesWidget = new QStackedWidget(groupBox); - groupLayout->addWidget(mPagesWidget); - - QPushButton *playButton = new QPushButton(tr("Play")); - - QDialogButtonBox *buttonBox = new QDialogButtonBox(centralWidget); - buttonBox->setStandardButtons(QDialogButtonBox::Close); - buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole); - - QVBoxLayout *dialogLayout = new QVBoxLayout(centralWidget); - dialogLayout->addWidget(mIconWidget); - dialogLayout->addWidget(groupBox); - dialogLayout->addWidget(buttonBox); - - setWindowTitle(tr("OpenMW Launcher")); - setWindowIcon(QIcon(":/images/openmw.png")); - // Remove what's this? button - setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); - setMinimumSize(QSize(575, 575)); - // Install the stylesheet font QFile file; QFontDatabase fontDatabase; @@ -56,64 +29,67 @@ MainDialog::MainDialog() // Check if the font is installed if (!fonts.contains("EB Garamond")) { - QString font = QString::fromStdString((mCfgMgr.getGlobalDataPath() / "resources/mygui/EBGaramond-Regular.ttf").string()); - file.setFileName(font); + QString font = QString::fromStdString(mCfgMgr.getGlobalDataPath().string()) + QString("resources/mygui/EBGaramond-Regular.ttf"); + file.setFileName(font); - if (!file.exists()) { - font = QString::fromStdString((mCfgMgr.getLocalPath() / "resources/mygui/EBGaramond-Regular.ttf").string()); - } + if (!file.exists()) { + font = QString::fromStdString(mCfgMgr.getLocalPath().string()) + QString("resources/mygui/EBGaramond-Regular.ttf"); + } - fontDatabase.addApplicationFont(font); + fontDatabase.addApplicationFont(font); } - // Load the stylesheet - QString config = QString::fromStdString((mCfgMgr.getGlobalDataPath() / "resources/launcher.qss").string()); - file.setFileName(config); + setupUi(this); - if (!file.exists()) { - file.setFileName(QString::fromStdString((mCfgMgr.getLocalPath() / "launcher.qss").string())); - } + iconWidget->setViewMode(QListView::IconMode); + iconWidget->setWrapping(false); + iconWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Just to be sure + iconWidget->setIconSize(QSize(48, 48)); + iconWidget->setMovement(QListView::Static); - file.open(QFile::ReadOnly); - QString styleSheet = QLatin1String(file.readAll()); - qApp->setStyleSheet(styleSheet); - file.close(); + iconWidget->setSpacing(4); + iconWidget->setCurrentRow(0); + iconWidget->setFlow(QListView::LeftToRight); + + QPushButton *playButton = new QPushButton(tr("Play")); + buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole); connect(buttonBox, SIGNAL(rejected()), this, SLOT(close())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(play())); + // Remove what's this? button + setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); + createIcons(); - createPages(); } void MainDialog::createIcons() { - if (!QIcon::hasThemeIcon("document-new")) { + if (!QIcon::hasThemeIcon("document-new")) QIcon::setThemeName("tango"); - } // We create a fallback icon because the default fallback doesn't work QIcon graphicsIcon = QIcon(":/icons/tango/video-display.png"); - QListWidgetItem *playButton = new QListWidgetItem(mIconWidget); + QListWidgetItem *playButton = new QListWidgetItem(iconWidget); playButton->setIcon(QIcon(":/images/openmw.png")); playButton->setText(tr("Play")); playButton->setTextAlignment(Qt::AlignCenter); playButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - QListWidgetItem *graphicsButton = new QListWidgetItem(mIconWidget); + QListWidgetItem *graphicsButton = new QListWidgetItem(iconWidget); graphicsButton->setIcon(QIcon::fromTheme("video-display", graphicsIcon)); graphicsButton->setText(tr("Graphics")); graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute); graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - QListWidgetItem *dataFilesButton = new QListWidgetItem(mIconWidget); + QListWidgetItem *dataFilesButton = new QListWidgetItem(iconWidget); dataFilesButton->setIcon(QIcon(":/images/openmw-plugin.png")); dataFilesButton->setText(tr("Data Files")); dataFilesButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom); dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - connect(mIconWidget, + connect(iconWidget, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*))); @@ -122,77 +98,205 @@ void MainDialog::createIcons() void MainDialog::createPages() { mPlayPage = new PlayPage(this); - mGraphicsPage = new GraphicsPage(mCfgMgr, this); - mDataFilesPage = new DataFilesPage(mCfgMgr, this); + mGraphicsPage = new GraphicsPage(mCfgMgr, mGraphicsSettings, this); + mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this); // Set the combobox of the play page to imitate the combobox on the datafilespage - mPlayPage->mProfilesComboBox->setModel(mDataFilesPage->mProfilesComboBox->model()); - mPlayPage->mProfilesComboBox->setCurrentIndex(mDataFilesPage->mProfilesComboBox->currentIndex()); + mPlayPage->setProfilesComboBoxModel(mDataFilesPage->profilesComboBoxModel()); + mPlayPage->setProfilesComboBoxIndex(mDataFilesPage->profilesComboBoxIndex()); // Add the pages to the stacked widget - mPagesWidget->addWidget(mPlayPage); - mPagesWidget->addWidget(mGraphicsPage); - mPagesWidget->addWidget(mDataFilesPage); + pagesWidget->addWidget(mPlayPage); + pagesWidget->addWidget(mGraphicsPage); + pagesWidget->addWidget(mDataFilesPage); // Select the first page - mIconWidget->setCurrentItem(mIconWidget->item(0), QItemSelectionModel::Select); - - connect(mPlayPage->mPlayButton, SIGNAL(clicked()), this, SLOT(play())); + iconWidget->setCurrentItem(iconWidget->item(0), QItemSelectionModel::Select); - connect(mPlayPage->mProfilesComboBox, - SIGNAL(currentIndexChanged(int)), - mDataFilesPage->mProfilesComboBox, SLOT(setCurrentIndex(int))); + connect(mPlayPage, SIGNAL(playButtonClicked()), this, SLOT(play())); - connect(mDataFilesPage->mProfilesComboBox, - SIGNAL(currentIndexChanged(int)), - mPlayPage->mProfilesComboBox, SLOT(setCurrentIndex(int))); + connect(mPlayPage, SIGNAL(profileChanged(int)), mDataFilesPage, SLOT(setProfilesComboBoxIndex(int))); + connect(mDataFilesPage, SIGNAL(profileChanged(int)), mPlayPage, SLOT(setProfilesComboBoxIndex(int))); } - -bool MainDialog::setup() +bool MainDialog::showFirstRunDialog() { - // Create the settings manager and load default settings file - const std::string localdefault = (mCfgMgr.getLocalPath() / "settings-default.cfg").string(); - const std::string globaldefault = (mCfgMgr.getGlobalPath() / "settings-default.cfg").string(); - - // prefer local - if (boost::filesystem::exists(localdefault)) { - mSettings.loadDefault(localdefault); - } else if (boost::filesystem::exists(globaldefault)) { - mSettings.loadDefault(globaldefault); - } else { + QStringList iniPaths; + + foreach (const QString &path, mGameSettings.getDataDirs()) { + QDir dir(path); + dir.setPath(dir.canonicalPath()); // Resolve symlinks + + if (!dir.cdUp()) + continue; // Cannot move from Data Files + + if (dir.exists(QString("Morrowind.ini"))) + iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini"))); + } + + // Ask the user where the Morrowind.ini is + if (iniPaths.empty()) { QMessageBox msgBox; - msgBox.setWindowTitle("Error reading OpenMW configuration file"); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not find %0

\ - The problem may be due to an incomplete installation of OpenMW.
\ - Reinstalling OpenMW may resolve the problem.").arg(QString::fromStdString(globaldefault))); + msgBox.setWindowTitle(tr("Error detecting Morrowind configuration")); + msgBox.setIcon(QMessageBox::Warning); + msgBox.setStandardButtons(QMessageBox::Cancel); + msgBox.setText(QObject::tr("
Could not find Morrowind.ini

\ + OpenMW needs to import settings from this file.

\ + Press \"Browse...\" to specify the location manually.
")); + + QAbstractButton *dirSelectButton = + msgBox.addButton(QObject::tr("B&rowse..."), QMessageBox::ActionRole); + msgBox.exec(); - return false; + + QString iniFile; + if (msgBox.clickedButton() == dirSelectButton) { + iniFile = QFileDialog::getOpenFileName( + NULL, + QObject::tr("Select configuration file"), + QDir::currentPath(), + QString(tr("Morrowind configuration file (*.ini)"))); + } + + if (iniFile.isEmpty()) + return false; // Cancel was clicked; + + QFileInfo info(iniFile); + iniPaths.clear(); + iniPaths.append(info.absoluteFilePath()); } - // load user settings if they exist, otherwise just load the default settings as user settings - const std::string settingspath = (mCfgMgr.getUserPath() / "settings.cfg").string(); + CheckableMessageBox msgBox(this); + msgBox.setWindowTitle(tr("Morrowind installation detected")); + + QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxQuestion); + int size = QApplication::style()->pixelMetric(QStyle::PM_MessageBoxIconSize); + msgBox.setIconPixmap(icon.pixmap(size, size)); + + QAbstractButton *importerButton = + msgBox.addButton(tr("Import"), QDialogButtonBox::AcceptRole); // ActionRole doesn't work?! + QAbstractButton *skipButton = + msgBox.addButton(tr("Skip"), QDialogButtonBox::RejectRole); + + Q_UNUSED(skipButton); // Surpress compiler unused warning + + msgBox.setStandardButtons(QDialogButtonBox::NoButton); + msgBox.setText(tr("
An existing Morrowind configuration was detected
\ +
Would you like to import settings from Morrowind.ini?
\ +
Warning: In most cases OpenMW needs these settings to run properly
")); + msgBox.setCheckBoxText(tr("Include selected masters and plugins (creates a new profile)")); + msgBox.exec(); + + + if (msgBox.clickedButton() == importerButton) { + + if (iniPaths.count() > 1) { + // Multiple Morrowind.ini files found + bool ok; + QString path = QInputDialog::getItem(this, tr("Multiple configurations found"), + tr("
There are multiple Morrowind.ini files found.

\ + Please select the one you wish to import from:"), iniPaths, 0, false, &ok); + if (ok && !path.isEmpty()) { + iniPaths.clear(); + iniPaths.append(path); + } else { + // Cancel was clicked + return false; + } + } + + // Create the file if it doesn't already exist, else the importer will fail + QString path = QString::fromStdString(mCfgMgr.getUserPath().string()) + QString("openmw.cfg"); + QFile file(path); + + if (!file.exists()) { + if (!file.open(QIODevice::ReadWrite)) { + // File cannot be created + QMessageBox msgBox; + msgBox.setWindowTitle(tr("Error writing OpenMW configuration file")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Could not open or create %0 for writing

\ + Please make sure you have the right permissions \ + and try again.
").arg(file.fileName())); + msgBox.exec(); + return false; + } + + file.close(); + } + + // Construct the arguments to run the importer + QStringList arguments; + + if (msgBox.isChecked()) + arguments.append(QString("--game-files")); + + arguments.append(QString("--encoding")); + arguments.append(mGameSettings.value(QString("encoding"), QString("win1252"))); + arguments.append(QString("--ini")); + arguments.append(iniPaths.first()); + arguments.append(QString("--cfg")); + arguments.append(path); + + if (!startProgram(QString("mwiniimport"), arguments, false)) + return false; + + // Re-read the game settings + if (!setupGameSettings()) + return false; + + // Add a new profile + if (msgBox.isChecked()) { + mLauncherSettings.setValue(QString("Profiles/currentprofile"), QString("Imported")); + + mLauncherSettings.remove(QString("Profiles/Imported/master")); + mLauncherSettings.remove(QString("Profiles/Imported/plugin")); + + QStringList masters = mGameSettings.values(QString("master")); + QStringList plugins = mGameSettings.values(QString("plugin")); + + foreach (const QString &master, masters) { + mLauncherSettings.setMultiValue(QString("Profiles/Imported/master"), master); + } + + foreach (const QString &plugin, plugins) { + mLauncherSettings.setMultiValue(QString("Profiles/Imported/plugin"), plugin); + } + } + + } - if (boost::filesystem::exists(settingspath)) - mSettings.loadUser(settingspath); - else if (boost::filesystem::exists(localdefault)) - mSettings.loadUser(localdefault); - else if (boost::filesystem::exists(globaldefault)) - mSettings.loadUser(globaldefault); + return true; +} - // Setup the Graphics page - if (!mGraphicsPage->setupOgre()) { +bool MainDialog::setup() +{ + if (!setupLauncherSettings()) + return false; + + if (!setupGameSettings()) return false; - } - // Setup the Data Files page - if (!mDataFilesPage->setupDataFiles()) { + if (!setupGraphicsSettings()) return false; + + // Check if we need to show the importer + if (mLauncherSettings.value(QString("General/firstrun"), QString("true")) == QLatin1String("true")) + { + if (!showFirstRunDialog()) + return false; } + // Now create the pages as they need the settings + createPages(); + + // Call this so we can exit on Ogre errors before mainwindow is shown + if (!mGraphicsPage->setupOgre()) + return false; + + loadSettings(); return true; } @@ -201,89 +305,410 @@ void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) if (!current) current = previous; - mPagesWidget->setCurrentIndex(mIconWidget->row(current)); + pagesWidget->setCurrentIndex(iconWidget->row(current)); } -void MainDialog::closeEvent(QCloseEvent *event) +bool MainDialog::setupLauncherSettings() +{ + QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); + + QStringList paths; + paths.append(QString("launcher.cfg")); + paths.append(userPath + QString("launcher.cfg")); + + foreach (const QString &path, paths) { + qDebug() << "Loading config file:" << qPrintable(path); + QFile file(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")); + + mLauncherSettings.readFile(stream); + } + file.close(); + } + + return true; +} + +bool MainDialog::setupGameSettings() +{ + QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); + QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string()); + + QStringList paths; + paths.append(userPath + QString("openmw.cfg")); + paths.append(QString("openmw.cfg")); + paths.append(globalPath + QString("openmw.cfg")); + + foreach (const QString &path, paths) { + qDebug() << "Loading config file:" << qPrintable(path); + + QFile file(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.readFile(stream); + } + file.close(); + } + + QStringList dataDirs; + + // Check if the paths actually contain data files + foreach (const QString path, mGameSettings.getDataDirs()) { + QDir dir(path); + QStringList filters; + filters << "*.esp" << "*.esm"; + + if (!dir.entryList(filters).isEmpty()) + dataDirs.append(path); + } + + if (dataDirs.isEmpty()) + { + QMessageBox msgBox; + msgBox.setWindowTitle(tr("Error detecting Morrowind installation")); + msgBox.setIcon(QMessageBox::Warning); + msgBox.setStandardButtons(QMessageBox::Cancel); + msgBox.setText(QObject::tr("
Could not find the Data Files location

\ + The directory containing the data files was not found.

\ + Press \"Browse...\" to specify the location manually.
")); + + QAbstractButton *dirSelectButton = + msgBox.addButton(QObject::tr("B&rowse..."), QMessageBox::ActionRole); + + msgBox.exec(); + + QString selectedFile; + if (msgBox.clickedButton() == dirSelectButton) { + selectedFile = QFileDialog::getOpenFileName( + NULL, + QObject::tr("Select master file"), + QDir::currentPath(), + QString(tr("Morrowind master file (*.esm)"))); + } + + if (selectedFile.isEmpty()) + return false; // Cancel was clicked; + + QFileInfo info(selectedFile); + + // Add the new dir to the settings file and to the data dir container + mGameSettings.setMultiValue(QString("data"), info.absolutePath()); + mGameSettings.addDataDir(info.absolutePath()); + } + + return true; +} + +bool MainDialog::setupGraphicsSettings() +{ + QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); + QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string()); + + QFile localDefault(QString("settings-default.cfg")); + QFile globalDefault(globalPath + QString("settings-default.cfg")); + + if (!localDefault.exists() && !globalDefault.exists()) { + QMessageBox msgBox; + msgBox.setWindowTitle(tr("Error reading OpenMW configuration file")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(QObject::tr("
Could not find settings-default.cfg

\ + The problem may be due to an incomplete installation of OpenMW.
\ + Reinstalling OpenMW may resolve the problem.")); + msgBox.exec(); + return false; + } + + + QStringList paths; + paths.append(globalPath + QString("settings-default.cfg")); + paths.append(QString("settings-default.cfg")); + paths.append(userPath + QString("settings.cfg")); + + foreach (const QString &path, paths) { + qDebug() << "Loading config file:" << qPrintable(path); + QFile file(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")); + + mGraphicsSettings.readFile(stream); + } + file.close(); + } + + return true; +} + +void MainDialog::loadSettings() +{ + int width = mLauncherSettings.value(QString("General/MainWindow/width")).toInt(); + int height = mLauncherSettings.value(QString("General/MainWindow/height")).toInt(); + + int posX = mLauncherSettings.value(QString("General/MainWindow/posx")).toInt(); + int posY = mLauncherSettings.value(QString("General/MainWindow/posy")).toInt(); + + resize(width, height); + move(posX, posY); +} + +void MainDialog::saveSettings() +{ + QString width = QString::number(this->width()); + QString height = QString::number(this->height()); + + mLauncherSettings.setValue(QString("General/MainWindow/width"), width); + mLauncherSettings.setValue(QString("General/MainWindow/height"), height); + + QString posX = QString::number(this->pos().x()); + QString posY = QString::number(this->pos().y()); + + mLauncherSettings.setValue(QString("General/MainWindow/posx"), posX); + mLauncherSettings.setValue(QString("General/MainWindow/posy"), posY); + + mLauncherSettings.setValue(QString("General/firstrun"), QString("false")); + +} + +bool MainDialog::writeSettings() { // Now write all config files - mDataFilesPage->writeConfig(); - mGraphicsPage->writeConfig(); + saveSettings(); + mGraphicsPage->saveSettings(); + mDataFilesPage->saveSettings(); + + QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); + QDir dir(userPath); + + if (!dir.exists()) { + if (!dir.mkpath(userPath)) { + QMessageBox msgBox; + msgBox.setWindowTitle(tr("Error creating OpenMW configuration directory")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Could not create %0

\ + Please make sure you have the right permissions \ + and try again.
").arg(userPath)); + msgBox.exec(); + return false; + } + } + + // Game settings + QFile file(userPath + QString("openmw.cfg")); + + if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) { + // File cannot be opened or created + QMessageBox msgBox; + msgBox.setWindowTitle(tr("Error writing OpenMW configuration file")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Could not open or create %0 for writing

\ + 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.writeFile(stream); + file.close(); - // Save user settings - const std::string settingspath = (mCfgMgr.getUserPath() / "settings.cfg").string(); - mSettings.saveUser(settingspath); + // Graphics settings + file.setFileName(userPath + QString("settings.cfg")); + if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) { + // File cannot be opened or created + QMessageBox msgBox; + msgBox.setWindowTitle(tr("Error writing OpenMW configuration file")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Could not open or create %0 for writing

\ + Please make sure you have the right permissions \ + and try again.
").arg(file.fileName())); + msgBox.exec(); + return false; + } + + stream.setDevice(&file); + stream.setCodec(QTextCodec::codecForName("UTF-8")); + + mGraphicsSettings.writeFile(stream); + file.close(); + + // Launcher settings + file.setFileName(userPath + QString("launcher.cfg")); + + if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) { + // File cannot be opened or created + QMessageBox msgBox; + msgBox.setWindowTitle(tr("Error writing Launcher configuration file")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Could not open or create %0 for writing

\ + Please make sure you have the right permissions \ + and try again.
").arg(file.fileName())); + msgBox.exec(); + return false; + } + + stream.setDevice(&file); + stream.setCodec(QTextCodec::codecForName("UTF-8")); + + mLauncherSettings.writeFile(stream); + file.close(); + + return true; +} + +void MainDialog::closeEvent(QCloseEvent *event) +{ + writeSettings(); event->accept(); } void MainDialog::play() { - // First do a write of all the configs, just to be sure - mDataFilesPage->writeConfig(); - mGraphicsPage->writeConfig(); + if (!writeSettings()) + qApp->quit(); - // Save user settings - const std::string settingspath = (mCfgMgr.getUserPath() / "settings.cfg").string(); - mSettings.saveUser(settingspath); + // Launch the game detached + startProgram(QString("openmw"), true); + qApp->quit(); +} +bool MainDialog::startProgram(const QString &name, const QStringList &arguments, bool detached) +{ + QString path = name; #ifdef Q_OS_WIN - QString game = "./openmw.exe"; - QFile file(game); + path.append(QString(".exe")); #elif defined(Q_OS_MAC) QDir dir(QCoreApplication::applicationDirPath()); - QString game = dir.absoluteFilePath("openmw"); - QFile file(game); - game = "\"" + game + "\""; + path = dir.absoluteFilePath(name); #else - QString game = "./openmw"; - QFile file(game); + path.prepend(QString("./")); #endif + QFile file(path); + QProcess process; QFileInfo info(file); if (!file.exists()) { QMessageBox msgBox; - msgBox.setWindowTitle("Error starting OpenMW"); + msgBox.setWindowTitle(tr("Error starting executable")); msgBox.setIcon(QMessageBox::Warning); msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not find OpenMW

\ - The OpenMW application is not found.
\ - Please make sure OpenMW is installed correctly and try again.
")); + msgBox.setText(tr("
Could not find %1

\ + The application is not found.
\ + Please make sure OpenMW is installed correctly and try again.
").arg(info.fileName())); msgBox.exec(); - return; + return false; } if (!info.isExecutable()) { QMessageBox msgBox; - msgBox.setWindowTitle("Error starting OpenMW"); + msgBox.setWindowTitle(tr("Error starting executable")); msgBox.setIcon(QMessageBox::Warning); msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not start OpenMW

\ - The OpenMW application is not executable.
\ - Please make sure you have the right permissions and try again.
")); + msgBox.setText(tr("
Could not start %1

\ + The application is not executable.
\ + Please make sure you have the right permissions and try again.
").arg(info.fileName())); msgBox.exec(); - return; + return false; } - // Start the game - if (!process.startDetached(game)) { - QMessageBox msgBox; - msgBox.setWindowTitle("Error starting OpenMW"); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not start OpenMW

\ - An error occurred while starting OpenMW.

\ - Press \"Show Details...\" for more information.
")); - msgBox.setDetailedText(process.errorString()); - msgBox.exec(); - - return; + // Start the executable + if (detached) { + if (!process.startDetached(path, arguments)) { + QMessageBox msgBox; + msgBox.setWindowTitle(tr("Error starting executable")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Could not start %1

\ + An error occurred while starting %1.

\ + Press \"Show Details...\" for more information.
").arg(info.fileName())); + msgBox.setDetailedText(process.errorString()); + msgBox.exec(); + + return false; + } } else { - qApp->quit(); + process.start(path, arguments); + if (!process.waitForFinished()) { + QMessageBox msgBox; + msgBox.setWindowTitle(tr("Error starting executable")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Could not start %1

\ + An error occurred while starting %1.

\ + Press \"Show Details...\" for more information.
").arg(info.fileName())); + msgBox.setDetailedText(process.errorString()); + msgBox.exec(); + + return false; + } + + if (process.exitCode() != 0 || process.exitStatus() == QProcess::CrashExit) { + QString error(process.readAllStandardError()); + error.append(tr("\nArguments:\n")); + error.append(arguments.join(" ")); + + QMessageBox msgBox; + msgBox.setWindowTitle(tr("Error running executable")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Executable %1 returned an error

\ + An error occurred while running %1.

\ + Press \"Show Details...\" for more information.
").arg(info.fileName())); + msgBox.setDetailedText(error); + msgBox.exec(); + + return false; + } } -} + return true; + +} diff --git a/apps/launcher/maindialog.hpp b/apps/launcher/maindialog.hpp index bf98011cc..824dff6e8 100644 --- a/apps/launcher/maindialog.hpp +++ b/apps/launcher/maindialog.hpp @@ -2,9 +2,14 @@ #define MAINDIALOG_H #include - +#ifndef Q_MOC_RUN #include -#include +#endif +#include "settings/gamesettings.hpp" +#include "settings/graphicssettings.hpp" +#include "settings/launchersettings.hpp" + +#include "ui_mainwindow.h" class QListWidget; class QListWidgetItem; @@ -17,32 +22,46 @@ class PlayPage; class GraphicsPage; class DataFilesPage; -class MainDialog : public QMainWindow +class MainDialog : public QMainWindow, private Ui::MainWindow { Q_OBJECT public: MainDialog(); + bool setup(); + bool showFirstRunDialog(); public slots: void changePage(QListWidgetItem *current, QListWidgetItem *previous); void play(); - bool setup(); private: void createIcons(); void createPages(); - void closeEvent(QCloseEvent *event); - QListWidget *mIconWidget; - QStackedWidget *mPagesWidget; + bool setupLauncherSettings(); + bool setupGameSettings(); + bool setupGraphicsSettings(); + + void loadSettings(); + void saveSettings(); + bool writeSettings(); + + inline bool startProgram(const QString &name, bool detached = false) { return startProgram(name, QStringList(), detached); } + bool startProgram(const QString &name, const QStringList &arguments, bool detached = false); + + void closeEvent(QCloseEvent *event); PlayPage *mPlayPage; GraphicsPage *mGraphicsPage; DataFilesPage *mDataFilesPage; Files::ConfigurationManager mCfgMgr; - Settings::Manager mSettings; + + GameSettings mGameSettings; + GraphicsSettings mGraphicsSettings; + LauncherSettings mLauncherSettings; + }; #endif diff --git a/apps/launcher/playpage.cpp b/apps/launcher/playpage.cpp index cb993a8fa..46900c595 100644 --- a/apps/launcher/playpage.cpp +++ b/apps/launcher/playpage.cpp @@ -1,43 +1,43 @@ -#include - #include "playpage.hpp" -PlayPage::PlayPage(QWidget *parent) : QWidget(parent) -{ - QWidget *playWidget = new QWidget(this); - playWidget->setObjectName("PlayGroup"); - playWidget->setFixedSize(QSize(425, 375)); +#include - mPlayButton = new QPushButton(tr("Play"), playWidget); - mPlayButton->setObjectName("PlayButton"); - mPlayButton->setMinimumSize(QSize(200, 50)); +#ifdef Q_OS_MAC +#include +#endif - QLabel *profileLabel = new QLabel(tr("Current Profile:"), playWidget); - profileLabel->setObjectName("ProfileLabel"); +PlayPage::PlayPage(QWidget *parent) : QWidget(parent) +{ + setupUi(this); + // Hacks to get the stylesheet look properly +#ifdef Q_OS_MAC QPlastiqueStyle *style = new QPlastiqueStyle; - mProfilesComboBox = new QComboBox(playWidget); - mProfilesComboBox->setObjectName("ProfilesComboBox"); - mProfilesComboBox->setStyle(style); + profilesComboBox->setStyle(style); +#endif + profilesComboBox->setView(new QListView()); - QGridLayout *playLayout = new QGridLayout(playWidget); + connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentIndexChanged(int))); + connect(playButton, SIGNAL(clicked()), this, SLOT(slotPlayClicked())); - QSpacerItem *hSpacer1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - QSpacerItem *hSpacer2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); +} - QSpacerItem *vSpacer1 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); - QSpacerItem *vSpacer2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); - - playLayout->addWidget(mPlayButton, 1, 1, 1, 1); - playLayout->addWidget(profileLabel, 2, 1, 1, 1); - playLayout->addWidget(mProfilesComboBox, 3, 1, 1, 1); - playLayout->addItem(hSpacer1, 2, 0, 1, 1); - playLayout->addItem(hSpacer2, 2, 2, 1, 1); - playLayout->addItem(vSpacer1, 0, 1, 1, 1); - playLayout->addItem(vSpacer2, 4, 1, 1, 1); +void PlayPage::setProfilesComboBoxModel(QAbstractItemModel *model) +{ + profilesComboBox->setModel(model); +} - QHBoxLayout *pageLayout = new QHBoxLayout(this); +void PlayPage::setProfilesComboBoxIndex(int index) +{ + profilesComboBox->setCurrentIndex(index); +} - pageLayout->addWidget(playWidget); +void PlayPage::slotCurrentIndexChanged(int index) +{ + emit profileChanged(index); +} -} \ No newline at end of file +void PlayPage::slotPlayClicked() +{ + emit playButtonClicked(); +} diff --git a/apps/launcher/playpage.hpp b/apps/launcher/playpage.hpp index efec6f2b3..4306396bd 100644 --- a/apps/launcher/playpage.hpp +++ b/apps/launcher/playpage.hpp @@ -3,19 +3,33 @@ #include +#include "ui_playpage.h" + class QComboBox; class QPushButton; +class QAbstractItemModel; -class PlayPage : public QWidget +class PlayPage : public QWidget, private Ui::PlayPage { Q_OBJECT public: PlayPage(QWidget *parent = 0); + void setProfilesComboBoxModel(QAbstractItemModel *model); + +signals: + void profileChanged(int index); + void playButtonClicked(); + +public slots: + void setProfilesComboBoxIndex(int index); + +private slots: + void slotCurrentIndexChanged(int index); + void slotPlayClicked(); + - QComboBox *mProfilesComboBox; - QPushButton *mPlayButton; }; -#endif \ No newline at end of file +#endif diff --git a/apps/launcher/resources.qrc b/apps/launcher/resources.qrc deleted file mode 100644 index 2b56f80fd..000000000 --- a/apps/launcher/resources.qrc +++ /dev/null @@ -1,21 +0,0 @@ - - - resources/images/clear.png - resources/images/down.png - resources/images/openmw.png - resources/images/openmw-plugin.png - resources/images/openmw-header.png - resources/images/playpage-background.png - - - resources/icons/tango/index.theme - resources/icons/tango/video-display.png - resources/icons/tango/document-new.png - resources/icons/tango/edit-copy.png - resources/icons/tango/edit-delete.png - resources/icons/tango/go-bottom.png - resources/icons/tango/go-down.png - resources/icons/tango/go-top.png - resources/icons/tango/go-up.png - - diff --git a/apps/launcher/resources/images/clear.png b/apps/launcher/resources/images/clear.png deleted file mode 100644 index 6c4b83b7a..000000000 Binary files a/apps/launcher/resources/images/clear.png and /dev/null differ diff --git a/apps/launcher/settings/gamesettings.cpp b/apps/launcher/settings/gamesettings.cpp new file mode 100644 index 000000000..9a9b8df41 --- /dev/null +++ b/apps/launcher/settings/gamesettings.cpp @@ -0,0 +1,177 @@ +#include "gamesettings.hpp" + +#include +#include +#include +#include +#include + +#include + +#include + +#include +/** + * Workaround for problems with whitespaces in paths in older versions of Boost library + */ +#if (BOOST_VERSION <= 104600) +namespace boost +{ + + template<> + inline boost::filesystem::path lexical_cast(const std::string& arg) + { + return boost::filesystem::path(arg); + } + +} /* namespace boost */ +#endif /* (BOOST_VERSION <= 104600) */ + + +GameSettings::GameSettings(Files::ConfigurationManager &cfg) + : mCfgMgr(cfg) +{ +} + +GameSettings::~GameSettings() +{ +} + +void GameSettings::validatePaths() +{ + if (mSettings.isEmpty() || !mDataDirs.isEmpty()) + return; // Don't re-validate paths if they are already parsed + + QStringList paths = mSettings.values(QString("data")); + Files::PathContainer dataDirs; + + foreach (const QString &path, paths) { + dataDirs.push_back(Files::PathContainer::value_type(path.toStdString())); + } + + // Parse the data dirs to convert the tokenized paths + mCfgMgr.processPaths(dataDirs); + mDataDirs.clear(); + + for (Files::PathContainer::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) { + QString path = QString::fromStdString(it->string()); + path.remove(QChar('\"')); + + QDir dir(path); + if (dir.exists()) + mDataDirs.append(path); + } + + // Do the same for data-local + QString local = mSettings.value(QString("data-local")); + + if (local.isEmpty()) + return; + + dataDirs.clear(); + dataDirs.push_back(Files::PathContainer::value_type(local.toStdString())); + + mCfgMgr.processPaths(dataDirs); + + if (!dataDirs.empty()) { + QString path = QString::fromStdString(dataDirs.front().string()); + path.remove(QChar('\"')); + + QDir dir(path); + if (dir.exists()) + mDataLocal = path; + } +} + +QStringList GameSettings::values(const QString &key, const QStringList &defaultValues) +{ + if (!mSettings.values(key).isEmpty()) + return mSettings.values(key); + return defaultValues; +} + +bool GameSettings::readFile(QTextStream &stream) +{ + QMap cache; + QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); + + while (!stream.atEnd()) { + QString line = stream.readLine(); + + if (line.isEmpty() || line.startsWith("#")) + continue; + + if (keyRe.indexIn(line) != -1) { + + QString key = keyRe.cap(1); + QString value = keyRe.cap(2); + + // Don't remove existing data entries + if (key != QLatin1String("data")) + mSettings.remove(key); + + QStringList values = cache.values(key); + values.append(mSettings.values(key)); + + if (!values.contains(value)) { + cache.insertMulti(key, value); + } + } + } + + if (mSettings.isEmpty()) { + mSettings = 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); + validatePaths(); + + return true; +} + +bool GameSettings::writeFile(QTextStream &stream) +{ + // Iterate in reverse order to preserve insertion order + QMapIterator i(mSettings); + i.toBack(); + + while (i.hasPrevious()) { + i.previous(); + + if (i.key() == QLatin1String("master") || i.key() == QLatin1String("plugin")) + continue; + + // Quote paths with spaces + if (i.key() == QLatin1String("data") + || i.key() == QLatin1String("data-local") + || i.key() == QLatin1String("resources")) + { + if (i.value().contains(QChar(' '))) + { + QString stripped = i.value(); + stripped.remove(QChar('\"')); // Remove quotes + + stream << i.key() << "=\"" << stripped << "\"\n"; + continue; + } + } + + stream << i.key() << "=" << i.value() << "\n"; + + } + + QStringList masters = mSettings.values(QString("master")); + for (int i = masters.count(); i--;) { + stream << "master=" << masters.at(i) << "\n"; + } + + QStringList plugins = mSettings.values(QString("plugin")); + for (int i = plugins.count(); i--;) { + stream << "plugin=" << plugins.at(i) << "\n"; + } + + return true; +} diff --git a/apps/launcher/settings/gamesettings.hpp b/apps/launcher/settings/gamesettings.hpp new file mode 100644 index 000000000..7a17ef9af --- /dev/null +++ b/apps/launcher/settings/gamesettings.hpp @@ -0,0 +1,61 @@ +#ifndef GAMESETTINGS_HPP +#define GAMESETTINGS_HPP + +#include +#include +#include +#include + +#include + +namespace Files { typedef std::vector PathContainer; + struct ConfigurationManager;} + +class GameSettings +{ +public: + GameSettings(Files::ConfigurationManager &cfg); + ~GameSettings(); + + inline QString value(const QString &key, const QString &defaultValue = QString()) + { + return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key); + } + + + inline void setValue(const QString &key, const QString &value) + { + mSettings.insert(key, value); + } + + inline void setMultiValue(const QString &key, const QString &value) + { + QStringList values = mSettings.values(key); + if (!values.contains(value)) + mSettings.insertMulti(key, value); + } + + inline void remove(const QString &key) + { + mSettings.remove(key); + } + + inline QStringList getDataDirs() { return mDataDirs; } + inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); } + inline QString getDataLocal() {return mDataLocal; } + + QStringList values(const QString &key, const QStringList &defaultValues = QStringList()); + bool readFile(QTextStream &stream); + bool writeFile(QTextStream &stream); + +private: + Files::ConfigurationManager &mCfgMgr; + + void validatePaths(); + QMap mSettings; + + QStringList mDataDirs; + QString mDataLocal; +}; + +#endif // GAMESETTINGS_HPP diff --git a/apps/launcher/settings/graphicssettings.cpp b/apps/launcher/settings/graphicssettings.cpp new file mode 100644 index 000000000..0c5580091 --- /dev/null +++ b/apps/launcher/settings/graphicssettings.cpp @@ -0,0 +1,44 @@ +#include "graphicssettings.hpp" + +#include +#include +#include +#include + +GraphicsSettings::GraphicsSettings() +{ +} + +GraphicsSettings::~GraphicsSettings() +{ +} + +bool GraphicsSettings::writeFile(QTextStream &stream) +{ + QString sectionPrefix; + QRegExp sectionRe("([^/]+)/(.+)$"); + QMap settings = SettingsBase::getSettings(); + + QMapIterator i(settings); + while (i.hasNext()) { + i.next(); + + QString prefix; + QString key; + + if (sectionRe.exactMatch(i.key())) { + prefix = sectionRe.cap(1); + key = sectionRe.cap(2); + } + + if (sectionPrefix != prefix) { + sectionPrefix = prefix; + stream << "\n[" << prefix << "]\n"; + } + + stream << key << " = " << i.value() << "\n"; + } + + return true; + +} diff --git a/apps/launcher/settings/graphicssettings.hpp b/apps/launcher/settings/graphicssettings.hpp new file mode 100644 index 000000000..3e8617849 --- /dev/null +++ b/apps/launcher/settings/graphicssettings.hpp @@ -0,0 +1,16 @@ +#ifndef GRAPHICSSETTINGS_HPP +#define GRAPHICSSETTINGS_HPP + +#include "settingsbase.hpp" + +class GraphicsSettings : public SettingsBase > +{ +public: + GraphicsSettings(); + ~GraphicsSettings(); + + bool writeFile(QTextStream &stream); + +}; + +#endif // GRAPHICSSETTINGS_HPP diff --git a/apps/launcher/settings/launchersettings.cpp b/apps/launcher/settings/launchersettings.cpp new file mode 100644 index 000000000..5d298e814 --- /dev/null +++ b/apps/launcher/settings/launchersettings.cpp @@ -0,0 +1,101 @@ +#include "launchersettings.hpp" + +#include +#include +#include +#include + +LauncherSettings::LauncherSettings() +{ +} + +LauncherSettings::~LauncherSettings() +{ +} + +QStringList LauncherSettings::values(const QString &key, Qt::MatchFlags flags) +{ + QMap settings = SettingsBase::getSettings(); + + if (flags == Qt::MatchExactly) + return settings.values(key); + + QStringList result; + + if (flags == Qt::MatchStartsWith) { + QStringList keys = settings.keys(); + + foreach (const QString ¤tKey, keys) { + if (currentKey.startsWith(key)) + result.append(settings.value(currentKey)); + } + } + + return result; +} + +QStringList LauncherSettings::subKeys(const QString &key) +{ + QMap settings = SettingsBase::getSettings(); + QStringList keys = settings.uniqueKeys(); + + QRegExp keyRe("(.+)/"); + + QStringList result; + + foreach (const QString ¤tKey, keys) { + + if (keyRe.indexIn(currentKey) != -1) { + + QString prefixedKey = keyRe.cap(1); + if(prefixedKey.startsWith(key)) { + + QString subKey = prefixedKey.remove(key); + if (!subKey.isEmpty()) + result.append(subKey); + } + } + } + + result.removeDuplicates(); + return result; +} + +bool LauncherSettings::writeFile(QTextStream &stream) +{ + QString sectionPrefix; + QRegExp sectionRe("([^/]+)/(.+)$"); + QMap settings = SettingsBase::getSettings(); + + QMapIterator i(settings); + i.toBack(); + + while (i.hasPrevious()) { + i.previous(); + + QString prefix; + QString key; + + if (sectionRe.exactMatch(i.key())) { + prefix = sectionRe.cap(1); + key = sectionRe.cap(2); + } + + // Get rid of legacy settings + if (key.contains(QChar('\\'))) + continue; + + if (key == QLatin1String("CurrentProfile")) + continue; + + if (sectionPrefix != prefix) { + sectionPrefix = prefix; + stream << "\n[" << prefix << "]\n"; + } + + stream << key << "=" << i.value() << "\n"; + } + + return true; + +} diff --git a/apps/launcher/settings/launchersettings.hpp b/apps/launcher/settings/launchersettings.hpp new file mode 100644 index 000000000..60c6f86bc --- /dev/null +++ b/apps/launcher/settings/launchersettings.hpp @@ -0,0 +1,19 @@ +#ifndef LAUNCHERSETTINGS_HPP +#define LAUNCHERSETTINGS_HPP + +#include "settingsbase.hpp" + +class LauncherSettings : public SettingsBase > +{ +public: + LauncherSettings(); + ~LauncherSettings(); + + QStringList subKeys(const QString &key); + QStringList values(const QString &key, Qt::MatchFlags flags = Qt::MatchExactly); + + bool writeFile(QTextStream &stream); + +}; + +#endif // LAUNCHERSETTINGS_HPP diff --git a/apps/launcher/settings/settingsbase.hpp b/apps/launcher/settings/settingsbase.hpp new file mode 100644 index 000000000..21029b3ad --- /dev/null +++ b/apps/launcher/settings/settingsbase.hpp @@ -0,0 +1,99 @@ +#ifndef SETTINGSBASE_HPP +#define SETTINGSBASE_HPP + +#include +#include +#include +#include +#include + +#include + +template +class SettingsBase +{ + +public: + SettingsBase() {} + ~SettingsBase() {} + + inline QString value(const QString &key, const QString &defaultValue = QString()) + { + return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key); + } + + inline void setValue(const QString &key, const QString &value) + { + QStringList values = mSettings.values(key); + if (!values.contains(value)) + mSettings.insert(key, value); + } + + inline void setMultiValue(const QString &key, const QString &value) + { + QStringList values = mSettings.values(key); + if (!values.contains(value)) + mSettings.insertMulti(key, value); + } + + inline void remove(const QString &key) + { + mSettings.remove(key); + } + + Map getSettings() {return mSettings;} + + bool readFile(QTextStream &stream) + { + mCache.clear(); + + QString sectionPrefix; + + QRegExp sectionRe("^\\[([^]]+)\\]"); + QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); + + while (!stream.atEnd()) { + QString line = stream.readLine(); + + if (line.isEmpty() || line.startsWith("#")) + continue; + + if (sectionRe.exactMatch(line)) { + sectionPrefix = sectionRe.cap(1); + sectionPrefix.append("/"); + continue; + } + + if (keyRe.indexIn(line) != -1) { + + QString key = keyRe.cap(1); + QString value = keyRe.cap(2); + + if (!sectionPrefix.isEmpty()) + key.prepend(sectionPrefix); + + mSettings.remove(key); + + QStringList values = mCache.values(key); + if (!values.contains(value)) { + mCache.insertMulti(key, value); + } + } + } + + if (mSettings.isEmpty()) { + mSettings = mCache; // This is the first time we read a file + return true; + } + + // Merge the changed keys with those which didn't + mSettings.unite(mCache); + return true; + } + +private: + Map mSettings; + Map mCache; +}; + +#endif // SETTINGSBASE_HPP diff --git a/apps/launcher/utils/checkablemessagebox.cpp b/apps/launcher/utils/checkablemessagebox.cpp new file mode 100644 index 000000000..41207a8de --- /dev/null +++ b/apps/launcher/utils/checkablemessagebox.cpp @@ -0,0 +1,269 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "checkablemessagebox.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/*! + \class Utils::CheckableMessageBox + + \brief A messagebox suitable for questions with a + "Do not ask me again" checkbox. + + Emulates the QMessageBox API with + static conveniences. The message label can open external URLs. +*/ + +class CheckableMessageBoxPrivate +{ +public: + CheckableMessageBoxPrivate(QDialog *q) + : clickedButton(0) + { + QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); + + pixmapLabel = new QLabel(q); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth()); + pixmapLabel->setSizePolicy(sizePolicy); + pixmapLabel->setVisible(false); + + QSpacerItem *pixmapSpacer = + new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); + + messageLabel = new QLabel(q); + messageLabel->setMinimumSize(QSize(300, 0)); + messageLabel->setWordWrap(true); + messageLabel->setOpenExternalLinks(true); + messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse); + + QSpacerItem *checkBoxRightSpacer = + new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum); + QSpacerItem *buttonSpacer = + new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum); + + checkBox = new QCheckBox(q); + checkBox->setText(CheckableMessageBox::tr("Do not ask again")); + + buttonBox = new QDialogButtonBox(q); + buttonBox->setOrientation(Qt::Horizontal); + buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); + + QVBoxLayout *verticalLayout = new QVBoxLayout(); + verticalLayout->addWidget(pixmapLabel); + verticalLayout->addItem(pixmapSpacer); + + QHBoxLayout *horizontalLayout_2 = new QHBoxLayout(); + horizontalLayout_2->addLayout(verticalLayout); + horizontalLayout_2->addWidget(messageLabel); + + QHBoxLayout *horizontalLayout = new QHBoxLayout(); + horizontalLayout->addWidget(checkBox); + horizontalLayout->addItem(checkBoxRightSpacer); + + QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q); + verticalLayout_2->addLayout(horizontalLayout_2); + verticalLayout_2->addLayout(horizontalLayout); + verticalLayout_2->addItem(buttonSpacer); + verticalLayout_2->addWidget(buttonBox); + } + + QLabel *pixmapLabel; + QLabel *messageLabel; + QCheckBox *checkBox; + QDialogButtonBox *buttonBox; + QAbstractButton *clickedButton; +}; + +CheckableMessageBox::CheckableMessageBox(QWidget *parent) : + QDialog(parent), + d(new CheckableMessageBoxPrivate(this)) +{ + setModal(true); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + connect(d->buttonBox, SIGNAL(accepted()), SLOT(accept())); + connect(d->buttonBox, SIGNAL(rejected()), SLOT(reject())); + connect(d->buttonBox, SIGNAL(clicked(QAbstractButton*)), + SLOT(slotClicked(QAbstractButton*))); +} + +CheckableMessageBox::~CheckableMessageBox() +{ + delete d; +} + +void CheckableMessageBox::slotClicked(QAbstractButton *b) +{ + d->clickedButton = b; +} + +QAbstractButton *CheckableMessageBox::clickedButton() const +{ + return d->clickedButton; +} + +QDialogButtonBox::StandardButton CheckableMessageBox::clickedStandardButton() const +{ + if (d->clickedButton) + return d->buttonBox->standardButton(d->clickedButton); + return QDialogButtonBox::NoButton; +} + +QString CheckableMessageBox::text() const +{ + return d->messageLabel->text(); +} + +void CheckableMessageBox::setText(const QString &t) +{ + d->messageLabel->setText(t); +} + +QPixmap CheckableMessageBox::iconPixmap() const +{ + if (const QPixmap *p = d->pixmapLabel->pixmap()) + return QPixmap(*p); + return QPixmap(); +} + +void CheckableMessageBox::setIconPixmap(const QPixmap &p) +{ + d->pixmapLabel->setPixmap(p); + d->pixmapLabel->setVisible(!p.isNull()); +} + +bool CheckableMessageBox::isChecked() const +{ + return d->checkBox->isChecked(); +} + +void CheckableMessageBox::setChecked(bool s) +{ + d->checkBox->setChecked(s); +} + +QString CheckableMessageBox::checkBoxText() const +{ + return d->checkBox->text(); +} + +void CheckableMessageBox::setCheckBoxText(const QString &t) +{ + d->checkBox->setText(t); +} + +bool CheckableMessageBox::isCheckBoxVisible() const +{ + return d->checkBox->isVisible(); +} + +void CheckableMessageBox::setCheckBoxVisible(bool v) +{ + d->checkBox->setVisible(v); +} + +QDialogButtonBox::StandardButtons CheckableMessageBox::standardButtons() const +{ + return d->buttonBox->standardButtons(); +} + +void CheckableMessageBox::setStandardButtons(QDialogButtonBox::StandardButtons s) +{ + d->buttonBox->setStandardButtons(s); +} + +QPushButton *CheckableMessageBox::button(QDialogButtonBox::StandardButton b) const +{ + return d->buttonBox->button(b); +} + +QPushButton *CheckableMessageBox::addButton(const QString &text, QDialogButtonBox::ButtonRole role) +{ + return d->buttonBox->addButton(text, role); +} + +QDialogButtonBox::StandardButton CheckableMessageBox::defaultButton() const +{ + foreach (QAbstractButton *b, d->buttonBox->buttons()) + if (QPushButton *pb = qobject_cast(b)) + if (pb->isDefault()) + return d->buttonBox->standardButton(pb); + return QDialogButtonBox::NoButton; +} + +void CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s) +{ + if (QPushButton *b = d->buttonBox->button(s)) { + b->setDefault(true); + b->setFocus(); + } +} + +QDialogButtonBox::StandardButton +CheckableMessageBox::question(QWidget *parent, + const QString &title, + const QString &question, + const QString &checkBoxText, + bool *checkBoxSetting, + QDialogButtonBox::StandardButtons buttons, + QDialogButtonBox::StandardButton defaultButton) +{ + CheckableMessageBox mb(parent); + mb.setWindowTitle(title); + mb.setIconPixmap(QMessageBox::standardIcon(QMessageBox::Question)); + mb.setText(question); + mb.setCheckBoxText(checkBoxText); + mb.setChecked(*checkBoxSetting); + mb.setStandardButtons(buttons); + mb.setDefaultButton(defaultButton); + mb.exec(); + *checkBoxSetting = mb.isChecked(); + return mb.clickedStandardButton(); +} + +QMessageBox::StandardButton CheckableMessageBox::dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton db) +{ + return static_cast(int(db)); +} diff --git a/apps/launcher/utils/checkablemessagebox.hpp b/apps/launcher/utils/checkablemessagebox.hpp new file mode 100644 index 000000000..93fd43fe1 --- /dev/null +++ b/apps/launcher/utils/checkablemessagebox.hpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef CHECKABLEMESSAGEBOX_HPP +#define CHECKABLEMESSAGEBOX_HPP + +#include +#include +#include + +class CheckableMessageBoxPrivate; + +class CheckableMessageBox : public QDialog +{ + Q_OBJECT + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap) + Q_PROPERTY(bool isChecked READ isChecked WRITE setChecked) + Q_PROPERTY(QString checkBoxText READ checkBoxText WRITE setCheckBoxText) + Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons) + Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton) + +public: + explicit CheckableMessageBox(QWidget *parent); + virtual ~CheckableMessageBox(); + + static QDialogButtonBox::StandardButton + question(QWidget *parent, + const QString &title, + const QString &question, + const QString &checkBoxText, + bool *checkBoxSetting, + QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Yes|QDialogButtonBox::No, + QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::No); + + QString text() const; + void setText(const QString &); + + bool isChecked() const; + void setChecked(bool s); + + QString checkBoxText() const; + void setCheckBoxText(const QString &); + + bool isCheckBoxVisible() const; + void setCheckBoxVisible(bool); + + QDialogButtonBox::StandardButtons standardButtons() const; + void setStandardButtons(QDialogButtonBox::StandardButtons s); + QPushButton *button(QDialogButtonBox::StandardButton b) const; + QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role); + + QDialogButtonBox::StandardButton defaultButton() const; + void setDefaultButton(QDialogButtonBox::StandardButton s); + + // See static QMessageBox::standardPixmap() + QPixmap iconPixmap() const; + void setIconPixmap (const QPixmap &p); + + // Query the result + QAbstractButton *clickedButton() const; + QDialogButtonBox::StandardButton clickedStandardButton() const; + + // Conversion convenience + static QMessageBox::StandardButton dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton); + +private slots: + void slotClicked(QAbstractButton *b); + +private: + CheckableMessageBoxPrivate *d; +}; + +#endif // CHECKABLEMESSAGEBOX_HPP diff --git a/apps/launcher/utils/filedialog.cpp b/apps/launcher/utils/filedialog.cpp deleted file mode 100644 index 16d677533..000000000 --- a/apps/launcher/utils/filedialog.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "filedialog.hpp" -#include -#include - -FileDialog::FileDialog(QWidget *parent) - : QFileDialog(parent) -{ - // Remove the default Choose button to prevent it being updated elsewhere - QDialogButtonBox *box = qFindChild(this); - Q_ASSERT(box); - box->removeButton(box->button(QDialogButtonBox::Open)); - - // Add our own button so we can disable/enable it - mChooseButton = new QPushButton(tr("&Choose")); - mChooseButton->setIcon(QIcon::fromTheme("document-open")); - mChooseButton->setEnabled(false); - box->addButton(mChooseButton, QDialogButtonBox::AcceptRole); - - connect(this, SIGNAL(directoryEntered(const QString&)), this, SLOT(updateChooseButton(const QString&))); - emit directoryEntered(QDir::currentPath()); -} - -QString FileDialog::getExistingDirectory(QWidget *parent, - const QString &caption, - const QString &dir, - Options options) -{ - // create a non-native file dialog - FileDialog dialog; - dialog.setFileMode(DirectoryOnly); - dialog.setOptions(options |= QFileDialog::DontUseNativeDialog | QFileDialog::ShowDirsOnly | QFileDialog::ReadOnly); - - if (!caption.isEmpty()) - dialog.setWindowTitle(caption); - - if (!dir.isEmpty()) - dialog.setDirectory(dir); - - if (dialog.exec() == QDialog::Accepted) { - return dialog.selectedFiles().value(0); - } - return QString(); -} - -void FileDialog::updateChooseButton(const QString &directory) -{ - QDir currentDir = QDir(directory); - currentDir.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks); - currentDir.setNameFilters(QStringList() << "*.esm" << "*.esp"); - - if (!currentDir.entryList().isEmpty()) { - // There are data files in the current dir - mChooseButton->setEnabled(true); - } else { - mChooseButton->setEnabled(false); - } -} diff --git a/apps/launcher/utils/filedialog.hpp b/apps/launcher/utils/filedialog.hpp deleted file mode 100644 index 7a161ecb9..000000000 --- a/apps/launcher/utils/filedialog.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef FILEDIALOG_HPP -#define FILEDIALOG_HPP - -#include - -class QPushButton; - -class FileDialog : public QFileDialog -{ - Q_OBJECT - -public: - FileDialog(QWidget *parent = 0); - - static QString getExistingDirectory(QWidget *parent = 0, - const QString &caption = QString(), - const QString &dir = QString(), - Options options = ShowDirsOnly); - -private slots: - void updateChooseButton(const QString &directory); - -private: - QPushButton *mChooseButton; -}; - - -#endif // FILEDIALOG_HPP diff --git a/apps/launcher/utils/profilescombobox.cpp b/apps/launcher/utils/profilescombobox.cpp deleted file mode 100644 index 8354d4a78..000000000 --- a/apps/launcher/utils/profilescombobox.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include - -#include "profilescombobox.hpp" - -ProfilesComboBox::ProfilesComboBox(QWidget *parent) : - QComboBox(parent) -{ - mValidator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore - - setEditable(true); - setValidator(mValidator); - setCompleter(0); - - connect(this, SIGNAL(currentIndexChanged(int)), this, - SLOT(slotIndexChanged(int))); - connect(lineEdit(), SIGNAL(returnPressed()), this, - SLOT(slotReturnPressed())); -} - -void ProfilesComboBox::setEditEnabled(bool editable) -{ - if (!editable) - return setEditable(false); - - // Reset the completer and validator - setEditable(true); - setValidator(mValidator); - setCompleter(0); -} - -void ProfilesComboBox::slotReturnPressed() -{ - QString current = currentText(); - QString previous = itemText(currentIndex()); - - if (findText(current) != -1) - return; - - setItemText(currentIndex(), current); - emit(profileRenamed(previous, current)); -} - -void ProfilesComboBox::slotIndexChanged(int index) -{ - if (index == -1) - return; - - emit(profileChanged(mOldProfile, currentText())); - mOldProfile = itemText(index); -} diff --git a/apps/launcher/utils/textinputdialog.cpp b/apps/launcher/utils/textinputdialog.cpp index 16cadb661..a4b36b95e 100644 --- a/apps/launcher/utils/textinputdialog.cpp +++ b/apps/launcher/utils/textinputdialog.cpp @@ -1,13 +1,13 @@ +#include "textinputdialog.hpp" + #include +#include #include -#include -#include #include #include +#include -#include "lineedit.hpp" - -#include "textinputdialog.hpp" +#include TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWidget *parent) : QDialog(parent) @@ -17,33 +17,33 @@ TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWid mButtonBox->addButton(QDialogButtonBox::Ok); mButtonBox->addButton(QDialogButtonBox::Cancel); - setMaximumHeight(height()); - setOkButtonEnabled(false); - setModal(true); - - // Messageboxes on mac have no title -#ifndef Q_OS_MAC - setWindowTitle(title); -#else - Q_UNUSED(title); -#endif - - QLabel *label = new QLabel(this); - label->setText(text); - // Line edit QValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore mLineEdit = new LineEdit(this); mLineEdit->setValidator(validator); mLineEdit->setCompleter(0); + QLabel *label = new QLabel(this); + label->setText(text); + QVBoxLayout *dialogLayout = new QVBoxLayout(this); dialogLayout->addWidget(label); dialogLayout->addWidget(mLineEdit); dialogLayout->addWidget(mButtonBox); + // Messageboxes on mac have no title +#ifndef Q_OS_MAC + setWindowTitle(title); +#else + Q_UNUSED(title); +#endif + + setOkButtonEnabled(false); + setModal(true); + connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject())); + } int TextInputDialog::exec() @@ -55,7 +55,17 @@ int TextInputDialog::exec() void TextInputDialog::setOkButtonEnabled(bool enabled) { - QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok); okButton->setEnabled(enabled); + + QPalette *palette = new QPalette(); + palette->setColor(QPalette::Text,Qt::red); + + if (enabled) { + mLineEdit->setPalette(QApplication::palette()); + } else { + // Existing profile name, make the text red + mLineEdit->setPalette(*palette); + } + } diff --git a/apps/mwiniimporter/CMakeLists.txt b/apps/mwiniimporter/CMakeLists.txt index deab88ce2..702f66513 100644 --- a/apps/mwiniimporter/CMakeLists.txt +++ b/apps/mwiniimporter/CMakeLists.txt @@ -22,3 +22,8 @@ if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(mwiniimport gcov) endif() + +if(DPKG_PROGRAM) + INSTALL(TARGETS mwiniimport RUNTIME DESTINATION games COMPONENT mwiniimport) +endif() + diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 077b62be1..fc9ce417c 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -777,6 +777,39 @@ void MwIniImporter::insertMultistrmap(multistrmap &cfg, std::string key, std::st cfg[key].push_back(value); } +void MwIniImporter::importArchives(multistrmap &cfg, multistrmap &ini) { + std::vector archives; + std::string baseArchive("Archives:Archive "); + std::string archive; + + // Search archives listed in ini file + multistrmap::iterator it = ini.begin(); + for(int i=0; it != ini.end(); i++) { + archive = baseArchive; + archive.append(this->numberToString(i)); + + it = ini.find(archive); + if(it == ini.end()) { + break; + } + + for(std::vector::iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) { + archives.push_back(*entry); + } + } + + cfg.erase("fallback-archive"); + cfg.insert( std::make_pair > ("fallback-archive", std::vector())); + + // Add Morrowind.bsa by default, since Vanilla loads this archive even if it + // does not appears in the ini file + cfg["fallback-archive"].push_back("Morrowind.bsa"); + + for(std::vector::iterator it=archives.begin(); it!=archives.end(); ++it) { + cfg["fallback-archive"].push_back(*it); + } +} + void MwIniImporter::importGameFiles(multistrmap &cfg, multistrmap &ini) { std::vector esmFiles; std::vector espFiles; @@ -794,7 +827,7 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, multistrmap &ini) { } for(std::vector::iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) { - std::string filetype(entry->substr(entry->length()-4, 3)); + std::string filetype(entry->substr(entry->length()-3)); Misc::StringUtils::toLower(filetype); if(filetype.compare("esm") == 0) { diff --git a/apps/mwiniimporter/importer.hpp b/apps/mwiniimporter/importer.hpp index c87fd3e16..6b99810bc 100644 --- a/apps/mwiniimporter/importer.hpp +++ b/apps/mwiniimporter/importer.hpp @@ -23,6 +23,7 @@ class MwIniImporter { void merge(multistrmap &cfg, multistrmap &ini); void mergeFallback(multistrmap &cfg, multistrmap &ini); void importGameFiles(multistrmap &cfg, multistrmap &ini); + void importArchives(multistrmap &cfg, multistrmap &ini); void writeToFile(boost::iostreams::stream &out, multistrmap &cfg); private: diff --git a/apps/mwiniimporter/main.cpp b/apps/mwiniimporter/main.cpp index e90f26dd2..c9d88c0bb 100644 --- a/apps/mwiniimporter/main.cpp +++ b/apps/mwiniimporter/main.cpp @@ -18,6 +18,7 @@ int main(int argc, char *argv[]) { ("cfg,c", bpo::value(), "openmw.cfg file") ("output,o", bpo::value()->default_value(""), "openmw.cfg file") ("game-files,g", "import esm and esp files") + ("no-archives,A", "disable bsa archives import") ("encoding,e", bpo::value()-> default_value("win1252"), "Character encoding used in OpenMW game messages:\n" "\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n" @@ -76,6 +77,10 @@ int main(int argc, char *argv[]) { importer.importGameFiles(cfg, ini); } + if(!vm.count("no-archives")) { + importer.importArchives(cfg, ini); + } + std::cout << "write to: " << outputFile << std::endl; boost::iostreams::stream file(outputFile); importer.writeToFile(file, cfg); diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index abbc953ca..9787719af 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -1,50 +1,116 @@ +set (OPENCS_SRC main.cpp) -set (OPENCS_SRC - main.cpp editor.cpp +opencs_units (. editor) - model/doc/documentmanager.cpp model/doc/document.cpp +set (CMAKE_BUILD_TYPE DEBUG) - model/world/universalid.cpp model/world/idcollection.cpp model/world/data.cpp model/world/idtable.cpp - model/world/commands.cpp model/world/idtableproxymodel.cpp model/world/record.cpp - model/world/columnbase.cpp +opencs_units (model/doc + document + ) + +opencs_units_noqt (model/doc + documentmanager + ) + +opencs_hdrs_noqt (model/doc + state + ) + + +opencs_units (model/world + idtable idtableproxymodel + ) + + +opencs_units_noqt (model/world + universalid data record idcollection commands columnbase scriptcontext cell refidcollection + refidadapter refiddata refidadapterimp + ) + +opencs_hdrs_noqt (model/world + columns + ) + + +opencs_units (model/tools + tools operation reportmodel + ) + +opencs_units_noqt (model/tools + stage verifier mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck + birthsigncheck spellcheck + ) - model/tools/tools.cpp model/tools/operation.cpp model/tools/stage.cpp model/tools/verifier.cpp - model/tools/mandatoryid.cpp model/tools/reportmodel.cpp - view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp view/doc/subviewfactory.cpp - view/doc/subview.cpp +opencs_units (view/doc + viewmanager view operations operation subview startup filedialog + ) - view/world/table.cpp view/world/tablesubview.cpp view/world/subviews.cpp view/world/util.cpp - view/world/dialoguesubview.cpp - view/tools/reportsubview.cpp view/tools/subviews.cpp +opencs_units_noqt (view/doc + subviewfactory ) -set (OPENCS_HDR - editor.hpp +opencs_hdrs_noqt (view/doc + subviewfactoryimp + ) - model/doc/documentmanager.hpp model/doc/document.hpp model/doc/state.hpp - model/world/universalid.hpp model/world/record.hpp model/world/idcollection.hpp model/world/data.hpp - model/world/idtable.hpp model/world/columns.hpp model/world/idtableproxymodel.hpp - model/world/commands.hpp model/world/columnbase.hpp +opencs_units (view/world + table tablesubview scriptsubview + ) - model/tools/tools.hpp model/tools/operation.hpp model/tools/stage.hpp model/tools/verifier.hpp - model/tools/mandatoryid.hpp model/tools/reportmodel.hpp +opencs_units_noqt (view/world + dialoguesubview util subviews enumdelegate vartypedelegate scripthighlighter + ) - view/doc/viewmanager.hpp view/doc/view.hpp view/doc/operations.hpp view/doc/operation.hpp view/doc/subviewfactory.hpp - view/doc/subview.hpp view/doc/subviewfactoryimp.hpp - view/world/table.hpp view/world/tablesubview.hpp view/world/subviews.hpp view/world/util.hpp - view/world/dialoguesubview.hpp +opencs_units (view/tools + reportsubview + ) + +opencs_units_noqt (view/tools + subviews + ) - view/tools/reportsubview.hpp view/tools/subviews.hpp +opencs_units (view/settings + abstractblock + proxyblock + abstractwidget + usersettingsdialog + editorpage + ) + +opencs_units_noqt (view/settings + abstractpage + blankpage + groupblock + customblock + groupbox + itemblock + settingwidget + toggleblock + support + ) + +opencs_units (model/settings + usersettings + settingcontainer + ) + +opencs_units_noqt (model/settings + support + settingsitem ) set (OPENCS_US ) -set (OPENCS_RES +set (OPENCS_RES ../../files/opencs/resources.qrc + ../../files/launcher/launcher.qrc + ) + +set (OPENCS_UI ../../files/ui/datafilespage.ui ) source_group (opencs FILES ${OPENCS_SRC} ${OPENCS_HDR}) @@ -57,7 +123,7 @@ find_package(Qt4 COMPONENTS QtCore QtGui QtXml QtXmlPatterns REQUIRED) include(${QT_USE_FILE}) qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI}) -qt4_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR}) +qt4_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT}) qt4_add_resources(OPENCS_RES_SRC ${OPENCS_RES}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) @@ -73,4 +139,4 @@ target_link_libraries(opencs ${Boost_LIBRARIES} ${QT_LIBRARIES} components -) \ No newline at end of file +) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 1632ed220..8dc5366a9 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -1,49 +1,119 @@ #include "editor.hpp" -#include - #include #include "model/doc/document.hpp" #include "model/world/data.hpp" -CS::Editor::Editor() : mViewManager (mDocumentManager), mNewDocumentIndex (0) +CS::Editor::Editor() : mViewManager (mDocumentManager) { connect (&mViewManager, SIGNAL (newDocumentRequest ()), this, SLOT (createDocument ())); + connect (&mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ())); + + connect (&mStartup, SIGNAL (createDocument()), this, SLOT (createDocument ())); + connect (&mStartup, SIGNAL (loadDocument()), this, SLOT (loadDocument ())); + + connect (&mFileDialog, SIGNAL(openFiles()), this, SLOT(openFiles())); + connect (&mFileDialog, SIGNAL(createNewFile()), this, SLOT(createNewFile())); + + setupDataFiles(); } -void CS::Editor::createDocument() +void CS::Editor::setupDataFiles() { - std::ostringstream stream; + boost::program_options::variables_map variables; + boost::program_options::options_description desc; - stream << "NewDocument" << (++mNewDocumentIndex); + desc.add_options() + ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()) + ("data-local", boost::program_options::value()->default_value("")) + ("fs-strict", boost::program_options::value()->implicit_value(true)->default_value(false)) + ("encoding", boost::program_options::value()->default_value("win1252")); - CSMDoc::Document *document = mDocumentManager.addDocument (stream.str()); + boost::program_options::notify(variables); - static const char *sGlobals[] = - { - "Day", "DaysPassed", "GameHour", "Month", "PCRace", "PCVampire", "PCWerewolf", "PCYear", 0 - }; + mCfgMgr.readConfiguration(variables, desc); + + Files::PathContainer mDataDirs, mDataLocal; + if (!variables["data"].empty()) { + mDataDirs = Files::PathContainer(variables["data"].as()); + } + + std::string local = variables["data-local"].as(); + if (!local.empty()) { + mDataLocal.push_back(Files::PathContainer::value_type(local)); + } - for (int i=0; sGlobals[i]; ++i) + mCfgMgr.processPaths(mDataDirs); + mCfgMgr.processPaths(mDataLocal); + + // Set the charset for reading the esm/esp files + QString encoding = QString::fromStdString(variables["encoding"].as()); + mFileDialog.setEncoding(encoding); + + Files::PathContainer dataDirs; + dataDirs.insert(dataDirs.end(), mDataDirs.begin(), mDataDirs.end()); + dataDirs.insert(dataDirs.end(), mDataLocal.begin(), mDataLocal.end()); + + for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) { - ESM::Global record; - record.mId = sGlobals[i]; - record.mValue = i==0 ? 1 : 0; - record.mType = ESM::VT_Float; - document->getData().getGlobals().add (record); + QString path = QString::fromStdString(iter->string()); + mFileDialog.addFiles(path); + } +} + +void CS::Editor::createDocument() +{ + mStartup.hide(); + + mFileDialog.newFile(); +} + +void CS::Editor::loadDocument() +{ + mStartup.hide(); + + mFileDialog.openFile(); +} + +void CS::Editor::openFiles() +{ + std::vector files; + QStringList paths = mFileDialog.checkedItemsPaths(); + + foreach (const QString &path, paths) { + files.push_back(path.toStdString()); } - document->getData().merge(); /// \todo remove once proper ESX loading is implemented + CSMDoc::Document *document = mDocumentManager.addDocument(files, false); mViewManager.addView (document); + mFileDialog.hide(); +} + +void CS::Editor::createNewFile() +{ + std::vector files; + QStringList paths = mFileDialog.checkedItemsPaths(); + + foreach (const QString &path, paths) { + files.push_back(path.toStdString()); + } + + files.push_back(mFileDialog.fileName().toStdString()); + + CSMDoc::Document *document = mDocumentManager.addDocument (files, true); + + mViewManager.addView (document); + mFileDialog.hide(); } int CS::Editor::run() { - /// \todo Instead of creating an empty document, open a small welcome dialogue window with buttons for new/load/recent projects - createDocument(); + mStartup.show(); + + QApplication::setQuitOnLastWindowClosed (true); return QApplication::exec(); -} \ No newline at end of file +} diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index 60f7beaf1..1c4bcb1ee 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -2,9 +2,14 @@ #define CS_EDITOR_H #include - +#ifndef Q_MOC_RUN +#include +#endif #include "model/doc/documentmanager.hpp" + #include "view/doc/viewmanager.hpp" +#include "view/doc/startup.hpp" +#include "view/doc/filedialog.hpp" namespace CS { @@ -12,10 +17,13 @@ namespace CS { Q_OBJECT - int mNewDocumentIndex; ///< \todo remove when the proper new document dialogue is implemented. - CSMDoc::DocumentManager mDocumentManager; CSVDoc::ViewManager mViewManager; + CSVDoc::StartupDialogue mStartup; + FileDialog mFileDialog; + + Files::ConfigurationManager mCfgMgr; + void setupDataFiles(); // not implemented Editor (const Editor&); @@ -28,10 +36,14 @@ namespace CS int run(); ///< \return error status - public slots: + private slots: void createDocument(); + + void loadDocument(); + void openFiles(); + void createNewFile(); }; } -#endif \ No newline at end of file +#endif diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index 4b1a688c2..aa315804b 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -5,6 +5,7 @@ #include #include +#include class Application : public QApplication { @@ -31,9 +32,12 @@ class Application : public QApplication int main(int argc, char *argv[]) { + Q_INIT_RESOURCE (resources); Application mApplication (argc, argv); + mApplication.setWindowIcon (QIcon (":./opencs.png")); + CS::Editor editor; return editor.run(); -} \ No newline at end of file +} diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 9d2694483..7a66487fb 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -1,10 +1,246 @@ #include "document.hpp" -CSMDoc::Document::Document (const std::string& name) +#include + +#include +void CSMDoc::Document::load (const std::vector::const_iterator& begin, + const std::vector::const_iterator& end, bool lastAsModified) +{ + assert (begin!=end); + + std::vector::const_iterator end2 (end); + + if (lastAsModified) + --end2; + + for (std::vector::const_iterator iter (begin); iter!=end2; ++iter) + getData().loadFile (*iter, true); + + if (lastAsModified) + getData().loadFile (*end2, false); +} + +void CSMDoc::Document::addOptionalGmsts() +{ + static const char *sFloats[] = + { + "fCombatDistanceWerewolfMod", + "fFleeDistance", + "fWereWolfAcrobatics", + "fWereWolfAgility", + "fWereWolfAlchemy", + "fWereWolfAlteration", + "fWereWolfArmorer", + "fWereWolfAthletics", + "fWereWolfAxe", + "fWereWolfBlock", + "fWereWolfBluntWeapon", + "fWereWolfConjuration", + "fWereWolfDestruction", + "fWereWolfEnchant", + "fWereWolfEndurance", + "fWereWolfFatigue", + "fWereWolfHandtoHand", + "fWereWolfHealth", + "fWereWolfHeavyArmor", + "fWereWolfIllusion", + "fWereWolfIntellegence", + "fWereWolfLightArmor", + "fWereWolfLongBlade", + "fWereWolfLuck", + "fWereWolfMagicka", + "fWereWolfMarksman", + "fWereWolfMediumArmor", + "fWereWolfMerchantile", + "fWereWolfMysticism", + "fWereWolfPersonality", + "fWereWolfRestoration", + "fWereWolfRunMult", + "fWereWolfSecurity", + "fWereWolfShortBlade", + "fWereWolfSilverWeaponDamageMult", + "fWereWolfSneak", + "fWereWolfSpear", + "fWereWolfSpeechcraft", + "fWereWolfSpeed", + "fWereWolfStrength", + "fWereWolfUnarmored", + "fWereWolfWillPower", + 0 + }; + + static const char *sIntegers[] = + { + "iWereWolfBounty", + "iWereWolfFightMod", + "iWereWolfFleeMod", + "iWereWolfLevelToAttack", + 0 + }; + + static const char *sStrings[] = + { + "sCompanionShare", + "sCompanionWarningButtonOne", + "sCompanionWarningButtonTwo", + "sCompanionWarningMessage", + "sDeleteNote", + "sEditNote", + "sEffectSummonCreature01", + "sEffectSummonCreature02", + "sEffectSummonCreature03", + "sEffectSummonCreature04", + "sEffectSummonCreature05", + "sEffectSummonFabricant", + "sLevitateDisabled", + "sMagicCreature01ID", + "sMagicCreature02ID", + "sMagicCreature03ID", + "sMagicCreature04ID", + "sMagicCreature05ID", + "sMagicFabricantID", + "sMaxSale", + "sProfitValue", + "sTeleportDisabled", + "sWerewolfAlarmMessage", + "sWerewolfPopup", + "sWerewolfRefusal", + "sWerewolfRestMessage", + 0 + }; + + for (int i=0; sFloats[i]; ++i) + { + ESM::GameSetting gmst; + gmst.mId = sFloats[i]; + gmst.mValue.setType (ESM::VT_Float); + addOptionalGmst (gmst); + } + + for (int i=0; sIntegers[i]; ++i) + { + ESM::GameSetting gmst; + gmst.mId = sIntegers[i]; + gmst.mValue.setType (ESM::VT_Int); + addOptionalGmst (gmst); + } + + for (int i=0; sStrings[i]; ++i) + { + ESM::GameSetting gmst; + gmst.mId = sStrings[i]; + gmst.mValue.setType (ESM::VT_String); + gmst.mValue.setString (""); + addOptionalGmst (gmst); + } +} + +void CSMDoc::Document::addOptionalGlobals() +{ + static const char *sGlobals[] = + { + "dayspassed", + "pcwerewolf", + "pcyear", + 0 + }; + + for (int i=0; sGlobals[i]; ++i) + { + ESM::Global global; + global.mId = sGlobals[i]; + global.mValue.setType (ESM::VT_Long); + + if (i==0) + global.mValue.setInteger (1); // dayspassed starts counting at 1 + + addOptionalGlobal (global); + } +} + +void CSMDoc::Document::addOptionalGmst (const ESM::GameSetting& gmst) +{ + if (getData().getGmsts().searchId (gmst.mId)==-1) + { + CSMWorld::Record record; + record.mBase = gmst; + record.mState = CSMWorld::RecordBase::State_BaseOnly; + getData().getGmsts().appendRecord (record); + } +} + +void CSMDoc::Document::addOptionalGlobal (const ESM::Global& global) +{ + if (getData().getGlobals().searchId (global.mId)==-1) + { + CSMWorld::Record record; + record.mBase = global; + record.mState = CSMWorld::RecordBase::State_BaseOnly; + getData().getGlobals().appendRecord (record); + } +} + +void CSMDoc::Document::createBase() +{ + static const char *sGlobals[] = + { + "Day", "DaysPassed", "GameHour", "Month", "PCRace", "PCVampire", "PCWerewolf", "PCYear", 0 + }; + + for (int i=0; sGlobals[i]; ++i) + { + ESM::Global record; + + record.mId = sGlobals[i]; + + record.mValue.setType (i==2 ? ESM::VT_Float : ESM::VT_Long); + + if (i==0 || i==1) + record.mValue.setInteger (1); + + getData().getGlobals().add (record); + } + + /// \todo add GMSTs + + for (int i=0; i<27; ++i) + { + ESM::Skill record; + record.mIndex = i; + record.mId = ESM::Skill::indexToId (record.mIndex); + record.blank(); + + getData().getSkills().add (record); + } +} + +CSMDoc::Document::Document (const std::vector& files, bool new_) : mTools (mData) { - mName = name; ///< \todo replace with ESX list + if (files.empty()) + throw std::runtime_error ("Empty content file sequence"); + + /// \todo adjust last file name: + /// \li make sure it is located in the data-local directory (adjust path if necessary) + /// \li make sure the extension matches the new scheme (change it if necesarry) + + mName = files.back().filename().string(); + + if (new_ && files.size()==1) + createBase(); + else + { + std::vector::const_iterator end = files.end(); + + if (new_) + --end; + + load (files.begin(), end, !new_); + } + + addOptionalGmsts(); + addOptionalGlobals(); connect (&mUndoStack, SIGNAL (cleanChanged (bool)), this, SLOT (modificationStateChanged (bool))); @@ -16,6 +252,9 @@ CSMDoc::Document::Document (const std::string& name) connect (&mSaveTimer, SIGNAL(timeout()), this, SLOT (saving())); } +CSMDoc::Document::~Document() +{} + QUndoStack& CSMDoc::Document::getUndoStack() { return mUndoStack; @@ -63,16 +302,19 @@ void CSMDoc::Document::abortOperation (int type) if (type==State_Saving) { + mSaveCount=0; mSaveTimer.stop(); emit stateChanged (getState(), this); } } + void CSMDoc::Document::modificationStateChanged (bool clean) { emit stateChanged (getState(), this); } + void CSMDoc::Document::operationDone (int type) { emit stateChanged (getState(), this); @@ -86,10 +328,13 @@ void CSMDoc::Document::saving() if (mSaveCount>15) { - mSaveCount = 0; - mSaveTimer.stop(); - mUndoStack.setClean(); - emit stateChanged (getState(), this); + //clear the stack before resetting the save state + //to avoid emitting incorrect states + mUndoStack.setClean(); + + mSaveCount = 0; + mSaveTimer.stop(); + emit stateChanged (getState(), this); } } @@ -111,4 +356,4 @@ CSMTools::ReportModel *CSMDoc::Document::getReport (const CSMWorld::UniversalId& void CSMDoc::Document::progress (int current, int max, int type) { emit progress (current, max, type, 1, this); -} \ No newline at end of file +} diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 43e8bba37..94d5fe85c 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -3,6 +3,8 @@ #include +#include + #include #include #include @@ -15,6 +17,12 @@ class QAbstractItemModel; +namespace ESM +{ + struct GameSetting; + struct Global; +} + namespace CSMDoc { class Document : public QObject @@ -38,10 +46,24 @@ namespace CSMDoc Document (const Document&); Document& operator= (const Document&); + void load (const std::vector::const_iterator& begin, + const std::vector::const_iterator& end, bool lastAsModified); + ///< \param lastAsModified Store the last file in Modified instead of merging it into Base. + + void createBase(); + + void addOptionalGmsts(); + + void addOptionalGlobals(); + + void addOptionalGmst (const ESM::GameSetting& gmst); + + void addOptionalGlobal (const ESM::Global& global); + public: - Document (const std::string& name); - ///< \todo replace name with ESX list + Document (const std::vector& files, bool new_); + ~Document(); QUndoStack& getUndoStack(); @@ -84,4 +106,4 @@ namespace CSMDoc }; } -#endif \ No newline at end of file +#endif diff --git a/apps/opencs/model/doc/documentmanager.cpp b/apps/opencs/model/doc/documentmanager.cpp index 8ae2764f2..740c0b582 100644 --- a/apps/opencs/model/doc/documentmanager.cpp +++ b/apps/opencs/model/doc/documentmanager.cpp @@ -14,9 +14,10 @@ CSMDoc::DocumentManager::~DocumentManager() delete *iter; } -CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::string& name) +CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector& files, + bool new_) { - Document *document = new Document (name); + Document *document = new Document (files, new_); mDocuments.push_back (document); diff --git a/apps/opencs/model/doc/documentmanager.hpp b/apps/opencs/model/doc/documentmanager.hpp index 730c7fae1..a307b76a5 100644 --- a/apps/opencs/model/doc/documentmanager.hpp +++ b/apps/opencs/model/doc/documentmanager.hpp @@ -4,6 +4,8 @@ #include #include +#include + namespace CSMDoc { class Document; @@ -21,8 +23,11 @@ namespace CSMDoc ~DocumentManager(); - Document *addDocument (const std::string& name); + Document *addDocument (const std::vector& files, 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 + /// appropriate way. bool removeDocument (Document *document); ///< \return last document removed? diff --git a/apps/opencs/model/settings/settingcontainer.cpp b/apps/opencs/model/settings/settingcontainer.cpp new file mode 100644 index 000000000..a75a84ec5 --- /dev/null +++ b/apps/opencs/model/settings/settingcontainer.cpp @@ -0,0 +1,82 @@ +#include "settingcontainer.hpp" + +#include + +CSMSettings::SettingContainer::SettingContainer(QObject *parent) : + QObject(parent), mValue (0), mValues (0) +{ +} + +CSMSettings::SettingContainer::SettingContainer(const QString &value, QObject *parent) : + QObject(parent), mValue (new QString (value)), mValues (0) +{ +} + +void CSMSettings::SettingContainer::insert (const QString &value) +{ + if (mValue) + { + mValues = new QStringList; + mValues->push_back (*mValue); + mValues->push_back (value); + + delete mValue; + mValue = 0; + } + else + { + delete mValue; + mValue = new QString (value); + } + +} + +void CSMSettings::SettingContainer::update (const QString &value, int index) +{ + if (isEmpty()) + mValue = new QString(value); + + else if (mValue) + *mValue = value; + + else if (mValues) + mValues->replace(index, value); +} + +QString CSMSettings::SettingContainer::getValue (int index) const +{ + QString retVal(""); + + //if mValue is valid, it's a single-value property. + //ignore the index and return the value + if (mValue) + retVal = *mValue; + + //otherwise, if it's a multivalued property + //return the appropriate value at the index + else if (mValues) + { + if (index == -1) + retVal = mValues->at(0); + + else if (index < mValues->size()) + retVal = mValues->at(index); + } + + return retVal; +} + +int CSMSettings::SettingContainer::count () const +{ + int retVal = 0; + + if (!isEmpty()) + { + if (mValues) + retVal = mValues->size(); + else + retVal = 1; + } + + return retVal; +} diff --git a/apps/opencs/model/settings/settingcontainer.hpp b/apps/opencs/model/settings/settingcontainer.hpp new file mode 100644 index 000000000..eb264248d --- /dev/null +++ b/apps/opencs/model/settings/settingcontainer.hpp @@ -0,0 +1,37 @@ +#ifndef SETTINGCONTAINER_HPP +#define SETTINGCONTAINER_HPP + +#include + +class QStringList; + +namespace CSMSettings +{ + class SettingContainer : public QObject + { + Q_OBJECT + + QString *mValue; + QStringList *mValues; + + public: + explicit SettingContainer (QObject *parent = 0); + explicit SettingContainer (const QString &value, QObject *parent = 0); + + virtual QString getName() const {return "";} + + void insert (const QString &value); + void update (const QString &value, int index = 0); + + QString getValue (int index = -1) const; + inline QStringList *getValues() const { return mValues; } + int count() const; + + //test for empty container + //useful for default-constructed containers returned by QMap when invalid key is passed + inline bool isEmpty() const { return (!mValue && !mValues); } + inline bool isMultiValue() const { return (mValues); } + }; +} + +#endif // SETTINGCONTAINER_HPP diff --git a/apps/opencs/model/settings/settingsitem.cpp b/apps/opencs/model/settings/settingsitem.cpp new file mode 100644 index 000000000..fef9d3e2f --- /dev/null +++ b/apps/opencs/model/settings/settingsitem.cpp @@ -0,0 +1,102 @@ +#include "settingsitem.hpp" + +bool CSMSettings::SettingsItem::updateItem (const QStringList *values) +{ + QStringList::ConstIterator it = values->begin(); + + //if the item is not multivalued, + //save the last value passed in the container + if (!mIsMultiValue) + { + it = values->end(); + it--; + } + + bool isValid = true; + QString value (""); + + for (; it != values->end(); ++it) + { + value = *it; + isValid = validate(value); + + //skip only the invalid values + if (!isValid) + continue; + + insert(value); + } + + return isValid; +} + +bool CSMSettings::SettingsItem::updateItem (const QString &value) +{ + //takes a value or a SettingsContainer and updates itself accordingly + //after validating the data against it's own definition + + QString newValue = value; + + if (!validate (newValue)) + newValue = mDefaultValue; + + bool success = (getValue() != newValue); + + if (success) + { + if (mIsMultiValue) + insert (newValue); + else + update (newValue); + } + return success; +} + +bool CSMSettings::SettingsItem::updateItem(int valueListIndex) +{ + bool success = false; + + if (mValueList) + { + if (mValueList->size() > valueListIndex) + success = updateItem (mValueList->at(valueListIndex)); + } + return success; +} + +bool CSMSettings::SettingsItem::validate (const QString &value) +{ + bool isValid = true; + + //validation required only if a value list or min/max value pair has been provided + if (mValueList->size()>0) + { + for (QStringList::ConstIterator it = mValueList->begin(); it !=mValueList->end(); ++it) + { + isValid = ( value == *it); + + if (isValid) + break; + } + } + + else if (mValuePair) + { + int numVal = value.toInt(); + + isValid = (numVal > mValuePair->left.toInt() && numVal < mValuePair->right.toInt()); + } + + return isValid; +} + +void CSMSettings::SettingsItem::setDefaultValue (const QString &value) +{ + mDefaultValue = value; + update (value); +} + +QString CSMSettings::SettingsItem::getDefaultValue() const +{ + return mDefaultValue; +} diff --git a/apps/opencs/model/settings/settingsitem.hpp b/apps/opencs/model/settings/settingsitem.hpp new file mode 100644 index 000000000..8be05a4c7 --- /dev/null +++ b/apps/opencs/model/settings/settingsitem.hpp @@ -0,0 +1,47 @@ +#ifndef SETTINGSITEM_HPP +#define SETTINGSITEM_HPP + +#include +#include "support.hpp" +#include "settingcontainer.hpp" + +namespace CSMSettings +{ + class SettingsItem : public SettingContainer + { + QStringPair *mValuePair; + QStringList *mValueList; + bool mIsMultiValue; + QString mName; + QString mDefaultValue; + + public: + explicit SettingsItem(QString name, bool isMultiValue, + const QString& defaultValue, QObject *parent = 0) + : SettingContainer(defaultValue, parent), + mIsMultiValue (isMultiValue), mValueList (0), + mName (name), mValuePair (0), mDefaultValue (defaultValue) + {} + + bool updateItem (const QStringList *values); + bool updateItem (const QString &value); + bool updateItem (int valueListIndex); + + inline QStringList *getValueList() { return mValueList; } + inline void setValueList (QStringList *valueList) { mValueList = valueList; } + + inline QStringPair *getValuePair() { return mValuePair; } + inline void setValuePair (QStringPair valuePair) { mValuePair = new QStringPair(valuePair); } + + inline QString getName () const { return mName; } + inline bool isMultivalue () { return mIsMultiValue; } + + void setDefaultValue (const QString &value); + QString getDefaultValue () const; + + private: + bool validate (const QString &value); + }; +} +#endif // SETTINGSITEM_HPP + diff --git a/apps/opencs/model/settings/support.cpp b/apps/opencs/model/settings/support.cpp new file mode 100644 index 000000000..d79edfdb3 --- /dev/null +++ b/apps/opencs/model/settings/support.cpp @@ -0,0 +1 @@ +#include "support.hpp" diff --git a/apps/opencs/model/settings/support.hpp b/apps/opencs/model/settings/support.hpp new file mode 100644 index 000000000..1155d2a29 --- /dev/null +++ b/apps/opencs/model/settings/support.hpp @@ -0,0 +1,39 @@ +#ifndef MODEL_SUPPORT_HPP +#define MODEL_SUPPORT_HPP + +#include +#include + +class QLayout; +class QWidget; +class QListWidgetItem; + +namespace CSMSettings +{ + class SettingContainer; + + typedef QList SettingList; + typedef QMap SettingMap; + typedef QMap SectionMap; + + struct QStringPair + { + QStringPair(): left (""), right ("") + {} + + QStringPair (const QString &leftValue, const QString &rightValue) + : left (leftValue), right(rightValue) + {} + + QStringPair (const QStringPair &pair) + : left (pair.left), right (pair.right) + {} + + QString left; + QString right; + + bool isEmpty() const + { return (left.isEmpty() && right.isEmpty()); } + }; +} +#endif // MODEL_SUPPORT_HPP diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp new file mode 100644 index 000000000..aabda86b3 --- /dev/null +++ b/apps/opencs/model/settings/usersettings.cpp @@ -0,0 +1,137 @@ +#include "usersettings.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "settingcontainer.hpp" + +#include +/** + * Workaround for problems with whitespaces in paths in older versions of Boost library + */ +#if (BOOST_VERSION <= 104600) +namespace boost +{ + + template<> + inline boost::filesystem::path lexical_cast(const std::string& arg) + { + return boost::filesystem::path(arg); + } + +} /* namespace boost */ +#endif /* (BOOST_VERSION <= 104600) */ + + +CSMSettings::UserSettings::UserSettings() +{ + mUserSettingsInstance = this; +} + +CSMSettings::UserSettings::~UserSettings() +{ +} + +QFile *CSMSettings::UserSettings::openFile (const QString &filename) +{ + QFile *file = new QFile(filename); + + bool success = (file->open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) ; + + if (!success) + { + // File cannot be opened or created + QMessageBox msgBox; + msgBox.setWindowTitle(QObject::tr("Error writing OpenMW configuration file")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(QObject::tr("
Could not open or create %0 for writing

\ + Please make sure you have the right permissions \ + and try again.
").arg(file->fileName())); + msgBox.exec(); + delete file; + file = 0; + } + + return file; +} + +bool CSMSettings::UserSettings::writeFile(QFile *file, QMap &settings) +{ + if (!file) + return false; + + QTextStream stream(file); + stream.setCodec(QTextCodec::codecForName("UTF-8")); + + QList keyList = settings.keys(); + + foreach (QString key, keyList) + { + SettingList *sectionSettings = settings[key]; + + stream << "[" << key << "]" << '\n'; + + foreach (SettingContainer *item, *sectionSettings) + stream << item->getName() << " = " << item->getValue() << '\n'; + } + + file->close(); + + return true; +} + +void CSMSettings::UserSettings::getSettings(QTextStream &stream, SectionMap §ions) +{ + //looks for a square bracket, "'\\[" + //that has one or more "not nothing" in it, "([^]]+)" + //and is closed with a square bracket, "\\]" + + QRegExp sectionRe("^\\[([^]]+)\\]"); + + //Find any character(s) that is/are not equal sign(s), "[^=]+" + //followed by an optional whitespace, an equal sign, and another optional whirespace, "\\s*=\\s*" + //and one or more periods, "(.+)" + + QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); + + CSMSettings::SettingMap *settings = 0; + QString section = "none"; + + while (!stream.atEnd()) + { + QString line = stream.readLine().simplified(); + + if (line.isEmpty() || line.startsWith("#")) + continue; + + //if a section is found, push it onto a new QStringList + //and push the QStringList onto + if (sectionRe.exactMatch(line)) + { + //add the previous section's settings to the member map + if (settings) + sections.insert(section, settings); + + //save new section and create a new list + section = sectionRe.cap(1); + settings = new SettingMap; + continue; + } + + if (keyRe.indexIn(line) != -1) + { + SettingContainer *sc = new SettingContainer (keyRe.cap(2).simplified()); + (*settings)[keyRe.cap(1).simplified()] = sc; + } + + } + sections.insert(section, settings); +} diff --git a/apps/opencs/model/settings/usersettings.hpp b/apps/opencs/model/settings/usersettings.hpp new file mode 100644 index 000000000..343702eda --- /dev/null +++ b/apps/opencs/model/settings/usersettings.hpp @@ -0,0 +1,52 @@ +#ifndef USERSETTINGS_HPP +#define USERSETTINGS_HPP + +#include +#include +#include +#include + +#include + +#include "support.hpp" + +namespace Files { typedef std::vector PathContainer; + struct ConfigurationManager;} + +class QFile; + +namespace CSMSettings { + + struct UserSettings: public QObject + { + + Q_OBJECT + + public: + + static UserSettings &instance() + { + static UserSettings instance; + + return instance; + } + + QFile *openFile (const QString &); + bool writeFile(QFile *file, QMap §ions); + void getSettings (QTextStream &stream, SectionMap &settings); + + private: + + UserSettings *mUserSettingsInstance; + UserSettings(); + ~UserSettings(); + + UserSettings (UserSettings const &); //not implemented + void operator= (UserSettings const &); //not implemented + + signals: + void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue); + + }; +} +#endif // USERSETTINGS_HPP diff --git a/apps/opencs/model/tools/birthsigncheck.cpp b/apps/opencs/model/tools/birthsigncheck.cpp new file mode 100644 index 000000000..b673c93de --- /dev/null +++ b/apps/opencs/model/tools/birthsigncheck.cpp @@ -0,0 +1,39 @@ + +#include "birthsigncheck.hpp" + +#include +#include + +#include + +#include "../world/universalid.hpp" + +CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection& birthsigns) +: mBirthsigns (birthsigns) +{} + +int CSMTools::BirthsignCheckStage::setup() +{ + return mBirthsigns.getSize(); +} + +void CSMTools::BirthsignCheckStage::perform (int stage, std::vector& messages) +{ + const ESM::BirthSign& birthsign = mBirthsigns.getRecord (stage).get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId); + + // test for empty name, description and texture + if (birthsign.mName.empty()) + messages.push_back (id.toString() + "|" + birthsign.mId + " has an empty name"); + + if (birthsign.mDescription.empty()) + messages.push_back (id.toString() + "|" + birthsign.mId + " has an empty description"); + + if (birthsign.mTexture.empty()) + messages.push_back (id.toString() + "|" + birthsign.mId + " is missing a texture"); + + /// \todo test if the texture exists + + /// \todo check data members that can't be edited in the table view +} \ No newline at end of file diff --git a/apps/opencs/model/tools/birthsigncheck.hpp b/apps/opencs/model/tools/birthsigncheck.hpp new file mode 100644 index 000000000..42b5a6b24 --- /dev/null +++ b/apps/opencs/model/tools/birthsigncheck.hpp @@ -0,0 +1,29 @@ +#ifndef CSM_TOOLS_BIRTHSIGNCHECK_H +#define CSM_TOOLS_BIRTHSIGNCHECK_H + +#include + +#include "../world/idcollection.hpp" + +#include "stage.hpp" + +namespace CSMTools +{ + /// \brief VerifyStage: make sure that birthsign records are internally consistent + class BirthsignCheckStage : public Stage + { + const CSMWorld::IdCollection& mBirthsigns; + + public: + + BirthsignCheckStage (const CSMWorld::IdCollection& birthsigns); + + 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/classcheck.cpp b/apps/opencs/model/tools/classcheck.cpp new file mode 100644 index 000000000..da2e9f19a --- /dev/null +++ b/apps/opencs/model/tools/classcheck.cpp @@ -0,0 +1,72 @@ + +#include "classcheck.hpp" + +#include +#include + +#include +#include + +#include "../world/universalid.hpp" + +CSMTools::ClassCheckStage::ClassCheckStage (const CSMWorld::IdCollection& classes) +: mClasses (classes) +{} + +int CSMTools::ClassCheckStage::setup() +{ + return mClasses.getSize(); +} + +void CSMTools::ClassCheckStage::perform (int stage, std::vector& messages) +{ + const ESM::Class& class_= mClasses.getRecord (stage).get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Class, class_.mId); + + // test for empty name and description + if (class_.mName.empty()) + messages.push_back (id.toString() + "|" + class_.mId + " has an empty name"); + + if (class_.mDescription.empty()) + messages.push_back (id.toString() + "|" + class_.mId + " has an empty description"); + + // test for invalid attributes + for (int i=0; i<2; ++i) + if (class_.mData.mAttribute[i]==-1) + { + std::ostringstream stream; + + stream << id.toString() << "|Attribute #" << i << " of " << class_.mId << " is not set"; + + messages.push_back (stream.str()); + } + + if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1) + { + std::ostringstream stream; + + stream << id.toString() << "|Class lists same attribute twice"; + + messages.push_back (stream.str()); + } + + // test for non-unique skill + std::map skills; // ID, number of occurrences + + for (int i=0; i<5; ++i) + for (int i2=0; i2<2; ++i2) + ++skills[class_.mData.mSkills[i][i2]]; + + for (std::map::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter) + if (iter->second>1) + { + std::ostringstream stream; + + stream + << id.toString() << "|" + << ESM::Skill::indexToId (iter->first) << " is listed more than once"; + + messages.push_back (stream.str()); + } +} \ No newline at end of file diff --git a/apps/opencs/model/tools/classcheck.hpp b/apps/opencs/model/tools/classcheck.hpp new file mode 100644 index 000000000..a29d7c8b7 --- /dev/null +++ b/apps/opencs/model/tools/classcheck.hpp @@ -0,0 +1,29 @@ +#ifndef CSM_TOOLS_CLASSCHECK_H +#define CSM_TOOLS_CLASSCHECK_H + +#include + +#include "../world/idcollection.hpp" + +#include "stage.hpp" + +namespace CSMTools +{ + /// \brief VerifyStage: make sure that class records are internally consistent + class ClassCheckStage : public Stage + { + const CSMWorld::IdCollection& mClasses; + + public: + + ClassCheckStage (const CSMWorld::IdCollection& classes); + + 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/factioncheck.cpp b/apps/opencs/model/tools/factioncheck.cpp new file mode 100644 index 000000000..af26904ef --- /dev/null +++ b/apps/opencs/model/tools/factioncheck.cpp @@ -0,0 +1,61 @@ + +#include "factioncheck.hpp" + +#include +#include + +#include +#include + +#include "../world/universalid.hpp" + +CSMTools::FactionCheckStage::FactionCheckStage (const CSMWorld::IdCollection& factions) +: mFactions (factions) +{} + +int CSMTools::FactionCheckStage::setup() +{ + return mFactions.getSize(); +} + +void CSMTools::FactionCheckStage::perform (int stage, std::vector& messages) +{ + const ESM::Faction& faction = mFactions.getRecord (stage).get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Faction, faction.mId); + + // test for empty name + if (faction.mName.empty()) + messages.push_back (id.toString() + "|" + faction.mId + " has an empty name"); + + // test for invalid attributes + if (faction.mData.mAttribute[0]==faction.mData.mAttribute[1] && faction.mData.mAttribute[0]!=-1) + { + std::ostringstream stream; + + stream << id.toString() << "|Faction lists same attribute twice"; + + messages.push_back (stream.str()); + } + + // test for non-unique skill + std::map skills; // ID, number of occurrences + + for (int i=0; i<6; ++i) + if (faction.mData.mSkills[i]!=-1) + ++skills[faction.mData.mSkills[i]]; + + for (std::map::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter) + if (iter->second>1) + { + std::ostringstream stream; + + stream + << id.toString() << "|" + << ESM::Skill::indexToId (iter->first) << " is listed more than once"; + + messages.push_back (stream.str()); + } + + /// \todo check data members that can't be edited in the table view +} \ No newline at end of file diff --git a/apps/opencs/model/tools/factioncheck.hpp b/apps/opencs/model/tools/factioncheck.hpp new file mode 100644 index 000000000..868650572 --- /dev/null +++ b/apps/opencs/model/tools/factioncheck.hpp @@ -0,0 +1,29 @@ +#ifndef CSM_TOOLS_FACTIONCHECK_H +#define CSM_TOOLS_FACTIONCHECK_H + +#include + +#include "../world/idcollection.hpp" + +#include "stage.hpp" + +namespace CSMTools +{ + /// \brief VerifyStage: make sure that faction records are internally consistent + class FactionCheckStage : public Stage + { + const CSMWorld::IdCollection& mFactions; + + public: + + FactionCheckStage (const CSMWorld::IdCollection& factions); + + 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/racecheck.cpp b/apps/opencs/model/tools/racecheck.cpp new file mode 100644 index 000000000..1e7a4cab4 --- /dev/null +++ b/apps/opencs/model/tools/racecheck.cpp @@ -0,0 +1,68 @@ + +#include "racecheck.hpp" + +#include + +#include + +#include "../world/universalid.hpp" + +void CSMTools::RaceCheckStage::performPerRecord (int stage, std::vector& messages) +{ + const ESM::Race& race = mRaces.getRecord (stage).get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Race, race.mId); + + // test for empty name and description + if (race.mName.empty()) + messages.push_back (id.toString() + "|" + race.mId + " has an empty name"); + + if (race.mDescription.empty()) + messages.push_back (id.toString() + "|" + race.mId + " has an empty description"); + + // test for positive height + if (race.mData.mHeight.mMale<=0) + messages.push_back (id.toString() + "|male " + race.mId + " has non-positive height"); + + if (race.mData.mHeight.mFemale<=0) + messages.push_back (id.toString() + "|female " + race.mId + " has non-positive height"); + + // test for non-negative weight + if (race.mData.mWeight.mMale<0) + messages.push_back (id.toString() + "|male " + race.mId + " has negative weight"); + + if (race.mData.mWeight.mFemale<0) + messages.push_back (id.toString() + "|female " + race.mId + " has negative weight"); + + // remember playable flag + if (race.mData.mFlags & 0x1) + mPlayable = true; + + /// \todo check data members that can't be edited in the table view +} + +void CSMTools::RaceCheckStage::performFinal (std::vector& messages) +{ + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Races); + + if (!mPlayable) + messages.push_back (id.toString() + "|No playable race"); +} + +CSMTools::RaceCheckStage::RaceCheckStage (const CSMWorld::IdCollection& races) +: mRaces (races), mPlayable (false) +{} + +int CSMTools::RaceCheckStage::setup() +{ + mPlayable = false; + return mRaces.getSize()+1; +} + +void CSMTools::RaceCheckStage::perform (int stage, std::vector& messages) +{ + if (stage==mRaces.getSize()) + performFinal (messages); + else + performPerRecord (stage, messages); +} \ No newline at end of file diff --git a/apps/opencs/model/tools/racecheck.hpp b/apps/opencs/model/tools/racecheck.hpp new file mode 100644 index 000000000..155f79902 --- /dev/null +++ b/apps/opencs/model/tools/racecheck.hpp @@ -0,0 +1,34 @@ +#ifndef CSM_TOOLS_RACECHECK_H +#define CSM_TOOLS_RACECHECK_H + +#include + +#include "../world/idcollection.hpp" + +#include "stage.hpp" + +namespace CSMTools +{ + /// \brief VerifyStage: make sure that race records are internally consistent + class RaceCheckStage : public Stage + { + const CSMWorld::IdCollection& mRaces; + bool mPlayable; + + void performPerRecord (int stage, std::vector& messages); + + void performFinal (std::vector& messages); + + public: + + RaceCheckStage (const CSMWorld::IdCollection& races); + + 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/regioncheck.cpp b/apps/opencs/model/tools/regioncheck.cpp new file mode 100644 index 000000000..ac64ac027 --- /dev/null +++ b/apps/opencs/model/tools/regioncheck.cpp @@ -0,0 +1,33 @@ + +#include "regioncheck.hpp" + +#include +#include + +#include + +#include "../world/universalid.hpp" + +CSMTools::RegionCheckStage::RegionCheckStage (const CSMWorld::IdCollection& regions) +: mRegions (regions) +{} + +int CSMTools::RegionCheckStage::setup() +{ + return mRegions.getSize(); +} + +void CSMTools::RegionCheckStage::perform (int stage, std::vector& messages) +{ + const ESM::Region& region = mRegions.getRecord (stage).get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Region, region.mId); + + // test for empty name + if (region.mName.empty()) + messages.push_back (id.toString() + "|" + region.mId + " has an empty name"); + + /// \todo test that the ID in mSleeplist exists + + /// \todo check data members that can't be edited in the table view +} \ No newline at end of file diff --git a/apps/opencs/model/tools/regioncheck.hpp b/apps/opencs/model/tools/regioncheck.hpp new file mode 100644 index 000000000..b42135651 --- /dev/null +++ b/apps/opencs/model/tools/regioncheck.hpp @@ -0,0 +1,29 @@ +#ifndef CSM_TOOLS_REGIONCHECK_H +#define CSM_TOOLS_REGIONCHECK_H + +#include + +#include "../world/idcollection.hpp" + +#include "stage.hpp" + +namespace CSMTools +{ + /// \brief VerifyStage: make sure that region records are internally consistent + class RegionCheckStage : public Stage + { + const CSMWorld::IdCollection& mRegions; + + public: + + RegionCheckStage (const CSMWorld::IdCollection& regions); + + 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/skillcheck.cpp b/apps/opencs/model/tools/skillcheck.cpp new file mode 100644 index 000000000..897aeab47 --- /dev/null +++ b/apps/opencs/model/tools/skillcheck.cpp @@ -0,0 +1,37 @@ + +#include "skillcheck.hpp" + +#include + +#include + +#include "../world/universalid.hpp" + +CSMTools::SkillCheckStage::SkillCheckStage (const CSMWorld::IdCollection& skills) +: mSkills (skills) +{} + +int CSMTools::SkillCheckStage::setup() +{ + return mSkills.getSize(); +} + +void CSMTools::SkillCheckStage::perform (int stage, std::vector& messages) +{ + const ESM::Skill& skill = mSkills.getRecord (stage).get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Skill, skill.mId); + + for (int i=0; i<4; ++i) + if (skill.mData.mUseValue[i]<0) + { + std::ostringstream stream; + + stream << id.toString() << "|Use value #" << i << " of " << skill.mId << " is negative"; + + messages.push_back (stream.str()); + } + + if (skill.mDescription.empty()) + messages.push_back (id.toString() + "|" + skill.mId + " has an empty description"); +} \ No newline at end of file diff --git a/apps/opencs/model/tools/skillcheck.hpp b/apps/opencs/model/tools/skillcheck.hpp new file mode 100644 index 000000000..30a3f01ca --- /dev/null +++ b/apps/opencs/model/tools/skillcheck.hpp @@ -0,0 +1,29 @@ +#ifndef CSM_TOOLS_SKILLCHECK_H +#define CSM_TOOLS_SKILLCHECK_H + +#include + +#include "../world/idcollection.hpp" + +#include "stage.hpp" + +namespace CSMTools +{ + /// \brief VerifyStage: make sure that skill records are internally consistent + class SkillCheckStage : public Stage + { + const CSMWorld::IdCollection& mSkills; + + public: + + SkillCheckStage (const CSMWorld::IdCollection& skills); + + 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/soundcheck.cpp b/apps/opencs/model/tools/soundcheck.cpp new file mode 100644 index 000000000..52834e659 --- /dev/null +++ b/apps/opencs/model/tools/soundcheck.cpp @@ -0,0 +1,29 @@ + +#include "soundcheck.hpp" + +#include + +#include + +#include "../world/universalid.hpp" + +CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection& sounds) +: mSounds (sounds) +{} + +int CSMTools::SoundCheckStage::setup() +{ + return mSounds.getSize(); +} + +void CSMTools::SoundCheckStage::perform (int stage, std::vector& messages) +{ + const ESM::Sound& sound = mSounds.getRecord (stage).get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId); + + if (sound.mData.mMinRange>sound.mData.mMaxRange) + messages.push_back (id.toString() + "|Maximum range larger than minimum range"); + + /// \todo check, if the sound file exists +} \ No newline at end of file diff --git a/apps/opencs/model/tools/soundcheck.hpp b/apps/opencs/model/tools/soundcheck.hpp new file mode 100644 index 000000000..a309763a1 --- /dev/null +++ b/apps/opencs/model/tools/soundcheck.hpp @@ -0,0 +1,29 @@ +#ifndef CSM_TOOLS_SOUNDCHECK_H +#define CSM_TOOLS_SOUNDCHECK_H + +#include + +#include "../world/idcollection.hpp" + +#include "stage.hpp" + +namespace CSMTools +{ + /// \brief VerifyStage: make sure that sound records are internally consistent + class SoundCheckStage : public Stage + { + const CSMWorld::IdCollection& mSounds; + + public: + + SoundCheckStage (const CSMWorld::IdCollection& sounds); + + 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/spellcheck.cpp b/apps/opencs/model/tools/spellcheck.cpp new file mode 100644 index 000000000..3adee0a4e --- /dev/null +++ b/apps/opencs/model/tools/spellcheck.cpp @@ -0,0 +1,35 @@ + +#include "spellcheck.hpp" + +#include +#include + +#include + +#include "../world/universalid.hpp" + +CSMTools::SpellCheckStage::SpellCheckStage (const CSMWorld::IdCollection& spells) +: mSpells (spells) +{} + +int CSMTools::SpellCheckStage::setup() +{ + return mSpells.getSize(); +} + +void CSMTools::SpellCheckStage::perform (int stage, std::vector& messages) +{ + const ESM::Spell& spell = mSpells.getRecord (stage).get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Spell, spell.mId); + + // test for empty name and description + if (spell.mName.empty()) + messages.push_back (id.toString() + "|" + spell.mId + " has an empty name"); + + // test for invalid cost values + if (spell.mData.mCost<0) + messages.push_back (id.toString() + "|" + spell.mId + " has a negative spell costs"); + + /// \todo check data members that can't be edited in the table view +} \ No newline at end of file diff --git a/apps/opencs/model/tools/spellcheck.hpp b/apps/opencs/model/tools/spellcheck.hpp new file mode 100644 index 000000000..056639219 --- /dev/null +++ b/apps/opencs/model/tools/spellcheck.hpp @@ -0,0 +1,29 @@ +#ifndef CSM_TOOLS_SPELLCHECK_H +#define CSM_TOOLS_SPELLCHECK_H + +#include + +#include "../world/idcollection.hpp" + +#include "stage.hpp" + +namespace CSMTools +{ + /// \brief VerifyStage: make sure that spell records are internally consistent + class SpellCheckStage : public Stage + { + const CSMWorld::IdCollection& mSpells; + + public: + + SpellCheckStage (const CSMWorld::IdCollection& spells); + + 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 8dd1c0fe6..803861203 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -12,6 +12,14 @@ #include "reportmodel.hpp" #include "mandatoryid.hpp" +#include "skillcheck.hpp" +#include "classcheck.hpp" +#include "factioncheck.hpp" +#include "racecheck.hpp" +#include "soundcheck.hpp" +#include "regioncheck.hpp" +#include "birthsigncheck.hpp" +#include "spellcheck.hpp" CSMTools::Operation *CSMTools::Tools::get (int type) { @@ -51,6 +59,22 @@ CSMTools::Verifier *CSMTools::Tools::getVerifier() mVerifier->appendStage (new MandatoryIdStage (mData.getGlobals(), CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), mandatoryIds)); + + mVerifier->appendStage (new SkillCheckStage (mData.getSkills())); + + mVerifier->appendStage (new ClassCheckStage (mData.getClasses())); + + mVerifier->appendStage (new FactionCheckStage (mData.getFactions())); + + mVerifier->appendStage (new RaceCheckStage (mData.getRaces())); + + mVerifier->appendStage (new SoundCheckStage (mData.getSounds())); + + mVerifier->appendStage (new RegionCheckStage (mData.getRegions())); + + mVerifier->appendStage (new BirthsignCheckStage (mData.getBirthsigns())); + + mVerifier->appendStage (new SpellCheckStage (mData.getSpells())); } return mVerifier; diff --git a/apps/opencs/model/world/cell.cpp b/apps/opencs/model/world/cell.cpp new file mode 100644 index 000000000..759468fa8 --- /dev/null +++ b/apps/opencs/model/world/cell.cpp @@ -0,0 +1,20 @@ + +#include "cell.hpp" + +#include + +void CSMWorld::Cell::load (ESM::ESMReader &esm) +{ + mName = mId; + + ESM::Cell::load (esm, true); /// \todo set this to false, once the bug in ESM::Cell::load is fixed + + if (!(mData.mFlags & Interior)) + { + std::ostringstream stream; + + stream << "#" << mData.mX << " " << mData.mY; + + mId = stream.str(); + } +} \ No newline at end of file diff --git a/apps/opencs/model/world/cell.hpp b/apps/opencs/model/world/cell.hpp new file mode 100644 index 000000000..6a9676a55 --- /dev/null +++ b/apps/opencs/model/world/cell.hpp @@ -0,0 +1,17 @@ +#ifndef CSM_WOLRD_CELL_H +#define CSM_WOLRD_CELL_H + +#include + +namespace CSMWorld +{ + /// \brief Wrapper for Cell record + struct Cell : public ESM::Cell + { + std::string mId; + + void load (ESM::ESMReader &esm); + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/model/world/columnbase.cpp b/apps/opencs/model/world/columnbase.cpp index 7adc7e6c3..134c582c4 100644 --- a/apps/opencs/model/world/columnbase.cpp +++ b/apps/opencs/model/world/columnbase.cpp @@ -1,8 +1,8 @@ #include "columnbase.hpp" -CSMWorld::ColumnBase::ColumnBase (const std::string& title, int flags) -: mTitle (title), mFlags (flags) +CSMWorld::ColumnBase::ColumnBase (const std::string& title, Display displayType, int flags) +: mTitle (title), mDisplayType (displayType), mFlags (flags) {} CSMWorld::ColumnBase::~ColumnBase() {} diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index dc077eff6..8cc435f3a 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -14,7 +14,8 @@ namespace CSMWorld { enum Roles { - Role_Flags = Qt::UserRole + Role_Flags = Qt::UserRole, + Role_Display = Qt::UserRole+1 }; enum Flags @@ -23,10 +24,31 @@ namespace CSMWorld Flag_Dialogue = 2 // column should be displayed in dialogue view }; + enum Display + { + Display_String, + Display_Integer, + Display_Float, + Display_Var, + Display_GmstVarType, + Display_GlobalVarType, + Display_Specialisation, + Display_Attribute, + Display_Boolean, + Display_SpellType, + Display_Script, + Display_ApparatusType, + Display_ArmorType, + Display_ClothingType, + Display_CreatureType, + Display_WeaponType + }; + std::string mTitle; int mFlags; + Display mDisplayType; - ColumnBase (const std::string& title, int flag); + ColumnBase (const std::string& title, Display displayType, int flag); virtual ~ColumnBase(); @@ -34,6 +56,7 @@ namespace CSMWorld virtual bool isUserEditable() const; ///< Can this column be edited directly by the user? + }; template @@ -42,8 +65,8 @@ namespace CSMWorld std::string mTitle; int mFlags; - Column (const std::string& title, int flags = Flag_Table | Flag_Dialogue) - : ColumnBase (title, flags) {} + Column (const std::string& title, Display displayType, int flags = Flag_Table | Flag_Dialogue) + : ColumnBase (title, displayType, flags) {} virtual QVariant get (const Record& record) const = 0; diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 5abf4ea8b..f1d8d4ae6 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -1,6 +1,12 @@ #ifndef CSM_WOLRD_COLUMNS_H #define CSM_WOLRD_COLUMNS_H +#include + +#include + +#include + #include "columnbase.hpp" namespace CSMWorld @@ -8,18 +14,18 @@ namespace CSMWorld template struct FloatValueColumn : public Column { - FloatValueColumn() : Column ("Value") {} + FloatValueColumn() : Column ("Value", ColumnBase::Display_Float) {} virtual QVariant get (const Record& record) const { - return record.get().mValue; + return record.get().mValue.getFloat(); } virtual void set (Record& record, const QVariant& data) { - ESXRecordT base = record.getBase(); - base.mValue = data.toFloat(); - record.setModified (base); + ESXRecordT record2 = record.get(); + record2.mValue.setFloat (data.toFloat()); + record.setModified (record2); } virtual bool isEditable() const @@ -31,11 +37,11 @@ namespace CSMWorld template struct StringIdColumn : public Column { - StringIdColumn() : Column ("ID") {} + StringIdColumn() : Column ("ID", ColumnBase::Display_String) {} virtual QVariant get (const Record& record) const { - return record.get().mId.c_str(); + return QString::fromUtf8 (record.get().mId.c_str()); } virtual bool isEditable() const @@ -47,7 +53,7 @@ namespace CSMWorld template struct RecordStateColumn : public Column { - RecordStateColumn() : Column ("*") {} + RecordStateColumn() : Column ("*", ColumnBase::Display_Integer) {} virtual QVariant get (const Record& record) const { @@ -78,7 +84,8 @@ namespace CSMWorld { int mType; - FixedRecordTypeColumn (int type) : Column ("Type", 0), mType (type) {} + FixedRecordTypeColumn (int type) + : Column ("Record Type", ColumnBase::Display_Integer, 0), mType (type) {} virtual QVariant get (const Record& record) const { @@ -90,6 +97,682 @@ namespace CSMWorld return false; } }; + + /// \attention A var type column must be immediately followed by a suitable value column. + template + struct VarTypeColumn : public Column + { + VarTypeColumn (ColumnBase::Display display) : Column ("Type", display) {} + + virtual QVariant get (const Record& record) const + { + return static_cast (record.get().mValue.getType()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mValue.setType (static_cast (data.toInt())); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct VarValueColumn : public Column + { + VarValueColumn() : Column ("Value", ColumnBase::Display_Var) {} + + virtual QVariant get (const Record& record) const + { + switch (record.get().mValue.getType()) + { + case ESM::VT_String: + + return QString::fromUtf8 (record.get().mValue.getString().c_str()); + + case ESM::VT_Int: + case ESM::VT_Short: + case ESM::VT_Long: + + return record.get().mValue.getInteger(); + + case ESM::VT_Float: + + return record.get().mValue.getFloat(); + + default: return QVariant(); + } + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + switch (record2.mValue.getType()) + { + case ESM::VT_String: + + record2.mValue.setString (data.toString().toUtf8().constData()); + break; + + case ESM::VT_Int: + case ESM::VT_Short: + case ESM::VT_Long: + + record2.mValue.setInteger (data.toInt()); + break; + + case ESM::VT_Float: + + record2.mValue.setFloat (data.toFloat()); + break; + + default: break; + } + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct DescriptionColumn : public Column + { + DescriptionColumn() : Column ("Description", ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mDescription.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mDescription = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct SpecialisationColumn : public Column + { + SpecialisationColumn() : Column ("Specialisation", ColumnBase::Display_Specialisation) {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mSpecialization; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mData.mSpecialization = data.toInt(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct UseValueColumn : public Column + { + int mIndex; + + UseValueColumn (int index) + : Column ("Use value #" + boost::lexical_cast (index), + ColumnBase::Display_Float), mIndex (index) + {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mUseValue[mIndex]; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mData.mUseValue[mIndex] = data.toInt(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct AttributeColumn : public Column + { + AttributeColumn() : Column ("Attribute", ColumnBase::Display_Attribute) {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mAttribute; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mData.mAttribute = data.toInt(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct NameColumn : public Column + { + NameColumn() : Column ("Name", ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mName.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mName = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct AttributesColumn : public Column + { + int mIndex; + + AttributesColumn (int index) + : Column ("Attribute #" + boost::lexical_cast (index), + ColumnBase::Display_Attribute), mIndex (index) {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mAttribute[mIndex]; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mData.mAttribute[mIndex] = data.toInt(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct SkillsColumn : public Column + { + int mIndex; + bool mMajor; + + SkillsColumn (int index, bool typePrefix = false, bool major = false) + : Column ((typePrefix ? (major ? "Major Skill #" : "Minor Skill #") : "Skill #")+ + boost::lexical_cast (index), ColumnBase::Display_String), + mIndex (index), mMajor (major) + {} + + virtual QVariant get (const Record& record) const + { + int skill = record.get().mData.getSkill (mIndex, mMajor); + + return QString::fromUtf8 (ESM::Skill::indexToId (skill).c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + std::istringstream stream (data.toString().toUtf8().constData()); + + int index = -1; + char c; + + stream >> c >> index; + + if (index!=-1) + { + ESXRecordT record2 = record.get(); + + record2.mData.getSkill (mIndex, mMajor) = index; + + record.setModified (record2); + } + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct PlayableColumn : public Column + { + PlayableColumn() : Column ("Playable", ColumnBase::Display_Boolean) {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mIsPlayable!=0; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mData.mIsPlayable = data.toInt(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct HiddenColumn : public Column + { + HiddenColumn() : Column ("Hidden", ColumnBase::Display_Boolean) {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mIsHidden!=0; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mData.mIsHidden = data.toInt(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct FlagColumn : public Column + { + int mMask; + + FlagColumn (const std::string& name, int mask) + : Column (name, ColumnBase::Display_Boolean), mMask (mask) + {} + + virtual QVariant get (const Record& record) const + { + return (record.get().mData.mFlags & mMask)!=0; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + int flags = record2.mData.mFlags & ~mMask; + + if (data.toInt()) + flags |= mMask; + + record2.mData.mFlags = flags; + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct WeightHeightColumn : public Column + { + bool mMale; + bool mWeight; + + WeightHeightColumn (bool male, bool weight) + : Column (male ? (weight ? "Male Weight" : "Male Height") : + (weight ? "Female Weight" : "Female Height"), ColumnBase::Display_Float), + mMale (male), mWeight (weight) + {} + + virtual QVariant get (const Record& record) const + { + const ESM::Race::MaleFemaleF& value = + mWeight ? record.get().mData.mWeight : record.get().mData.mHeight; + + return mMale ? value.mMale : value.mFemale; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + ESM::Race::MaleFemaleF& value = + mWeight ? record2.mData.mWeight : record2.mData.mHeight; + + (mMale ? value.mMale : value.mFemale) = data.toFloat(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct SoundParamColumn : public Column + { + enum Type + { + Type_Volume, + Type_MinRange, + Type_MaxRange + }; + + Type mType; + + SoundParamColumn (Type type) + : Column ( + type==Type_Volume ? "Volume" : (type==Type_MinRange ? "Min Range" : "Max Range"), + ColumnBase::Display_Integer), + mType (type) + {} + + virtual QVariant get (const Record& record) const + { + int value = 0; + + switch (mType) + { + case Type_Volume: value = record.get().mData.mVolume; break; + case Type_MinRange: value = record.get().mData.mMinRange; break; + case Type_MaxRange: value = record.get().mData.mMaxRange; break; + } + + return value; + } + + virtual void set (Record& record, const QVariant& data) + { + int value = data.toInt(); + + if (value<0) + value = 0; + else if (value>255) + value = 255; + + ESXRecordT record2 = record.get(); + + switch (mType) + { + case Type_Volume: record2.mData.mVolume = static_cast (value); break; + case Type_MinRange: record2.mData.mMinRange = static_cast (value); break; + case Type_MaxRange: record2.mData.mMaxRange = static_cast (value); break; + } + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct SoundFileColumn : public Column + { + SoundFileColumn() : Column ("Sound File", ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mSound.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mSound = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + /// \todo QColor is a GUI class and should not be in model. Need to think of an alternative + /// solution. + template + struct MapColourColumn : public Column + { + /// \todo Replace Display_Integer with something that displays the colour value more directly. + MapColourColumn() : Column ("Map Colour", ColumnBase::Display_Integer) {} + + virtual QVariant get (const Record& record) const + { + int colour = record.get().mMapColor; + + return QColor (colour & 0xff, (colour>>8) & 0xff, (colour>>16) & 0xff); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + QColor colour = data.value(); + + record2.mMapColor = colour.rgb() & 0xffffff; + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct SleepListColumn : public Column + { + SleepListColumn() : Column ("Sleep Encounter", ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mSleepList.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mSleepList = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct TextureColumn : public Column + { + TextureColumn() : Column ("Texture", ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mTexture.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mTexture = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct SpellTypeColumn : public Column + { + SpellTypeColumn() : Column ("Type", ColumnBase::Display_SpellType) {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mType; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mData.mType = data.toInt(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct CostColumn : public Column + { + CostColumn() : Column ("Cost", ColumnBase::Display_Integer) {} + + virtual QVariant get (const Record& record) const + { + return record.get().mData.mCost; + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mData.mCost = data.toInt(); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct ScriptColumn : public Column + { + ScriptColumn() : Column ("Script", ColumnBase::Display_Script, 0) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mScriptText.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mScriptText = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct RegionColumn : public Column + { + RegionColumn() : Column ("Region", ColumnBase::Display_String) {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mRegion.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mRegion = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; } #endif \ No newline at end of file diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index e22ecf992..eaded5b70 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -1,7 +1,7 @@ #include "commands.hpp" -#include +#include #include "idtableproxymodel.hpp" #include "idtable.hpp" @@ -71,7 +71,7 @@ void CSMWorld::RevertCommand::redo() void CSMWorld::RevertCommand::undo() { - mModel.setRecord (*mOld); + mModel.setRecord (mId, *mOld); } CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand *parent) @@ -104,5 +104,5 @@ void CSMWorld::DeleteCommand::redo() void CSMWorld::DeleteCommand::undo() { - mModel.setRecord (*mOld); + mModel.setRecord (mId, *mOld); } \ No newline at end of file diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index a3522503e..1701e4289 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -3,14 +3,16 @@ #include -#include +#include +#include +#include #include #include "idtable.hpp" #include "columns.hpp" -void CSMWorld::Data::addModel (QAbstractTableModel *model, UniversalId::Type type1, +void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type1, UniversalId::Type type2) { mModels.push_back (model); @@ -25,14 +27,126 @@ CSMWorld::Data::Data() mGlobals.addColumn (new StringIdColumn); mGlobals.addColumn (new RecordStateColumn); mGlobals.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Global)); - mGlobals.addColumn (new FloatValueColumn); + mGlobals.addColumn (new VarTypeColumn (ColumnBase::Display_GlobalVarType)); + mGlobals.addColumn (new VarValueColumn); + + mGmsts.addColumn (new StringIdColumn); + mGmsts.addColumn (new RecordStateColumn); + mGmsts.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Gmst)); + mGmsts.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Gmst)); + mGmsts.addColumn (new VarTypeColumn (ColumnBase::Display_GmstVarType)); + mGmsts.addColumn (new VarValueColumn); + + mSkills.addColumn (new StringIdColumn); + mSkills.addColumn (new RecordStateColumn); + mSkills.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Skill)); + mSkills.addColumn (new AttributeColumn); + mSkills.addColumn (new SpecialisationColumn); + for (int i=0; i<4; ++i) + mSkills.addColumn (new UseValueColumn (i)); + mSkills.addColumn (new DescriptionColumn); + + mClasses.addColumn (new StringIdColumn); + mClasses.addColumn (new RecordStateColumn); + mClasses.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Class)); + mClasses.addColumn (new NameColumn); + mClasses.addColumn (new AttributesColumn (0)); + mClasses.addColumn (new AttributesColumn (1)); + mClasses.addColumn (new SpecialisationColumn); + for (int i=0; i<5; ++i) + mClasses.addColumn (new SkillsColumn (i, true, true)); + for (int i=0; i<5; ++i) + mClasses.addColumn (new SkillsColumn (i, true, false)); + mClasses.addColumn (new PlayableColumn); + mClasses.addColumn (new DescriptionColumn); + + mFactions.addColumn (new StringIdColumn); + mFactions.addColumn (new RecordStateColumn); + mFactions.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Faction)); + mFactions.addColumn (new NameColumn); + mFactions.addColumn (new AttributesColumn (0)); + mFactions.addColumn (new AttributesColumn (1)); + mFactions.addColumn (new HiddenColumn); + for (int i=0; i<6; ++i) + mFactions.addColumn (new SkillsColumn (i)); + + mRaces.addColumn (new StringIdColumn); + mRaces.addColumn (new RecordStateColumn); + mRaces.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Race)); + mRaces.addColumn (new NameColumn); + mRaces.addColumn (new DescriptionColumn); + mRaces.addColumn (new FlagColumn ("Playable", 0x1)); + mRaces.addColumn (new FlagColumn ("Beast Race", 0x2)); + mRaces.addColumn (new WeightHeightColumn (true, true)); + mRaces.addColumn (new WeightHeightColumn (true, false)); + mRaces.addColumn (new WeightHeightColumn (false, true)); + mRaces.addColumn (new WeightHeightColumn (false, false)); + + mSounds.addColumn (new StringIdColumn); + mSounds.addColumn (new RecordStateColumn); + mSounds.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Sound)); + mSounds.addColumn (new SoundParamColumn (SoundParamColumn::Type_Volume)); + mSounds.addColumn (new SoundParamColumn (SoundParamColumn::Type_MinRange)); + mSounds.addColumn (new SoundParamColumn (SoundParamColumn::Type_MaxRange)); + mSounds.addColumn (new SoundFileColumn); + + mScripts.addColumn (new StringIdColumn); + mScripts.addColumn (new RecordStateColumn); + mScripts.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Script)); + mScripts.addColumn (new ScriptColumn); + + mRegions.addColumn (new StringIdColumn); + mRegions.addColumn (new RecordStateColumn); + mRegions.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Region)); + mRegions.addColumn (new NameColumn); + mRegions.addColumn (new MapColourColumn); + mRegions.addColumn (new SleepListColumn); + + mBirthsigns.addColumn (new StringIdColumn); + mBirthsigns.addColumn (new RecordStateColumn); + mBirthsigns.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Birthsign)); + mBirthsigns.addColumn (new NameColumn); + mBirthsigns.addColumn (new TextureColumn); + mBirthsigns.addColumn (new DescriptionColumn); + + mSpells.addColumn (new StringIdColumn); + mSpells.addColumn (new RecordStateColumn); + mSpells.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Spell)); + mSpells.addColumn (new NameColumn); + mSpells.addColumn (new SpellTypeColumn); + mSpells.addColumn (new CostColumn); + mSpells.addColumn (new FlagColumn ("Autocalc", 0x1)); + mSpells.addColumn (new FlagColumn ("Starter Spell", 0x2)); + mSpells.addColumn (new FlagColumn ("Always Succeeds", 0x4)); + + mCells.addColumn (new StringIdColumn); + mCells.addColumn (new RecordStateColumn); + mCells.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Cell)); + mCells.addColumn (new NameColumn); + mCells.addColumn (new FlagColumn ("Sleep forbidden", ESM::Cell::NoSleep)); + mCells.addColumn (new FlagColumn ("Interior Water", ESM::Cell::HasWater)); + mCells.addColumn (new FlagColumn ("Interior Sky", ESM::Cell::QuasiEx)); + mCells.addColumn (new RegionColumn); addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global); + addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst); + addModel (new IdTable (&mSkills), UniversalId::Type_Skills, UniversalId::Type_Skill); + addModel (new IdTable (&mClasses), UniversalId::Type_Classes, UniversalId::Type_Class); + addModel (new IdTable (&mFactions), UniversalId::Type_Factions, UniversalId::Type_Faction); + addModel (new IdTable (&mRaces), UniversalId::Type_Races, UniversalId::Type_Race); + addModel (new IdTable (&mSounds), UniversalId::Type_Sounds, UniversalId::Type_Sound); + addModel (new IdTable (&mScripts), UniversalId::Type_Scripts, UniversalId::Type_Script); + addModel (new IdTable (&mRegions), UniversalId::Type_Regions, UniversalId::Type_Region); + addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsigns, UniversalId::Type_Birthsign); + addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell); + addModel (new IdTable (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell); + addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables, + UniversalId::Type_Referenceable); } CSMWorld::Data::~Data() { - for (std::vector::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter) + for (std::vector::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter) delete *iter; } @@ -46,9 +160,129 @@ CSMWorld::IdCollection& CSMWorld::Data::getGlobals() return mGlobals; } -QAbstractTableModel *CSMWorld::Data::getTableModel (const UniversalId& id) +const CSMWorld::IdCollection& CSMWorld::Data::getGmsts() const +{ + return mGmsts; +} + +CSMWorld::IdCollection& CSMWorld::Data::getGmsts() +{ + return mGmsts; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getSkills() const +{ + return mSkills; +} + +CSMWorld::IdCollection& CSMWorld::Data::getSkills() +{ + return mSkills; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getClasses() const { - std::map::iterator iter = mModelIndex.find (id.getType()); + return mClasses; +} + +CSMWorld::IdCollection& CSMWorld::Data::getClasses() +{ + return mClasses; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getFactions() const +{ + return mFactions; +} + +CSMWorld::IdCollection& CSMWorld::Data::getFactions() +{ + return mFactions; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getRaces() const +{ + return mRaces; +} + +CSMWorld::IdCollection& CSMWorld::Data::getRaces() +{ + return mRaces; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getSounds() const +{ + return mSounds; +} + +CSMWorld::IdCollection& CSMWorld::Data::getSounds() +{ + return mSounds; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getScripts() const +{ + return mScripts; +} + +CSMWorld::IdCollection& CSMWorld::Data::getScripts() +{ + return mScripts; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getRegions() const +{ + return mRegions; +} + +CSMWorld::IdCollection& CSMWorld::Data::getRegions() +{ + return mRegions; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getBirthsigns() const +{ + return mBirthsigns; +} + +CSMWorld::IdCollection& CSMWorld::Data::getBirthsigns() +{ + return mBirthsigns; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getSpells() const +{ + return mSpells; +} + +CSMWorld::IdCollection& CSMWorld::Data::getSpells() +{ + return mSpells; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getCells() const +{ + return mCells; +} + +CSMWorld::IdCollection& CSMWorld::Data::getCells() +{ + return mCells; +} + +const CSMWorld::RefIdCollection& CSMWorld::Data::getReferenceables() const +{ + return mReferenceables; +} + +CSMWorld::RefIdCollection& CSMWorld::Data::getReferenceables() +{ + return mReferenceables; +} + +QAbstractItemModel *CSMWorld::Data::getTableModel (const UniversalId& id) +{ + std::map::iterator iter = mModelIndex.find (id.getType()); if (iter==mModelIndex.end()) throw std::logic_error ("No table model available for " + id.toString()); @@ -59,4 +293,68 @@ QAbstractTableModel *CSMWorld::Data::getTableModel (const UniversalId& id) void CSMWorld::Data::merge() { mGlobals.merge(); +} + +void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) +{ + ESM::ESMReader reader; + + /// \todo set encoding properly, once config implementation has been fixed. + ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding ("win1252")); + reader.setEncoder (&encoder); + + reader.open (path.string()); + + // Note: We do not need to send update signals here, because at this point the model is not connected + // to any view. + while (reader.hasMoreRecs()) + { + ESM::NAME n = reader.getRecName(); + reader.getRecHeader(); + + switch (n.val) + { + case ESM::REC_GLOB: mGlobals.load (reader, base); break; + case ESM::REC_GMST: mGmsts.load (reader, base); break; + case ESM::REC_SKIL: mSkills.load (reader, base); break; + case ESM::REC_CLAS: mClasses.load (reader, base); break; + case ESM::REC_FACT: mFactions.load (reader, base); break; + case ESM::REC_RACE: mRaces.load (reader, base); break; + case ESM::REC_SOUN: mSounds.load (reader, base); break; + case ESM::REC_SCPT: mScripts.load (reader, base); break; + case ESM::REC_REGN: mRegions.load (reader, base); break; + case ESM::REC_BSGN: mBirthsigns.load (reader, base); break; + case ESM::REC_SPEL: mSpells.load (reader, base); break; + case ESM::REC_CELL: mCells.load (reader, base); break; + + case ESM::REC_ACTI: mReferenceables.load (reader, base, UniversalId::Type_Activator); break; + case ESM::REC_ALCH: mReferenceables.load (reader, base, UniversalId::Type_Potion); break; + case ESM::REC_APPA: mReferenceables.load (reader, base, UniversalId::Type_Apparatus); break; + case ESM::REC_ARMO: mReferenceables.load (reader, base, UniversalId::Type_Armor); break; + case ESM::REC_BOOK: mReferenceables.load (reader, base, UniversalId::Type_Book); break; + case ESM::REC_CLOT: mReferenceables.load (reader, base, UniversalId::Type_Clothing); break; + case ESM::REC_CONT: mReferenceables.load (reader, base, UniversalId::Type_Container); break; + case ESM::REC_CREA: mReferenceables.load (reader, base, UniversalId::Type_Creature); break; + case ESM::REC_DOOR: mReferenceables.load (reader, base, UniversalId::Type_Door); break; + case ESM::REC_INGR: mReferenceables.load (reader, base, UniversalId::Type_Ingredient); break; + case ESM::REC_LEVC: + mReferenceables.load (reader, base, UniversalId::Type_CreatureLevelledList); break; + case ESM::REC_LEVI: + mReferenceables.load (reader, base, UniversalId::Type_ItemLevelledList); break; + case ESM::REC_LIGH: mReferenceables.load (reader, base, UniversalId::Type_Light); break; + case ESM::REC_LOCK: mReferenceables.load (reader, base, UniversalId::Type_Lockpick); break; + case ESM::REC_MISC: + mReferenceables.load (reader, base, UniversalId::Type_Miscellaneous); break; + case ESM::REC_NPC_: mReferenceables.load (reader, base, UniversalId::Type_Npc); break; + case ESM::REC_PROB: mReferenceables.load (reader, base, UniversalId::Type_Probe); break; + case ESM::REC_REPA: mReferenceables.load (reader, base, UniversalId::Type_Repair); break; + case ESM::REC_STAT: mReferenceables.load (reader, base, UniversalId::Type_Static); break; + case ESM::REC_WEAP: mReferenceables.load (reader, base, UniversalId::Type_Weapon); break; + + default: + + /// \todo throw an exception instead, once all records are implemented + reader.skipRecord(); + } + } } \ No newline at end of file diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index f7748cb5d..ad6e4ba69 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -4,26 +4,52 @@ #include #include +#include + #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "idcollection.hpp" #include "universalid.hpp" +#include "cell.hpp" +#include "refidcollection.hpp" -class QAbstractTableModel; +class QAbstractItemModel; namespace CSMWorld { class Data { IdCollection mGlobals; - std::vector mModels; - std::map mModelIndex; + IdCollection mGmsts; + IdCollection mSkills; + IdCollection mClasses; + IdCollection mFactions; + IdCollection mRaces; + IdCollection mSounds; + IdCollection mScripts; + IdCollection mRegions; + IdCollection mBirthsigns; + IdCollection mSpells; + IdCollection mCells; + RefIdCollection mReferenceables; + std::vector mModels; + std::map mModelIndex; // not implemented Data (const Data&); Data& operator= (const Data&); - void addModel (QAbstractTableModel *model, UniversalId::Type type1, + void addModel (QAbstractItemModel *model, UniversalId::Type type1, UniversalId::Type type2 = UniversalId::Type_None); public: @@ -36,7 +62,55 @@ namespace CSMWorld IdCollection& getGlobals(); - QAbstractTableModel *getTableModel (const UniversalId& id); + const IdCollection& getGmsts() const; + + IdCollection& getGmsts(); + + const IdCollection& getSkills() const; + + IdCollection& getSkills(); + + const IdCollection& getClasses() const; + + IdCollection& getClasses(); + + const IdCollection& getFactions() const; + + IdCollection& getFactions(); + + const IdCollection& getRaces() const; + + IdCollection& getRaces(); + + const IdCollection& getSounds() const; + + IdCollection& getSounds(); + + const IdCollection& getScripts() const; + + IdCollection& getScripts(); + + const IdCollection& getRegions() const; + + IdCollection& getRegions(); + + const IdCollection& getBirthsigns() const; + + IdCollection& getBirthsigns(); + + const IdCollection& getSpells() const; + + IdCollection& getSpells(); + + const IdCollection& getCells() const; + + IdCollection& getCells(); + + const RefIdCollection& getReferenceables() const; + + RefIdCollection& getReferenceables(); + + QAbstractItemModel *getTableModel (const UniversalId& id); ///< If no table model is available for \a id, an exception is thrown. /// /// \note The returned table may either be the model for the ID itself or the model that @@ -44,6 +118,9 @@ namespace CSMWorld void merge(); ///< Merge modified into base. + + void loadFile (const boost::filesystem::path& path, bool base); + ///< Merging content of a file into base or modified. }; } diff --git a/apps/opencs/model/world/idcollection.cpp b/apps/opencs/model/world/idcollection.cpp index fc4bb1ef6..5ea953279 100644 --- a/apps/opencs/model/world/idcollection.cpp +++ b/apps/opencs/model/world/idcollection.cpp @@ -3,4 +3,4 @@ CSMWorld::IdCollectionBase::IdCollectionBase() {} -CSMWorld::IdCollectionBase::~IdCollectionBase() {} \ No newline at end of file +CSMWorld::IdCollectionBase::~IdCollectionBase() {} diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index 963997924..193e4f8e2 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -11,9 +11,13 @@ #include -#include "columnbase.hpp" +#include + #include +#include "columnbase.hpp" +#include "universalid.hpp" + namespace CSMWorld { class IdCollectionBase @@ -42,15 +46,19 @@ namespace CSMWorld virtual void setData (int index, int column, const QVariant& data) = 0; - virtual void merge() = 0; +// Not in use. Temporarily removed so that the implementation of RefIdCollection can continue without +// these functions for now. +// virtual void merge() = 0; ///< Merge modified into base. - virtual void purge() = 0; +// virtual void purge() = 0; ///< Remove records that are flagged as erased. virtual void removeRows (int index, int count) = 0; - virtual void appendBlankRecord (const std::string& id) = 0; + virtual void appendBlankRecord (const std::string& id, + UniversalId::Type type = UniversalId::Type_None) = 0; + ///< \param type Will be ignored, unless the collection supports multiple record types virtual int searchId (const std::string& id) const = 0; ////< Search record with \a id. @@ -60,20 +68,47 @@ namespace CSMWorld ///< If the record type does not match, an exception is thrown. /// /// \attention \a record must not change the ID. + ///< \param type Will be ignored, unless the collection supports multiple record types - virtual void appendRecord (const RecordBase& record) = 0; + virtual void appendRecord (const RecordBase& record, + UniversalId::Type type = UniversalId::Type_None) = 0; ///< If the record type does not match, an exception is thrown. - virtual std::string getId (const RecordBase& record) const = 0; - ///< Return ID for \a record. - /// - /// \attention Throw san exception, if the type of \a record does not match. - virtual const RecordBase& getRecord (const std::string& id) const = 0; + + virtual const RecordBase& getRecord (int index) const = 0; + + virtual void load (ESM::ESMReader& reader, bool base, + UniversalId::Type type = UniversalId::Type_None) = 0; + ///< \param type Will be ignored, unless the collection supports multiple record types + + virtual int getAppendIndex (UniversalId::Type type = UniversalId::Type_None) const = 0; + ///< \param type Will be ignored, unless the collection supports multiple record types }; - ///< \brief Collection of ID-based records + ///< \brief Access to ID field in records template + struct IdAccessor + { + std::string& getId (ESXRecordT& record); + + const std::string getId (const ESXRecordT& record) const; + }; + + template + std::string& IdAccessor::getId (ESXRecordT& record) + { + return record.mId; + } + + template + const std::string IdAccessor::getId (const ESXRecordT& record) const + { + return record.mId; + } + + ///< \brief Collection of ID-based records + template > class IdCollection : public IdCollectionBase { std::vector > mRecords; @@ -115,7 +150,9 @@ namespace CSMWorld virtual void removeRows (int index, int count) ; - virtual void appendBlankRecord (const std::string& id); + 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 int searchId (const std::string& id) const; ////< Search record with \a id. @@ -126,34 +163,40 @@ namespace CSMWorld /// /// \attention \a record must not change the ID. - virtual void appendRecord (const RecordBase& record); + virtual void appendRecord (const RecordBase& record, + UniversalId::Type type = UniversalId::Type_None); ///< If the record type does not match, an exception is thrown. + ///< \param type Will be ignored, unless the collection supports multiple record types - virtual std::string getId (const RecordBase& record) const; - ///< Return ID for \a record. - /// - /// \attention Throw san exception, if the type of \a record does not match. + virtual const Record& getRecord (const std::string& id) const; + + virtual const Record& getRecord (int index) const; + + virtual void load (ESM::ESMReader& reader, bool base, + UniversalId::Type type = UniversalId::Type_None); + ///< \param type Will be ignored, unless the collection supports multiple record types - virtual const RecordBase& getRecord (const std::string& id) const; + virtual int getAppendIndex (UniversalId::Type type = UniversalId::Type_None) const; + ///< \param type Will be ignored, unless the collection supports multiple record types void addColumn (Column *column); }; - template - IdCollection::IdCollection() + template + IdCollection::IdCollection() {} - template - IdCollection::~IdCollection() + template + IdCollection::~IdCollection() { for (typename std::vector *>::iterator iter (mColumns.begin()); iter!=mColumns.end(); ++iter) delete *iter; } - template - void IdCollection::add (const ESXRecordT& record) + template + void IdCollection::add (const ESXRecordT& record) { - std::string id = Misc::StringUtils::lowerCase(record.mId); + std::string id = Misc::StringUtils::lowerCase (IdAccessorT().getId (record)); std::map::iterator iter = mIndex.find (id); @@ -164,7 +207,7 @@ namespace CSMWorld record2.mModified = record; mRecords.push_back (record2); - mIndex.insert (std::make_pair (id, mRecords.size()-1)); + mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), mRecords.size()-1)); } else { @@ -172,20 +215,20 @@ namespace CSMWorld } } - template - int IdCollection::getSize() const + template + int IdCollection::getSize() const { return mRecords.size(); } - template - std::string IdCollection::getId (int index) const + template + std::string IdCollection::getId (int index) const { - return mRecords.at (index).get().mId; + return IdAccessorT().getId (mRecords.at (index).get()); } - template - int IdCollection::getIndex (const std::string& id) const + template + int IdCollection::getIndex (const std::string& id) const { int index = searchId (id); @@ -195,38 +238,38 @@ namespace CSMWorld return index; } - template - int IdCollection::getColumns() const + template + int IdCollection::getColumns() const { return mColumns.size(); } - template - QVariant IdCollection::getData (int index, int column) const + template + QVariant IdCollection::getData (int index, int column) const { return mColumns.at (column)->get (mRecords.at (index)); } - template - void IdCollection::setData (int index, int column, const QVariant& data) + template + void IdCollection::setData (int index, int column, const QVariant& data) { return mColumns.at (column)->set (mRecords.at (index), data); } - template - const ColumnBase& IdCollection::getColumn (int column) const + template + const ColumnBase& IdCollection::getColumn (int column) const { return *mColumns.at (column); } - template - void IdCollection::addColumn (Column *column) + template + void IdCollection::addColumn (Column *column) { mColumns.push_back (column); } - template - void IdCollection::merge() + template + void IdCollection::merge() { for (typename std::vector >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter) iter->merge(); @@ -234,16 +277,22 @@ namespace CSMWorld purge(); } - template - void IdCollection::purge() + template + void IdCollection::purge() { - mRecords.erase (std::remove_if (mRecords.begin(), mRecords.end(), - std::mem_fun_ref (&Record::isErased) // I want lambda :( - ), mRecords.end()); + int i = 0; + + while (i (mRecords.size())) + { + if (mRecords[i].isErased()) + removeRows (i, 1); + else + ++i; + } } - template - void IdCollection::removeRows (int index, int count) + template + void IdCollection::removeRows (int index, int count) { mRecords.erase (mRecords.begin()+index, mRecords.begin()+index+count); @@ -267,17 +316,18 @@ namespace CSMWorld } } - template - void IdCollection::appendBlankRecord (const std::string& id) + template + void IdCollection::appendBlankRecord (const std::string& id, + UniversalId::Type type) { ESXRecordT record; - record.mId = id; + IdAccessorT().getId (record) = id; record.blank(); add (record); } - template - int IdCollection::searchId (const std::string& id) const + template + int IdCollection::searchId (const std::string& id) const { std::string id2 = Misc::StringUtils::lowerCase(id); @@ -289,32 +339,100 @@ namespace CSMWorld return iter->second; } - template - void IdCollection::replace (int index, const RecordBase& record) + template + void IdCollection::replace (int index, const RecordBase& record) { mRecords.at (index) = dynamic_cast&> (record); } - template - void IdCollection::appendRecord (const RecordBase& record) + template + void IdCollection::appendRecord (const RecordBase& record, + UniversalId::Type type) { mRecords.push_back (dynamic_cast&> (record)); - mIndex.insert (std::make_pair (getId (record), mRecords.size()-1)); + mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId ( + dynamic_cast&> (record).get())), + mRecords.size()-1)); } - template - std::string IdCollection::getId (const RecordBase& record) const + template + void IdCollection::load (ESM::ESMReader& reader, bool base, + UniversalId::Type type) { - const Record& record2 = dynamic_cast&> (record); - return (record2.isModified() ? record2.mModified : record2.mBase).mId; + std::string id = reader.getHNOString ("NAME"); + + if (reader.isNextSub ("DELE")) + { + int index = searchId (id); + + reader.skipRecord(); + + if (index==-1) + { + // deleting a record that does not exist + + // ignore it for now + + /// \todo report the problem to the user + } + else if (base) + { + removeRows (index, 1); + } + else + { + mRecords[index].mState = RecordBase::State_Deleted; + } + } + else + { + ESXRecordT record; + IdAccessorT().getId (record) = id; + record.load (reader); + + int index = searchId (IdAccessorT().getId (record)); + + if (index==-1) + { + // new record + Record record2; + record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; + (base ? record2.mBase : record2.mModified) = record; + + appendRecord (record2); + } + else + { + // old record + Record& record2 = mRecords[index]; + + if (base) + record2.mBase = record; + else + record2.setModified (record); + } + } } - template - const RecordBase& IdCollection::getRecord (const std::string& id) const + template + int IdCollection::getAppendIndex (UniversalId::Type type) const + { + return static_cast (mRecords.size()); + } + + template + const Record& IdCollection::getRecord (const std::string& id) const { int index = getIndex (id); return mRecords.at (index); } + + template + const Record& IdCollection::getRecord (int index) const + { + return mRecords.at (index); + } + } #endif diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 2815e318c..386ca8702 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -51,6 +51,9 @@ QVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation if (role==ColumnBase::Role_Flags) return mIdCollection->getColumn (section).mFlags; + if (role==ColumnBase::Role_Display) + return mIdCollection->getColumn (section).mDisplayType; + return QVariant(); } @@ -93,9 +96,28 @@ bool CSMWorld::IdTable::removeRows (int row, int count, const QModelIndex& paren return true; } +QModelIndex CSMWorld::IdTable::index (int row, int column, const QModelIndex& parent) const +{ + if (parent.isValid()) + return QModelIndex(); + + if (row<0 || row>=mIdCollection->getSize()) + return QModelIndex(); + + if (column<0 || column>=mIdCollection->getColumns()) + return QModelIndex(); + + return createIndex (row, column); +} + +QModelIndex CSMWorld::IdTable::parent (const QModelIndex& index) const +{ + return QModelIndex(); +} + void CSMWorld::IdTable::addRecord (const std::string& id) { - int index = mIdCollection->getSize(); + int index = mIdCollection->getAppendIndex(); beginInsertRows (QModelIndex(), index, index); @@ -109,13 +131,13 @@ QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) return index (mIdCollection->getIndex (id), column); } -void CSMWorld::IdTable::setRecord (const RecordBase& record) +void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& record) { - int index = mIdCollection->searchId (mIdCollection->getId (record)); + int index = mIdCollection->searchId (id); if (index==-1) { - int index = mIdCollection->getSize(); + int index = mIdCollection->getAppendIndex(); beginInsertRows (QModelIndex(), index, index); diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index deaebaa38..de0dde56e 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -1,14 +1,14 @@ #ifndef CSM_WOLRD_IDTABLE_H #define CSM_WOLRD_IDTABLE_H -#include +#include namespace CSMWorld { class IdCollectionBase; class RecordBase; - class IdTable : public QAbstractTableModel + class IdTable : public QAbstractItemModel { Q_OBJECT @@ -39,11 +39,16 @@ namespace CSMWorld virtual bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex()); + virtual QModelIndex index (int row, int column, const QModelIndex& parent = QModelIndex()) + const; + + virtual QModelIndex parent (const QModelIndex& index) const; + void addRecord (const std::string& id); QModelIndex getModelIndex (const std::string& id, int column) const; - void setRecord (const RecordBase& record); + void setRecord (const std::string& id, const RecordBase& record); ///< Add record or overwrite existing recrod. const RecordBase& getRecord (const std::string& id) const; diff --git a/apps/opencs/model/world/record.cpp b/apps/opencs/model/world/record.cpp index 229985a8a..14f63c155 100644 --- a/apps/opencs/model/world/record.cpp +++ b/apps/opencs/model/world/record.cpp @@ -3,19 +3,19 @@ CSMWorld::RecordBase::~RecordBase() {} -bool CSMWorld::RecordBase::RecordBase::isDeleted() const +bool CSMWorld::RecordBase::isDeleted() const { return mState==State_Deleted || mState==State_Erased; } -bool CSMWorld::RecordBase::RecordBase::isErased() const +bool CSMWorld::RecordBase::isErased() const { return mState==State_Erased; } -bool CSMWorld::RecordBase::RecordBase::isModified() const +bool CSMWorld::RecordBase::isModified() const { return mState==State_Modified || mState==State_ModifiedOnly; } \ No newline at end of file diff --git a/apps/opencs/model/world/record.hpp b/apps/opencs/model/world/record.hpp index 53bb7ea2c..861fc47a3 100644 --- a/apps/opencs/model/world/record.hpp +++ b/apps/opencs/model/world/record.hpp @@ -22,6 +22,9 @@ namespace CSMWorld virtual RecordBase *clone() const = 0; + virtual void assign (const RecordBase& record) = 0; + ///< Will throw an exception if the types don't match. + bool isDeleted() const; bool isErased() const; @@ -37,9 +40,14 @@ namespace CSMWorld virtual RecordBase *clone() const; + virtual void assign (const RecordBase& record); + const ESXRecordT& get() const; ///< Throws an exception, if the record is deleted. + ESXRecordT& get(); + ///< Throws an exception, if the record is deleted. + const ESXRecordT& getBase() const; ///< Throws an exception, if the record is deleted. Returns modified, if there is no base. @@ -56,13 +64,28 @@ namespace CSMWorld return new Record (*this); } + template + void Record::assign (const RecordBase& record) + { + *this = dynamic_cast& > (record); + } + template const ESXRecordT& Record::get() const { if (mState==State_Erased) throw std::logic_error ("attempt to access a deleted record"); - return mState==State_BaseOnly ? mBase : mModified; + return mState==State_BaseOnly || mState==State_Deleted ? mBase : mModified; + } + + template + ESXRecordT& Record::get() + { + if (mState==State_Erased) + throw std::logic_error ("attempt to access a deleted record"); + + return mState==State_BaseOnly || mState==State_Deleted ? mBase : mModified; } template @@ -83,7 +106,7 @@ namespace CSMWorld mModified = modified; if (mState!=State_ModifiedOnly) - mState = mBase==mModified ? State_BaseOnly : State_Modified; + mState = State_Modified; } template diff --git a/apps/opencs/model/world/refidadapter.cpp b/apps/opencs/model/world/refidadapter.cpp new file mode 100644 index 000000000..94ae38c3c --- /dev/null +++ b/apps/opencs/model/world/refidadapter.cpp @@ -0,0 +1,6 @@ + +#include "refidadapter.hpp" + +CSMWorld::RefIdAdapter::RefIdAdapter() {} + +CSMWorld::RefIdAdapter::~RefIdAdapter() {} \ No newline at end of file diff --git a/apps/opencs/model/world/refidadapter.hpp b/apps/opencs/model/world/refidadapter.hpp new file mode 100644 index 000000000..df0ceae70 --- /dev/null +++ b/apps/opencs/model/world/refidadapter.hpp @@ -0,0 +1,37 @@ +#ifndef CSM_WOLRD_REFIDADAPTER_H +#define CSM_WOLRD_REFIDADAPTER_H + +#include + +class QVariant; + +namespace CSMWorld +{ + class RefIdColumn; + class RefIdData; + class RecordBase; + + class RefIdAdapter + { + // not implemented + RefIdAdapter (const RefIdAdapter&); + RefIdAdapter& operator= (const RefIdAdapter&); + + public: + + RefIdAdapter(); + + virtual ~RefIdAdapter(); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int idnex) + const = 0; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const = 0; + ///< If the data type does not match an exception is thrown. + + virtual std::string getId (const RecordBase& record) const = 0; + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp new file mode 100644 index 000000000..f00e9fc77 --- /dev/null +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -0,0 +1,575 @@ + +#include "refidadapterimp.hpp" + +CSMWorld::PotionRefIdAdapter::PotionRefIdAdapter (const InventoryColumns& columns, + const RefIdColumn *autoCalc) +: InventoryRefIdAdapter (UniversalId::Type_Potion, columns), + mAutoCalc (autoCalc) +{} + +QVariant CSMWorld::PotionRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Potion))); + + if (column==mAutoCalc) + return record.get().mData.mAutoCalc!=0; + + return InventoryRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::PotionRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Potion))); + + if (column==mAutoCalc) + record.get().mData.mAutoCalc = value.toInt(); + else + InventoryRefIdAdapter::setData (column, data, index, value); +} + + +CSMWorld::ApparatusRefIdAdapter::ApparatusRefIdAdapter (const InventoryColumns& columns, + const RefIdColumn *type, const RefIdColumn *quality) +: InventoryRefIdAdapter (UniversalId::Type_Apparatus, columns), + mType (type), mQuality (quality) +{} + +QVariant CSMWorld::ApparatusRefIdAdapter::getData (const RefIdColumn *column, + const RefIdData& data, int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Apparatus))); + + if (column==mType) + return record.get().mData.mType; + + if (column==mQuality) + return record.get().mData.mQuality; + + return InventoryRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::ApparatusRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Apparatus))); + + if (column==mType) + record.get().mData.mType = value.toInt(); + else if (column==mQuality) + record.get().mData.mQuality = value.toFloat(); + else + InventoryRefIdAdapter::setData (column, data, index, value); +} + + +CSMWorld::ArmorRefIdAdapter::ArmorRefIdAdapter (const EnchantableColumns& columns, + const RefIdColumn *type, const RefIdColumn *health, const RefIdColumn *armor) +: EnchantableRefIdAdapter (UniversalId::Type_Armor, columns), + mType (type), mHealth (health), mArmor (armor) +{} + +QVariant CSMWorld::ArmorRefIdAdapter::getData (const RefIdColumn *column, + const RefIdData& data, int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Armor))); + + if (column==mType) + return record.get().mData.mType; + + if (column==mHealth) + return record.get().mData.mHealth; + + if (column==mArmor) + return record.get().mData.mArmor; + + return EnchantableRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::ArmorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Armor))); + + if (column==mType) + record.get().mData.mType = value.toInt(); + else if (column==mHealth) + record.get().mData.mHealth = value.toInt(); + else if (column==mArmor) + record.get().mData.mArmor = value.toInt(); + else + EnchantableRefIdAdapter::setData (column, data, index, value); +} + +CSMWorld::BookRefIdAdapter::BookRefIdAdapter (const EnchantableColumns& columns, + const RefIdColumn *scroll, const RefIdColumn *skill) +: EnchantableRefIdAdapter (UniversalId::Type_Book, columns), + mScroll (scroll), mSkill (skill) +{} + +QVariant CSMWorld::BookRefIdAdapter::getData (const RefIdColumn *column, + const RefIdData& data, int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Book))); + + if (column==mScroll) + return record.get().mData.mIsScroll!=0; + + if (column==mSkill) + return record.get().mData.mSkillID; + + return EnchantableRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::BookRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Book))); + + if (column==mScroll) + record.get().mData.mIsScroll = value.toInt(); + else if (column==mSkill) + record.get().mData.mSkillID = value.toInt(); + else + EnchantableRefIdAdapter::setData (column, data, index, value); +} + +CSMWorld::ClothingRefIdAdapter::ClothingRefIdAdapter (const EnchantableColumns& columns, + const RefIdColumn *type) +: EnchantableRefIdAdapter (UniversalId::Type_Clothing, columns), mType (type) +{} + +QVariant CSMWorld::ClothingRefIdAdapter::getData (const RefIdColumn *column, + const RefIdData& data, int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Clothing))); + + if (column==mType) + return record.get().mData.mType; + + return EnchantableRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::ClothingRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Clothing))); + + if (column==mType) + record.get().mData.mType = value.toInt(); + else + EnchantableRefIdAdapter::setData (column, data, index, value); +} + +CSMWorld::ContainerRefIdAdapter::ContainerRefIdAdapter (const NameColumns& columns, + const RefIdColumn *weight, const RefIdColumn *organic, const RefIdColumn *respawn) +: NameRefIdAdapter (UniversalId::Type_Container, columns), mWeight (weight), + mOrganic (organic), mRespawn (respawn) +{} + +QVariant CSMWorld::ContainerRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Container))); + + if (column==mWeight) + return record.get().mWeight; + + if (column==mOrganic) + return (record.get().mFlags & ESM::Container::Organic)!=0; + + if (column==mRespawn) + return (record.get().mFlags & ESM::Container::Respawn)!=0; + + return NameRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::ContainerRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Container))); + + if (column==mWeight) + record.get().mWeight = value.toFloat(); + else if (column==mOrganic) + { + if (value.toInt()) + record.get().mFlags |= ESM::Container::Organic; + else + record.get().mFlags &= ~ESM::Container::Organic; + } + else if (column==mRespawn) + { + if (value.toInt()) + record.get().mFlags |= ESM::Container::Respawn; + else + record.get().mFlags &= ~ESM::Container::Respawn; + } + else + NameRefIdAdapter::setData (column, data, index, value); +} + +CSMWorld::CreatureColumns::CreatureColumns (const ActorColumns& actorColumns) +: ActorColumns (actorColumns) +{} + +CSMWorld::CreatureRefIdAdapter::CreatureRefIdAdapter (const CreatureColumns& columns) +: ActorRefIdAdapter (UniversalId::Type_Creature, columns), mColumns (columns) +{} + +QVariant CSMWorld::CreatureRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); + + if (column==mColumns.mType) + return record.get().mData.mType; + + if (column==mColumns.mSoul) + return record.get().mData.mSoul; + + if (column==mColumns.mScale) + return record.get().mScale; + + if (column==mColumns.mOriginal) + return QString::fromUtf8 (record.get().mOriginal.c_str()); + + std::map::const_iterator iter = + mColumns.mFlags.find (column); + + if (iter!=mColumns.mFlags.end()) + return (record.get().mFlags & iter->second)!=0; + + return ActorRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::CreatureRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); + + if (column==mColumns.mType) + record.get().mData.mType = value.toInt(); + else if (column==mColumns.mSoul) + record.get().mData.mSoul = value.toInt(); + else if (column==mColumns.mScale) + record.get().mScale = value.toFloat(); + else if (column==mColumns.mOriginal) + record.get().mOriginal = value.toString().toUtf8().constData(); + else + { + std::map::const_iterator iter = + mColumns.mFlags.find (column); + + if (iter!=mColumns.mFlags.end()) + { + if (value.toInt()!=0) + record.get().mFlags |= iter->second; + else + record.get().mFlags &= ~iter->second; + } + else + ActorRefIdAdapter::setData (column, data, index, value); + } +} + +CSMWorld::DoorRefIdAdapter::DoorRefIdAdapter (const NameColumns& columns, + const RefIdColumn *openSound, const RefIdColumn *closeSound) +: NameRefIdAdapter (UniversalId::Type_Door, columns), mOpenSound (openSound), + mCloseSound (closeSound) +{} + +QVariant CSMWorld::DoorRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Door))); + + if (column==mOpenSound) + return QString::fromUtf8 (record.get().mOpenSound.c_str()); + + if (column==mCloseSound) + return QString::fromUtf8 (record.get().mCloseSound.c_str()); + + return NameRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::DoorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Door))); + + if (column==mOpenSound) + record.get().mOpenSound = value.toString().toUtf8().constData(); + else if (column==mCloseSound) + record.get().mCloseSound = value.toString().toUtf8().constData(); + else + NameRefIdAdapter::setData (column, data, index, value); +} + +CSMWorld::LightColumns::LightColumns (const InventoryColumns& columns) +: InventoryColumns (columns) {} + +CSMWorld::LightRefIdAdapter::LightRefIdAdapter (const LightColumns& columns) +: InventoryRefIdAdapter (UniversalId::Type_Light, columns), mColumns (columns) +{} + +QVariant CSMWorld::LightRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Light))); + + if (column==mColumns.mTime) + return record.get().mData.mTime; + + if (column==mColumns.mRadius) + return record.get().mData.mRadius; + + if (column==mColumns.mColor) + return record.get().mData.mColor; + + if (column==mColumns.mSound) + return QString::fromUtf8 (record.get().mSound.c_str()); + + std::map::const_iterator iter = + mColumns.mFlags.find (column); + + if (iter!=mColumns.mFlags.end()) + return (record.get().mData.mFlags & iter->second)!=0; + + return InventoryRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::LightRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Light))); + + if (column==mColumns.mTime) + record.get().mData.mTime = value.toInt(); + else if (column==mColumns.mRadius) + record.get().mData.mRadius = value.toInt(); + else if (column==mColumns.mColor) + record.get().mData.mColor = value.toInt(); + else if (column==mColumns.mSound) + record.get().mSound = value.toString().toUtf8().constData(); + else + { + std::map::const_iterator iter = + mColumns.mFlags.find (column); + + if (iter!=mColumns.mFlags.end()) + { + if (value.toInt()!=0) + record.get().mData.mFlags |= iter->second; + else + record.get().mData.mFlags &= ~iter->second; + } + else + InventoryRefIdAdapter::setData (column, data, index, value); + } +} + +CSMWorld::MiscRefIdAdapter::MiscRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *key) +: InventoryRefIdAdapter (UniversalId::Type_Miscellaneous, columns), mKey (key) +{} + +QVariant CSMWorld::MiscRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Miscellaneous))); + + if (column==mKey) + return record.get().mData.mIsKey!=0; + + return InventoryRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::MiscRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Miscellaneous))); + + if (column==mKey) + record.get().mData.mIsKey = value.toInt(); + else + InventoryRefIdAdapter::setData (column, data, index, value); +} + +CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns) : ActorColumns (actorColumns) {} + +CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns) +: ActorRefIdAdapter (UniversalId::Type_Npc, columns), mColumns (columns) +{} + +QVariant CSMWorld::NpcRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) + const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); + + if (column==mColumns.mRace) + return QString::fromUtf8 (record.get().mRace.c_str()); + + if (column==mColumns.mClass) + return QString::fromUtf8 (record.get().mClass.c_str()); + + if (column==mColumns.mFaction) + return QString::fromUtf8 (record.get().mFaction.c_str()); + + if (column==mColumns.mHair) + return QString::fromUtf8 (record.get().mHair.c_str()); + + if (column==mColumns.mHead) + return QString::fromUtf8 (record.get().mHead.c_str()); + + std::map::const_iterator iter = + mColumns.mFlags.find (column); + + if (iter!=mColumns.mFlags.end()) + return (record.get().mFlags & iter->second)!=0; + + return ActorRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); + + if (column==mColumns.mRace) + record.get().mRace = value.toString().toUtf8().constData(); + else if (column==mColumns.mClass) + record.get().mClass = value.toString().toUtf8().constData(); + else if (column==mColumns.mFaction) + record.get().mFaction = value.toString().toUtf8().constData(); + else if (column==mColumns.mHair) + record.get().mHair = value.toString().toUtf8().constData(); + else if (column==mColumns.mHead) + record.get().mHead = value.toString().toUtf8().constData(); + else + { + std::map::const_iterator iter = + mColumns.mFlags.find (column); + + if (iter!=mColumns.mFlags.end()) + { + if (value.toInt()!=0) + record.get().mFlags |= iter->second; + else + record.get().mFlags &= ~iter->second; + } + else + ActorRefIdAdapter::setData (column, data, index, value); + } +} + +CSMWorld::WeaponColumns::WeaponColumns (const EnchantableColumns& columns) +: EnchantableColumns (columns) {} + +CSMWorld::WeaponRefIdAdapter::WeaponRefIdAdapter (const WeaponColumns& columns) +: EnchantableRefIdAdapter (UniversalId::Type_Weapon, columns), mColumns (columns) +{} + +QVariant CSMWorld::WeaponRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const +{ + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Weapon))); + + if (column==mColumns.mType) + return record.get().mData.mType; + + if (column==mColumns.mHealth) + return record.get().mData.mHealth; + + if (column==mColumns.mSpeed) + return record.get().mData.mSpeed; + + if (column==mColumns.mReach) + return record.get().mData.mReach; + + for (int i=0; i<2; ++i) + { + if (column==mColumns.mChop[i]) + return record.get().mData.mChop[i]; + + if (column==mColumns.mSlash[i]) + return record.get().mData.mSlash[i]; + + if (column==mColumns.mThrust[i]) + return record.get().mData.mThrust[i]; + } + + std::map::const_iterator iter = + mColumns.mFlags.find (column); + + if (iter!=mColumns.mFlags.end()) + return (record.get().mData.mFlags & iter->second)!=0; + + return EnchantableRefIdAdapter::getData (column, data, index); +} + +void CSMWorld::WeaponRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const +{ + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Weapon))); + + if (column==mColumns.mType) + record.get().mData.mType = value.toInt(); + else if (column==mColumns.mHealth) + record.get().mData.mHealth = value.toInt(); + else if (column==mColumns.mSpeed) + record.get().mData.mSpeed = value.toFloat(); + else if (column==mColumns.mReach) + record.get().mData.mReach = value.toFloat(); + else if (column==mColumns.mChop[0]) + record.get().mData.mChop[0] = value.toInt(); + else if (column==mColumns.mChop[1]) + record.get().mData.mChop[1] = value.toInt(); + else if (column==mColumns.mSlash[0]) + record.get().mData.mSlash[0] = value.toInt(); + else if (column==mColumns.mSlash[1]) + record.get().mData.mSlash[1] = value.toInt(); + else if (column==mColumns.mThrust[0]) + record.get().mData.mThrust[0] = value.toInt(); + else if (column==mColumns.mThrust[1]) + record.get().mData.mThrust[1] = value.toInt(); + else + { + std::map::const_iterator iter = + mColumns.mFlags.find (column); + + if (iter!=mColumns.mFlags.end()) + { + if (value.toInt()!=0) + record.get().mData.mFlags |= iter->second; + else + record.get().mData.mFlags &= ~iter->second; + } + else + EnchantableRefIdAdapter::setData (column, data, index, value); + } +} \ No newline at end of file diff --git a/apps/opencs/model/world/refidadapterimp.hpp b/apps/opencs/model/world/refidadapterimp.hpp new file mode 100644 index 000000000..d5d84a8f7 --- /dev/null +++ b/apps/opencs/model/world/refidadapterimp.hpp @@ -0,0 +1,766 @@ +#ifndef CSM_WOLRD_REFIDADAPTERIMP_H +#define CSM_WOLRD_REFIDADAPTERIMP_H + +#include + +#include + +#include +#include + +#include "record.hpp" +#include "refiddata.hpp" +#include "universalid.hpp" +#include "refidadapter.hpp" + +namespace CSMWorld +{ + struct BaseColumns + { + const RefIdColumn *mId; + const RefIdColumn *mModified; + const RefIdColumn *mType; + }; + + /// \brief Base adapter for all refereceable record types + template + class BaseRefIdAdapter : public RefIdAdapter + { + UniversalId::Type mType; + BaseColumns mBase; + + public: + + BaseRefIdAdapter (UniversalId::Type type, const BaseColumns& base); + + virtual std::string getId (const RecordBase& record) const; + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + + UniversalId::Type getType() const; + }; + + template + BaseRefIdAdapter::BaseRefIdAdapter (UniversalId::Type type, const BaseColumns& base) + : mType (type), mBase (base) + {} + + template + std::string BaseRefIdAdapter::getId (const RecordBase& record) const + { + return dynamic_cast&> (record).get().mId; + } + + template + QVariant BaseRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const + { + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, mType))); + + if (column==mBase.mId) + return QString::fromUtf8 (record.get().mId.c_str()); + + if (column==mBase.mModified) + { + if (record.mState==Record::State_Erased) + return static_cast (Record::State_Deleted); + + return static_cast (record.mState); + } + + if (column==mBase.mType) + return static_cast (mType); + + return QVariant(); + } + + template + void BaseRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const + { + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, mType))); + + if (column==mBase.mModified) + record.mState = static_cast (value.toInt()); + } + + template + UniversalId::Type BaseRefIdAdapter::getType() const + { + return mType; + } + + + struct ModelColumns : public BaseColumns + { + const RefIdColumn *mModel; + + ModelColumns (const BaseColumns& base) : BaseColumns (base) {} + }; + + /// \brief Adapter for IDs with models (all but levelled lists) + template + class ModelRefIdAdapter : public BaseRefIdAdapter + { + ModelColumns mModel; + + public: + + ModelRefIdAdapter (UniversalId::Type type, const ModelColumns& columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + template + ModelRefIdAdapter::ModelRefIdAdapter (UniversalId::Type type, const ModelColumns& columns) + : BaseRefIdAdapter (type, columns), mModel (columns) + {} + + template + QVariant ModelRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const + { + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mModel.mModel) + return QString::fromUtf8 (record.get().mModel.c_str()); + + return BaseRefIdAdapter::getData (column, data, index); + } + + template + void ModelRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const + { + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mModel.mModel) + record.get().mModel = value.toString().toUtf8().constData(); + else + BaseRefIdAdapter::setData (column, data, index, value); + } + + struct NameColumns : public ModelColumns + { + const RefIdColumn *mName; + const RefIdColumn *mScript; + + NameColumns (const ModelColumns& base) : ModelColumns (base) {} + }; + + /// \brief Adapter for IDs with names (all but levelled lists and statics) + template + class NameRefIdAdapter : public ModelRefIdAdapter + { + NameColumns mName; + + public: + + NameRefIdAdapter (UniversalId::Type type, const NameColumns& columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + template + NameRefIdAdapter::NameRefIdAdapter (UniversalId::Type type, const NameColumns& columns) + : ModelRefIdAdapter (type, columns), mName (columns) + {} + + template + QVariant NameRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const + { + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mName.mName) + return QString::fromUtf8 (record.get().mName.c_str()); + + if (column==mName.mScript) + return QString::fromUtf8 (record.get().mScript.c_str()); + + return ModelRefIdAdapter::getData (column, data, index); + } + + template + void NameRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const + { + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mName.mName) + record.get().mName = value.toString().toUtf8().constData(); + else if (column==mName.mScript) + record.get().mScript = value.toString().toUtf8().constData(); + else + ModelRefIdAdapter::setData (column, data, index, value); + } + + struct InventoryColumns : public NameColumns + { + const RefIdColumn *mIcon; + const RefIdColumn *mWeight; + const RefIdColumn *mValue; + + InventoryColumns (const NameColumns& base) : NameColumns (base) {} + }; + + /// \brief Adapter for IDs that can go into an inventory + template + class InventoryRefIdAdapter : public NameRefIdAdapter + { + InventoryColumns mInventory; + + public: + + InventoryRefIdAdapter (UniversalId::Type type, const InventoryColumns& columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + template + InventoryRefIdAdapter::InventoryRefIdAdapter (UniversalId::Type type, + const InventoryColumns& columns) + : NameRefIdAdapter (type, columns), mInventory (columns) + {} + + template + QVariant InventoryRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const + { + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mInventory.mIcon) + return QString::fromUtf8 (record.get().mIcon.c_str()); + + if (column==mInventory.mWeight) + return record.get().mData.mWeight; + + if (column==mInventory.mValue) + return record.get().mData.mValue; + + return NameRefIdAdapter::getData (column, data, index); + } + + template + void InventoryRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const + { + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mInventory.mIcon) + record.get().mIcon = value.toString().toUtf8().constData(); + else if (column==mInventory.mWeight) + record.get().mData.mWeight = value.toFloat(); + else if (column==mInventory.mValue) + record.get().mData.mValue = value.toInt(); + else + NameRefIdAdapter::setData (column, data, index, value); + } + + class PotionRefIdAdapter : public InventoryRefIdAdapter + { + const RefIdColumn *mAutoCalc; + + public: + + PotionRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *autoCalc); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + struct EnchantableColumns : public InventoryColumns + { + const RefIdColumn *mEnchantment; + const RefIdColumn *mEnchantmentPoints; + + EnchantableColumns (const InventoryColumns& base) : InventoryColumns (base) {} + }; + + /// \brief Adapter for enchantable IDs + template + class EnchantableRefIdAdapter : public InventoryRefIdAdapter + { + EnchantableColumns mEnchantable; + + public: + + EnchantableRefIdAdapter (UniversalId::Type type, const EnchantableColumns& columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + template + EnchantableRefIdAdapter::EnchantableRefIdAdapter (UniversalId::Type type, + const EnchantableColumns& columns) + : InventoryRefIdAdapter (type, columns), mEnchantable (columns) + {} + + template + QVariant EnchantableRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const + { + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mEnchantable.mEnchantment) + return QString::fromUtf8 (record.get().mEnchant.c_str()); + + if (column==mEnchantable.mEnchantmentPoints) + return static_cast (record.get().mData.mEnchant); + + return InventoryRefIdAdapter::getData (column, data, index); + } + + template + void EnchantableRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, + int index, const QVariant& value) const + { + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mEnchantable.mEnchantment) + record.get().mEnchant = value.toString().toUtf8().constData(); + else if (column==mEnchantable.mEnchantmentPoints) + record.get().mData.mEnchant = value.toInt(); + else + InventoryRefIdAdapter::setData (column, data, index, value); + } + + struct ToolColumns : public InventoryColumns + { + const RefIdColumn *mQuality; + const RefIdColumn *mUses; + + ToolColumns (const InventoryColumns& base) : InventoryColumns (base) {} + }; + + /// \brief Adapter for tools with limited uses IDs (lockpick, repair, probes) + template + class ToolRefIdAdapter : public InventoryRefIdAdapter + { + ToolColumns mTools; + + public: + + ToolRefIdAdapter (UniversalId::Type type, const ToolColumns& columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + template + ToolRefIdAdapter::ToolRefIdAdapter (UniversalId::Type type, const ToolColumns& columns) + : InventoryRefIdAdapter (type, columns), mTools (columns) + {} + + template + QVariant ToolRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const + { + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mTools.mQuality) + return record.get().mData.mQuality; + + if (column==mTools.mUses) + return record.get().mData.mUses; + + return InventoryRefIdAdapter::getData (column, data, index); + } + + template + void ToolRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, + int index, const QVariant& value) const + { + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mTools.mQuality) + record.get().mData.mQuality = value.toFloat(); + else if (column==mTools.mUses) + record.get().mData.mUses = value.toInt(); + else + InventoryRefIdAdapter::setData (column, data, index, value); + } + + struct ActorColumns : public NameColumns + { + const RefIdColumn *mHasAi; + const RefIdColumn *mHello; + const RefIdColumn *mFlee; + const RefIdColumn *mFight; + const RefIdColumn *mAlarm; + std::map mServices; + + ActorColumns (const NameColumns& base) : NameColumns (base) {} + }; + + /// \brief Adapter for actor IDs (handles common AI functionality) + template + class ActorRefIdAdapter : public NameRefIdAdapter + { + ActorColumns mActors; + + public: + + ActorRefIdAdapter (UniversalId::Type type, const ActorColumns& columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + template + ActorRefIdAdapter::ActorRefIdAdapter (UniversalId::Type type, + const ActorColumns& columns) + : NameRefIdAdapter (type, columns), mActors (columns) + {} + + template + QVariant ActorRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, + int index) const + { + const Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mActors.mHasAi) + return record.get().mHasAI!=0; + + if (column==mActors.mHello) + return record.get().mAiData.mHello; + + if (column==mActors.mFlee) + return record.get().mAiData.mFlee; + + if (column==mActors.mFight) + return record.get().mAiData.mFight; + + if (column==mActors.mAlarm) + return record.get().mAiData.mAlarm; + + std::map::const_iterator iter = + mActors.mServices.find (column); + + if (iter!=mActors.mServices.end()) + return (record.get().mAiData.mServices & iter->second)!=0; + + return NameRefIdAdapter::getData (column, data, index); + } + + template + void ActorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const + { + Record& record = static_cast&> ( + data.getRecord (RefIdData::LocalIndex (index, BaseRefIdAdapter::getType()))); + + if (column==mActors.mHasAi) + record.get().mHasAI = value.toInt(); + else if (column==mActors.mHello) + record.get().mAiData.mHello = value.toInt(); + else if (column==mActors.mFlee) + record.get().mAiData.mFlee = value.toInt(); + else if (column==mActors.mFight) + record.get().mAiData.mFight = value.toInt(); + else if (column==mActors.mAlarm) + record.get().mAiData.mAlarm = value.toInt(); + else + { + typename std::map::const_iterator iter = + mActors.mServices.find (column); + if (iter!=mActors.mServices.end()) + { + if (value.toInt()!=0) + record.get().mAiData.mServices |= iter->second; + else + record.get().mAiData.mServices &= ~iter->second; + } + else + NameRefIdAdapter::setData (column, data, index, value); + } + } + + class ApparatusRefIdAdapter : public InventoryRefIdAdapter + { + const RefIdColumn *mType; + const RefIdColumn *mQuality; + + public: + + ApparatusRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *type, + const RefIdColumn *quality); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + class ArmorRefIdAdapter : public EnchantableRefIdAdapter + { + const RefIdColumn *mType; + const RefIdColumn *mHealth; + const RefIdColumn *mArmor; + + public: + + ArmorRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *type, + const RefIdColumn *health, const RefIdColumn *armor); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + class BookRefIdAdapter : public EnchantableRefIdAdapter + { + const RefIdColumn *mScroll; + const RefIdColumn *mSkill; + + public: + + BookRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *scroll, + const RefIdColumn *skill); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + class ClothingRefIdAdapter : public EnchantableRefIdAdapter + { + const RefIdColumn *mType; + + public: + + ClothingRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *type); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + class ContainerRefIdAdapter : public NameRefIdAdapter + { + const RefIdColumn *mWeight; + const RefIdColumn *mOrganic; + const RefIdColumn *mRespawn; + + public: + + ContainerRefIdAdapter (const NameColumns& columns, const RefIdColumn *weight, + const RefIdColumn *organic, const RefIdColumn *respawn); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + struct CreatureColumns : public ActorColumns + { + std::map mFlags; + const RefIdColumn *mType; + const RefIdColumn *mSoul; + const RefIdColumn *mScale; + const RefIdColumn *mOriginal; + + CreatureColumns (const ActorColumns& actorColumns); + }; + + class CreatureRefIdAdapter : public ActorRefIdAdapter + { + CreatureColumns mColumns; + + public: + + CreatureRefIdAdapter (const CreatureColumns& columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + class DoorRefIdAdapter : public NameRefIdAdapter + { + const RefIdColumn *mOpenSound; + const RefIdColumn *mCloseSound; + + public: + + DoorRefIdAdapter (const NameColumns& columns, const RefIdColumn *openSound, + const RefIdColumn *closeSound); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + struct LightColumns : public InventoryColumns + { + const RefIdColumn *mTime; + const RefIdColumn *mRadius; + const RefIdColumn *mColor; + const RefIdColumn *mSound; + std::map mFlags; + + LightColumns (const InventoryColumns& columns); + }; + + class LightRefIdAdapter : public InventoryRefIdAdapter + { + LightColumns mColumns; + + public: + + LightRefIdAdapter (const LightColumns& columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + class MiscRefIdAdapter : public InventoryRefIdAdapter + { + const RefIdColumn *mKey; + + public: + + MiscRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *key); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + struct NpcColumns : public ActorColumns + { + std::map mFlags; + const RefIdColumn *mRace; + const RefIdColumn *mClass; + const RefIdColumn *mFaction; + const RefIdColumn *mHair; + const RefIdColumn *mHead; + + NpcColumns (const ActorColumns& actorColumns); + }; + + class NpcRefIdAdapter : public ActorRefIdAdapter + { + NpcColumns mColumns; + + public: + + NpcRefIdAdapter (const NpcColumns& columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; + + struct WeaponColumns : public EnchantableColumns + { + const RefIdColumn *mType; + const RefIdColumn *mHealth; + const RefIdColumn *mSpeed; + const RefIdColumn *mReach; + const RefIdColumn *mChop[2]; + const RefIdColumn *mSlash[2]; + const RefIdColumn *mThrust[2]; + std::map mFlags; + + WeaponColumns (const EnchantableColumns& columns); + }; + + class WeaponRefIdAdapter : public EnchantableRefIdAdapter + { + WeaponColumns mColumns; + + public: + + WeaponRefIdAdapter (const WeaponColumns& columns); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) + const; + + virtual void setData (const RefIdColumn *column, RefIdData& data, int index, + const QVariant& value) const; + ///< If the data type does not match an exception is thrown. + }; +} + +#endif diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp new file mode 100644 index 000000000..e11fe0fc8 --- /dev/null +++ b/apps/opencs/model/world/refidcollection.cpp @@ -0,0 +1,540 @@ + +#include "refidcollection.hpp" + +#include + +#include "refidadapter.hpp" +#include "refidadapterimp.hpp" + +CSMWorld::RefIdColumn::RefIdColumn (const std::string& title, Display displayType, int flag, + bool editable, bool userEditable) +: ColumnBase (title, displayType, flag), mEditable (editable), mUserEditable (userEditable) +{} + +bool CSMWorld::RefIdColumn::isEditable() const +{ + return mEditable; +} + +bool CSMWorld::RefIdColumn::isUserEditable() const +{ + return mUserEditable; +} + + +const CSMWorld::RefIdAdapter& CSMWorld::RefIdCollection::findAdaptor (UniversalId::Type type) const +{ + std::map::const_iterator iter = mAdapters.find (type); + + if (iter==mAdapters.end()) + throw std::logic_error ("unsupported type in RefIdCollection"); + + return *iter->second; +} + +CSMWorld::RefIdCollection::RefIdCollection() +{ + BaseColumns baseColumns; + + mColumns.push_back (RefIdColumn ("ID", ColumnBase::Display_String, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); + baseColumns.mId = &mColumns.back(); + mColumns.push_back (RefIdColumn ("*", ColumnBase::Display_Integer, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); + baseColumns.mModified = &mColumns.back(); + mColumns.push_back (RefIdColumn ("Type", ColumnBase::Display_Integer, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); + baseColumns.mType = &mColumns.back(); + + ModelColumns modelColumns (baseColumns); + + mColumns.push_back (RefIdColumn ("Model", ColumnBase::Display_String)); + modelColumns.mModel = &mColumns.back(); + + NameColumns nameColumns (modelColumns); + + mColumns.push_back (RefIdColumn ("Name", ColumnBase::Display_String)); + nameColumns.mName = &mColumns.back(); + mColumns.push_back (RefIdColumn ("Script", ColumnBase::Display_String)); + nameColumns.mScript = &mColumns.back(); + + InventoryColumns inventoryColumns (nameColumns); + + mColumns.push_back (RefIdColumn ("Icon", ColumnBase::Display_String)); + inventoryColumns.mIcon = &mColumns.back(); + mColumns.push_back (RefIdColumn ("Weight", ColumnBase::Display_Float)); + inventoryColumns.mWeight = &mColumns.back(); + mColumns.push_back (RefIdColumn ("Value", ColumnBase::Display_Integer)); + inventoryColumns.mValue = &mColumns.back(); + + EnchantableColumns enchantableColumns (inventoryColumns); + + mColumns.push_back (RefIdColumn ("Enchantment", ColumnBase::Display_String)); + enchantableColumns.mEnchantment = &mColumns.back(); + mColumns.push_back (RefIdColumn ("Enchantment Points", ColumnBase::Display_Integer)); + enchantableColumns.mEnchantmentPoints = &mColumns.back(); + + ToolColumns toolsColumns (inventoryColumns); + + mColumns.push_back (RefIdColumn ("Quality", ColumnBase::Display_Float)); + toolsColumns.mQuality = &mColumns.back(); + mColumns.push_back (RefIdColumn ("Uses", ColumnBase::Display_Integer)); + toolsColumns.mUses = &mColumns.back(); + + ActorColumns actorsColumns (nameColumns); + + mColumns.push_back (RefIdColumn ("AI", ColumnBase::Display_Boolean)); + actorsColumns.mHasAi = &mColumns.back(); + mColumns.push_back (RefIdColumn ("AI Hello", ColumnBase::Display_Integer)); + actorsColumns.mHello = &mColumns.back(); + mColumns.push_back (RefIdColumn ("AI Flee", ColumnBase::Display_Integer)); + actorsColumns.mFlee = &mColumns.back(); + mColumns.push_back (RefIdColumn ("AI Fight", ColumnBase::Display_Integer)); + actorsColumns.mFight = &mColumns.back(); + mColumns.push_back (RefIdColumn ("AI Alarm", ColumnBase::Display_Integer)); + actorsColumns.mAlarm = &mColumns.back(); + + static const struct + { + const char *mName; + unsigned int mFlag; + } sServiceTable[] = + { + { "Buys Weapons", ESM::NPC::Weapon}, + { "Buys Armor", ESM::NPC::Armor}, + { "Buys Clothing", ESM::NPC::Clothing}, + { "Buys Books", ESM::NPC::Books}, + { "Buys Ingredients", ESM::NPC::Ingredients}, + { "Buys Lockpicks", ESM::NPC::Picks}, + { "Buys Probes", ESM::NPC::Probes}, + { "Buys Lights", ESM::NPC::Lights}, + { "Buys Apparati", ESM::NPC::Apparatus}, + { "Buys Repair Items", ESM::NPC::RepairItem}, + { "Buys Misc Items", ESM::NPC::Misc}, + { "Buys Potions", ESM::NPC::Potions}, + { "Buys Magic Items", ESM::NPC::MagicItems}, + { "Sells Spells", ESM::NPC::Spells}, + { "Trainer", ESM::NPC::Training}, + { "Spellmaking", ESM::NPC::Spellmaking}, + { "Enchanting Service", ESM::NPC::Enchanting}, + { "Repair Serivce", ESM::NPC::Repair}, + { 0, 0 } + }; + + for (int i=0; sServiceTable[i].mName; ++i) + { + mColumns.push_back (RefIdColumn (sServiceTable[i].mName, ColumnBase::Display_Boolean)); + actorsColumns.mServices.insert (std::make_pair (&mColumns.back(), sServiceTable[i].mFlag)); + } + + mColumns.push_back (RefIdColumn ("Auto Calc", ColumnBase::Display_Boolean)); + const RefIdColumn *autoCalc = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Apparatus Type", ColumnBase::Display_ApparatusType)); + const RefIdColumn *apparatusType = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Armor Type", ColumnBase::Display_ArmorType)); + const RefIdColumn *armorType = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Health", ColumnBase::Display_Integer)); + const RefIdColumn *health = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Armor Value", ColumnBase::Display_Integer)); + const RefIdColumn *armor = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Scroll", ColumnBase::Display_Boolean)); + const RefIdColumn *scroll = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Attribute", ColumnBase::Display_Attribute)); + const RefIdColumn *attribute = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Clothing Type", ColumnBase::Display_ClothingType)); + const RefIdColumn *clothingType = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Weight Capacity", ColumnBase::Display_Float)); + const RefIdColumn *weightCapacity = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Organic Container", ColumnBase::Display_Boolean)); + const RefIdColumn *organic = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Respawn", ColumnBase::Display_Boolean)); + const RefIdColumn *respawn = &mColumns.back(); + + CreatureColumns creatureColumns (actorsColumns); + + mColumns.push_back (RefIdColumn ("Creature Type", ColumnBase::Display_CreatureType)); + creatureColumns.mType = &mColumns.back(); + mColumns.push_back (RefIdColumn ("Soul Points", ColumnBase::Display_Integer)); + creatureColumns.mSoul = &mColumns.back(); + mColumns.push_back (RefIdColumn ("Scale", ColumnBase::Display_Float)); + creatureColumns.mScale = &mColumns.back(); + mColumns.push_back (RefIdColumn ("Original Creature", ColumnBase::Display_String)); + creatureColumns.mOriginal = &mColumns.back(); + + static const struct + { + const char *mName; + unsigned int mFlag; + } sCreatureFlagTable[] = + { + { "Biped", ESM::Creature::Biped }, + { "Has Weapon", ESM::Creature::Weapon }, + { "No Movement", ESM::Creature::None }, + { "Swims", ESM::Creature::Swims }, + { "Flies", ESM::Creature::Flies }, + { "Walks", ESM::Creature::Walks }, + { "Essential", ESM::Creature::Essential }, + { "Skeleton Blood", ESM::Creature::Skeleton }, + { "Metal Blood", ESM::Creature::Metal }, + { 0, 0 } + }; + + // for re-use in NPC records + const RefIdColumn *essential = 0; + const RefIdColumn *skeletonBlood = 0; + const RefIdColumn *metalBlood = 0; + + for (int i=0; sCreatureFlagTable[i].mName; ++i) + { + mColumns.push_back (RefIdColumn (sCreatureFlagTable[i].mName, ColumnBase::Display_Boolean)); + creatureColumns.mFlags.insert (std::make_pair (&mColumns.back(), sCreatureFlagTable[i].mFlag)); + + switch (sCreatureFlagTable[i].mFlag) + { + case ESM::Creature::Essential: essential = &mColumns.back(); break; + case ESM::Creature::Skeleton: skeletonBlood = &mColumns.back(); break; + case ESM::Creature::Metal: metalBlood = &mColumns.back(); break; + } + } + + creatureColumns.mFlags.insert (std::make_pair (respawn, ESM::Creature::Respawn)); + + mColumns.push_back (RefIdColumn ("Open Sound", ColumnBase::Display_String)); + const RefIdColumn *openSound = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Close Sound", ColumnBase::Display_String)); + const RefIdColumn *closeSound = &mColumns.back(); + + LightColumns lightColumns (inventoryColumns); + + mColumns.push_back (RefIdColumn ("Duration", ColumnBase::Display_Integer)); + lightColumns.mTime = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Radius", ColumnBase::Display_Integer)); + lightColumns.mRadius = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Colour", ColumnBase::Display_Integer)); + lightColumns.mColor = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Sound", ColumnBase::Display_String)); + lightColumns.mSound = &mColumns.back(); + + static const struct + { + const char *mName; + unsigned int mFlag; + } sLightFlagTable[] = + { + { "Dynamic", ESM::Light::Dynamic }, + { "Portable", ESM::Light::Carry }, + { "Negative Light", ESM::Light::Negative }, + { "Flickering", ESM::Light::Flicker }, + { "Slow Flickering", ESM::Light::Flicker }, + { "Pulsing", ESM::Light::Pulse }, + { "Slow Pulsing", ESM::Light::PulseSlow }, + { "Fire", ESM::Light::Fire }, + { "Off by default", ESM::Light::OffDefault }, + { 0, 0 } + }; + + for (int i=0; sLightFlagTable[i].mName; ++i) + { + mColumns.push_back (RefIdColumn (sLightFlagTable[i].mName, ColumnBase::Display_Boolean)); + lightColumns.mFlags.insert (std::make_pair (&mColumns.back(), sLightFlagTable[i].mFlag)); + } + + mColumns.push_back (RefIdColumn ("Key", ColumnBase::Display_Boolean)); + const RefIdColumn *key = &mColumns.back(); + + NpcColumns npcColumns (actorsColumns); + + mColumns.push_back (RefIdColumn ("Race", ColumnBase::Display_String)); + npcColumns.mRace = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Class", ColumnBase::Display_String)); + npcColumns.mClass = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Faction", ColumnBase::Display_String)); + npcColumns.mFaction = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Hair", ColumnBase::Display_String)); + npcColumns.mHair = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Head", ColumnBase::Display_String)); + npcColumns.mHead = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Female", ColumnBase::Display_Boolean)); + npcColumns.mFlags.insert (std::make_pair (&mColumns.back(), ESM::NPC::Female)); + + npcColumns.mFlags.insert (std::make_pair (essential, ESM::NPC::Essential)); + + npcColumns.mFlags.insert (std::make_pair (respawn, ESM::NPC::Respawn)); + + npcColumns.mFlags.insert (std::make_pair (autoCalc, ESM::NPC::Autocalc)); + + npcColumns.mFlags.insert (std::make_pair (skeletonBlood, ESM::NPC::Skeleton)); + + npcColumns.mFlags.insert (std::make_pair (metalBlood, ESM::NPC::Metal)); + + WeaponColumns weaponColumns (enchantableColumns); + + mColumns.push_back (RefIdColumn ("Weapon Type", ColumnBase::Display_WeaponType)); + weaponColumns.mType = &mColumns.back(); + + weaponColumns.mHealth = health; + + mColumns.push_back (RefIdColumn ("Weapon Speed", ColumnBase::Display_Float)); + weaponColumns.mSpeed = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Weapon Reach", ColumnBase::Display_Float)); + weaponColumns.mReach = &mColumns.back(); + + for (int i=0; i<2; ++i) + { + std::string suffix = i==0 ? "Min " : "Max "; + + mColumns.push_back (RefIdColumn ("Chop" + suffix, ColumnBase::Display_Integer)); + weaponColumns.mChop[i] = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Slash" + suffix, ColumnBase::Display_Integer)); + weaponColumns.mSlash[i] = &mColumns.back(); + + mColumns.push_back (RefIdColumn ("Thrust" + suffix, ColumnBase::Display_Integer)); + weaponColumns.mThrust[i] = &mColumns.back(); + } + + static const struct + { + const char *mName; + unsigned int mFlag; + } sWeaponFlagTable[] = + { + { "Magical", ESM::Weapon::Magical }, + { "Silver", ESM::Weapon::Silver }, + { 0, 0 } + }; + + for (int i=0; sWeaponFlagTable[i].mName; ++i) + { + mColumns.push_back (RefIdColumn (sWeaponFlagTable[i].mName, ColumnBase::Display_Boolean)); + weaponColumns.mFlags.insert (std::make_pair (&mColumns.back(), sWeaponFlagTable[i].mFlag)); + } + + mAdapters.insert (std::make_pair (UniversalId::Type_Activator, + new NameRefIdAdapter (UniversalId::Type_Activator, nameColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_Potion, + new PotionRefIdAdapter (inventoryColumns, autoCalc))); + mAdapters.insert (std::make_pair (UniversalId::Type_Apparatus, + new ApparatusRefIdAdapter (inventoryColumns, apparatusType, toolsColumns.mQuality))); + mAdapters.insert (std::make_pair (UniversalId::Type_Armor, + new ArmorRefIdAdapter (enchantableColumns, armorType, health, armor))); + mAdapters.insert (std::make_pair (UniversalId::Type_Book, + new BookRefIdAdapter (enchantableColumns, scroll, attribute))); + mAdapters.insert (std::make_pair (UniversalId::Type_Clothing, + new ClothingRefIdAdapter (enchantableColumns, clothingType))); + mAdapters.insert (std::make_pair (UniversalId::Type_Container, + new ContainerRefIdAdapter (nameColumns, weightCapacity, organic, respawn))); + mAdapters.insert (std::make_pair (UniversalId::Type_Creature, + new CreatureRefIdAdapter (creatureColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_Door, + new DoorRefIdAdapter (nameColumns, openSound, closeSound))); + mAdapters.insert (std::make_pair (UniversalId::Type_Ingredient, + new InventoryRefIdAdapter (UniversalId::Type_Ingredient, inventoryColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_CreatureLevelledList, + new BaseRefIdAdapter ( + UniversalId::Type_CreatureLevelledList, baseColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_ItemLevelledList, + new BaseRefIdAdapter (UniversalId::Type_ItemLevelledList, baseColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_Light, + new LightRefIdAdapter (lightColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_Lockpick, + new ToolRefIdAdapter (UniversalId::Type_Lockpick, toolsColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_Miscellaneous, + new MiscRefIdAdapter (inventoryColumns, key))); + mAdapters.insert (std::make_pair (UniversalId::Type_Npc, + new NpcRefIdAdapter (npcColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_Probe, + new ToolRefIdAdapter (UniversalId::Type_Probe, toolsColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_Repair, + new ToolRefIdAdapter (UniversalId::Type_Repair, toolsColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_Static, + new ModelRefIdAdapter (UniversalId::Type_Static, modelColumns))); + mAdapters.insert (std::make_pair (UniversalId::Type_Weapon, + new WeaponRefIdAdapter (weaponColumns))); +} + +CSMWorld::RefIdCollection::~RefIdCollection() +{ + for (std::map::iterator iter (mAdapters.begin()); + iter!=mAdapters.end(); ++iter) + delete iter->second; +} + +int CSMWorld::RefIdCollection::getSize() const +{ + return mData.getSize(); +} + +std::string CSMWorld::RefIdCollection::getId (int index) const +{ + return getData (index, 0).toString().toUtf8().constData(); +} + +int CSMWorld::RefIdCollection::getIndex (const std::string& id) const +{ + int index = searchId (id); + + if (index==-1) + throw std::runtime_error ("invalid ID: " + id); + + return index; +} + +int CSMWorld::RefIdCollection::getColumns() const +{ + return mColumns.size(); +} + +const CSMWorld::ColumnBase& CSMWorld::RefIdCollection::getColumn (int column) const +{ + return mColumns.at (column); +} + +QVariant CSMWorld::RefIdCollection::getData (int index, int column) const +{ + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); + + const RefIdAdapter& adaptor = findAdaptor (localIndex.second); + + return adaptor.getData (&mColumns.at (column), mData, localIndex.first); +} + +void CSMWorld::RefIdCollection::setData (int index, int column, const QVariant& data) +{ + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); + + const RefIdAdapter& adaptor = findAdaptor (localIndex.second); + + adaptor.setData (&mColumns.at (column), mData, localIndex.first, data); +} + +void CSMWorld::RefIdCollection::removeRows (int index, int count) +{ + mData.erase (index, count); +} + +void CSMWorld::RefIdCollection::appendBlankRecord (const std::string& id, UniversalId::Type type) +{ + mData.appendRecord (type, id); +} + +int CSMWorld::RefIdCollection::searchId (const std::string& id) const +{ + RefIdData::LocalIndex localIndex = mData.searchId (id); + + if (localIndex.first==-1) + return -1; + + return mData.localToGlobalIndex (localIndex); +} + +void CSMWorld::RefIdCollection::replace (int index, const RecordBase& record) +{ + mData.getRecord (mData.globalToLocalIndex (index)).assign (record); +} + +void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, + UniversalId::Type type) +{ + std::string id = findAdaptor (type).getId (record); + + int index = mData.getAppendIndex (type); + + mData.appendRecord (type, id); + + mData.getRecord (mData.globalToLocalIndex (index)).assign (record); +} + +const CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (const std::string& id) const +{ + return mData.getRecord (mData.searchId (id)); +} + +const CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (int index) const +{ + return mData.getRecord (mData.globalToLocalIndex (index)); +} + +void CSMWorld::RefIdCollection::load (ESM::ESMReader& reader, bool base, UniversalId::Type type) +{ + std::string id = reader.getHNOString ("NAME"); + + int index = searchId (id); + + if (reader.isNextSub ("DELE")) + { + reader.skipRecord(); + + if (index==-1) + { + // deleting a record that does not exist + + // ignore it for now + + /// \todo report the problem to the user + } + else if (base) + { + mData.erase (index, 1); + } + else + { + mData.getRecord (mData.globalToLocalIndex (index)).mState = RecordBase::State_Deleted; + } + } + else + { + if (index==-1) + { + // new record + int index = mData.getAppendIndex (type); + mData.appendRecord (type, id); + + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); + + mData.load (localIndex, reader, base); + + mData.getRecord (localIndex).mState = + base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; + } + else + { + // old record + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); + + if (!base) + if (mData.getRecord (localIndex).mState==RecordBase::State_Erased) + throw std::logic_error ("attempt to access a deleted record"); + + mData.load (localIndex, reader, base); + + if (!base) + mData.getRecord (localIndex).mState = RecordBase::State_Modified; + } + } +} + +int CSMWorld::RefIdCollection::getAppendIndex (UniversalId::Type type) const +{ + return mData.getAppendIndex (type); +} \ No newline at end of file diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp new file mode 100644 index 000000000..dc86c128c --- /dev/null +++ b/apps/opencs/model/world/refidcollection.hpp @@ -0,0 +1,95 @@ +#ifndef CSM_WOLRD_REFIDCOLLECTION_H +#define CSM_WOLRD_REFIDCOLLECTION_H + +#include +#include +#include + +#include "columnbase.hpp" +#include "idcollection.hpp" +#include "refiddata.hpp" + +namespace CSMWorld +{ + class RefIdAdapter; + + class RefIdColumn : public ColumnBase + { + bool mEditable; + bool mUserEditable; + + public: + + RefIdColumn (const std::string& title, Display displayType, + int flag = Flag_Table | Flag_Dialogue, bool editable = true, + bool userEditable = true); + + virtual bool isEditable() const; + + virtual bool isUserEditable() const; + }; + + class RefIdCollection : public IdCollectionBase + { + private: + + RefIdData mData; + std::deque mColumns; + std::map mAdapters; + + private: + + const RefIdAdapter& findAdaptor (UniversalId::Type) const; + ///< Throws an exception if no adaptor for \a Type can be found. + + public: + + RefIdCollection(); + + virtual ~RefIdCollection(); + + virtual int getSize() const; + + virtual std::string getId (int index) const; + + virtual int getIndex (const std::string& id) const; + + virtual int getColumns() const; + + virtual const ColumnBase& getColumn (int column) const; + + virtual QVariant getData (int index, int column) const; + + virtual void setData (int index, int column, const QVariant& data); + + virtual void removeRows (int index, int count); + + 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; + ////< Search record with \a id. + /// \return index of record (if found) or -1 (not found) + + 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); + ///< 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 (int index) const; + + virtual void load (ESM::ESMReader& reader, bool base, UniversalId::Type type); + + virtual int getAppendIndex (UniversalId::Type type) const; + ///< \param type Will be ignored, unless the collection supports multiple record types + }; +} + +#endif diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp new file mode 100644 index 000000000..c95db045f --- /dev/null +++ b/apps/opencs/model/world/refiddata.cpp @@ -0,0 +1,198 @@ + +#include "refiddata.hpp" + +#include + +#include + +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)); +} + +CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::globalToLocalIndex (int index) const +{ + for (std::map::const_iterator iter ( + mRecordContainers.begin()); iter!=mRecordContainers.end(); ++iter) + { + if (indexsecond->getSize()) + return LocalIndex (index, iter->first); + + index -= iter->second->getSize(); + } + + throw std::runtime_error ("RefIdData index out of range"); +} + +int CSMWorld::RefIdData::localToGlobalIndex (const LocalIndex& index) + const +{ + std::map::const_iterator end = + mRecordContainers.find (index.second); + + 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) + globalIndex += iter->second->getSize(); + + return globalIndex; +} + +CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::searchId ( + const std::string& id) const +{ + std::string id2 = Misc::StringUtils::lowerCase (id); + + std::map >::const_iterator iter = mIndex.find (id2); + + if (iter==mIndex.end()) + return std::make_pair (-1, CSMWorld::UniversalId::Type_None); + + return iter->second; +} + +void CSMWorld::RefIdData::erase (int index, int count) +{ + LocalIndex localIndex = globalToLocalIndex (index); + + std::map::const_iterator iter = + mRecordContainers.find (localIndex.second); + + while (count>0 && iter!=mRecordContainers.end()) + { + int size = iter->second->getSize(); + + if (localIndex.first+count>size) + { + erase (localIndex, size-localIndex.first); + count -= size-localIndex.first; + + ++iter; + + 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); + count = 0; + } + } +} + +const CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord (const LocalIndex& index) const +{ + std::map::const_iterator iter = + mRecordContainers.find (index.second); + + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); + + return iter->second->getRecord (index.first); +} + +CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord (const LocalIndex& index) +{ + std::map::iterator iter = + mRecordContainers.find (index.second); + + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); + + return iter->second->getRecord (index.first); +} + +void CSMWorld::RefIdData::appendRecord (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->appendRecord (id); + + mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), + LocalIndex (iter->second->getSize()-1, type))); +} + +int CSMWorld::RefIdData::getAppendIndex (UniversalId::Type type) const +{ + int index = 0; + + for (std::map::const_iterator iter ( + mRecordContainers.begin()); iter!=mRecordContainers.end(); ++iter) + { + index += iter->second->getSize(); + + if (type==iter->first) + break; + } + + return index; +} + +void CSMWorld::RefIdData::load (const LocalIndex& index, ESM::ESMReader& reader, bool base) +{ + std::map::iterator iter = + mRecordContainers.find (index.second); + + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); + + iter->second->load (index.first, reader, base); +} + +void CSMWorld::RefIdData::erase (const LocalIndex& index, int count) +{ + std::map::iterator iter = + mRecordContainers.find (index.second); + + 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))); + + if (result!=mIndex.end()) + mIndex.erase (result); + } + + iter->second->erase (index.first, count); +} + +int CSMWorld::RefIdData::getSize() const +{ + return mIndex.size(); +} diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp new file mode 100644 index 000000000..475566fb5 --- /dev/null +++ b/apps/opencs/model/world/refiddata.hpp @@ -0,0 +1,188 @@ +#ifndef CSM_WOLRD_REFIDDATA_H +#define CSM_WOLRD_REFIDDATA_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "record.hpp" +#include "universalid.hpp" + +namespace ESM +{ + class ESMReader; +} + +namespace CSMWorld +{ + struct RefIdDataContainerBase + { + virtual ~RefIdDataContainerBase(); + + virtual int getSize() const = 0; + + virtual const RecordBase& getRecord (int index) const = 0; + + virtual RecordBase& getRecord (int index)= 0; + + virtual void appendRecord (const std::string& id) = 0; + + virtual void load (int index, ESM::ESMReader& reader, bool base) = 0; + + virtual void erase (int index, int count) = 0; + + virtual std::string getId (int index) const = 0; + }; + + template + struct RefIdDataContainer : public RefIdDataContainerBase + { + std::vector > mContainer; + + virtual int getSize() const; + + virtual const RecordBase& getRecord (int index) const; + + virtual RecordBase& getRecord (int index); + + virtual void appendRecord (const std::string& id); + + virtual void load (int index, ESM::ESMReader& reader, bool base); + + virtual void erase (int index, int count); + + virtual std::string getId (int index) const; + }; + + template + int RefIdDataContainer::getSize() const + { + return static_cast (mContainer.size()); + } + + template + const RecordBase& RefIdDataContainer::getRecord (int index) const + { + return mContainer.at (index); + } + + template + RecordBase& RefIdDataContainer::getRecord (int index) + { + return mContainer.at (index); + } + + template + 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); + } + + template + void RefIdDataContainer::load (int index, ESM::ESMReader& reader, bool base) + { + (base ? mContainer.at (index).mBase : mContainer.at (index).mModified).load (reader); + } + + template + void RefIdDataContainer::erase (int index, int count) + { + if (index<0 || index+count>=getSize()) + throw std::runtime_error ("invalid RefIdDataContainer index"); + + mContainer.erase (mContainer.begin()+index, mContainer.begin()+index+count); + } + + template + std::string RefIdDataContainer::getId (int index) const + { + return mContainer.at (index).get().mId; + } + + class RefIdData + { + public: + + typedef std::pair LocalIndex; + + private: + + RefIdDataContainer mActivators; + RefIdDataContainer mPotions; + RefIdDataContainer mApparati; + RefIdDataContainer mArmors; + RefIdDataContainer mBooks; + RefIdDataContainer mClothing; + RefIdDataContainer mContainers; + RefIdDataContainer mCreatures; + RefIdDataContainer mDoors; + RefIdDataContainer mIngredients; + RefIdDataContainer mCreatureLevelledLists; + RefIdDataContainer mItemLevelledLists; + RefIdDataContainer mLights; + RefIdDataContainer mLockpicks; + RefIdDataContainer mMiscellaneous; + RefIdDataContainer mNpcs; + RefIdDataContainer mProbes; + RefIdDataContainer mRepairs; + RefIdDataContainer mStatics; + RefIdDataContainer mWeapons; + + std::map mIndex; + + std::map mRecordContainers; + + void erase (const LocalIndex& index, int count); + ///< Must not spill over into another type. + + public: + + RefIdData(); + + LocalIndex globalToLocalIndex (int index) const; + + int localToGlobalIndex (const LocalIndex& index) const; + + LocalIndex searchId (const std::string& id) const; + + void erase (int index, int count); + + const RecordBase& getRecord (const LocalIndex& index) const; + + RecordBase& getRecord (const LocalIndex& index); + + void appendRecord (UniversalId::Type type, const std::string& id); + + int getAppendIndex (UniversalId::Type type) const; + + void load (const LocalIndex& index, ESM::ESMReader& reader, bool base); + + int getSize() const; + }; +} + +#endif diff --git a/apps/opencs/model/world/scriptcontext.cpp b/apps/opencs/model/world/scriptcontext.cpp new file mode 100644 index 000000000..69b72abf2 --- /dev/null +++ b/apps/opencs/model/world/scriptcontext.cpp @@ -0,0 +1,22 @@ + +#include "scriptcontext.hpp" + +bool CSMWorld::ScriptContext::canDeclareLocals() const +{ + return false; +} + +char CSMWorld::ScriptContext::getGlobalType (const std::string& name) const +{ + return ' '; +} + +char CSMWorld::ScriptContext::getMemberType (const std::string& name, const std::string& id) const +{ + return ' '; +} + +bool CSMWorld::ScriptContext::isId (const std::string& name) const +{ + return false; +} \ No newline at end of file diff --git a/apps/opencs/model/world/scriptcontext.hpp b/apps/opencs/model/world/scriptcontext.hpp new file mode 100644 index 000000000..1231aea64 --- /dev/null +++ b/apps/opencs/model/world/scriptcontext.hpp @@ -0,0 +1,26 @@ +#ifndef CSM_WORLD_SCRIPTCONTEXT_H +#define CSM_WORLD_SCRIPTCONTEXT_H + +#include + +namespace CSMWorld +{ + class ScriptContext : public Compiler::Context + { + public: + + virtual bool canDeclareLocals() const; + ///< Is the compiler allowed to declare local variables? + + 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 bool isId (const std::string& name) const; + ///< Does \a name match an ID, that can be referenced? + }; +} + +#endif diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index d8775643a..bd1632e3e 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -18,6 +18,19 @@ namespace { { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "empty" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, + "Referenceables" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker }; @@ -25,6 +38,41 @@ namespace static const TypeData sIdArg[] = { { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Skill, "Skill" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Class, "Class" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Faction, "Faction" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Race, "Race" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Sound, "Sound" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Script, "Script" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Activator, "Activator" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Potion, "Potion" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Apparatus, "Apparatus" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Armor, "Armor" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Book, "Book" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Clothing, "Clothing" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Container, "Container" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Creature, "Creature" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Door, "Door" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Ingredient, "Ingredient" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_CreatureLevelledList, + "Creature Levelled List" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_ItemLevelledList, + "Item Levelled List" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Light, "Light" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Lockpick, "Lockpick" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Miscellaneous, + "Miscellaneous" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Npc, "NPC" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Probe, "Probe" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Repair, "Repair" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Static, "Static" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Weapon, "Weapon" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker }; @@ -41,48 +89,45 @@ CSMWorld::UniversalId::UniversalId (const std::string& universalId) { std::string::size_type index = universalId.find (':'); - if (index==std::string::npos) + if (index!=std::string::npos) { std::string type = universalId.substr (0, index); - if (index==std::string::npos) - { - for (int i=0; sNoArg[i].mName; ++i) - if (type==sNoArg[i].mName) - { - mArgumentType = ArgumentType_None; - mType = sNoArg[i].mType; - mClass = sNoArg[i].mClass; + for (int i=0; sIdArg[i].mName; ++i) + if (type==sIdArg[i].mName) + { + mArgumentType = ArgumentType_Id; + mType = sIdArg[i].mType; + mClass = sIdArg[i].mClass; + mId = universalId.substr (index+2); + return; + } + + for (int i=0; sIndexArg[i].mName; ++i) + if (type==sIndexArg[i].mName) + { + mArgumentType = ArgumentType_Index; + mType = sIndexArg[i].mType; + mClass = sIndexArg[i].mClass; + + std::istringstream stream (universalId.substr (index+2)); + + if (stream >> mIndex) return; - } - } - else - { - for (int i=0; sIdArg[i].mName; ++i) - if (type==sIdArg[i].mName) - { - mArgumentType = ArgumentType_Id; - mType = sIdArg[i].mType; - mClass = sIdArg[i].mClass; - mId = universalId.substr (0, index); - return; - } - - for (int i=0; sIndexArg[i].mName; ++i) - if (type==sIndexArg[i].mName) - { - mArgumentType = ArgumentType_Index; - mType = sIndexArg[i].mType; - mClass = sIndexArg[i].mClass; - std::istringstream stream (universalId.substr (0, index)); - - if (stream >> mIndex) - return; - - break; - } - } + break; + } + } + else + { + for (int i=0; sNoArg[i].mName; ++i) + if (universalId==sNoArg[i].mName) + { + mArgumentType = ArgumentType_None; + mType = sNoArg[i].mType; + mClass = sNoArg[i].mClass; + return; + } } throw std::runtime_error ("invalid UniversalId: " + universalId); diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index 4a73feb12..2c4b14eaf 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -33,12 +33,54 @@ namespace CSMWorld enum Type { Type_None, - Type_Globals, - Type_Global, + Type_VerificationResults, + Type_Gmsts, + Type_Gmst, + Type_Skills, + Type_Skill, + Type_Classes, + Type_Class, + Type_Factions, + Type_Faction, + Type_Races, + Type_Race, + Type_Sounds, + Type_Sound, + Type_Scripts, + Type_Script, + Type_Regions, + Type_Region, + Type_Birthsigns, + Type_Birthsign, + Type_Spells, + Type_Spell, + Type_Cells, + Type_Cell, + Type_Referenceables, + Type_Referenceable, + Type_Activator, + Type_Potion, + Type_Apparatus, + Type_Armor, + Type_Book, + Type_Clothing, + Type_Container, + Type_Creature, + Type_Door, + Type_Ingredient, + Type_CreatureLevelledList, + Type_ItemLevelledList, + Type_Light, + Type_Lockpick, + Type_Miscellaneous, + Type_Npc, + Type_Probe, + Type_Repair, + Type_Static, + Type_Weapon - Type_VerificationResults }; private: diff --git a/apps/opencs/ocspropertywidget.cpp b/apps/opencs/ocspropertywidget.cpp new file mode 100644 index 000000000..68315201a --- /dev/null +++ b/apps/opencs/ocspropertywidget.cpp @@ -0,0 +1,6 @@ +#include "ocspropertywidget.hpp" + +OcsPropertyWidget::OcsPropertyWidget(QObject *parent) : + QObject(parent) +{ +} diff --git a/apps/opencs/ocspropertywidget.hpp b/apps/opencs/ocspropertywidget.hpp new file mode 100644 index 000000000..fc64a0a69 --- /dev/null +++ b/apps/opencs/ocspropertywidget.hpp @@ -0,0 +1,18 @@ +#ifndef OCSPROPERTYWIDGET_HPP +#define OCSPROPERTYWIDGET_HPP + +#include + +class OcsPropertyWidget : public QObject +{ + Q_OBJECT +public: + explicit OcsPropertyWidget(QObject *parent = 0); + +signals: + +public slots: + +}; + +#endif // OCSPROPERTYWIDGET_HPP diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp new file mode 100644 index 000000000..f956317a7 --- /dev/null +++ b/apps/opencs/view/doc/filedialog.cpp @@ -0,0 +1,272 @@ +#include "filedialog.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +FileDialog::FileDialog(QWidget *parent) : + QDialog(parent) +{ + setupUi(this); + + // Models + mDataFilesModel = new DataFilesModel(this); + + mMastersProxyModel = new QSortFilterProxyModel(); + mMastersProxyModel->setFilterRegExp(QString("^.*\\.esm")); + mMastersProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + mMastersProxyModel->setSourceModel(mDataFilesModel); + + mPluginsProxyModel = new PluginsProxyModel(); + mPluginsProxyModel->setFilterRegExp(QString("^.*\\.esp")); + mPluginsProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + mPluginsProxyModel->setSourceModel(mDataFilesModel); + + mFilterProxyModel = new QSortFilterProxyModel(); + mFilterProxyModel->setDynamicSortFilter(true); + mFilterProxyModel->setSourceModel(mPluginsProxyModel); + + QCheckBox checkBox; + unsigned int height = checkBox.sizeHint().height() + 4; + + mastersTable->setModel(mMastersProxyModel); + mastersTable->setObjectName("MastersTable"); + mastersTable->setContextMenuPolicy(Qt::CustomContextMenu); + mastersTable->setSortingEnabled(false); + mastersTable->setSelectionBehavior(QAbstractItemView::SelectRows); + mastersTable->setSelectionMode(QAbstractItemView::ExtendedSelection); + mastersTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + mastersTable->setAlternatingRowColors(true); + mastersTable->horizontalHeader()->setStretchLastSection(true); + + // Set the row height to the size of the checkboxes + mastersTable->verticalHeader()->setDefaultSectionSize(height); + mastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); + mastersTable->verticalHeader()->hide(); + + pluginsTable->setModel(mFilterProxyModel); + pluginsTable->setObjectName("PluginsTable"); + pluginsTable->setContextMenuPolicy(Qt::CustomContextMenu); + pluginsTable->setSortingEnabled(false); + pluginsTable->setSelectionBehavior(QAbstractItemView::SelectRows); + pluginsTable->setSelectionMode(QAbstractItemView::ExtendedSelection); + pluginsTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + pluginsTable->setAlternatingRowColors(true); + pluginsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem); + pluginsTable->horizontalHeader()->setStretchLastSection(true); + + pluginsTable->verticalHeader()->setDefaultSectionSize(height); + pluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); + + // Hide the profile elements + profileLabel->hide(); + profilesComboBox->hide(); + newProfileButton->hide(); + deleteProfileButton->hide(); + + // Add some extra widgets + QHBoxLayout *nameLayout = new QHBoxLayout(); + QSpacerItem *spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + + mNameLabel = new QLabel(tr("File Name:"), this); + + QRegExpValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9\\s]*$")); + mNameLineEdit = new LineEdit(this); + mNameLineEdit->setValidator(validator); + + nameLayout->addSpacerItem(spacer); + nameLayout->addWidget(mNameLabel); + nameLayout->addWidget(mNameLineEdit); + + mButtonBox = new QDialogButtonBox(this); + + mCreateButton = new QPushButton(tr("Create"), this); + mCreateButton->setEnabled(false); + + verticalLayout->addLayout(nameLayout); + verticalLayout->addWidget(mButtonBox); + + // Set sizes + QList sizeList; + sizeList << 175; + sizeList << 200; + + splitter->setSizes(sizeList); + + resize(600, 400); + + connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); + connect(mDataFilesModel, SIGNAL(checkedItemsChanged(QStringList)), this, SLOT(updateOpenButton(QStringList))); + connect(mNameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(updateCreateButton(QString))); + + connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); + + connect(pluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); + connect(mastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); + + connect(mCreateButton, SIGNAL(clicked()), this, SLOT(createButtonClicked())); + + connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject())); +} + +void FileDialog::updateViews() +{ + // Ensure the columns are hidden because sort() re-enables them + mastersTable->setColumnHidden(1, true); + mastersTable->setColumnHidden(3, true); + mastersTable->setColumnHidden(4, true); + mastersTable->setColumnHidden(5, true); + mastersTable->setColumnHidden(6, true); + mastersTable->setColumnHidden(7, true); + mastersTable->setColumnHidden(8, true); + mastersTable->resizeColumnsToContents(); + + pluginsTable->setColumnHidden(1, true); + pluginsTable->setColumnHidden(3, true); + pluginsTable->setColumnHidden(4, true); + pluginsTable->setColumnHidden(5, true); + pluginsTable->setColumnHidden(6, true); + pluginsTable->setColumnHidden(7, true); + pluginsTable->setColumnHidden(8, true); + pluginsTable->resizeColumnsToContents(); + +} + +void FileDialog::updateOpenButton(const QStringList &items) +{ + QPushButton *openButton = mButtonBox->button(QDialogButtonBox::Open); + + if (!openButton) + return; + + openButton->setEnabled(!items.isEmpty()); +} + +void FileDialog::updateCreateButton(const QString &name) +{ + if (!mCreateButton->isVisible()) + return; + + mCreateButton->setEnabled(!name.isEmpty()); +} + +void FileDialog::filterChanged(const QString &filter) +{ + QRegExp filterRe(filter, Qt::CaseInsensitive, QRegExp::FixedString); + mFilterProxyModel->setFilterRegExp(filterRe); +} + +void FileDialog::addFiles(const QString &path) +{ + mDataFilesModel->addFiles(path); + mDataFilesModel->sort(3); // Sort by date accessed +} + +void FileDialog::setEncoding(const QString &encoding) +{ + mDataFilesModel->setEncoding(encoding); +} + +void FileDialog::setCheckState(QModelIndex index) +{ + if (!index.isValid()) + return; + + QObject *object = QObject::sender(); + + // Not a signal-slot call + if (!object) + return; + + + if (object->objectName() == QLatin1String("PluginsTable")) { + QModelIndex sourceIndex = mPluginsProxyModel->mapToSource( + mFilterProxyModel->mapToSource(index)); + + if (sourceIndex.isValid()) { + (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) + ? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked) + : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); + } + } + + if (object->objectName() == QLatin1String("MastersTable")) { + QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index); + + if (sourceIndex.isValid()) { + (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) + ? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked) + : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); + } + } + + return; +} + +QStringList FileDialog::checkedItemsPaths() +{ + return mDataFilesModel->checkedItemsPaths(); +} + +QString FileDialog::fileName() +{ + return mNameLineEdit->text(); +} + +void FileDialog::openFile() +{ + setWindowTitle(tr("Open")); + + mNameLabel->hide(); + mNameLineEdit->hide(); + mCreateButton->hide(); + + mButtonBox->removeButton(mCreateButton); + mButtonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Open); + QPushButton *openButton = mButtonBox->button(QDialogButtonBox::Open); + openButton->setEnabled(false); + + show(); + raise(); + activateWindow(); +} + +void FileDialog::newFile() +{ + setWindowTitle(tr("New")); + + mNameLabel->show(); + mNameLineEdit->clear(); + mNameLineEdit->show(); + mCreateButton->show(); + + mButtonBox->setStandardButtons(QDialogButtonBox::Cancel); + mButtonBox->addButton(mCreateButton, QDialogButtonBox::ActionRole); + + show(); + raise(); + activateWindow(); +} + +void FileDialog::accept() +{ + emit openFiles(); +} + +void FileDialog::createButtonClicked() +{ + emit createNewFile(); +} diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp new file mode 100644 index 000000000..b21618d5d --- /dev/null +++ b/apps/opencs/view/doc/filedialog.hpp @@ -0,0 +1,66 @@ +#ifndef FILEDIALOG_HPP +#define FILEDIALOG_HPP + +#include +#include + +#include "ui_datafilespage.h" + +class QDialogButtonBox; +class QSortFilterProxyModel; +class QAbstractItemModel; +class QPushButton; +class QStringList; +class QString; +class QMenu; + +class DataFilesModel; +class PluginsProxyModel; + +class FileDialog : public QDialog, private Ui::DataFilesPage +{ + Q_OBJECT +public: + explicit FileDialog(QWidget *parent = 0); + void addFiles(const QString &path); + void setEncoding(const QString &encoding); + + void openFile(); + void newFile(); + void accepted(); + + QStringList checkedItemsPaths(); + QString fileName(); + +signals: + void openFiles(); + void createNewFile(); + +public slots: + void accept(); + +private slots: + void updateViews(); + void updateOpenButton(const QStringList &items); + void updateCreateButton(const QString &name); + void setCheckState(QModelIndex index); + + void filterChanged(const QString &filter); + + void createButtonClicked(); + +private: + QLabel *mNameLabel; + LineEdit *mNameLineEdit; + + QPushButton *mCreateButton; + QDialogButtonBox *mButtonBox; + + DataFilesModel *mDataFilesModel; + + PluginsProxyModel *mPluginsProxyModel; + QSortFilterProxyModel *mMastersProxyModel; + QSortFilterProxyModel *mFilterProxyModel; +}; + +#endif // FILEDIALOG_HPP diff --git a/apps/opencs/view/doc/opendialog.cpp b/apps/opencs/view/doc/opendialog.cpp new file mode 100644 index 000000000..7b62aafa3 --- /dev/null +++ b/apps/opencs/view/doc/opendialog.cpp @@ -0,0 +1,62 @@ +#include +#include + +#include + +#include "opendialog.hpp" + +OpenDialog::OpenDialog(QWidget * parent) : QDialog(parent) +{ + QVBoxLayout *layout = new QVBoxLayout(this); + mFileSelector = new DataFilesList(mCfgMgr, this); + layout->addWidget(mFileSelector); + + /// \todo move config to Editor class and add command line options. + // We use the Configuration Manager to retrieve the configuration values + boost::program_options::variables_map variables; + boost::program_options::options_description desc; + + desc.add_options() + ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()) + ("data-local", boost::program_options::value()->default_value("")) + ("fs-strict", boost::program_options::value()->implicit_value(true)->default_value(false)) + ("encoding", boost::program_options::value()->default_value("win1252")); + + boost::program_options::notify(variables); + + mCfgMgr.readConfiguration(variables, desc); + + Files::PathContainer mDataDirs, mDataLocal; + if (!variables["data"].empty()) { + mDataDirs = Files::PathContainer(variables["data"].as()); + } + + std::string local = variables["data-local"].as(); + if (!local.empty()) { + mDataLocal.push_back(Files::PathContainer::value_type(local)); + } + + mCfgMgr.processPaths(mDataDirs); + mCfgMgr.processPaths(mDataLocal); + + // Set the charset for reading the esm/esp files + QString encoding = QString::fromStdString(variables["encoding"].as()); + + Files::PathContainer dataDirs; + dataDirs.insert(dataDirs.end(), mDataDirs.begin(), mDataDirs.end()); + dataDirs.insert(dataDirs.end(), mDataLocal.begin(), mDataLocal.end()); + mFileSelector->setupDataFiles(dataDirs, encoding); + + buttonBox = new QDialogButtonBox(QDialogButtonBox::Open | QDialogButtonBox::Cancel, Qt::Horizontal, this); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + layout->addWidget(buttonBox); + + setLayout(layout); + setWindowTitle(tr("Open")); +} + +void OpenDialog::getFileList(std::vector& paths) +{ + mFileSelector->selectedFiles(paths); +} diff --git a/apps/opencs/view/doc/opendialog.hpp b/apps/opencs/view/doc/opendialog.hpp new file mode 100644 index 000000000..6355aea44 --- /dev/null +++ b/apps/opencs/view/doc/opendialog.hpp @@ -0,0 +1,17 @@ +#include +#include + +class DataFilesList; +class QDialogButtonBox; + +class OpenDialog : public QDialog { + Q_OBJECT +public: + OpenDialog(QWidget * parent = 0); + + void getFileList(std::vector& paths); +private: + DataFilesList * mFileSelector; + QDialogButtonBox * buttonBox; + Files::ConfigurationManager mCfgMgr; +}; \ No newline at end of file diff --git a/apps/opencs/view/doc/operation.cpp b/apps/opencs/view/doc/operation.cpp index 3f415da03..6977d7953 100644 --- a/apps/opencs/view/doc/operation.cpp +++ b/apps/opencs/view/doc/operation.cpp @@ -1,8 +1,11 @@ - #include "operation.hpp" #include +#include +#include +#include + #include "../../model/doc/document.hpp" void CSVDoc::Operation::updateLabel (int threads) @@ -28,27 +31,121 @@ void CSVDoc::Operation::updateLabel (int threads) stream << name << " (%p%)"; } - setFormat (stream.str().c_str()); + mProgressBar->setFormat (stream.str().c_str()); } } -CSVDoc::Operation::Operation (int type) : mType (type), mStalling (false) +CSVDoc::Operation::Operation (int type, QWidget* parent) : mType (type), mStalling (false) { /// \todo Add a cancel button or a pop up menu with a cancel item - + initWidgets(); + setBarColor( type); updateLabel(); /// \todo assign different progress bar colours to allow the user to distinguish easily between operation types } +CSVDoc::Operation::~Operation() +{ + delete mLayout; + delete mProgressBar; + delete mAbortButton; +} + +void CSVDoc::Operation::initWidgets() +{ + mProgressBar = new QProgressBar (); + mAbortButton = new QPushButton("Abort"); + mLayout = new QHBoxLayout(); + + mLayout->addWidget (mProgressBar); + mLayout->addWidget (mAbortButton); + + connect (mAbortButton, SIGNAL (clicked()), this, SLOT (abortOperation())); +} + void CSVDoc::Operation::setProgress (int current, int max, int threads) { updateLabel (threads); - setRange (0, max); - setValue (current); + mProgressBar->setRange (0, max); + mProgressBar->setValue (current); } int CSVDoc::Operation::getType() const { return mType; -} \ No newline at end of file +} + +void CSVDoc::Operation::setBarColor (int type) +{ + QString style ="QProgressBar {" + "text-align: center;" + "}" + "QProgressBar::chunk {" + "background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 %1, stop:.50 %2 stop: .51 %3 stop:1 %4);" + "text-align: center;" + "margin: 2px 1px 1p 2px;" + "}"; + + QString topColor = "#F2F6F8"; + QString bottomColor = "#E0EFF9"; + QString midTopColor = "#D8E1E7"; + QString midBottomColor = "#B5C6D0"; // default gray gloss + + // colors inspired by samples from: + // http://www.colorzilla.com/gradient-editor/ + + switch (type) + { + case CSMDoc::State_Saving: + + topColor = "#FECCB1"; + midTopColor = "#F17432"; + midBottomColor = "#EA5507"; + bottomColor = "#FB955E"; // red gloss #2 + break; + + case CSMDoc::State_Searching: + + topColor = "#EBF1F6"; + midTopColor = "#ABD3EE"; + midBottomColor = "#89C3EB"; + bottomColor = "#D5EBFB"; //blue gloss #4 + break; + + case CSMDoc::State_Verifying: + + topColor = "#BFD255"; + midTopColor = "#8EB92A"; + midBottomColor = "#72AA00"; + bottomColor = "#9ECB2D"; //green gloss + break; + + case CSMDoc::State_Compiling: + + topColor = "#F3E2C7"; + midTopColor = "#C19E67"; + midBottomColor = "#B68D4C"; + bottomColor = "#E9D4B3"; //l Brown 3D + break; + + default: + + topColor = "#F2F6F8"; + bottomColor = "#E0EFF9"; + midTopColor = "#D8E1E7"; + midBottomColor = "#B5C6D0"; // gray gloss for undefined ops + } + + mProgressBar->setStyleSheet(style.arg(topColor).arg(midTopColor).arg(midBottomColor).arg(bottomColor)); +} + +QHBoxLayout *CSVDoc::Operation::getLayout() const +{ + return mLayout; +} + +void CSVDoc::Operation::abortOperation() +{ + emit abortOperation (mType); +} diff --git a/apps/opencs/view/doc/operation.hpp b/apps/opencs/view/doc/operation.hpp index 362725b6f..48839fada 100644 --- a/apps/opencs/view/doc/operation.hpp +++ b/apps/opencs/view/doc/operation.hpp @@ -1,16 +1,23 @@ #ifndef CSV_DOC_OPERATION_H #define CSV_DOC_OPERATION_H -#include +#include + +class QHBoxLayout; +class QPushButton; +class QProgressBar; namespace CSVDoc { - class Operation : public QProgressBar + class Operation : public QObject { Q_OBJECT int mType; bool mStalling; + QProgressBar *mProgressBar; + QPushButton *mAbortButton; + QHBoxLayout *mLayout; // not implemented Operation (const Operation&); @@ -20,12 +27,27 @@ namespace CSVDoc public: - Operation (int type); + Operation (int type, QWidget *parent); + ~Operation(); void setProgress (int current, int max, int threads); int getType() const; + QHBoxLayout *getLayout() const; + + private: + + void setBarColor (int type); + void initWidgets(); + + signals: + + void abortOperation (int type); + + private slots: + + void abortOperation(); }; } -#endif \ No newline at end of file +#endif diff --git a/apps/opencs/view/doc/operations.cpp b/apps/opencs/view/doc/operations.cpp index ba444a119..7ee4b8726 100644 --- a/apps/opencs/view/doc/operations.cpp +++ b/apps/opencs/view/doc/operations.cpp @@ -1,7 +1,7 @@ - #include "operations.hpp" #include +#include #include "operation.hpp" @@ -11,12 +11,14 @@ CSVDoc::Operations::Operations() setFeatures (QDockWidget::NoDockWidgetFeatures); - QWidget *widget = new QWidget; - setWidget (widget); - + QWidget *widgetContainer = new QWidget (this); mLayout = new QVBoxLayout; - widget->setLayout (mLayout); + widgetContainer->setLayout (mLayout); + setWidget (widgetContainer); + setVisible (false); + setFixedHeight (widgetContainer->height()); + setTitleBarWidget (new QWidget (this)); } void CSVDoc::Operations::setProgress (int current, int max, int type, int threads) @@ -28,11 +30,20 @@ void CSVDoc::Operations::setProgress (int current, int max, int type, int thread return; } - Operation *operation = new Operation (type); + int oldCount = mOperations.size(); + int newCount = oldCount + 1; + + Operation *operation = new Operation (type, this); + connect (operation, SIGNAL (abortOperation (int)), this, SIGNAL (abortOperation (int))); - mLayout->addWidget (operation); + mLayout->addLayout (operation->getLayout()); mOperations.push_back (operation); operation->setProgress (current, max, threads); + + if ( oldCount > 0) + setFixedHeight (height()/oldCount * newCount); + + setVisible (true); } void CSVDoc::Operations::quitOperation (int type) @@ -40,8 +51,19 @@ void CSVDoc::Operations::quitOperation (int type) for (std::vector::iterator iter (mOperations.begin()); iter!=mOperations.end(); ++iter) if ((*iter)->getType()==type) { - delete *iter; + int oldCount = mOperations.size(); + int newCount = oldCount - 1; + + mLayout->removeItem ((*iter)->getLayout()); + + (*iter)->deleteLater(); mOperations.erase (iter); + + if (oldCount > 1) + setFixedHeight (height() / oldCount * newCount); + else + setVisible (false); + break; } -} \ No newline at end of file +} diff --git a/apps/opencs/view/doc/operations.hpp b/apps/opencs/view/doc/operations.hpp index b96677450..71c595f66 100644 --- a/apps/opencs/view/doc/operations.hpp +++ b/apps/opencs/view/doc/operations.hpp @@ -31,7 +31,11 @@ namespace CSVDoc void quitOperation (int type); ///< Calling this function for an operation that is not running is a no-op. + + signals: + + void abortOperation (int type); }; } -#endif \ No newline at end of file +#endif diff --git a/apps/opencs/view/doc/startup.cpp b/apps/opencs/view/doc/startup.cpp new file mode 100644 index 000000000..6c1e74058 --- /dev/null +++ b/apps/opencs/view/doc/startup.cpp @@ -0,0 +1,27 @@ + +#include "startup.hpp" + +#include +#include +#include +#include +#include + +CSVDoc::StartupDialogue::StartupDialogue() +{ + QHBoxLayout *layout = new QHBoxLayout (this); + + QPushButton *createDocument = new QPushButton ("new", this); + connect (createDocument, SIGNAL (clicked()), this, SIGNAL (createDocument())); + layout->addWidget (createDocument); + + QPushButton *loadDocument = new QPushButton ("load", this); + connect (loadDocument, SIGNAL (clicked()), this, SIGNAL (loadDocument())); + layout->addWidget (loadDocument); + + setLayout (layout); + + QRect scr = QApplication::desktop()->screenGeometry(); + QRect rect = geometry(); + move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y()); +} diff --git a/apps/opencs/view/doc/startup.hpp b/apps/opencs/view/doc/startup.hpp new file mode 100644 index 000000000..f24d2a64b --- /dev/null +++ b/apps/opencs/view/doc/startup.hpp @@ -0,0 +1,24 @@ +#ifndef CSV_DOC_STARTUP_H +#define CSV_DOC_STARTUP_H + +#include + +namespace CSVDoc +{ + class StartupDialogue : public QWidget + { + Q_OBJECT + + public: + + StartupDialogue(); + + signals: + + void createDocument(); + + void loadDocument(); + }; +} + +#endif diff --git a/apps/opencs/view/doc/subview.cpp b/apps/opencs/view/doc/subview.cpp index 1c356fa73..affada012 100644 --- a/apps/opencs/view/doc/subview.cpp +++ b/apps/opencs/view/doc/subview.cpp @@ -1,4 +1,3 @@ - #include "subview.hpp" CSVDoc::SubView::SubView (const CSMWorld::UniversalId& id) : mUniversalId (id) diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 13edb6e74..2857f4a54 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -1,4 +1,3 @@ - #include "view.hpp" #include @@ -7,13 +6,13 @@ #include #include #include +#include +#include #include "../../model/doc/document.hpp" - #include "../world/subviews.hpp" - #include "../tools/subviews.hpp" - +#include "../settings/usersettingsdialog.hpp" #include "viewmanager.hpp" #include "operations.hpp" #include "subview.hpp" @@ -32,9 +31,27 @@ void CSVDoc::View::setupFileMenu() connect (new_, SIGNAL (triggered()), this, SIGNAL (newDocumentRequest())); file->addAction (new_); + QAction *open = new QAction (tr ("&Open"), this); + connect (open, SIGNAL (triggered()), this, SIGNAL (loadDocumentRequest())); + file->addAction (open); + mSave = new QAction (tr ("&Save"), this); connect (mSave, SIGNAL (triggered()), this, SLOT (save())); file->addAction (mSave); + + mVerify = new QAction (tr ("&Verify"), this); + connect (mVerify, SIGNAL (triggered()), this, SLOT (verify())); + file->addAction (mVerify); + + QAction *close = new QAction (tr ("&Close"), this); + connect (close, SIGNAL (triggered()), this, SLOT (close())); + file->addAction(close); + + QAction *exit = new QAction (tr ("&Exit"), this); + connect (exit, SIGNAL (triggered()), this, SLOT (exit())); + connect (this, SIGNAL(exitApplicationRequest(CSVDoc::View *)), &mViewManager, SLOT(exitApplication(CSVDoc::View *))); + + file->addAction(exit); } void CSVDoc::View::setupEditMenu() @@ -48,6 +65,10 @@ void CSVDoc::View::setupEditMenu() mRedo= mDocument->getUndoStack().createRedoAction (this, tr("&Redo")); mRedo->setShortcuts (QKeySequence::Redo); edit->addAction (mRedo); + + QAction *userSettings = new QAction (tr ("&Preferences"), this); + connect (userSettings, SIGNAL (triggered()), this, SLOT (showUserSettings())); + edit->addAction (userSettings); } void CSVDoc::View::setupViewMenu() @@ -67,9 +88,54 @@ void CSVDoc::View::setupWorldMenu() connect (globals, SIGNAL (triggered()), this, SLOT (addGlobalsSubView())); world->addAction (globals); - mVerify = new QAction (tr ("&Verify"), this); - connect (mVerify, SIGNAL (triggered()), this, SLOT (verify())); - world->addAction (mVerify); + QAction *gmsts = new QAction (tr ("Game settings"), this); + connect (gmsts, SIGNAL (triggered()), this, SLOT (addGmstsSubView())); + world->addAction (gmsts); + + QAction *skills = new QAction (tr ("Skills"), this); + connect (skills, SIGNAL (triggered()), this, SLOT (addSkillsSubView())); + world->addAction (skills); + + QAction *classes = new QAction (tr ("Classes"), this); + connect (classes, SIGNAL (triggered()), this, SLOT (addClassesSubView())); + world->addAction (classes); + + QAction *factions = new QAction (tr ("Factions"), this); + connect (factions, SIGNAL (triggered()), this, SLOT (addFactionsSubView())); + world->addAction (factions); + + QAction *races = new QAction (tr ("Races"), this); + connect (races, SIGNAL (triggered()), this, SLOT (addRacesSubView())); + world->addAction (races); + + QAction *sounds = new QAction (tr ("Sounds"), this); + connect (sounds, SIGNAL (triggered()), this, SLOT (addSoundsSubView())); + world->addAction (sounds); + + QAction *scripts = new QAction (tr ("Scripts"), this); + connect (scripts, SIGNAL (triggered()), this, SLOT (addScriptsSubView())); + world->addAction (scripts); + + QAction *regions = new QAction (tr ("Regions"), this); + connect (regions, SIGNAL (triggered()), this, SLOT (addRegionsSubView())); + world->addAction (regions); + + QAction *birthsigns = new QAction (tr ("Birthsigns"), this); + connect (birthsigns, SIGNAL (triggered()), this, SLOT (addBirthsignsSubView())); + world->addAction (birthsigns); + + QAction *spells = new QAction (tr ("Spells"), this); + connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView())); + world->addAction (spells); + + QAction *cells = new QAction (tr ("Cells"), this); + connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView())); + world->addAction (cells); + + QAction *referenceables = new QAction (tr ("Referenceables"), this); + connect (referenceables, SIGNAL (triggered()), this, SLOT (addReferenceablesSubView())); + world->addAction (referenceables); + } void CSVDoc::View::setupUi() @@ -110,12 +176,15 @@ void CSVDoc::View::updateActions() } CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews) -: mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), mViewTotal (totalViews) + : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), + mViewTotal (totalViews) { - setDockOptions (QMainWindow::AllowNestedDocks); - resize (300, 300); /// \todo get default size from settings and set reasonable minimal size + mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks); + + setCentralWidget (&mSubViewWindow); + mOperations = new Operations; addDockWidget (Qt::BottomDockWidgetArea, mOperations); @@ -125,6 +194,8 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to CSVWorld::addSubViewFactories (mSubViewFactory); CSVTools::addSubViewFactories (mSubViewFactory); + + connect (mOperations, SIGNAL (abortOperation (int)), this, SLOT (abortOperation (int))); } CSVDoc::View::~View() @@ -163,7 +234,7 @@ void CSVDoc::View::updateDocumentState() for (int i=0; operations[i]!=-1; ++i) if (!(state & operations[i])) - mOperations->quitOperation (operations[i]); + mOperations->quitOperation (operations[i]); QList subViews = findChildren(); @@ -187,7 +258,7 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id) /// \todo add an user setting to reuse sub views (on a per document basis or on a per top level view basis) SubView *view = mSubViewFactory.makeSubView (id, *mDocument); - addDockWidget (Qt::TopDockWidgetArea, view); + mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view); connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&)), this, SLOT (addSubView (const CSMWorld::UniversalId&))); @@ -213,4 +284,102 @@ void CSVDoc::View::verify() void CSVDoc::View::addGlobalsSubView() { addSubView (CSMWorld::UniversalId::Type_Globals); -} \ No newline at end of file +} + +void CSVDoc::View::addGmstsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Gmsts); +} + +void CSVDoc::View::addSkillsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Skills); +} + +void CSVDoc::View::addClassesSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Classes); +} + +void CSVDoc::View::addFactionsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Factions); +} + +void CSVDoc::View::addRacesSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Races); +} + +void CSVDoc::View::addSoundsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Sounds); +} + +void CSVDoc::View::addScriptsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Scripts); +} + +void CSVDoc::View::addRegionsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Regions); +} + +void CSVDoc::View::addBirthsignsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Birthsigns); +} + +void CSVDoc::View::addSpellsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Spells); +} + +void CSVDoc::View::addCellsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Cells); +} + +void CSVDoc::View::addReferenceablesSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Referenceables); +} + +void CSVDoc::View::abortOperation (int type) +{ + mDocument->abortOperation (type); + updateActions(); +} + +CSVDoc::Operations *CSVDoc::View::getOperations() const +{ + return mOperations; +} + +void CSVDoc::View::exit() +{ + emit exitApplicationRequest (this); +} + +void CSVDoc::View::showUserSettings() +{ + CSVSettings::UserSettingsDialog *settingsDialog = new CSVSettings::UserSettingsDialog(this); + + connect (&(CSMSettings::UserSettings::instance()), SIGNAL (signalUpdateEditorSetting (const QString &, const QString &)), + this, SLOT (slotUpdateEditorSetting (const QString &, const QString &)) ); + + settingsDialog->show(); +} + +void CSVDoc::View::slotUpdateEditorSetting(const QString &settingName, const QString &settingValue) +{ + static QString lastValue = ""; + + if (lastValue != settingValue) + { + //evaluate settingName against tokens to determine which function to call to update Editor application. + + lastValue = settingValue; + } +} diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index b1dedafe9..0552a86cb 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -9,6 +9,7 @@ #include "subviewfactory.hpp" class QAction; +class QDockWidget; namespace CSMDoc { @@ -40,6 +41,8 @@ namespace CSVDoc std::vector mEditingActions; Operations *mOperations; SubViewFactoryManager mSubViewFactory; + QMainWindow mSubViewWindow; + // not implemented View (const View&); @@ -63,9 +66,12 @@ namespace CSVDoc void updateActions(); + void exitApplication(); + public: View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews); + ///< The ownership of \a document is not transferred to *this. virtual ~View(); @@ -80,24 +86,62 @@ namespace CSVDoc void updateProgress (int current, int max, int type, int threads); + Operations *getOperations() const; + signals: void newDocumentRequest(); + void loadDocumentRequest(); + + void exitApplicationRequest (CSVDoc::View *view); + public slots: void addSubView (const CSMWorld::UniversalId& id); + void abortOperation (int type); + + void slotUpdateEditorSetting (const QString &settingName, const QString &settingValue); + private slots: void newView(); void save(); + void exit(); + void verify(); void addGlobalsSubView(); + + void addGmstsSubView(); + + void addSkillsSubView(); + + void addClassesSubView(); + + void addFactionsSubView(); + + void addRacesSubView(); + + void addSoundsSubView(); + + void addScriptsSubView(); + + void addRegionsSubView(); + + void addBirthsignsSubView(); + + void addSpellsSubView(); + + void addCellsSubView(); + + void addReferenceablesSubView(); + + void showUserSettings(); }; } -#endif \ No newline at end of file +#endif diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 22847c78b..d044098fe 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -3,11 +3,23 @@ #include +#include +#include + #include "../../model/doc/documentmanager.hpp" #include "../../model/doc/document.hpp" +#include "../world/util.hpp" +#include "../world/enumdelegate.hpp" +#include "../world/vartypedelegate.hpp" + #include "view.hpp" +#include +#include +#include +#include + void CSVDoc::ViewManager::updateIndices() { std::map > documents; @@ -27,13 +39,90 @@ void CSVDoc::ViewManager::updateIndices() } CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) -: mDocumentManager (documentManager) + : mDocumentManager (documentManager), mExitOnSaveStateChange(false), mUserWarned(false) { + static const char *sSpecialisations[] = + { + "Combat", "Magic", "Stealth", 0 + }; + + static const char *sAttributes[] = + { + "Strength", "Intelligence", "Willpower", "Agility", "Speed", "Endurance", "Personality", + "Luck", 0 + }; + + static const char *sSpellTypes[] = + { + "Spell", "Ability", "Blight", "Disease", "Curse", "Power", 0 + }; + + static const char *sApparatusTypes[] = + { + "Mortar & Pestle", "Albemic", "Calcinator", "Retort", 0 + }; + + static const char *sArmorTypes[] = + { + "Helmet", "Cuirass", "Left Pauldron", "Right Pauldron", "Greaves", "Boots", "Left Gauntlet", + "Right Gauntlet", "Shield", "Left Bracer", "Right Bracer", 0 + }; + + static const char *sClothingTypes[] = + { + "Pants", "Shoes", "Shirt", "Belt", "Robe", "Right Glove", "Left Glove", "Skirt", "Ring", + "Amulet", 0 + }; + + static const char *sCreatureTypes[] = + { + "Creature", "Deadra", "Undead", "Humanoid", 0 + }; + + static const char *sWeaponTypes[] = + { + "Short Blade 1H", "Long Blade 1H", "Long Blade 2H", "Blunt 1H", "Blunt 2H Close", + "Blunt 2H Wide", "Spear 2H", "Axe 1H", "Axe 2H", "Bow", "Crossbow", "Thrown", "Arrow", + "Bolt", 0 + }; + + mDelegateFactories = new CSVWorld::CommandDelegateFactoryCollection; + + mDelegateFactories->add (CSMWorld::ColumnBase::Display_GmstVarType, + new CSVWorld::VarTypeDelegateFactory (ESM::VT_None, ESM::VT_String, ESM::VT_Int, ESM::VT_Float)); + + mDelegateFactories->add (CSMWorld::ColumnBase::Display_GlobalVarType, + new CSVWorld::VarTypeDelegateFactory (ESM::VT_Short, ESM::VT_Long, ESM::VT_Float)); + + mDelegateFactories->add (CSMWorld::ColumnBase::Display_Specialisation, + new CSVWorld::EnumDelegateFactory (sSpecialisations)); + + mDelegateFactories->add (CSMWorld::ColumnBase::Display_Attribute, + new CSVWorld::EnumDelegateFactory (sAttributes, true)); + mDelegateFactories->add (CSMWorld::ColumnBase::Display_SpellType, + new CSVWorld::EnumDelegateFactory (sSpellTypes)); + + mDelegateFactories->add (CSMWorld::ColumnBase::Display_ApparatusType, + new CSVWorld::EnumDelegateFactory (sApparatusTypes)); + + mDelegateFactories->add (CSMWorld::ColumnBase::Display_ArmorType, + new CSVWorld::EnumDelegateFactory (sArmorTypes)); + + mDelegateFactories->add (CSMWorld::ColumnBase::Display_ClothingType, + new CSVWorld::EnumDelegateFactory (sClothingTypes)); + + mDelegateFactories->add (CSMWorld::ColumnBase::Display_CreatureType, + new CSVWorld::EnumDelegateFactory (sCreatureTypes)); + + mDelegateFactories->add (CSMWorld::ColumnBase::Display_WeaponType, + new CSVWorld::EnumDelegateFactory (sWeaponTypes)); } CSVDoc::ViewManager::~ViewManager() { + delete mDelegateFactories; + for (std::vector::iterator iter (mViews.begin()); iter!=mViews.end(); ++iter) delete *iter; } @@ -52,11 +141,13 @@ CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) View *view = new View (*this, document, countViews (document)+1); + mViews.push_back (view); view->show(); connect (view, SIGNAL (newDocumentRequest ()), this, SIGNAL (newDocumentRequest())); + connect (view, SIGNAL (loadDocumentRequest ()), this, SIGNAL (loadDocumentRequest())); updateIndices(); @@ -78,23 +169,143 @@ bool CSVDoc::ViewManager::closeRequest (View *view) { std::vector::iterator iter = std::find (mViews.begin(), mViews.end(), view); + bool continueWithClose = true; + if (iter!=mViews.end()) { bool last = countViews (view->getDocument())<=1; - /// \todo check if save is in progress -> warn user about possible data loss - /// \todo check if document has not been saved -> return false and start close dialogue - - mViews.erase (iter); - view->deleteLater(); - if (last) - mDocumentManager.removeDocument (view->getDocument()); + continueWithClose = notifySaveOnClose (view); else + { + (*iter)->deleteLater(); + mViews.erase (iter); + updateIndices(); + } } - return true; + return continueWithClose; +} + +bool CSVDoc::ViewManager::notifySaveOnClose (CSVDoc::View *view) +{ + bool result = true; + CSMDoc::Document *document = view->getDocument(); + + //notify user of saving in progress + if ( (document->getState() & CSMDoc::State_Saving) ) + result = showSaveInProgressMessageBox (view); + + //notify user of unsaved changes and process response + else if ( document->getState() & CSMDoc::State_Modified) + result = showModifiedDocumentMessageBox (view); + + return result; +} + +bool CSVDoc::ViewManager::showModifiedDocumentMessageBox (CSVDoc::View *view) +{ + QMessageBox messageBox; + CSMDoc::Document *document = view->getDocument(); + + messageBox.setText ("The document has been modified."); + messageBox.setInformativeText ("Do you want to save your changes?"); + messageBox.setStandardButtons (QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + messageBox.setDefaultButton (QMessageBox::Save); + + bool retVal = true; + + connect (this, SIGNAL (closeMessageBox()), &messageBox, SLOT (close())); + + connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + + mUserWarned = true; + int response = messageBox.exec(); + mUserWarned = false; + + switch (response) + { + case QMessageBox::Save: + + document->save(); + mExitOnSaveStateChange = true; + retVal = false; + break; + + case QMessageBox::Discard: + + disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + break; + + case QMessageBox::Cancel: + + //disconnect to prevent unintended view closures + disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + retVal = false; + break; + + default: + break; + + } + + return retVal; +} + +bool CSVDoc::ViewManager::showSaveInProgressMessageBox (CSVDoc::View *view) +{ + QMessageBox messageBox; + CSMDoc::Document *document = view->getDocument(); + + messageBox.setText ("The document is currently being saved."); + messageBox.setInformativeText("Do you want to close now and abort saving, or wait until saving has completed?"); + + QPushButton* waitButton = messageBox.addButton (tr("Wait"), QMessageBox::YesRole); + QPushButton* closeButton = messageBox.addButton (tr("Close Now"), QMessageBox::RejectRole); + QPushButton* cancelButton = messageBox.addButton (tr("Cancel"), QMessageBox::NoRole); + + messageBox.setDefaultButton (waitButton); + + bool retVal = true; + + //Connections shut down message box if operation ends before user makes a decision. + connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + connect (this, SIGNAL (closeMessageBox()), &messageBox, SLOT (close())); + + //set / clear the user warned flag to indicate whether or not the message box is currently active. + mUserWarned = true; + messageBox.exec(); + mUserWarned = false; + + //if closed by the warning handler, defaults to the RejectRole button (closeButton) + if (messageBox.clickedButton() == waitButton) + { + //save the View iterator for shutdown after the save operation ends + mExitOnSaveStateChange = true; + retVal = false; + } + + else if (messageBox.clickedButton() == closeButton) + { + //disconnect to avoid segmentation fault + disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + + view->abortOperation(CSMDoc::State_Saving); + mExitOnSaveStateChange = true; + } + + else if (messageBox.clickedButton() == cancelButton) + { + //abort shutdown, allow save to complete + //disconnection to prevent unintended view closures + mExitOnSaveStateChange = false; + disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + retVal = false; + } + + return retVal; } void CSVDoc::ViewManager::documentStateChanged (int state, CSMDoc::Document *document) @@ -109,4 +320,26 @@ void CSVDoc::ViewManager::progress (int current, int max, int type, int threads, for (std::vector::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter) if ((*iter)->getDocument()==document) (*iter)->updateProgress (current, max, type, threads); -} \ No newline at end of file +} + +void CSVDoc::ViewManager::onExitWarningHandler (int state, CSMDoc::Document *document) +{ + if ( !(state & CSMDoc::State_Saving) ) + { + //if the user is being warned (message box is active), shut down the message box, + //as there is no save operation currently running + if ( mUserWarned ) + emit closeMessageBox(); + + //otherwise, the user has closed the message box before the save operation ended. + //exit the application + else if (mExitOnSaveStateChange) + QApplication::instance()->exit(); + } +} + +void CSVDoc::ViewManager::exitApplication (CSVDoc::View *view) +{ + if (notifySaveOnClose (view)) + QApplication::instance()->exit(); +} diff --git a/apps/opencs/view/doc/viewmanager.hpp b/apps/opencs/view/doc/viewmanager.hpp index 5e4b1be07..90f23eaa1 100644 --- a/apps/opencs/view/doc/viewmanager.hpp +++ b/apps/opencs/view/doc/viewmanager.hpp @@ -11,6 +11,11 @@ namespace CSMDoc class DocumentManager; } +namespace CSVWorld +{ + class CommandDelegateFactoryCollection; +} + namespace CSVDoc { class View; @@ -21,12 +26,18 @@ namespace CSVDoc CSMDoc::DocumentManager& mDocumentManager; std::vector mViews; + CSVWorld::CommandDelegateFactoryCollection *mDelegateFactories; + bool mExitOnSaveStateChange; + bool mUserWarned; // not implemented ViewManager (const ViewManager&); ViewManager& operator= (const ViewManager&); void updateIndices(); + bool notifySaveOnClose (View *view = 0); + bool showModifiedDocumentMessageBox (View *view); + bool showSaveInProgressMessageBox (View *view); public: @@ -46,13 +57,23 @@ namespace CSVDoc void newDocumentRequest(); + void loadDocumentRequest(); + + void closeMessageBox(); + + public slots: + + void exitApplication (CSVDoc::View *view); + private slots: void documentStateChanged (int state, CSMDoc::Document *document); void progress (int current, int max, int type, int threads, CSMDoc::Document *document); + + void onExitWarningHandler(int state, CSMDoc::Document* document); }; } -#endif \ No newline at end of file +#endif diff --git a/apps/opencs/view/settings/abstractblock.cpp b/apps/opencs/view/settings/abstractblock.cpp new file mode 100644 index 000000000..72d360348 --- /dev/null +++ b/apps/opencs/view/settings/abstractblock.cpp @@ -0,0 +1,112 @@ +#include "abstractblock.hpp" + +CSVSettings::AbstractBlock::AbstractBlock(QWidget* parent) + : QObject (parent), mBox ( new GroupBox (parent) ), mWidgetParent (parent) +{} + +CSVSettings::AbstractBlock::AbstractBlock(bool isVisible, QWidget* parent) + : QObject (parent), mBox ( new GroupBox (isVisible, parent)), mWidgetParent (parent) +{} + +QLayout *CSVSettings::AbstractBlock::createLayout (Orientation direction, + bool isZeroMargin, QWidget* parent) +{ + QLayout *layout = 0; + + if (direction == Orient_Vertical) + layout = new QVBoxLayout (parent); + else + layout = new QHBoxLayout (parent); + + if (isZeroMargin) + layout->setContentsMargins(0, 0, 0, 0); + + return layout; +} + +QGroupBox *CSVSettings::AbstractBlock::getGroupBox() +{ + return mBox; +} + +CSVSettings::AbstractWidget *CSVSettings::AbstractBlock::buildWidget (const QString& widgetName, WidgetDef &def, + QLayout *layout, bool isConnected) const +{ + AbstractWidget *widg = 0; + + switch (def.type) + { + + case Widget_RadioButton: + widg = createSettingWidget (def, layout); + break; + + case Widget_SpinBox: + widg = createSettingWidget (def, layout); + break; + + case Widget_CheckBox: + widg = createSettingWidget (def, layout); + break; + + case Widget_LineEdit: + widg = createSettingWidget (def, layout); + break; + + case Widget_ListBox: + widg = createSettingWidget (def, layout); + break; + + case Widget_ComboBox: + widg = createSettingWidget (def, layout); + break; + + default: + break; + }; + + if (!mBox->layout()) + mBox->setLayout(widg->getLayout()); + + widg->widget()->setObjectName(widgetName); + + if (isConnected) + connect (widg, SIGNAL (signalUpdateItem (const QString &)), this, SLOT (slotUpdate (const QString &))); + connect (this, SIGNAL (signalUpdateWidget (const QString &)), widg, SLOT (slotUpdateWidget (const QString &) )); + + return widg; +} + +void CSVSettings::AbstractBlock::setVisible (bool isVisible) +{ + mBox->setBorderVisibility (isVisible); +} + +bool CSVSettings::AbstractBlock::isVisible () const +{ + return mBox->borderVisibile(); +} + +QWidget *CSVSettings::AbstractBlock::getParent() const +{ + return mWidgetParent; +} + +void CSVSettings::AbstractBlock::slotUpdate (const QString &value) +{ + slotUpdateSetting (objectName(), value); +} + +void CSVSettings::AbstractBlock::slotSetEnabled(bool value) +{ + mBox->setEnabled(value); +} + +void CSVSettings::AbstractBlock::slotUpdateSetting (const QString &settingName, const QString &settingValue) +{ + bool doEmit = true; + updateBySignal (settingName, settingValue, doEmit); + + if (doEmit) + emit signalUpdateSetting (settingName, settingValue); +} diff --git a/apps/opencs/view/settings/abstractblock.hpp b/apps/opencs/view/settings/abstractblock.hpp new file mode 100644 index 000000000..42e00b6d7 --- /dev/null +++ b/apps/opencs/view/settings/abstractblock.hpp @@ -0,0 +1,71 @@ +#ifndef ABSTRACTBLOCK_HPP +#define ABSTRACTBLOCK_HPP + +#include +#include + +#include "settingwidget.hpp" +#include "../../model/settings/settingsitem.hpp" +#include "groupbox.hpp" + +namespace CSVSettings +{ + + class AbstractBlock : public QObject + { + Q_OBJECT + + protected: + + typedef QMap SettingsItemMap; + GroupBox *mBox; + QWidget *mWidgetParent; + + public: + + explicit AbstractBlock (QWidget *parent = 0); + explicit AbstractBlock (bool isVisible, QWidget *parent = 0); + + QGroupBox *getGroupBox(); + void setVisible (bool isVisible); + bool isVisible() const; + + virtual CSMSettings::SettingList *getSettings() = 0; + virtual bool updateSettings (const CSMSettings::SettingMap &settings) = 0; + virtual bool updateBySignal (const QString &name, const QString &value, bool &doEmit) + { return false; } + + protected: + + QLayout *createLayout (Orientation direction, bool isZeroMargin, QWidget* parent = 0); + AbstractWidget *buildWidget (const QString &widgetName, WidgetDef &wDef, + QLayout *layout = 0, bool isConnected = true) const; + + template + AbstractWidget *createSettingWidget (WidgetDef &wDef, QLayout *layout) const + { + return new SettingWidget (wDef, layout, mBox); + } + + QWidget *getParent() const; + + public slots: + + void slotSetEnabled (bool value); + void slotUpdateSetting (const QString &settingName, const QString &settingValue); + + private slots: + + void slotUpdate (const QString &value); + + signals: + + //signal to functions outside the settings tab widget + void signalUpdateSetting (const QString &propertyName, const QString &propertyValue); + void signalUpdateWidget (const QString & value); + + //propertyName and propertyValue are for properties for which the updated setting acts as a proxy + void signalUpdateProxySetting (const QString &propertyName, const QString &propertyValue); + }; +} +#endif // ABSTRACTBLOCK_HPP diff --git a/apps/opencs/view/settings/abstractpage.cpp b/apps/opencs/view/settings/abstractpage.cpp new file mode 100644 index 000000000..512b5abbd --- /dev/null +++ b/apps/opencs/view/settings/abstractpage.cpp @@ -0,0 +1,39 @@ +#include "abstractpage.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +CSVSettings::AbstractPage::AbstractPage(QWidget *parent): + QWidget(parent) +{ +} + +CSVSettings::AbstractPage::AbstractPage(const QString &pageName, QWidget *parent): + QWidget(parent) +{ + QWidget::setObjectName (pageName); +} + +CSVSettings::AbstractPage::~AbstractPage() +{ +} + +CSMSettings::SettingList *CSVSettings::AbstractPage::getSettings() +{ + CSMSettings::SettingList *settings = new CSMSettings::SettingList(); + + foreach (AbstractBlock *block, mAbstractBlocks) + { + CSMSettings::SettingList *groupSettings = block->getSettings(); + settings->append (*groupSettings); + } + + return settings; +} diff --git a/apps/opencs/view/settings/abstractpage.hpp b/apps/opencs/view/settings/abstractpage.hpp new file mode 100644 index 000000000..15ac82a62 --- /dev/null +++ b/apps/opencs/view/settings/abstractpage.hpp @@ -0,0 +1,59 @@ +#ifndef ABSTRACTPAGE_HPP +#define ABSTRACTPAGE_HPP + +#include +#include +#include + +#include "abstractblock.hpp" + +class SettingMap; +class SettingList; + +namespace CSVSettings { + + typedef QList AbstractBlockList; + + class AbstractPage: public QWidget + { + + protected: + + AbstractBlockList mAbstractBlocks; + + public: + + AbstractPage(QWidget *parent = 0); + AbstractPage (const QString &pageName, QWidget* parent = 0); + + ~AbstractPage(); + + virtual void setupUi()=0; + + virtual void initializeWidgets (const CSMSettings::SettingMap &settings) = 0; + + CSMSettings::SettingList *getSettings(); + + void setObjectName(); + + protected: + + template + AbstractBlock *buildBlock (T &def) + { + S *block = new S (this); + int ret = block->build (def); + + if (ret < 0) + return 0; + + QWidget::layout()->addWidget (block->getGroupBox()); + + return block; + } + + + }; +} + +#endif // ABSTRACTPAGE_HPP diff --git a/apps/opencs/view/settings/abstractwidget.cpp b/apps/opencs/view/settings/abstractwidget.cpp new file mode 100644 index 000000000..94044e267 --- /dev/null +++ b/apps/opencs/view/settings/abstractwidget.cpp @@ -0,0 +1,78 @@ +#include "abstractwidget.hpp" + +#include +#include + +void CSVSettings::AbstractWidget::build(QWidget *widget, WidgetDef &def, bool noLabel) +{ + if (!mLayout) + createLayout(def.orientation, true); + + buildLabelAndWidget (widget, def, noLabel); + +} + +void CSVSettings::AbstractWidget::buildLabelAndWidget (QWidget *widget, WidgetDef &def, bool noLabel) +{ + if (def.widgetWidth > -1) + widget->setFixedWidth (def.widgetWidth); + + if (!(def.caption.isEmpty() || noLabel) ) + { + QLabel *label = new QLabel (def.caption, dynamic_cast(parent())); + label->setBuddy (widget); + mLayout->addWidget (label); + + if (def.labelWidth > -1) + label->setFixedWidth(def.labelWidth); + } + + mLayout->addWidget (widget); + mLayout->setAlignment (widget, getAlignment (def.widgetAlignment)); +} + +void CSVSettings::AbstractWidget::createLayout + (Orientation direction, bool isZeroMargin) +{ + if (direction == Orient_Vertical) + mLayout = new QVBoxLayout (); + else + mLayout = new QHBoxLayout (); + + if (isZeroMargin) + mLayout->setContentsMargins(0, 0, 0, 0); +} + +QFlags CSVSettings::AbstractWidget::getAlignment (CSVSettings::Alignment flag) +{ + return QFlags(static_cast(flag)); +} + +QLayout *CSVSettings::AbstractWidget::getLayout() +{ + return mLayout; +} + +void CSVSettings::AbstractWidget::slotUpdateWidget (const QString &value) +{ + updateWidget (value); +} + +void CSVSettings::AbstractWidget::slotUpdateItem(const QString &value) +{ + emit signalUpdateItem (value); +} + +void CSVSettings::AbstractWidget::slotUpdateItem(bool value) +{ + if (value) + emit signalUpdateItem (widget()->objectName()); +} + +void CSVSettings::AbstractWidget::slotUpdateItem(int value) +{ + emit signalUpdateItem (QString::number(value)); +} + +void CSVSettings::AbstractWidget::slotUpdateItem (QListWidgetItem* current, QListWidgetItem* previous) +{} diff --git a/apps/opencs/view/settings/abstractwidget.hpp b/apps/opencs/view/settings/abstractwidget.hpp new file mode 100644 index 000000000..ef00993fc --- /dev/null +++ b/apps/opencs/view/settings/abstractwidget.hpp @@ -0,0 +1,64 @@ +#ifndef ABSTRACTWIDGET_HPP +#define ABSTRACTWIDGET_HPP + +#include +#include "support.hpp" + +class QLayout; + +namespace CSVSettings +{ + class AbstractWidget : public QObject + { + Q_OBJECT + + QLayout *mLayout; + + public: + + explicit AbstractWidget (QLayout *layout = 0, QWidget* parent = 0) + : QObject (parent), mLayout (layout) + {} + + //retrieve layout for insertion into itemblock + QLayout *getLayout(); + + //create the derived widget instance + void build (QWidget* widget, WidgetDef &def, bool noLabel = false); + + //reference to the derived widget instance + virtual QWidget *widget() = 0; + + protected: + + //called by inbound signal for type-specific widget udpates + virtual void updateWidget (const QString &value) = 0; + + //converts user-defined enum to Qt equivalents + QFlags getAlignment (Alignment flag); + + private: + + //widget initialization utilities + void createLayout (Orientation direction, bool isZeroMargin); + void buildLabelAndWidget (QWidget *widget, WidgetDef &def, bool noLabel); + + + signals: + + //outbound update + void signalUpdateItem (const QString &value); + + public slots: + + //inbound updates + void slotUpdateWidget (const QString &value); + + //Outbound updates from derived widget signal + void slotUpdateItem (const QString &value); + void slotUpdateItem (bool value); + void slotUpdateItem (int value); + void slotUpdateItem (QListWidgetItem* current, QListWidgetItem* previous); + }; +} +#endif // ABSTRACTWIDGET_HPP diff --git a/apps/opencs/view/settings/blankpage.cpp b/apps/opencs/view/settings/blankpage.cpp new file mode 100644 index 000000000..57d5c7caa --- /dev/null +++ b/apps/opencs/view/settings/blankpage.cpp @@ -0,0 +1,58 @@ +#include "blankpage.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_MAC +#include +#endif + +#include "../../model/settings/usersettings.hpp" +#include "groupblock.hpp" +#include "toggleblock.hpp" + +CSVSettings::BlankPage::BlankPage(QWidget *parent): + AbstractPage("Blank", parent) +{ + initPage(); +} + +CSVSettings::BlankPage::BlankPage(const QString &title, QWidget *parent): + AbstractPage(title, parent) +{ + initPage(); +} + +void CSVSettings::BlankPage::initPage() +{ + // Hacks to get the stylesheet look properly +#ifdef Q_OS_MAC + QPlastiqueStyle *style = new QPlastiqueStyle; + //profilesComboBox->setStyle(style); +#endif + + setupUi(); +} + +void CSVSettings::BlankPage::setupUi() +{ + QGroupBox *pageBox = new QGroupBox(this); + QLayout* pageLayout = new QVBoxLayout(); + + setLayout(pageLayout); + pageLayout->addWidget(pageBox); +} + +void CSVSettings::BlankPage::initializeWidgets (const CSMSettings::SettingMap &settings) +{ + //iterate each item in each blocks in this section + //validate the corresponding setting against the defined valuelist if any. + foreach (AbstractBlock *block, mAbstractBlocks) + block->updateSettings (settings); +} diff --git a/apps/opencs/view/settings/blankpage.hpp b/apps/opencs/view/settings/blankpage.hpp new file mode 100644 index 000000000..648d373f3 --- /dev/null +++ b/apps/opencs/view/settings/blankpage.hpp @@ -0,0 +1,29 @@ +#ifndef BLANKPAGE_HPP +#define BLANKPAGE_HPP + +#include "abstractpage.hpp" + +class QGroupBox; + +namespace CSVSettings { + + class UserSettings; + class AbstractBlock; + + class BlankPage : public AbstractPage + { + + public: + + BlankPage (QWidget *parent = 0); + BlankPage (const QString &title, QWidget *parent); + + void setupUi(); + void initializeWidgets (const CSMSettings::SettingMap &settings); + + private: + void initPage(); + }; +} + +#endif // BLANKPAGE_HPP diff --git a/apps/opencs/view/settings/customblock.cpp b/apps/opencs/view/settings/customblock.cpp new file mode 100644 index 000000000..f44189569 --- /dev/null +++ b/apps/opencs/view/settings/customblock.cpp @@ -0,0 +1,120 @@ +#include "customblock.hpp" +#include "groupblock.hpp" +#include "itemblock.hpp" +#include "proxyblock.hpp" + +CSVSettings::CustomBlock::CustomBlock (QWidget *parent) : AbstractBlock (parent) +{ +} + +int CSVSettings::CustomBlock::build(GroupBlockDefList &defList, GroupBlockDefList::iterator *it) +{ + int retVal = 0; + + GroupBlockDefList::iterator defaultIt; + GroupBlockDefList::iterator listIt = defList.begin(); + GroupBlockDefList::iterator proxyIt = defaultIt; + + if (it) + listIt = *it; + + ProxyBlock *proxyBlock = new ProxyBlock(getParent()); + + for (; listIt != defList.end(); ++listIt) + { + if (!(*listIt)->isProxy) + retVal = buildGroupBlock (*(*listIt)); + else + { + mGroupList << proxyBlock; + proxyIt = listIt; + } + } + + if (proxyIt != defaultIt) + retVal = buildProxyBlock (*(*proxyIt), proxyBlock); + + return retVal; +} + +CSVSettings::GroupBox *CSVSettings::CustomBlock::buildGroupBox (Orientation orientation) +{ + GroupBox *box = new GroupBox (false, mBox); + QLayout *layout = createLayout (orientation, true, box); + + return box; +} + +int CSVSettings::CustomBlock::buildGroupBlock(GroupBlockDef &def) +{ + GroupBlock *block = new GroupBlock (getParent()); + + mGroupList << block; + + connect (block, SIGNAL (signalUpdateSetting(const QString &, const QString &)), + this, SLOT (slotUpdateSetting (const QString &, const QString &))); + + return block->build(def); +} + +int CSVSettings::CustomBlock::buildProxyBlock(GroupBlockDef& def, ProxyBlock *block) +{ + if (def.properties.size() != 1) + return -1; + + int retVal = block->build(def); + + if (retVal != 0) + return retVal; + + foreach (QStringList *list, *(def.properties.at(0)->proxyList)) + { + QString proxiedBlockName = list->at(0); + + //iterate each group in the custom block, matching it to each proxied setting + //and connecting it appropriately + foreach (GroupBlock *groupBlock, mGroupList) + { + ItemBlock *proxiedBlock = groupBlock->getItemBlock (proxiedBlockName); + + if (proxiedBlock) + { + block->addSetting(proxiedBlock, list); + + //connect the proxy block's update signal to the custom block's slot + connect (block, SIGNAL (signalUpdateSetting (const QString &, const QString &)), + this, SLOT (slotUpdateSetting (const QString &, const QString &))); + } + } + } + + return 0; +} + +CSMSettings::SettingList *CSVSettings::CustomBlock::getSettings() +{ + CSMSettings::SettingList *settings = new CSMSettings::SettingList(); + + foreach (GroupBlock *block, mGroupList) + { + CSMSettings::SettingList *groupSettings = block->getSettings(); + + if (groupSettings) + settings->append(*groupSettings); + } + + return settings; +} + +bool CSVSettings::CustomBlock::updateSettings (const CSMSettings::SettingMap &settings) +{ + bool success = true; + + foreach (GroupBlock *block, mGroupList) + { + bool success2 = block->updateSettings (settings); + success = success && success2; + } + + return success; +} diff --git a/apps/opencs/view/settings/customblock.hpp b/apps/opencs/view/settings/customblock.hpp new file mode 100644 index 000000000..b2f2a0261 --- /dev/null +++ b/apps/opencs/view/settings/customblock.hpp @@ -0,0 +1,36 @@ +#ifndef CUSTOMBLOCK_HPP +#define CUSTOMBLOCK_HPP + +#include "abstractblock.hpp" + +namespace CSVSettings +{ + + class ProxyBlock; + + class CustomBlock : public AbstractBlock + { + + protected: + + GroupBlockList mGroupList; + + public: + + explicit CustomBlock (QWidget *parent = 0); + + bool updateSettings (const CSMSettings::SettingMap &settings); + CSMSettings::SettingList *getSettings(); + int build (GroupBlockDefList &defList, GroupBlockDefList::Iterator *it = 0); + + protected: + + GroupBox *buildGroupBox (Orientation orientation); + + private: + + int buildGroupBlock(GroupBlockDef &def); + int buildProxyBlock(GroupBlockDef &def, ProxyBlock *block); + }; +} +#endif // CUSTOMBLOCK_HPP diff --git a/apps/opencs/view/settings/editorpage.cpp b/apps/opencs/view/settings/editorpage.cpp new file mode 100644 index 000000000..ad5a15f6f --- /dev/null +++ b/apps/opencs/view/settings/editorpage.cpp @@ -0,0 +1,163 @@ +#include "editorpage.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_MAC +#include +#endif + +#include "../../model/settings/usersettings.hpp" +#include "groupblock.hpp" +#include "toggleblock.hpp" + +CSVSettings::EditorPage::EditorPage(QWidget *parent): + AbstractPage("Editor", parent) +{ + // Hacks to get the stylesheet look properly +#ifdef Q_OS_MAC + QPlastiqueStyle *style = new QPlastiqueStyle; + //profilesComboBox->setStyle(style); +#endif + + setupUi(); +} + +void CSVSettings::EditorPage::setupUi() +{ + GroupBlockDef undoStack (QString("Undo Stack Size")); + GroupBlockDef topLevelWindowCount (QString("Maximum Top-Level Window Count")); + GroupBlockDef reuseSubwindow (QString("Reuse Subwindows")); + GroupBlockDef customWindowSize (QString ("Custom Window Size")); + GroupBlockDef definedWindowSize (QString ("Pre-Defined Window Size")); + GroupBlockDef windowSizeToggle (QString ("Window Size")); + CustomBlockDef windowSize (QString ("Window Size")); + + //////////////////////////// + //undo stack size property + /////////////////////////// + + SettingsItemDef *undoStackItem = new SettingsItemDef (undoStack.title, "32"); + undoStack.properties << undoStackItem; + undoStackItem->minMax.left = "0"; + undoStackItem->minMax.right = "64"; + + WidgetDef stackWidget (Widget_SpinBox); + stackWidget.minMax = &(undoStackItem->minMax); + stackWidget.widgetWidth = 50; + + undoStackItem->widget = stackWidget; + + ////////////////////////////////////// + //number of top level windows property + ///////////////////////////////////// + + SettingsItemDef *topLevelItem = new SettingsItemDef (topLevelWindowCount.title, "100"); + topLevelWindowCount.properties << topLevelItem; + topLevelItem->minMax.left = "1"; + topLevelItem->minMax.right = "256"; + + WidgetDef topLvlWinWidget (Widget_SpinBox); + topLvlWinWidget.minMax = &(topLevelItem->minMax); + topLvlWinWidget.widgetWidth = 50; + + topLevelItem->widget = topLvlWinWidget; + + /////////////////////////// + //reuse subwindows property + //////////////////////////// + + SettingsItemDef *reuseSubItem = new SettingsItemDef (reuseSubwindow.title, "Reuse Subwindows"); + *(reuseSubItem->valueList) << "None" << "Top-Level" << "Document-Level"; + + WidgetDef reuseSubWidget (Widget_RadioButton); + reuseSubWidget.valueList = (reuseSubItem->valueList); + reuseSubWidget.widgetAlignment = Align_Left; + + reuseSubwindow.properties << reuseSubItem; + reuseSubItem->widget = reuseSubWidget; + + /////////////////////////////// + //custom window size properties + /////////////////////////////// + + //custom width + SettingsItemDef *widthItem = new SettingsItemDef ("Window Width", "640"); + widthItem->widget = WidgetDef (Widget_LineEdit); + widthItem->widget.widgetWidth = 45; + + //custom height + SettingsItemDef *heightItem = new SettingsItemDef ("Window Height", "480"); + heightItem->widget = WidgetDef (Widget_LineEdit); + heightItem->widget.widgetWidth = 45; + heightItem->widget.caption = "x"; + + customWindowSize.properties << widthItem << heightItem; + customWindowSize.widgetOrientation = Orient_Horizontal; + customWindowSize.isVisible = false; + + + //pre-defined + SettingsItemDef *widthByHeightItem = new SettingsItemDef ("Window Size", "640x480"); + WidgetDef widthByHeightWidget = WidgetDef (Widget_ComboBox); + widthByHeightWidget.widgetWidth = 90; + *(widthByHeightItem->valueList) << "640x480" << "800x600" << "1024x768"; + + QStringList *widthProxy = new QStringList; + QStringList *heightProxy = new QStringList; + + (*widthProxy) << "Window Width" << "640" << "800" << "1024"; + (*heightProxy) << "Window Height" << "480" << "600" << "768"; + + *(widthByHeightItem->proxyList) << widthProxy << heightProxy; + + widthByHeightItem->widget = widthByHeightWidget; + + definedWindowSize.properties << widthByHeightItem; + definedWindowSize.isProxy = true; + definedWindowSize.isVisible = false; + + // window size toggle + windowSizeToggle.captions << "Pre-Defined" << "Custom"; + windowSizeToggle.widgetOrientation = Orient_Vertical; + windowSizeToggle.isVisible = false; + + //define a widget for each group in the toggle + for (int i = 0; i < 2; i++) + windowSizeToggle.widgets << new WidgetDef (Widget_RadioButton); + + windowSizeToggle.widgets.at(0)->isDefault = false; + + windowSize.blockDefList << &windowSizeToggle << &definedWindowSize << &customWindowSize; + windowSize.defaultValue = "Custom"; + + QGridLayout *pageLayout = new QGridLayout(this); + + setLayout (pageLayout); + + mAbstractBlocks << buildBlock (topLevelWindowCount) + << buildBlock (reuseSubwindow) + << buildBlock (windowSize) + << buildBlock (undoStack); + + foreach (AbstractBlock *block, mAbstractBlocks) + { + connect (block, SIGNAL (signalUpdateSetting (const QString &, const QString &)), + this, SIGNAL (signalUpdateEditorSetting (const QString &, const QString &)) ); + } +} + +void CSVSettings::EditorPage::initializeWidgets (const CSMSettings::SettingMap &settings) +{ + //iterate each item in each blocks in this section + //validate the corresponding setting against the defined valuelist if any. + for (AbstractBlockList::Iterator it_block = mAbstractBlocks.begin(); + it_block != mAbstractBlocks.end(); ++it_block) + (*it_block)->updateSettings (settings); +} diff --git a/apps/opencs/view/settings/editorpage.hpp b/apps/opencs/view/settings/editorpage.hpp new file mode 100644 index 000000000..0c1e80e89 --- /dev/null +++ b/apps/opencs/view/settings/editorpage.hpp @@ -0,0 +1,28 @@ +#ifndef EDITORPAGE_H +#define EDITORPAGE_H + +#include "abstractpage.hpp" + +class QGroupBox; + +namespace CSVSettings { + + class UserSettings; + class AbstractBlock; + + class EditorPage : public AbstractPage + { + Q_OBJECT + + public: + + EditorPage(QWidget *parent = 0); + + void setupUi(); + void initializeWidgets (const CSMSettings::SettingMap &settings); + + signals: + void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue); + }; +} +#endif //EDITORPAGE_H diff --git a/apps/opencs/view/settings/groupblock.cpp b/apps/opencs/view/settings/groupblock.cpp new file mode 100644 index 000000000..b18f629ba --- /dev/null +++ b/apps/opencs/view/settings/groupblock.cpp @@ -0,0 +1,108 @@ +#include "groupblock.hpp" +#include "itemblock.hpp" + +CSVSettings::GroupBlock::GroupBlock (QWidget* parent) + : AbstractBlock (parent) +{} + +CSVSettings::GroupBlock::GroupBlock (bool isVisible, QWidget *parent) + : AbstractBlock (isVisible, parent) +{} + +int CSVSettings::GroupBlock::build (GroupBlockDef &def) +{ + + if (def.properties.size() == 0) + return -1; + + int retVal = 0; + + setVisible (def.isVisible); + + mBox->setLayout(createLayout (def.widgetOrientation, true)); + + setObjectName (def.title); + mBox->setTitle (def.title); + + foreach (SettingsItemDef *itemDef, def.properties) + { + ItemBlock *block = new ItemBlock (mBox); + + if (block->build (*itemDef) < 0) + { + retVal = -2; + break; + } + + mItemBlockList << block; + mBox->layout()->addWidget (block->getGroupBox()); + + connect (block, SIGNAL (signalUpdateSetting (const QString &, const QString &)), + this, SLOT (slotUpdateSetting (const QString &, const QString &) )); + } + + return retVal; +} + +CSMSettings::SettingList *CSVSettings::GroupBlock::getSettings() +{ + CSMSettings::SettingList *settings = 0; + + foreach (ItemBlock *block, mItemBlockList) + { + if (!settings) + settings = new CSMSettings::SettingList(); + + settings->append(*(block->getSettings ())); + } + + return settings; +} + +CSVSettings::ItemBlock *CSVSettings::GroupBlock::getItemBlock (const QString &name, ItemBlockList *blockList) +{ + ItemBlock *retBlock = 0; + + if (!blockList) + blockList = &mItemBlockList; + + foreach (ItemBlock *block, *blockList) + { + if (block->objectName() == name) + { + retBlock = block; + break; + } + } + + return retBlock; +} + +CSVSettings::ItemBlock *CSVSettings::GroupBlock::getItemBlock (int index) +{ + ItemBlock *retBlock = 0; + + if (mItemBlockList.size() > index) + retBlock = mItemBlockList.at(index); + + return retBlock; +} + +bool CSVSettings::GroupBlock::updateSettings (const CSMSettings::SettingMap &settings) +{ + bool success = true; + + //update all non-proxy settings + foreach (ItemBlock *block, mItemBlockList) + { + CSMSettings::SettingContainer *setting = settings[block->objectName()]; + + if (setting) + { + bool success2 = block->update (setting->getValue()); + success = success && success2; + } + } + + return success; +} diff --git a/apps/opencs/view/settings/groupblock.hpp b/apps/opencs/view/settings/groupblock.hpp new file mode 100644 index 000000000..a8fd4e3b4 --- /dev/null +++ b/apps/opencs/view/settings/groupblock.hpp @@ -0,0 +1,32 @@ +#ifndef GROUPBLOCK_HPP +#define GROUPBLOCK_HPP + +#include +#include "abstractblock.hpp" + +namespace CSVSettings +{ + class ItemBlock; + + class GroupBlock : public AbstractBlock + { + ItemBlockList mItemBlockList; + + public: + GroupBlock (QWidget* parent = 0); + GroupBlock (bool isVisible, QWidget *parent = 0); + + int build (GroupBlockDef &def); + + bool updateSettings (const CSMSettings::SettingMap &settings); + + CSMSettings::SettingList *getSettings(); + ItemBlock *getItemBlock (const QString &name, ItemBlockList *blockList = 0); + ItemBlock *getItemBlock (int index); + + protected: + int buildLayout (GroupBlockDef &def); + + }; +} +#endif // GROUPBLOCK_HPP diff --git a/apps/opencs/view/settings/groupbox.cpp b/apps/opencs/view/settings/groupbox.cpp new file mode 100644 index 000000000..da2cc2571 --- /dev/null +++ b/apps/opencs/view/settings/groupbox.cpp @@ -0,0 +1,56 @@ +#include "groupbox.hpp" + +const QString CSVSettings::GroupBox::INVISIBLE_BOX_STYLE = + QString::fromUtf8("QGroupBox { border: 0px; padding 0px; margin: 0px;}"); + +CSVSettings::GroupBox::GroupBox(QWidget *parent) : + QGroupBox (parent) +{ + initBox(); +} + +CSVSettings::GroupBox::GroupBox (bool isVisible, QWidget *parent) : + QGroupBox (parent) +{ + initBox(isVisible); +} + +void CSVSettings::GroupBox::initBox(bool isVisible) +{ + setFlat (true); + VISIBLE_BOX_STYLE = styleSheet(); + + if (!isVisible) + setStyleSheet (INVISIBLE_BOX_STYLE); +} + +bool CSVSettings::GroupBox::borderVisibile() const +{ + return (styleSheet() != INVISIBLE_BOX_STYLE); +} + +void CSVSettings::GroupBox::setTitle (const QString &title) +{ + if (borderVisibile() ) + { + QGroupBox::setTitle (title); + setMinimumWidth(); + } +} + +void CSVSettings::GroupBox::setBorderVisibility (bool value) +{ + if (value) + setStyleSheet(VISIBLE_BOX_STYLE); + else + setStyleSheet(INVISIBLE_BOX_STYLE); +} + +void CSVSettings::GroupBox::setMinimumWidth() +{ + //set minimum width to accommodate title, if needed + //1.5 multiplier to account for bold title. + QFontMetrics fm (font()); + int minWidth = fm.width(title()); + QGroupBox::setMinimumWidth (minWidth * 1.5); +} diff --git a/apps/opencs/view/settings/groupbox.hpp b/apps/opencs/view/settings/groupbox.hpp new file mode 100644 index 000000000..43cb2e0c3 --- /dev/null +++ b/apps/opencs/view/settings/groupbox.hpp @@ -0,0 +1,27 @@ +#ifndef GROUPBOX_HPP +#define GROUPBOX_HPP + +#include + +namespace CSVSettings +{ + class GroupBox : public QGroupBox + { + static const QString INVISIBLE_BOX_STYLE; + QString VISIBLE_BOX_STYLE; //not a const... + + public: + explicit GroupBox (QWidget *parent = 0); + explicit GroupBox (bool isVisible, QWidget *parent = 0); + + void setTitle (const QString &title); + void setBorderVisibility (bool value); + bool borderVisibile() const; + + private: + void setMinimumWidth(); + void initBox(bool isVisible = true); + }; +} + +#endif // GROUPBOX_HPP diff --git a/apps/opencs/view/settings/itemblock.cpp b/apps/opencs/view/settings/itemblock.cpp new file mode 100644 index 000000000..9cb0ae1a1 --- /dev/null +++ b/apps/opencs/view/settings/itemblock.cpp @@ -0,0 +1,115 @@ +#include "itemblock.hpp" + +#include + +CSVSettings::ItemBlock::ItemBlock (QWidget* parent) + : mSetting (0), AbstractBlock (false, parent) +{ +} + +int CSVSettings::ItemBlock::build(SettingsItemDef &iDef) +{ + buildItemBlock (iDef); + buildItemBlockWidgets (iDef); + + return 0; +} + +void CSVSettings::ItemBlock::buildItemBlockWidgets (SettingsItemDef &iDef) +{ + WidgetDef wDef = iDef.widget; + QLayout *blockLayout = 0; + QString defaultValue = iDef.defaultValue; + + switch (wDef.type) + { + + case Widget_CheckBox: + case Widget_RadioButton: + + foreach (QString item, *(iDef.valueList)) + { + wDef.caption = item; + wDef.isDefault = (item == defaultValue); + + blockLayout = buildWidget (item, wDef, blockLayout)->getLayout(); + } + + break; + + case Widget_ComboBox: + case Widget_ListBox: + + //assign the item's value list to the widget's value list. + //pass through to default to finish widget construction. + if (!wDef.valueList) + wDef.valueList = iDef.valueList; + + default: + //only one instance of this non-list widget type. + //Set it's value to the default value for the item and build the widget. + + if (wDef.value.isEmpty()) + wDef.value = iDef.defaultValue; + + buildWidget (iDef.name, wDef); + } +} + +void CSVSettings::ItemBlock::buildItemBlock (SettingsItemDef &iDef) +{ + QString defaultValue = iDef.defaultValue; + + setObjectName(iDef.name); + + mSetting = new CSMSettings::SettingsItem (objectName(), + iDef.hasMultipleValues, iDef.defaultValue, + parent()); + + if (iDef.valueList) + mSetting->setValueList(iDef.valueList); + + if (!iDef.minMax.isEmpty()) + mSetting->setValuePair(iDef.minMax); +} + + +bool CSVSettings::ItemBlock::update (const QString &value) +{ + bool success = updateItem (value); + + if (success) + signalUpdateWidget (value); + + return success; +} + + +bool CSVSettings::ItemBlock::updateItem (const QString &value) +{ + return mSetting->updateItem(value); +} + + +bool CSVSettings::ItemBlock::updateBySignal(const QString &name, const QString &value, bool &doEmit) +{ + bool success = (mSetting->getValue() != value); + + if (success) + success = updateItem(value); + + return success; +} + +CSMSettings::SettingList *CSVSettings::ItemBlock::getSettings () +{ + CSMSettings::SettingList *list = new CSMSettings::SettingList(); + list->push_back(mSetting); + + return list; +} + +QString CSVSettings::ItemBlock::getValue() const +{ + return mSetting->getValue(); +} diff --git a/apps/opencs/view/settings/itemblock.hpp b/apps/opencs/view/settings/itemblock.hpp new file mode 100644 index 000000000..c7714ac64 --- /dev/null +++ b/apps/opencs/view/settings/itemblock.hpp @@ -0,0 +1,38 @@ +#ifndef ITEMBLOCK_HPP +#define ITEMBLOCK_HPP + +#include "abstractblock.hpp" + +namespace CSVSettings +{ + + class ItemBlock : public AbstractBlock + { + CSMSettings::SettingsItem *mSetting; + WidgetList mWidgetList; + + public: + + ItemBlock (QWidget* parent = 0); + + bool updateSettings (const CSMSettings::SettingMap &settings) { return false; } + + CSMSettings::SettingList *getSettings (); + QString getValue () const; + + int getSettingCount(); + bool update (const QString &value); + + int build(SettingsItemDef &iDef); + + private: + + void buildItemBlock (SettingsItemDef& iDef); + void buildItemBlockWidgets (SettingsItemDef& iDef); + bool updateItem (const QString &); + + bool updateBySignal (const QString &name, const QString &value, bool &doEmit); + }; +} + +#endif // ITEMBLOCK_HPP diff --git a/apps/opencs/view/settings/proxyblock.cpp b/apps/opencs/view/settings/proxyblock.cpp new file mode 100644 index 000000000..71a307084 --- /dev/null +++ b/apps/opencs/view/settings/proxyblock.cpp @@ -0,0 +1,149 @@ +#include "proxyblock.hpp" +#include "itemblock.hpp" + +CSVSettings::ProxyBlock::ProxyBlock (QWidget *parent) + : GroupBlock (parent) +{ +} +int CSVSettings::ProxyBlock::build (GroupBlockDef &proxyDef) +{ + //get the list of pre-defined values for the proxy + mValueList = proxyDef.properties.at(0)->valueList; + + bool success = GroupBlock::build(proxyDef); + + //connect the item block of the proxy setting to the proxy-update slot + connect (getItemBlock(0), SIGNAL (signalUpdateSetting(const QString &, const QString &)), + this, SLOT (slotUpdateProxySetting (const QString &, const QString &))); + + return success; +} + +void CSVSettings::ProxyBlock::addSetting (ItemBlock *settingBlock, QStringList *proxyList) +{ + //connect the item block of the proxied seting to the generic update slot + connect (settingBlock, SIGNAL (signalUpdateSetting(const QString &, const QString &)), + this, SLOT (slotUpdateProxySetting(const QString &, const QString &))); + + mProxiedItemBlockList << settingBlock; + mProxyList << proxyList; +} + +bool CSVSettings::ProxyBlock::updateSettings (const CSMSettings::SettingMap &settings) +{ + return updateByProxiedSettings(&settings); +} + +bool CSVSettings::ProxyBlock::updateBySignal(const QString &name, const QString &value, bool &doEmit) +{ + doEmit = false; + return updateProxiedSettings(); +} + +void CSVSettings::ProxyBlock::slotUpdateProxySetting (const QString &name, const QString &value) +{ + updateByProxiedSettings(); +} + +bool CSVSettings::ProxyBlock::updateProxiedSettings() +{ + foreach (ItemBlock *block, mProxiedItemBlockList) + { + QString value = getItemBlock(0)->getValue(); + + bool success = false; + int i = 0; + for (; i < mValueList->size(); ++i) + { + success = (value == mValueList->at(i)); + + if (success) + break; + } + + if (!success) + return false; + + foreach (QStringList *list, mProxyList) + { + if ( list->at(0) == block->objectName()) + block->update (list->at(++i)); + } + } + + return true; +} + +bool CSVSettings::ProxyBlock::updateByProxiedSettings(const CSMSettings::SettingMap *settings) +{ + bool success = false; + int commonIndex = -1; + + //update all proxy settings based on values from non-proxies + foreach (QStringList *list, mProxyList) + { + //Iterate each proxy item's proxied setting list, getting the current values + //Compare those value indices. + //If indices match, they correlate to one of the proxy's values in it's value list + + //first value is always the name of the setting the proxy setting manages + QStringList::Iterator itProxyValue = list->begin(); + QString proxiedSettingName = (*itProxyValue); + QString proxiedSettingValue = ""; + itProxyValue++; + + if (!settings) + { + //get the actual setting value + ItemBlock *block = getProxiedItemBlock (proxiedSettingName); + + if (block) + proxiedSettingValue = block->getValue(); + } + else + proxiedSettingValue = (*settings)[proxiedSettingName]->getValue(); + + int j = 0; + + //iterate each value in the proxy string list + for (; itProxyValue != (list)->end(); ++itProxyValue) + { + success = ((*itProxyValue) == proxiedSettingValue); + + if (success) + break; + + j++; + } + + //break if no match was found + if ( !success ) + break; + + if (commonIndex != -1) + success = (commonIndex == j); + else + commonIndex = j; + + //break if indices were found, but mismatch + if (!success) + break; + } + + //if successful, the proxied setting values match a pre-defined value in the + //proxy's value list. Set the proxy to that value index + if (success) + { + ItemBlock *block = getItemBlock(0); + + if (block) + block->update (mValueList->at(commonIndex)); + } + + return success; +} + +CSVSettings::ItemBlock *CSVSettings::ProxyBlock::getProxiedItemBlock (const QString &name) +{ + return getItemBlock (name, &mProxiedItemBlockList); +} diff --git a/apps/opencs/view/settings/proxyblock.hpp b/apps/opencs/view/settings/proxyblock.hpp new file mode 100644 index 000000000..f757842ea --- /dev/null +++ b/apps/opencs/view/settings/proxyblock.hpp @@ -0,0 +1,42 @@ +#ifndef PROXYBLOCK_HPP +#define PROXYBLOCK_HPP + +#include "groupblock.hpp" + +namespace CSVSettings +{ + class ProxyBlock : public GroupBlock + { + Q_OBJECT + + //NOTE: mProxyItemBlockList and mProxyList + //should be combined into a value pair and stored in one list. + ItemBlockList mProxiedItemBlockList; + ProxyList mProxyList; + QStringList *mValueList; + + public: + + explicit ProxyBlock (QWidget *parent = 0); + explicit ProxyBlock (ItemBlock *proxyItemBlock, QWidget *parent = 0); + + void addSetting (ItemBlock* settingBlock, QStringList *proxyList); + int build (GroupBlockDef &def); + + CSMSettings::SettingList *getSettings() { return 0; } + bool updateSettings (const CSMSettings::SettingMap &settings); + bool updateBySignal (const QString &name, const QString &value, bool &doEmit); + + private: + + ItemBlock *getProxiedItemBlock (const QString &name); + bool updateByProxiedSettings(const CSMSettings::SettingMap *settings = 0); + bool updateProxiedSettings(); + + private slots: + + void slotUpdateProxySetting (const QString &name, const QString &value); + + }; +} +#endif // PROXYBLOCK_HPP diff --git a/apps/opencs/view/settings/settingwidget.cpp b/apps/opencs/view/settings/settingwidget.cpp new file mode 100644 index 000000000..2c93986e7 --- /dev/null +++ b/apps/opencs/view/settings/settingwidget.cpp @@ -0,0 +1 @@ +#include "settingwidget.hpp" diff --git a/apps/opencs/view/settings/settingwidget.hpp b/apps/opencs/view/settings/settingwidget.hpp new file mode 100644 index 000000000..b29523a3a --- /dev/null +++ b/apps/opencs/view/settings/settingwidget.hpp @@ -0,0 +1,208 @@ +#ifndef SETTINGWIDGET_HPP +#define SETTINGWIDGET_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "abstractwidget.hpp" + +namespace CSVSettings +{ + //VALID FOR RADIOBUTTON / CHECKBOX (or other toggle widget with it's own label) + template + class SettingWidget : public AbstractWidget + { + + T1 *mWidget; + + public: + + explicit SettingWidget (WidgetDef &def, QLayout *layout, QWidget* parent = 0) + : AbstractWidget (layout, parent), mWidget (new T1 (parent)) + { + mWidget->setText(def.caption); + build (mWidget, def, true); + mWidget->setChecked(def.isDefault); + + connect (mWidget, SIGNAL (toggled (bool)), + this, SLOT (slotUpdateItem (bool))); + } + + QWidget *widget() { return mWidget; } + + private: + + void updateWidget (const QString &value) + { + if ( value == mWidget->objectName() && !mWidget->isChecked() ) + mWidget->setChecked (true); + } + }; + + template <> + class SettingWidget : public AbstractWidget + { + + QSpinBox *mWidget; + + public: + + SettingWidget (WidgetDef &def, QLayout *layout, QWidget *parent = 0) + : AbstractWidget (layout, parent), mWidget (new QSpinBox (parent)) + { + def.caption += tr(" (%1 to %2)").arg(def.minMax->left).arg(def.minMax->right); + + mWidget->setMaximum (def.minMax->right.toInt()); + mWidget->setMinimum (def.minMax->left.toInt()); + mWidget->setValue (def.value.toInt()); + + build (mWidget, def); + + connect (mWidget, SIGNAL (valueChanged (int)), + this, SLOT (slotUpdateItem (int))); + + mWidget->setAlignment (getAlignment(def.valueAlignment)); + + + } + + QWidget *widget() { return mWidget; } + + private: + + void updateWidget (const QString &value) + { + int intVal = value.toInt(); + + if (intVal >= mWidget->minimum() && intVal <= mWidget->maximum() && intVal != mWidget->value()) + mWidget->setValue (intVal); + } + + signals: + + }; + + template <> + class SettingWidget : public CSVSettings::AbstractWidget + { + + QComboBox *mWidget; + + + public: + + explicit SettingWidget(WidgetDef &def, QLayout *layout, QWidget *parent = 0) + : AbstractWidget (layout, parent), mWidget (new QComboBox (parent)) + { + int i = 0; + + foreach (QString item, *(def.valueList)) + { + mWidget->addItem (item); + + if (item == def.value) + mWidget->setCurrentIndex(i); + + i++; + } + + build (mWidget, def); + + connect (mWidget, SIGNAL (currentIndexChanged (const QString &)), + this, SLOT (slotUpdateItem (const QString &))); + + //center the combo box items + mWidget->setEditable (true); + mWidget->lineEdit()->setReadOnly (true); + mWidget->lineEdit()->setAlignment (getAlignment(def.valueAlignment)); + + QFlags alignment = mWidget->lineEdit()->alignment(); + + for (int j = 0; j < mWidget->count(); j++) + mWidget->setItemData (j, QVariant(alignment), Qt::TextAlignmentRole); + } + + QWidget *widget() { return mWidget; } + + private: + + void updateWidget (const QString &value) + { + if (mWidget->currentText() != value) + mWidget->setCurrentIndex(mWidget->findText(value)); + } + + }; + + template <> + class SettingWidget : public CSVSettings::AbstractWidget + { + + QLineEdit *mWidget; + + public: + + explicit SettingWidget(WidgetDef &def, QLayout *layout, QWidget *parent = 0) + : AbstractWidget (layout, parent), mWidget (new QLineEdit (parent)) + { + if (!def.inputMask.isEmpty()) + mWidget->setInputMask (def.inputMask); + + mWidget->setText (def.value); + + build (mWidget, def); + + connect (mWidget, SIGNAL (textChanged (const QString &)), + this, SLOT (slotUpdateItem (const QString &))); + + mWidget->setAlignment (getAlignment(def.valueAlignment)); + } + + QWidget *widget() { return mWidget; } + + void updateWidget (const QString &value) + { + if (mWidget->text() != value) + mWidget->setText(value); + } + }; + + template <> + class SettingWidget : public CSVSettings::AbstractWidget + { + + QListWidget *mWidget; + + public: + + explicit SettingWidget(WidgetDef &def, QLayout *layout, QWidget *parent = 0 ) + : AbstractWidget (layout, parent), mWidget (new QListWidget (parent)) + { + int i = 0; + + foreach (QString item, *(def.valueList)) + { + mWidget->addItem (item); + + if (item == def.value) {} + i++; + } + build (mWidget, def); + } + + QWidget *widget() { return mWidget; } + + private: + void updateWidget (const QString &value) {} + }; + +} +#endif // SETTINGWIDGET_HPP diff --git a/apps/opencs/view/settings/support.cpp b/apps/opencs/view/settings/support.cpp new file mode 100644 index 000000000..d79edfdb3 --- /dev/null +++ b/apps/opencs/view/settings/support.cpp @@ -0,0 +1 @@ +#include "support.hpp" diff --git a/apps/opencs/view/settings/support.hpp b/apps/opencs/view/settings/support.hpp new file mode 100644 index 000000000..7185d2706 --- /dev/null +++ b/apps/opencs/view/settings/support.hpp @@ -0,0 +1,144 @@ +#ifndef VIEW_SUPPORT_HPP +#define VIEW_SUPPORT_HPP + +#include +#include + +#include "../../model/settings/support.hpp" + +namespace CSVSettings +{ + struct WidgetDef; + class ItemBlock; + class GroupBlock; + struct GroupBlockDef; + + typedef QList GroupBlockDefList; + typedef QList GroupBlockList; + typedef QList ItemBlockList; + typedef QList ProxyList; + typedef QList WidgetList; + typedef QMap ItemBlockMap; + + enum Orientation + { + Orient_Horizontal, + Orient_Vertical + }; + + enum WidgetType + { + Widget_CheckBox, + Widget_ComboBox, + Widget_LineEdit, + Widget_ListBox, + Widget_RadioButton, + Widget_SpinBox, + Widget_Undefined + }; + + enum Alignment + { + Align_Left = Qt::AlignLeft, + Align_Center = Qt::AlignHCenter, + Align_Right = Qt::AlignRight + }; + + //template for defining the widget of a property. + struct WidgetDef + { + WidgetType type; //type of widget providing input + int labelWidth; //width of caption label + int widgetWidth; //width of input widget + Orientation orientation; //label / widget orientation (horizontal / vertical) + QString inputMask; //input mask (line edit) + QString caption; //label caption. Leave empty for multiple items. See BlockDef::captionList + QString value; //widget value. Leave empty for multiple items. See BlockDef::valueList + CSMSettings::QStringPair *minMax; //Min/Max QString value pair. If empty, assigned to property item value pair. + QStringList *valueList; //value list for list widgets. If left empty, is assigned to property item value list during block build(). + bool isDefault; //isDefault - determined at runtime. + Alignment valueAlignment; //left / center / right-justify text in widget + Alignment widgetAlignment; //left / center / right-justify widget in group box + + + WidgetDef() : labelWidth (-1), widgetWidth (-1), + orientation (Orient_Horizontal), + isDefault (true), valueAlignment (Align_Center), + widgetAlignment (Align_Right), + inputMask (""), value (""), + caption (""), valueList (0) + {} + + WidgetDef (WidgetType widgType) + : type (widgType), orientation (Orient_Horizontal), + caption (""), value (""), valueAlignment (Align_Center), + widgetAlignment (Align_Right), + labelWidth (-1), widgetWidth (-1), + valueList (0), isDefault (true) + {} + + }; + + //Defines the attributes of the property as it is represented in the config file + //as well as the UI elements (group box and widget) that serve it. + //Only one widget may serve as the input widget for the property. + struct SettingsItemDef + { + QString name; //property name + QStringList *valueList; //list of valid values for the property. + //Used to populate option widget captions or list widget item lists (see WidgetDef::caption / value) + QString defaultValue; + bool hasMultipleValues; + CSMSettings::QStringPair minMax; //minimum / maximum value pair + WidgetDef widget; //definition of the input widget for this setting + Orientation orientation; //general orientation of the widget / label for this property + ProxyList *proxyList; //list of property and corresponding default values for proxy widget + + SettingsItemDef() : name (""), defaultValue (""), orientation (Orient_Vertical), hasMultipleValues (false) + {} + + SettingsItemDef (QString propName, QString propDefault, Orientation propOrient = Orient_Vertical) + : name (propName), defaultValue (propDefault), orientation (propOrient), + hasMultipleValues(false), valueList (new QStringList), proxyList ( new ProxyList) + {} + }; + + + //Hierarchically, this is a "sub-section" of properties within a section, solely for UI organization. + //Does not correlate to config file structure. + struct GroupBlockDef + { + QString title; //title of the block containing the property or properties of this sub-section + QStringList captions; //list of captions for widgets at the block level (not associated with any particular property) + WidgetList widgets; //list of widgets at the block level (not associated with any particular property) + QList properties; //list of the property(ies) which are subordinate to the property block. + Orientation widgetOrientation; //general orientation of widgets in group block + bool isVisible; //determines whether or not box border/title are visible + bool isProxy; //indicates whether or not this block defines a proxy block + QString defaultValue; //generic default value attribute + + GroupBlockDef (): title(""), widgetOrientation (Orient_Vertical), isVisible (true), isProxy (false), defaultValue ("") + {} + + GroupBlockDef (QString blockTitle) + : title (blockTitle), widgetOrientation (Orient_Vertical), isProxy (false), isVisible (true), defaultValue ("") + {} + }; + + struct CustomBlockDef + { + QString title; + QString defaultValue; //default value for widgets unique to the custom block + GroupBlockDefList blockDefList; //list of settings groups that comprise the settings within the custom block + Orientation blockOrientation; + + CustomBlockDef (): title (""), defaultValue (""), blockOrientation (Orient_Horizontal) + {} + + CustomBlockDef (const QString &blockTitle) + : title (blockTitle), defaultValue (""), blockOrientation (Orient_Horizontal) + {} + }; +} + +#endif // VIEW_SUPPORT_HPP diff --git a/apps/opencs/view/settings/toggleblock.cpp b/apps/opencs/view/settings/toggleblock.cpp new file mode 100644 index 000000000..12a42ca9c --- /dev/null +++ b/apps/opencs/view/settings/toggleblock.cpp @@ -0,0 +1,80 @@ +#include "toggleblock.hpp" +#include "groupblock.hpp" +#include "groupbox.hpp" +#include "itemblock.hpp" + +CSVSettings::ToggleBlock::ToggleBlock(QWidget *parent) : + CustomBlock(parent) +{} + +int CSVSettings::ToggleBlock::build(CustomBlockDef &def) +{ + if (def.blockDefList.size()==0) + return -1; + + QList::Iterator it = def.blockDefList.begin(); + + //first def in the list is the def for the toggle block + GroupBlockDef *toggleDef = *it++; + + if (toggleDef->captions.size() != def.blockDefList.size()-1 ) + return -2; + + if (toggleDef->widgets.size() == 0) + return -3; + + //create the toogle block UI structure + QLayout *blockLayout = createLayout (def.blockOrientation, true); + GroupBox *propertyBox = buildGroupBox (toggleDef->widgetOrientation); + + mBox->setLayout(blockLayout); + mBox->setTitle (toggleDef->title); + + //build the blocks contained in the def list + //this manages proxy block construction. + //Any settings managed by the proxy setting + //must be included in the blocks defined in the list. + CustomBlock::build (def.blockDefList, &it); + + for (GroupBlockList::iterator it = mGroupList.begin(); it != mGroupList.end(); ++it) + propertyBox->layout()->addWidget ((*it)->getGroupBox()); + + //build togle widgets, linking them to the settings + GroupBox *toggleBox = buildToggleWidgets (*toggleDef, def.defaultValue); + + blockLayout->addWidget(toggleBox); + blockLayout->addWidget(propertyBox); + blockLayout->setAlignment (propertyBox, Qt::AlignRight); + + return 0; +} + +CSVSettings::GroupBox *CSVSettings::ToggleBlock::buildToggleWidgets (GroupBlockDef &def, QString &defaultToggle) +{ + GroupBox *box = new GroupBox (false, getParent()); + + QLayout *layout = createLayout (def.widgetOrientation, true, static_cast(box)); + + for (int i = 0; i < def.widgets.size(); ++i) + { + QString caption = def.captions.at(i); + WidgetDef *wDef = def.widgets.at(i); + + wDef->caption = caption; + wDef->widgetAlignment = Align_Left; + + AbstractWidget *widg = buildWidget (caption, *wDef, layout, false); + + GroupBlock *block = mGroupList.at(i); + + //connect widget's update to the property block's enabled status + connect (widg->widget(), SIGNAL (toggled (bool)), block, SLOT (slotSetEnabled(bool))); + + //enable the default toggle option + block->getGroupBox()->setEnabled( caption == defaultToggle ); + + layout = widg->getLayout(); + } + + return box; +} diff --git a/apps/opencs/view/settings/toggleblock.hpp b/apps/opencs/view/settings/toggleblock.hpp new file mode 100644 index 000000000..8afe701b8 --- /dev/null +++ b/apps/opencs/view/settings/toggleblock.hpp @@ -0,0 +1,27 @@ +#ifndef TOGGLEBLOCK_HPP +#define TOGGLEBLOCK_HPP + +#include + +#include "customblock.hpp" + +namespace CSVSettings +{ + class GroupBlock; + class GroupBox; + class ToggleWidget; + class ItemBlock; + + class ToggleBlock : public CustomBlock + { + + public: + explicit ToggleBlock(QWidget *parent = 0); + + int build (CustomBlockDef &def); + + private: + GroupBox *buildToggleWidgets (GroupBlockDef &def, QString &defaultToggle); + }; +} +#endif // TOGGLEBLOCK_HPP diff --git a/apps/opencs/view/settings/usersettingsdialog.cpp b/apps/opencs/view/settings/usersettingsdialog.cpp new file mode 100644 index 000000000..012fc0408 --- /dev/null +++ b/apps/opencs/view/settings/usersettingsdialog.cpp @@ -0,0 +1,173 @@ +#include "usersettingsdialog.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "blankpage.hpp" +#include "editorpage.hpp" +#include "../../model/settings/support.hpp" + +#include "settingwidget.hpp" +#include + +CSVSettings::UserSettingsDialog::UserSettingsDialog(QMainWindow *parent) : + QMainWindow (parent), mStackedWidget (0) +{ + setWindowTitle(QString::fromUtf8 ("User Settings")); + buildPages(); + setWidgetStates (loadSettings()); + positionWindow (); + + connect (mListWidget, + SIGNAL (currentItemChanged(QListWidgetItem*, QListWidgetItem*)), + this, + SLOT (slotChangePage (QListWidgetItem*, QListWidgetItem*))); +} + +CSVSettings::UserSettingsDialog::~UserSettingsDialog() +{ +} + +void CSVSettings::UserSettingsDialog::closeEvent (QCloseEvent *event) +{ + writeSettings(); +} + +void CSVSettings::UserSettingsDialog::setWidgetStates (CSMSettings::SectionMap settingsMap) +{ + //iterate the tabWidget's pages (sections) + for (int i = 0; i < mStackedWidget->count(); i++) + { + //get the settings defined for the entire section + CSMSettings::SettingMap *settings = settingsMap [mStackedWidget->widget(i)->objectName()]; + + //if found, initialize the page's widgets + if (settings) + { + AbstractPage *page = getAbstractPage (i); + page->initializeWidgets(*settings); + } + } +} + +void CSVSettings::UserSettingsDialog::buildPages() +{ + //craete central widget with it's layout and immediate children + QWidget *centralWidget = new QWidget (this); + + mListWidget = new QListWidget (centralWidget); + mStackedWidget = new QStackedWidget (centralWidget); + + QLayout* dialogLayout = new QHBoxLayout(); + + dialogLayout->addWidget (mListWidget); + dialogLayout->addWidget (mStackedWidget); + + centralWidget->setLayout (dialogLayout); + + setCentralWidget (centralWidget); + setDockOptions (QMainWindow::AllowNestedDocks); + //uncomment to test with sample editor page. + //createSamplePage(); + createPage("Page1"); + createPage("Page2"); + createPage("Page3"); +} + +void CSVSettings::UserSettingsDialog::createSamplePage() +{ + //add pages to stackedwidget and items to listwidget + CSVSettings::AbstractPage *page + = new CSVSettings::EditorPage(this); + + mStackedWidget->addWidget (page); + + new QListWidgetItem (page->objectName(), mListWidget); + + connect ( page, SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &)), + &(CSMSettings::UserSettings::instance()), SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &))); +} + +void CSVSettings::UserSettingsDialog::positionWindow () +{ + QRect scr = QApplication::desktop()->screenGeometry(); + + move(scr.center().x() - (width() / 2), scr.center().y() - (height() / 2)); + +} + +CSMSettings::SectionMap CSVSettings::UserSettingsDialog::loadSettings () +{ + QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); + + mPaths.append(QString("opencs.cfg")); + mPaths.append(userPath + QString("opencs.cfg")); + + CSMSettings::SectionMap settingsMap; + + foreach (const QString &path, mPaths) + { + qDebug() << "Loading config file:" << qPrintable(path); + QFile file(path); + + if (file.exists()) + { + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + QMessageBox msgBox; + msgBox.setWindowTitle(tr("Error opening OpenCS 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 settingsMap; + } + + QTextStream stream(&file); + stream.setCodec(QTextCodec::codecForName("UTF-8")); + + CSMSettings::UserSettings::instance().getSettings(stream, settingsMap); + } + + file.close(); + } + + return settingsMap; +} + +void CSVSettings::UserSettingsDialog::writeSettings() +{ + QMap settings; + + for (int i = 0; i < mStackedWidget->count(); ++i) + { + AbstractPage *page = getAbstractPage (i); + settings [page->objectName()] = page->getSettings(); + } + + CSMSettings::UserSettings::instance().writeFile(CSMSettings::UserSettings::instance().openFile(mPaths.back()), settings); + +} + +CSVSettings::AbstractPage *CSVSettings::UserSettingsDialog::getAbstractPage (int index) +{ + return dynamic_cast(mStackedWidget->widget(index)); +} + +void CSVSettings::UserSettingsDialog::slotChangePage(QListWidgetItem *current, QListWidgetItem *previous) +{ + if (!current) + current = previous; + + if (!(current == previous)) + mStackedWidget->setCurrentIndex (mListWidget->row(current)); +} diff --git a/apps/opencs/view/settings/usersettingsdialog.hpp b/apps/opencs/view/settings/usersettingsdialog.hpp new file mode 100644 index 000000000..31d40ca01 --- /dev/null +++ b/apps/opencs/view/settings/usersettingsdialog.hpp @@ -0,0 +1,72 @@ +#ifndef USERSETTINGSDIALOG_H +#define USERSETTINGSDIALOG_H + +#include +#include +#include + +#ifndef Q_MOC_RUN +#include +#endif + +#include "../../model/settings/usersettings.hpp" +#include "../../model/settings/support.hpp" + +class QHBoxLayout; +class AbstractWidget; +class QStackedWidget; +class QListWidget; + +namespace CSVSettings { + + class AbstractPage; + + class UserSettingsDialog : public QMainWindow + { + Q_OBJECT + + QStringList mPaths; + QListWidget *mListWidget; + QStackedWidget *mStackedWidget; + Files::ConfigurationManager mCfgMgr; + + public: + UserSettingsDialog(QMainWindow *parent = 0); + ~UserSettingsDialog(); + + private: + + void closeEvent (QCloseEvent *event); + AbstractPage *getAbstractPage (int index); + void setWidgetStates (CSMSettings::SectionMap settingsMap); + void buildPages(); + void positionWindow (); + CSMSettings::SectionMap loadSettings(); + void writeSettings(); + void createSamplePage(); + + template + void createPage (const QString &title) + { + T *page = new T(title, this); + + mStackedWidget->addWidget (dynamic_cast(page)); + + new QListWidgetItem (page->objectName(), mListWidget); + + //finishing touches + if (mStackedWidget->sizeHint().width() < 640) + mStackedWidget->sizeHint().setWidth(640); + + if (mStackedWidget->sizeHint().height() < 480) + mStackedWidget->sizeHint().setHeight(480); + + resize (mStackedWidget->sizeHint()); + } + + public slots: + void slotChangePage (QListWidgetItem*, QListWidgetItem*); + }; + +} +#endif // USERSETTINGSDIALOG_H diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 354223757..cedb20de9 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -3,9 +3,14 @@ #include #include -#include +#include +#include +#include +#include +#include #include "../../model/world/columnbase.hpp" +#include "../../model/world/idtable.hpp" CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete) @@ -19,10 +24,13 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM widget->setLayout (layout); - QAbstractTableModel *model = document.getData().getTableModel (id); + QAbstractItemModel *model = document.getData().getTableModel (id); int columns = model->columnCount(); + mWidgetMapper = new QDataWidgetMapper (this); + mWidgetMapper->setModel (model); + for (int i=0; iheaderData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); @@ -30,8 +38,58 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM if (flags & CSMWorld::ColumnBase::Flag_Dialogue) { layout->addWidget (new QLabel (model->headerData (i, Qt::Horizontal).toString()), i, 0); + + CSMWorld::ColumnBase::Display display = static_cast + (model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + + QWidget *widget = 0; + + if (model->flags (model->index (0, i)) & Qt::ItemIsEditable) + { + switch (display) + { + case CSMWorld::ColumnBase::Display_String: + + layout->addWidget (widget = new QLineEdit, i, 1); + break; + + case CSMWorld::ColumnBase::Display_Integer: + + /// \todo configure widget properly (range) + layout->addWidget (widget = new QSpinBox, i, 1); + break; + + case CSMWorld::ColumnBase::Display_Float: + + /// \todo configure widget properly (range, format?) + layout->addWidget (widget = new QDoubleSpinBox, i, 1); + break; + + default: break; // silence warnings for other times for now + } + } + else + { + switch (display) + { + case CSMWorld::ColumnBase::Display_String: + case CSMWorld::ColumnBase::Display_Integer: + case CSMWorld::ColumnBase::Display_Float: + + layout->addWidget (widget = new QLabel, i, 1); + break; + + default: break; // silence warnings for other times for now + } + } + + if (widget) + mWidgetMapper->addMapping (widget, i); } } + + mWidgetMapper->setCurrentModelIndex ( + dynamic_cast (*model).getModelIndex (id.getId(), 0)); } void CSVWorld::DialogueSubView::setEditLock (bool locked) diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index c57dab108..64715f5b7 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -3,6 +3,8 @@ #include "../doc/subview.hpp" +class QDataWidgetMapper; + namespace CSMDoc { class Document; @@ -12,6 +14,7 @@ namespace CSVWorld { class DialogueSubView : public CSVDoc::SubView { + QDataWidgetMapper *mWidgetMapper; public: diff --git a/apps/opencs/view/world/enumdelegate.cpp b/apps/opencs/view/world/enumdelegate.cpp new file mode 100644 index 000000000..dd194abe9 --- /dev/null +++ b/apps/opencs/view/world/enumdelegate.cpp @@ -0,0 +1,121 @@ + +#include "enumdelegate.hpp" + +#include +#include + +#include +#include +#include + +#include "../../model/world/commands.hpp" + +void CSVWorld::EnumDelegate::setModelDataImp (QWidget *editor, QAbstractItemModel *model, + const QModelIndex& index) const +{ + if (QComboBox *comboBox = dynamic_cast (editor)) + { + QString value = comboBox->currentText(); + + for (std::vector >::const_iterator iter (mValues.begin()); + iter!=mValues.end(); ++iter) + if (iter->second==value) + { + addCommands (model, index, iter->first); + break; + } + } +} + +void CSVWorld::EnumDelegate::addCommands (QAbstractItemModel *model, + const QModelIndex& index, int type) const +{ + getUndoStack().push (new CSMWorld::ModifyCommand (*model, index, type)); +} + + +CSVWorld::EnumDelegate::EnumDelegate (const std::vector >& values, + QUndoStack& undoStack, QObject *parent) +: CommandDelegate (undoStack, parent), mValues (values) +{ + +} + +QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + if (!index.data().isValid()) + return 0; + + QComboBox *comboBox = new QComboBox (parent); + + for (std::vector >::const_iterator iter (mValues.begin()); + iter!=mValues.end(); ++iter) + comboBox->addItem (iter->second); + + return comboBox; +} + +void CSVWorld::EnumDelegate::setEditorData (QWidget *editor, const QModelIndex& index) const +{ + if (QComboBox *comboBox = dynamic_cast (editor)) + { + int value = index.data (Qt::EditRole).toInt(); + + std::size_t size = mValues.size(); + + for (std::size_t i=0; isetCurrentIndex (i); + break; + } + } +} + +void CSVWorld::EnumDelegate::paint (QPainter *painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + if (index.data().isValid()) + { + QStyleOptionViewItemV4 option2 (option); + + int value = index.data().toInt(); + + for (std::vector >::const_iterator iter (mValues.begin()); + iter!=mValues.end(); ++iter) + if (iter->first==value) + { + option2.text = iter->second; + + QApplication::style()->drawControl (QStyle::CE_ItemViewItem, &option2, painter); + + break; + } + } +} + + +CSVWorld::EnumDelegateFactory::EnumDelegateFactory() {} + +CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const char **names, bool allowNone) +{ + assert (names); + + if (allowNone) + add (-1, ""); + + for (int i=0; names[i]; ++i) + add (i, names[i]); +} + +CSVWorld::CommandDelegate *CSVWorld::EnumDelegateFactory::makeDelegate (QUndoStack& undoStack, + QObject *parent) const +{ + return new EnumDelegate (mValues, undoStack, parent); +} + +void CSVWorld::EnumDelegateFactory::add (int value, const QString& name) +{ + mValues.push_back (std::make_pair (value, name)); +} diff --git a/apps/opencs/view/world/enumdelegate.hpp b/apps/opencs/view/world/enumdelegate.hpp new file mode 100644 index 000000000..58f19ff78 --- /dev/null +++ b/apps/opencs/view/world/enumdelegate.hpp @@ -0,0 +1,63 @@ +#ifndef CSV_WORLD_ENUMDELEGATE_H +#define CSV_WORLD_ENUMDELEGATE_H + +#include + +#include + +#include + +#include "util.hpp" + +namespace CSVWorld +{ + /// \brief Integer value that represents an enum and is interacted with via a combobox + class EnumDelegate : public CommandDelegate + { + std::vector > mValues; + + private: + + virtual void setModelDataImp (QWidget *editor, QAbstractItemModel *model, + const QModelIndex& index) const; + + virtual void addCommands (QAbstractItemModel *model, + const QModelIndex& index, int type) const; + + public: + + EnumDelegate (const std::vector >& values, + QUndoStack& undoStack, QObject *parent); + + virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem& option, + const QModelIndex& index) const; + + virtual void setEditorData (QWidget *editor, const QModelIndex& index) const; + + virtual void paint (QPainter *painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const; + + }; + + class EnumDelegateFactory : public CommandDelegateFactory + { + std::vector > mValues; + + public: + + EnumDelegateFactory(); + + EnumDelegateFactory (const char **names, bool allowNone = false); + ///< \param names Array of char pointer with a 0-pointer as end mark + /// \param allowNone Use value of -1 for "none selected" (empty string) + + virtual CommandDelegate *makeDelegate (QUndoStack& undoStack, QObject *parent) const; + ///< The ownership of the returned CommandDelegate is transferred to the caller. + + void add (int value, const QString& name); + }; + + +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/world/scripthighlighter.cpp b/apps/opencs/view/world/scripthighlighter.cpp new file mode 100644 index 000000000..288a3d12a --- /dev/null +++ b/apps/opencs/view/world/scripthighlighter.cpp @@ -0,0 +1,118 @@ + +#include "scripthighlighter.hpp" + +#include + +#include + +bool CSVWorld::ScriptHighlighter::parseInt (int value, const Compiler::TokenLoc& loc, + Compiler::Scanner& scanner) +{ + highlight (loc, Type_Int); + return true; +} + +bool CSVWorld::ScriptHighlighter::parseFloat (float value, const Compiler::TokenLoc& loc, + Compiler::Scanner& scanner) +{ + highlight (loc, Type_Float); + return true; +} + +bool CSVWorld::ScriptHighlighter::parseName (const std::string& name, const Compiler::TokenLoc& loc, + Compiler::Scanner& scanner) +{ + highlight (loc, Type_Name); + return true; +} + +bool CSVWorld::ScriptHighlighter::parseKeyword (int keyword, const Compiler::TokenLoc& loc, + Compiler::Scanner& scanner) +{ + highlight (loc, Type_Keyword); + return true; +} + +bool CSVWorld::ScriptHighlighter::parseSpecial (int code, const Compiler::TokenLoc& loc, + Compiler::Scanner& scanner) +{ + highlight (loc, Type_Special); + return true; +} + +bool CSVWorld::ScriptHighlighter::parseComment (const std::string& comment, + const Compiler::TokenLoc& loc, Compiler::Scanner& scanner) +{ + highlight (loc, Type_Comment); + return true; +} + +void CSVWorld::ScriptHighlighter::parseEOF (Compiler::Scanner& scanner) +{} + +void CSVWorld::ScriptHighlighter::highlight (const Compiler::TokenLoc& loc, Type type) +{ + int length = static_cast (loc.mLiteral.size()); + + int index = loc.mColumn; + + // compensate for bug in Compiler::Scanner (position of token is the character after the token) + index -= length; + + setFormat (index, length, mScheme[type]); +} + +CSVWorld::ScriptHighlighter::ScriptHighlighter (QTextDocument *parent) +: QSyntaxHighlighter (parent), Compiler::Parser (mErrorHandler, mContext) +{ + /// \ŧodo replace this with user settings + { + QTextCharFormat format; + format.setForeground (Qt::darkMagenta); + mScheme.insert (std::make_pair (Type_Int, format)); + } + + { + QTextCharFormat format; + format.setForeground (Qt::magenta); + mScheme.insert (std::make_pair (Type_Float, format)); + } + + { + QTextCharFormat format; + format.setForeground (Qt::gray); + mScheme.insert (std::make_pair (Type_Name, format)); + } + + { + QTextCharFormat format; + format.setForeground (Qt::red); + mScheme.insert (std::make_pair (Type_Keyword, format)); + } + + { + QTextCharFormat format; + format.setForeground (Qt::darkYellow); + mScheme.insert (std::make_pair (Type_Special, format)); + } + + { + QTextCharFormat format; + format.setForeground (Qt::green); + mScheme.insert (std::make_pair (Type_Comment, format)); + } +} + +void CSVWorld::ScriptHighlighter::highlightBlock (const QString& text) +{ + std::istringstream stream (text.toUtf8().constData()); + + Compiler::Scanner scanner (mErrorHandler, stream, mContext.getExtensions()); + + try + { + scanner.scan (*this); + } + catch (...) {} // ignore syntax errors + +} \ No newline at end of file diff --git a/apps/opencs/view/world/scripthighlighter.hpp b/apps/opencs/view/world/scripthighlighter.hpp new file mode 100644 index 000000000..3ef697809 --- /dev/null +++ b/apps/opencs/view/world/scripthighlighter.hpp @@ -0,0 +1,80 @@ +#ifndef CSV_WORLD_SCRIPTHIGHLIGHTER_H +#define CSV_WORLD_SCRIPTHIGHLIGHTER_H + +#include + +#include + +#include +#include + +#include "../../model/world/scriptcontext.hpp" + +namespace CSVWorld +{ + class ScriptHighlighter : public QSyntaxHighlighter, private Compiler::Parser + { + public: + + enum Type + { + Type_Int, + Type_Float, + Type_Name, + Type_Keyword, + Type_Special, + Type_Comment + }; + + private: + + Compiler::NullErrorHandler mErrorHandler; + CSMWorld::ScriptContext mContext; + std::map mScheme; + + private: + + virtual bool parseInt (int value, const Compiler::TokenLoc& loc, + Compiler::Scanner& scanner); + ///< Handle an int token. + /// \return fetch another token? + + virtual bool parseFloat (float value, const Compiler::TokenLoc& loc, + Compiler::Scanner& scanner); + ///< Handle a float token. + /// \return fetch another token? + + virtual bool parseName (const std::string& name, + const Compiler::TokenLoc& loc, Compiler::Scanner& scanner); + ///< Handle a name token. + /// \return fetch another token? + + virtual bool parseKeyword (int keyword, const Compiler::TokenLoc& loc, + Compiler::Scanner& scanner); + ///< Handle a keyword token. + /// \return fetch another token? + + virtual bool parseSpecial (int code, const Compiler::TokenLoc& loc, + Compiler::Scanner& scanner); + ///< Handle a special character token. + /// \return fetch another token? + + virtual bool parseComment (const std::string& comment, const Compiler::TokenLoc& loc, + Compiler::Scanner& scanner); + ///< Handle comment token. + /// \return fetch another token? + + virtual void parseEOF (Compiler::Scanner& scanner); + ///< Handle EOF token. + + void highlight (const Compiler::TokenLoc& loc, Type type); + + public: + + ScriptHighlighter (QTextDocument *parent); + + virtual void highlightBlock (const QString& text); + }; +} + +#endif diff --git a/apps/opencs/view/world/scriptsubview.cpp b/apps/opencs/view/world/scriptsubview.cpp new file mode 100644 index 000000000..ab1c2d57c --- /dev/null +++ b/apps/opencs/view/world/scriptsubview.cpp @@ -0,0 +1,99 @@ + +#include "scriptsubview.hpp" + +#include + +#include + +#include "../../model/doc/document.hpp" +#include "../../model/world/universalid.hpp" +#include "../../model/world/data.hpp" +#include "../../model/world/columnbase.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/idtable.hpp" + +#include "scripthighlighter.hpp" + +CSVWorld::ScriptSubView::ChangeLock::ChangeLock (ScriptSubView& view) : mView (view) +{ + ++mView.mChangeLocked; +} + +CSVWorld::ScriptSubView::ChangeLock::~ChangeLock() +{ + --mView.mChangeLocked; +} + +CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) +: SubView (id), mDocument (document), mColumn (-1), mChangeLocked (0) +{ + setWidget (mEditor = new QTextEdit (this)); + + mEditor->setAcceptRichText (false); + mEditor->setLineWrapMode (QTextEdit::NoWrap); + mEditor->setTabStopWidth (4); + mEditor->setUndoRedoEnabled (false); // we use OpenCS-wide undo/redo instead + + mModel = &dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Scripts)); + + for (int i=0; icolumnCount(); ++i) + if (mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display)== + CSMWorld::ColumnBase::Display_Script) + { + mColumn = i; + break; + } + + if (mColumn==-1) + throw std::logic_error ("Can't find script column"); + + mEditor->setPlainText (mModel->data (mModel->getModelIndex (id.getId(), mColumn)).toString()); + + connect (mEditor, SIGNAL (textChanged()), this, SLOT (textChanged())); + + connect (mModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (dataChanged (const QModelIndex&, const QModelIndex&))); + + connect (mModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), + this, SLOT (rowsAboutToBeRemoved (const QModelIndex&, int, int))); + + new ScriptHighlighter (mEditor->document()); +} + +void CSVWorld::ScriptSubView::setEditLock (bool locked) +{ + mEditor->setReadOnly (locked); +} + +void CSVWorld::ScriptSubView::textChanged() +{ + ChangeLock lock (*this); + + mDocument.getUndoStack().push (new CSMWorld::ModifyCommand (*mModel, + mModel->getModelIndex (getUniversalId().getId(), mColumn), mEditor->toPlainText())); +} + +void CSVWorld::ScriptSubView::dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + if (mChangeLocked) + return; + + QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn); + + if (index.row()>=topLeft.row() && index.row()<=bottomRight.row() && + index.column()>=topLeft.column() && index.column()<=bottomRight.column()) + { + QTextCursor cursor = mEditor->textCursor(); + mEditor->setPlainText (mModel->data (index).toString()); + mEditor->setTextCursor (cursor); + } +} + +void CSVWorld::ScriptSubView::rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end) +{ + QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn); + + if (!parent.isValid() && index.row()>=start && index.row()<=end) + deleteLater(); +} \ No newline at end of file diff --git a/apps/opencs/view/world/scriptsubview.hpp b/apps/opencs/view/world/scriptsubview.hpp new file mode 100644 index 000000000..07d87d947 --- /dev/null +++ b/apps/opencs/view/world/scriptsubview.hpp @@ -0,0 +1,62 @@ +#ifndef CSV_WORLD_SCRIPTSUBVIEW_H +#define CSV_WORLD_SCRIPTSUBVIEW_H + +#include "../doc/subview.hpp" + +class QTextEdit; +class QModelIndex; + +namespace CSMDoc +{ + class Document; +} + +namespace CSMWorld +{ + class IdTable; +} + +namespace CSVWorld +{ + class ScriptSubView : public CSVDoc::SubView + { + Q_OBJECT + + QTextEdit *mEditor; + CSMDoc::Document& mDocument; + CSMWorld::IdTable *mModel; + int mColumn; + int mChangeLocked; + + class ChangeLock + { + ScriptSubView& mView; + + ChangeLock (const ChangeLock&); + ChangeLock& operator= (const ChangeLock&); + + public: + + ChangeLock (ScriptSubView& view); + ~ChangeLock(); + }; + + friend class ChangeLock; + + public: + + ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); + + virtual void setEditLock (bool locked); + + private slots: + + void textChanged(); + + void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end); + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 080a175ea..3d05f8860 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -5,12 +5,38 @@ #include "tablesubview.hpp" #include "dialoguesubview.hpp" +#include "scriptsubview.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { - manager.add (CSMWorld::UniversalId::Type_Globals, - new CSVDoc::SubViewFactoryWithCreateFlag (true)); + manager.add (CSMWorld::UniversalId::Type_Gmsts, + new CSVDoc::SubViewFactoryWithCreateFlag (false)); - manager.add (CSMWorld::UniversalId::Type_Global, - new CSVDoc::SubViewFactoryWithCreateFlag (true)); + manager.add (CSMWorld::UniversalId::Type_Skills, + new CSVDoc::SubViewFactoryWithCreateFlag (false)); + + static const CSMWorld::UniversalId::Type sTableTypes[] = + { + CSMWorld::UniversalId::Type_Globals, + CSMWorld::UniversalId::Type_Classes, + CSMWorld::UniversalId::Type_Factions, + CSMWorld::UniversalId::Type_Races, + CSMWorld::UniversalId::Type_Sounds, + CSMWorld::UniversalId::Type_Scripts, + CSMWorld::UniversalId::Type_Regions, + CSMWorld::UniversalId::Type_Birthsigns, + CSMWorld::UniversalId::Type_Spells, + CSMWorld::UniversalId::Type_Cells, + CSMWorld::UniversalId::Type_Referenceables, + + CSMWorld::UniversalId::Type_None // end marker + }; + + for (int i=0; sTableTypes[i]!=CSMWorld::UniversalId::Type_None; ++i) + manager.add (sTableTypes[i], new CSVDoc::SubViewFactoryWithCreateFlag (true)); + + manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory); + +// manager.add (CSMWorld::UniversalId::Type_Global, +// new CSVDoc::SubViewFactoryWithCreateFlag (true)); } \ No newline at end of file diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 0721ead2c..f9167d259 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -102,7 +102,12 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q if (flags & CSMWorld::ColumnBase::Flag_Table) { - CommandDelegate *delegate = new CommandDelegate (undoStack, this); + CSMWorld::ColumnBase::Display display = static_cast ( + mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + + CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate (display, + undoStack, this); + mDelegates.push_back (delegate); setItemDelegateForColumn (i, delegate); } diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index 7181dd4d1..5bbedbf3d 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -1,6 +1,8 @@ #include "util.hpp" +#include + #include #include "../../model/world/commands.hpp" @@ -35,6 +37,70 @@ QVariant CSVWorld::NastyTableModelHack::getData() const return mData; } + +CSVWorld::CommandDelegateFactory::~CommandDelegateFactory() {} + + +CSVWorld::CommandDelegateFactoryCollection *CSVWorld::CommandDelegateFactoryCollection::sThis = 0; + +CSVWorld::CommandDelegateFactoryCollection::CommandDelegateFactoryCollection() +{ + if (sThis) + throw std::logic_error ("multiple instances of CSVWorld::CommandDelegateFactoryCollection"); + + sThis = this; +} + +CSVWorld::CommandDelegateFactoryCollection::~CommandDelegateFactoryCollection() +{ + sThis = 0; + + for (std::map::iterator iter ( + mFactories.begin()); + iter!=mFactories.end(); ++iter) + delete iter->second; +} + +void CSVWorld::CommandDelegateFactoryCollection::add (CSMWorld::ColumnBase::Display display, + CommandDelegateFactory *factory) +{ + mFactories.insert (std::make_pair (display, factory)); +} + +CSVWorld::CommandDelegate *CSVWorld::CommandDelegateFactoryCollection::makeDelegate ( + CSMWorld::ColumnBase::Display display, QUndoStack& undoStack, QObject *parent) const +{ + std::map::const_iterator iter = + mFactories.find (display); + + if (iter!=mFactories.end()) + return iter->second->makeDelegate (undoStack, parent); + + return new CommandDelegate (undoStack, parent); +} + +const CSVWorld::CommandDelegateFactoryCollection& CSVWorld::CommandDelegateFactoryCollection::get() +{ + if (!sThis) + throw std::logic_error ("no instance of CSVWorld::CommandDelegateFactoryCollection"); + + return *sThis; +} + + +QUndoStack& CSVWorld::CommandDelegate::getUndoStack() const +{ + return mUndoStack; +} + +void CSVWorld::CommandDelegate::setModelDataImp (QWidget *editor, QAbstractItemModel *model, + const QModelIndex& index) const +{ + NastyTableModelHack hack (*model); + QStyledItemDelegate::setModelData (editor, &hack, index); + mUndoStack.push (new CSMWorld::ModifyCommand (*model, index, hack.getData())); +} + CSVWorld::CommandDelegate::CommandDelegate (QUndoStack& undoStack, QObject *parent) : QStyledItemDelegate (parent), mUndoStack (undoStack), mEditLock (false) {} @@ -44,14 +110,28 @@ void CSVWorld::CommandDelegate::setModelData (QWidget *editor, QAbstractItemMode { if (!mEditLock) { - NastyTableModelHack hack (*model); - QStyledItemDelegate::setModelData (editor, &hack, index); - mUndoStack.push (new CSMWorld::ModifyCommand (*model, index, hack.getData())); + setModelDataImp (editor, model, index); } + ///< \todo provide some kind of feedback to the user, indicating that editing is currently not possible. } -void CSVWorld::CommandDelegate::setEditLock (bool locked) +QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + if (!index.data().isValid()) + return 0; + + return QStyledItemDelegate::createEditor (parent, option, index); +} + + +void CSVWorld::CommandDelegate::setEditLock (bool locked) { mEditLock = locked; +} + +bool CSVWorld::CommandDelegate::isEditLocked() const +{ + return mEditLock; } \ No newline at end of file diff --git a/apps/opencs/view/world/util.hpp b/apps/opencs/view/world/util.hpp index 136583118..95dfec6c5 100644 --- a/apps/opencs/view/world/util.hpp +++ b/apps/opencs/view/world/util.hpp @@ -1,9 +1,13 @@ #ifndef CSV_WORLD_UTIL_H #define CSV_WORLD_UTIL_H +#include + #include #include +#include "../../model/world/columnbase.hpp" + class QUndoStack; namespace CSVWorld @@ -31,19 +35,76 @@ namespace CSVWorld QVariant getData() const; }; + class CommandDelegate; + + class CommandDelegateFactory + { + public: + + virtual ~CommandDelegateFactory(); + + virtual CommandDelegate *makeDelegate (QUndoStack& undoStack, QObject *parent) const = 0; + ///< The ownership of the returned CommandDelegate is transferred to the caller. + }; + + class CommandDelegateFactoryCollection + { + static CommandDelegateFactoryCollection *sThis; + std::map mFactories; + + private: + + // not implemented + CommandDelegateFactoryCollection (const CommandDelegateFactoryCollection&); + CommandDelegateFactoryCollection& operator= (const CommandDelegateFactoryCollection&); + + public: + + CommandDelegateFactoryCollection(); + + ~CommandDelegateFactoryCollection(); + + void add (CSMWorld::ColumnBase::Display display, CommandDelegateFactory *factory); + ///< The ownership of \æ factory is transferred to *this. + /// + /// This function must not be called more than once per value of \æ display. + + CommandDelegate *makeDelegate (CSMWorld::ColumnBase::Display display, QUndoStack& undoStack, + QObject *parent) const; + ///< The ownership of the returned CommandDelegate is transferred to the caller. + /// + /// If no factory is registered for \a display, a CommandDelegate will be returned. + + static const CommandDelegateFactoryCollection& get(); + + }; + ///< \brief Use commands instead of manipulating the model directly class CommandDelegate : public QStyledItemDelegate { QUndoStack& mUndoStack; bool mEditLock; + protected: + + QUndoStack& getUndoStack() const; + + virtual void setModelDataImp (QWidget *editor, QAbstractItemModel *model, + const QModelIndex& index) const; + public: CommandDelegate (QUndoStack& undoStack, QObject *parent); - void setModelData (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const; + virtual void setModelData (QWidget *editor, QAbstractItemModel *model, + const QModelIndex& index) const; + + virtual QWidget *createEditor (QWidget *parent, const QStyleOptionViewItem& option, + const QModelIndex& index) const; void setEditLock (bool locked); + + bool isEditLocked() const; }; } diff --git a/apps/opencs/view/world/vartypedelegate.cpp b/apps/opencs/view/world/vartypedelegate.cpp new file mode 100644 index 000000000..72cbaae42 --- /dev/null +++ b/apps/opencs/view/world/vartypedelegate.cpp @@ -0,0 +1,103 @@ + +#include "vartypedelegate.hpp" + +#include + +#include "../../model/world/commands.hpp" + +void CSVWorld::VarTypeDelegate::addCommands (QAbstractItemModel *model, const QModelIndex& index, int type) + const +{ + QModelIndex next = model->index (index.row(), index.column()+1); + + QVariant old = model->data (next); + + QVariant value; + + switch (type) + { + case ESM::VT_Short: + case ESM::VT_Int: + case ESM::VT_Long: + + value = old.toInt(); + break; + + case ESM::VT_Float: + + value = old.toFloat(); + break; + + case ESM::VT_String: + + value = old.toString(); + break; + + default: break; // ignore the rest + } + + getUndoStack().beginMacro ( + "Modify " + model->headerData (index.column(), Qt::Horizontal, Qt::DisplayRole).toString()); + + getUndoStack().push (new CSMWorld::ModifyCommand (*model, index, type)); + getUndoStack().push (new CSMWorld::ModifyCommand (*model, next, value)); + + getUndoStack().endMacro(); +} + +CSVWorld::VarTypeDelegate::VarTypeDelegate (const std::vector >& values, + QUndoStack& undoStack, QObject *parent) +: EnumDelegate (values, undoStack, parent) +{} + + +CSVWorld::VarTypeDelegateFactory::VarTypeDelegateFactory (ESM::VarType type0, + ESM::VarType type1, ESM::VarType type2, ESM::VarType type3) +{ + if (type0!=ESM::VT_Unknown) + add (type0); + + if (type1!=ESM::VT_Unknown) + add (type1); + + if (type2!=ESM::VT_Unknown) + add (type2); + + if (type3!=ESM::VT_Unknown) + add (type3); +} + +CSVWorld::CommandDelegate *CSVWorld::VarTypeDelegateFactory::makeDelegate (QUndoStack& undoStack, + QObject *parent) const +{ + return new VarTypeDelegate (mValues, undoStack, parent); +} + +void CSVWorld::VarTypeDelegateFactory::add (ESM::VarType type) +{ + struct Name + { + ESM::VarType mType; + const char *mName; + }; + + static const Name sNames[] = + { + { ESM::VT_None, "empty" }, + { ESM::VT_Short, "short" }, + { ESM::VT_Int, "integer" }, + { ESM::VT_Long, "long" }, + { ESM::VT_Float, "float" }, + { ESM::VT_String, "string" }, + { ESM::VT_Unknown, 0 } // end marker + }; + + for (int i=0; sNames[i].mName; ++i) + if (sNames[i].mType==type) + { + mValues.push_back (std::make_pair (type, sNames[i].mName)); + return; + } + + throw std::logic_error ("Unsupported variable type"); +} diff --git a/apps/opencs/view/world/vartypedelegate.hpp b/apps/opencs/view/world/vartypedelegate.hpp new file mode 100644 index 000000000..c8493f029 --- /dev/null +++ b/apps/opencs/view/world/vartypedelegate.hpp @@ -0,0 +1,40 @@ +#ifndef CSV_WORLD_VARTYPEDELEGATE_H +#define CSV_WORLD_VARTYPEDELEGATE_H + +#include + +#include "enumdelegate.hpp" + +namespace CSVWorld +{ + class VarTypeDelegate : public EnumDelegate + { + private: + + virtual void addCommands (QAbstractItemModel *model, + const QModelIndex& index, int type) const; + + public: + + VarTypeDelegate (const std::vector >& values, + QUndoStack& undoStack, QObject *parent); + }; + + class VarTypeDelegateFactory : public CommandDelegateFactory + { + std::vector > mValues; + + public: + + VarTypeDelegateFactory (ESM::VarType type0 = ESM::VT_Unknown, + ESM::VarType type1 = ESM::VT_Unknown, ESM::VarType type2 = ESM::VT_Unknown, + ESM::VarType type3 = ESM::VT_Unknown); + + virtual CommandDelegate *makeDelegate (QUndoStack& undoStack, QObject *parent) const; + ///< The ownership of the returned CommandDelegate is transferred to the caller. + + void add (ESM::VarType type); + }; +} + +#endif diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index faab842c3..0c85c2b5c 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -14,9 +14,9 @@ set(GAME_HEADER source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender - renderingmanager debugging sky player animation npcanimation creatureanimation actors objects - renderinginterface localmap occlusionquery terrain terrainmaterial water shadows - compositors characterpreview externalrendering globalmap videoplayer + renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation + actors objects renderinginterface localmap occlusionquery terrain terrainmaterial water shadows + compositors characterpreview externalrendering globalmap videoplayer ripplesimulation refraction ) add_openmw_dir (mwinput @@ -24,13 +24,16 @@ add_openmw_dir (mwinput ) add_openmw_dir (mwgui - text_input widgets race class birth review windowmanagerimp console dialogue - dialogue_history window_base stats_window messagebox journalwindow charactercreation - map_window window_pinnable_base cursorreplace tooltips scrollwindow bookwindow list + textinput widgets race class birth review windowmanagerimp console dialogue + windowbase statswindow messagebox journalwindow charactercreation + mapwindow windowpinnablebase tooltips scrollwindow bookwindow list formatting inventorywindow container hud countdialog tradewindow settingswindow confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu itemselection spellbuyingwindow loadingscreen levelupdialog waitdialog spellcreationdialog - enchantingdialog trainingwindow travelwindow imagebutton + enchantingdialog trainingwindow travelwindow imagebutton exposedwindow cursor spellicons + merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks + keywordsearch itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview + tradeitemmodel companionitemmodel pickpocketitemmodel fontloader ) add_openmw_dir (mwdialogue @@ -41,7 +44,7 @@ add_openmw_dir (mwscript locals scriptmanagerimp compilercontext interpretercontext cellextensions miscextensions guiextensions soundextensions skyextensions statsextensions containerextensions aiextensions controlextensions extensions globalscripts ref dialogueextensions - animationextensions transformationextensions consoleextensions userextensions + animationextensions transformationextensions consoleextensions userextensions locals ) add_openmw_dir (mwsound @@ -53,7 +56,7 @@ add_openmw_dir (mwworld containerstore actiontalk actiontake manualref player cellfunctors failedaction cells localscripts customdata weather inventorystore ptr actionopen actionread actionequip timestamp actionalchemy cellstore actionapply actioneat - esmstore store recordcmp + esmstore store recordcmp fallback actionrepair actionsoulgem livecellref actiondoor ) add_openmw_dir (mwclass @@ -62,8 +65,9 @@ add_openmw_dir (mwclass ) add_openmw_dir (mwmechanics - mechanicsmanagerimp stat creaturestats magiceffects movement actors drawstate spells - activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow aiescort aiactivate + mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects + drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow + aiescort aiactivate repair enchanting pathfinding security ) add_openmw_dir (mwbase @@ -72,15 +76,20 @@ add_openmw_dir (mwbase ) # Main executable + IF(OGRE_STATIC) +ADD_DEFINITIONS(-DENABLE_PLUGIN_OctreeSceneManager -DENABLE_PLUGIN_ParticleFX -DENABLE_PLUGIN_GL) +set(OGRE_STATIC_PLUGINS ${OGRE_Plugin_OctreeSceneManager_LIBRARIES} ${OGRE_Plugin_ParticleFX_LIBRARIES} ${OGRE_RenderSystem_GL_LIBRARIES}) IF(WIN32) -ADD_DEFINITIONS(-DENABLE_PLUGIN_CgProgramManager -DENABLE_PLUGIN_OctreeSceneManager -DENABLE_PLUGIN_ParticleFX -DENABLE_PLUGIN_-DENABLE_PLUGIN_Direct3D9 -DENABLE_PLUGIN_GL) -set(OGRE_STATIC_PLUGINS ${OGRE_Plugin_CgProgramManager_LIBRARIES} ${OGRE_Plugin_OctreeSceneManager_LIBRARIES} ${OGRE_Plugin_ParticleFX_LIBRARIES} ${OGRE_RenderSystem_Direct3D9_LIBRARIES} ${OGRE_RenderSystem_GL_LIBRARIES}) -ELSE(WIN32) -ADD_DEFINITIONS(-DENABLE_PLUGIN_CgProgramManager -DENABLE_PLUGIN_OctreeSceneManager -DENABLE_PLUGIN_ParticleFX -DENABLE_PLUGIN_GL) -set(OGRE_STATIC_PLUGINS ${OGRE_Plugin_CgProgramManager_LIBRARIES} ${Cg_LIBRARIES} ${OGRE_Plugin_OctreeSceneManager_LIBRARIES} ${OGRE_Plugin_ParticleFX_LIBRARIES} ${OGRE_RenderSystem_GL_LIBRARIES}) +ADD_DEFINITIONS(-DENABLE_PLUGIN_Direct3D9) +list (APPEND OGRE_STATIC_PLUGINS ${OGRE_RenderSystem_Direct3D9_LIBRARIES}) ENDIF(WIN32) +IF (Cg_FOUND) +ADD_DEFINITIONS(-DENABLE_PLUGIN_CgProgramManager) +list (APPEND OGRE_STATIC_PLUGINS ${OGRE_Plugin_CgProgramManager_LIBRARIES} ${Cg_LIBRARIES}) +ENDIF (Cg_FOUND) ENDIF(OGRE_STATIC) + add_executable(openmw ${OPENMW_LIBS} ${OPENMW_LIBS_HEADER} ${OPENMW_FILES} @@ -117,11 +126,23 @@ 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(CARBON_FRAMEWORK Carbon) find_library(COCOA_FRAMEWORK Cocoa) find_library(IOKIT_FRAMEWORK IOKit) target_link_libraries(openmw ${CARBON_FRAMEWORK} ${COCOA_FRAMEWORK} ${IOKIT_FRAMEWORK}) + + if (FFMPEG_FOUND) + find_library(COREVIDEO_FRAMEWORK CoreVideo) + find_library(VDA_FRAMEWORK VideoDecodeAcceleration) + target_link_libraries(openmw ${COREVIDEO_FRAMEWORK} ${VDA_FRAMEWORK}) + endif() endif(APPLE) if(DPKG_PROGRAM) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 3374ba304..92904b166 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -10,15 +10,15 @@ #include #include #include +#include #include -#include -#include +#include +#include #include "mwinput/inputmanagerimp.hpp" #include "mwgui/windowmanagerimp.hpp" -#include "mwgui/cursorreplace.hpp" #include "mwscript/scriptmanagerimp.hpp" #include "mwscript/extensions.hpp" @@ -65,20 +65,29 @@ void OMW::Engine::setAnimationVerbose(bool animverbose) { } +bool OMW::Engine::frameStarted (const Ogre::FrameEvent& evt) +{ + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) + MWBase::Environment::get().getWorld()->frameStarted(evt.timeSinceLastFrame); + MWBase::Environment::get().getWindowManager ()->frameStarted(evt.timeSinceLastFrame); + return true; +} + bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) { try { handleSDLMessages(); + float frametime = std::min(evt.timeSinceLastFrame, 0.2f); - mEnvironment.setFrameDuration (evt.timeSinceLastFrame); + mEnvironment.setFrameDuration (frametime); // update input - MWBase::Environment::get().getInputManager()->update(evt.timeSinceLastFrame, false); + MWBase::Environment::get().getInputManager()->update(frametime, false); // sound if (mUseSound) - MWBase::Environment::get().getSoundManager()->update (evt.timeSinceLastFrame); + MWBase::Environment::get().getSoundManager()->update(frametime); // global scripts MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); @@ -92,23 +101,19 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) // passing of time if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) - MWBase::Environment::get().getWorld()->advanceTime ( - mEnvironment.getFrameDuration()*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); + 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 - std::vector > movement; - MWBase::Environment::get().getMechanicsManager()->update (movement, mEnvironment.getFrameDuration(), + MWBase::Environment::get().getMechanicsManager()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode()); - if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) - MWBase::Environment::get().getWorld()->doPhysics (movement, mEnvironment.getFrameDuration()); - // update world - MWBase::Environment::get().getWorld()->update (evt.timeSinceLastFrame, MWBase::Environment::get().getWindowManager()->isGuiMode()); + MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode()); // update GUI Ogre::RenderWindow* window = mOgre->getWindow(); @@ -116,7 +121,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) MWBase::Environment::get().getWorld()->getTriangleBatchCount(tri, batch); MWBase::Environment::get().getWindowManager()->wmUpdateFps(window->getLastFPS(), tri, batch); - MWBase::Environment::get().getWindowManager()->onFrame(evt.timeSinceLastFrame); + MWBase::Environment::get().getWindowManager()->onFrame(frametime); //Flush any events that weren't handled this frame SDL_FlushEvents(0x0, 0xFFFFFFFF); @@ -206,25 +211,45 @@ OMW::Engine::~Engine() SDL_Quit(); } -// Load all BSA files in data directory. +// Load BSA files void OMW::Engine::loadBSA() { - const Files::MultiDirCollection& bsa = mFileCollections.getCollection (".bsa"); + // We use separate resource groups to handle location priority. + const Files::PathContainer& dataDirs = mFileCollections.getPaths(); - for (Files::MultiDirCollection::TIter iter(bsa.begin()); iter!=bsa.end(); ++iter) + int i=0; + for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) { - std::cout << "Adding " << iter->second.string() << std::endl; - Bsa::addBSA(iter->second.string()); + // Last data dir has the highest priority + std::string groupName = "Data" + Ogre::StringConverter::toString(dataDirs.size()-i, 8, '0'); + Ogre::ResourceGroupManager::getSingleton ().createResourceGroup (groupName); + + std::string dataDirectory = iter->string(); + std::cout << "Data dir " << dataDirectory << std::endl; + Bsa::addDir(dataDirectory, mFSStrict, groupName); + ++i; } - const Files::PathContainer& dataDirs = mFileCollections.getPaths(); - std::string dataDirectory; - for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) + i=0; + for (std::vector::const_iterator archive = mArchives.begin(); archive != mArchives.end(); ++archive) { - dataDirectory = iter->string(); - std::cout << "Data dir " << dataDirectory << std::endl; - Bsa::addDir(dataDirectory, mFSStrict); + if (mFileCollections.doesExist(*archive)) + { + // Last BSA has the highest priority + std::string groupName = "DataBSA" + Ogre::StringConverter::toString(mArchives.size()-i, 8, '0'); + + Ogre::ResourceGroupManager::getSingleton ().createResourceGroup (groupName); + + const std::string archivePath = mFileCollections.getPath(*archive).string(); + std::cout << "Adding BSA archive " << archivePath << std::endl; + Bsa::addBSA(archivePath, groupName); + ++i; + } + else + { + std::cout << "Archive " << *archive << " not found" << std::endl; + } } } @@ -256,6 +281,11 @@ void OMW::Engine::setDataDirs (const Files::PathContainer& dataDirs) mFileCollections = Files::Collections (dataDirs, !mFSStrict); } +// Add BSA archive +void OMW::Engine::addArchive (const std::string& archive) { + mArchives.push_back(archive); +} + // Set resource dir void OMW::Engine::setResourceDir (const boost::filesystem::path& parResDir) { @@ -271,18 +301,31 @@ void OMW::Engine::setCell (const std::string& cellName) // Set master file (esm) // - If the given name does not have an extension, ".esm" is added automatically -// - Currently OpenMW only supports one master at the same time. void OMW::Engine::addMaster (const std::string& master) { - assert (mMaster.empty()); - mMaster = master; + mMaster.push_back(master); + std::string &str = mMaster.back(); // Append .esm if not already there - std::string::size_type sep = mMaster.find_last_of ("."); + std::string::size_type sep = str.find_last_of ("."); + if (sep == std::string::npos) + { + str += ".esm"; + } +} + +// Add plugin file (esp) +void OMW::Engine::addPlugin (const std::string& plugin) +{ + mPlugins.push_back(plugin); + std::string &str = mPlugins.back(); + + // Append .esp if not already there + std::string::size_type sep = str.find_last_of ("."); if (sep == std::string::npos) { - mMaster += ".esm"; + str += ".esp"; } } @@ -354,7 +397,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) } mOgre = new OEngine::Render::OgreRenderer; - + mOgre->configure( mCfgMgr.getLogPath().string(), renderSystem, @@ -370,7 +413,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) addResourcesDirectory(mResDir / "mygui"); addResourcesDirectory(mResDir / "water"); - addResourcesDirectory(mResDir / "gbuffer"); addResourcesDirectory(mResDir / "shadows"); addZipResource(mResDir / "mygui" / "Obliviontt.zip"); @@ -386,24 +428,25 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) loadBSA(); - // cursor replacer (converts the cursor from the bsa so they can be used by mygui) - MWGui::CursorReplace replacer; - // Create the world - mEnvironment.setWorld (new MWWorld::World (*mOgre, mFileCollections, mMaster, - mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoder, mFallbackMap, + mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins, + mResDir, mCfgMgr.getCachePath(), mEncoder, mFallbackMap, mActivationDistanceOverride)); + MWBase::Environment::get().getWorld()->setupPlayer(); //Load translation data mTranslationDataStorage.setEncoder(mEncoder); - mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster); + for (size_t i = 0; i < mMaster.size(); i++) + mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster[i]); // Create window manager - this manages all the MW-specific GUI windows MWScript::registerExtensions (mExtensions); mEnvironment.setWindowManager (new MWGui::WindowManager( - mExtensions, mFpsLevel, mNewGame, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), - mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage)); + mExtensions, mFpsLevel, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), + mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage, mEncoding)); + if (mNewGame) + mEnvironment.getWindowManager()->setNewGame(true); // Create sound system mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound)); @@ -432,25 +475,34 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) MWBase::Environment::get().getWorld()->getPlayer(), *MWBase::Environment::get().getWindowManager(), *this, keybinderUser, keybinderUserExists)); - // load cell - ESM::Position pos; - pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; - pos.pos[2] = 0; - mEnvironment.getWorld()->renderPlayer(); - if (const ESM::Cell *exterior = MWBase::Environment::get().getWorld()->getExterior (mCellName)) + if (!mNewGame) { - MWBase::Environment::get().getWorld()->indexToPosition (exterior->mData.mX, exterior->mData.mY, - pos.pos[0], pos.pos[1], true); - MWBase::Environment::get().getWorld()->changeToExteriorCell (pos); + // load cell + ESM::Position pos; + pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; + pos.pos[2] = 0; + + if (const ESM::Cell *exterior = MWBase::Environment::get().getWorld()->getExterior (mCellName)) + { + MWBase::Environment::get().getWorld()->indexToPosition (exterior->mData.mX, exterior->mData.mY, + pos.pos[0], pos.pos[1], true); + MWBase::Environment::get().getWorld()->changeToExteriorCell (pos); + } + else + { + pos.pos[0] = pos.pos[1] = 0; + MWBase::Environment::get().getWorld()->changeToInteriorCell (mCellName, pos); + } } else - { - pos.pos[0] = pos.pos[1] = 0; - MWBase::Environment::get().getWorld()->changeToInteriorCell (mCellName, pos); - } + mEnvironment.getWorld()->startNewGame(); + Ogre::FrameEvent event; + event.timeSinceLastEvent = 0; + event.timeSinceLastFrame = 0; + frameRenderingQueued(event); mOgre->getRoot()->addFrameListener (this); // scripts @@ -492,15 +544,13 @@ void OMW::Engine::go() if (!mStartupScript.empty()) MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript); - std::cout << "\nPress Q/ESC or close window to exit.\n"; - // Start the main rendering loop mOgre->start(); // Save user settings settings.saveUser(settingspath); - std::cout << "Quitting peacefully.\n"; + std::cout << "Quitting peacefully." << std::endl; } void OMW::Engine::activate() diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 971445147..a206166a5 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -64,10 +64,12 @@ namespace OMW ToUTF8::FromType mEncoding; ToUTF8::Utf8Encoder* mEncoder; Files::PathContainer mDataDirs; + std::vector mArchives; boost::filesystem::path mResDir; OEngine::Render::OgreRenderer *mOgre; std::string mCellName; - std::string mMaster; + std::vector mMaster; + std::vector mPlugins; int mFpsLevel; bool mDebug; bool mVerboseScripts; @@ -98,7 +100,7 @@ namespace OMW /// add a .zip resource void addZipResource (const boost::filesystem::path& path); - /// Load all BSA files in data directory. + /// Load BSA files void loadBSA(); void executeLocalScripts(); @@ -106,6 +108,7 @@ namespace OMW void handleSDLMessages(); virtual bool frameRenderingQueued (const Ogre::FrameEvent& evt); + virtual bool frameStarted (const Ogre::FrameEvent& evt); /// Load settings from various files, returns the path to the user settings file std::string loadSettings (Settings::Manager & settings); @@ -126,6 +129,9 @@ namespace OMW /// Set data dirs void setDataDirs(const Files::PathContainer& dataDirs); + /// Add BSA archive + void addArchive(const std::string& archive); + /// Set resource dir void setResourceDir(const boost::filesystem::path& parResDir); @@ -134,9 +140,12 @@ namespace OMW /// Set master file (esm) /// - If the given name does not have an extension, ".esm" is added automatically - /// - Currently OpenMW only supports one master at the same time. void addMaster(const std::string& master); + /// Same as "addMaster", but for plugin files (esp) + /// - If the given name does not have an extension, ".esp" is added automatically + void addPlugin(const std::string& plugin); + /// Enable fps counter void showFPS(int level); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index d1a498610..1fa461c2f 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -100,6 +100,9 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("data-local", bpo::value()->default_value(""), "set local data directory (highest priority)") + ("fallback-archive", bpo::value()->default_value(StringsVector(), "fallback-archive") + ->multitoken(), "set fallback BSA archives (later archives have higher priority)") + ("resources", bpo::value()->default_value("resources"), "set resources directory") @@ -201,6 +204,13 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setDataDirs(dataDirs); + // fallback archives + StringsVector archives = variables["fallback-archive"].as(); + for (StringsVector::const_iterator it = archives.begin(); it != archives.end(); it++) + { + engine.addArchive(*it); + } + engine.setResourceDir(variables["resources"].as()); // master and plugin @@ -211,18 +221,21 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat master.push_back("Morrowind"); } - if (master.size() > 1) + StringsVector plugin = variables["plugin"].as(); + // Removed check for 255 files, which would be the hard-coded limit in Morrowind. + // I'll keep the following variable in, maybe we can use it for something different. + // Say, a feedback like "loading file x/cnt". + // Commenting this out for now to silence compiler warning. + //int cnt = master.size() + plugin.size(); + + // Prepare loading master/plugin files (i.e. send filenames to engine) + for (std::vector::size_type i = 0; i < master.size(); i++) { - std::cout - << "Ignoring all but the first master file (multiple master files not yet supported)." - << std::endl; + engine.addMaster(master[i]); } - engine.addMaster(master[0]); - - StringsVector plugin = variables["plugin"].as(); - if (!plugin.empty()) + for (std::vector::size_type i = 0; i < plugin.size(); i++) { - std::cout << "Ignoring plugin files (plugins not yet supported)." << std::endl; + engine.addPlugin(plugin[i]); } // startup-settings diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index a205e78db..12996cc30 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -23,8 +23,12 @@ namespace MWBase DialogueManager() {} + virtual void clear() = 0; + virtual ~DialogueManager() {} + virtual bool isInChoice() const = 0; + virtual void startDialogue (const MWWorld::Ptr& actor) = 0; virtual void addTopic (const std::string& topic) = 0; @@ -39,7 +43,9 @@ namespace MWBase //calbacks for the GUI virtual void keywordSelected (const std::string& keyword) = 0; virtual void goodbyeSelected() = 0; - virtual void questionAnswered (const std::string& answer) = 0; + virtual void questionAnswered (int answer) = 0; + + virtual bool checkServiceRefused () = 0; virtual void persuade (int type) = 0; virtual int getTemporaryDispositionChange () const = 0; diff --git a/apps/openmw/mwbase/journal.hpp b/apps/openmw/mwbase/journal.hpp index b3dfea45b..51e51edda 100644 --- a/apps/openmw/mwbase/journal.hpp +++ b/apps/openmw/mwbase/journal.hpp @@ -33,6 +33,8 @@ namespace MWBase Journal() {} + virtual void clear() = 0; + virtual ~Journal() {} virtual void addEntry (const std::string& id, int index) = 0; diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index a1023c986..7e09f9b4d 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -37,24 +37,24 @@ namespace MWBase virtual ~MechanicsManager() {} - virtual void addActor (const MWWorld::Ptr& ptr) = 0; - ///< Register an actor for stats management - /// - /// \note Dead actors are ignored. + virtual void add (const MWWorld::Ptr& ptr) = 0; + ///< Register an object for management + + virtual void remove (const MWWorld::Ptr& ptr) = 0; + ///< Deregister an object for management - virtual void removeActor (const MWWorld::Ptr& ptr) = 0; - ///< Deregister an actor for stats management + virtual void updateCell(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) = 0; + ///< Moves an object to a new cell - virtual void dropActors (const MWWorld::CellStore *cellStore) = 0; - ///< Deregister all actors in the given cell. + virtual void drop (const MWWorld::CellStore *cellStore) = 0; + ///< Deregister all objects in the given cell. virtual void watchActor (const MWWorld::Ptr& ptr) = 0; ///< On each update look for changes in a previously registered actor and update the /// GUI accordingly. - virtual void update (std::vector >& movement, - float duration, bool paused) = 0; - ///< Update actor stats and store desired velocity vectors in \a movement + virtual void update (float duration, bool paused) = 0; + ///< Update objects /// /// \param paused In game type does not currently advance (this usually means some GUI /// component is up). @@ -82,7 +82,7 @@ namespace MWBase virtual int getDerivedDisposition(const MWWorld::Ptr& ptr) = 0; ///< Calculate the diposition of an NPC toward the player. - + virtual int countDeaths (const std::string& id) const = 0; ///< Return the number of deaths for actors with the given ID. @@ -98,6 +98,22 @@ namespace MWBase virtual void getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, 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 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 bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0; }; } diff --git a/apps/openmw/mwbase/scriptmanager.hpp b/apps/openmw/mwbase/scriptmanager.hpp index ae146e064..32df2bfa3 100644 --- a/apps/openmw/mwbase/scriptmanager.hpp +++ b/apps/openmw/mwbase/scriptmanager.hpp @@ -35,6 +35,8 @@ 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/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index 0c706ab49..5d396fac0 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -84,7 +84,7 @@ namespace MWBase ///< Start playing music from the selected folder /// \param name of the folder that contains the playlist - virtual void say(MWWorld::Ptr reference, const std::string& filename) = 0; + virtual void say(const MWWorld::Ptr &reference, const std::string& filename) = 0; ///< Make an actor say some text. /// \param filename name of a sound file in "Sound/" in the data directory. @@ -92,10 +92,10 @@ namespace MWBase ///< Say some text, without an actor ref /// \param filename name of a sound file in "Sound/" in the data directory. - virtual bool sayDone(MWWorld::Ptr reference=MWWorld::Ptr()) const = 0; + virtual bool sayDone(const MWWorld::Ptr &reference=MWWorld::Ptr()) const = 0; ///< Is actor not speaking? - virtual void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()) = 0; + virtual void stopSay(const MWWorld::Ptr &reference=MWWorld::Ptr()) = 0; ///< Stop an actor speaking virtual SoundPtr playTrack(const MWSound::DecoderPtr& decoder, PlayType type) = 0; @@ -105,14 +105,14 @@ namespace MWBase PlayMode mode=Play_Normal) = 0; ///< Play a sound, independently of 3D-position - virtual SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId, + virtual SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId, float volume, float pitch, PlayMode mode=Play_Normal) = 0; ///< Play a sound from an object - virtual void stopSound3D(MWWorld::Ptr reference, const std::string& soundId) = 0; + virtual void stopSound3D(const MWWorld::Ptr &reference, const std::string& soundId) = 0; ///< Stop the given object from playing the given sound, - virtual void stopSound3D(MWWorld::Ptr reference) = 0; + virtual void stopSound3D(const MWWorld::Ptr &reference) = 0; ///< Stop the given object from playing all sounds. virtual void stopSound(const MWWorld::CellStore *cell) = 0; @@ -121,7 +121,7 @@ namespace MWBase virtual void stopSound(const std::string& soundId) = 0; ///< Stop a non-3d looping sound - virtual bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const = 0; + virtual bool getSoundPlaying(const MWWorld::Ptr &reference, const std::string& soundId) const = 0; ///< Is the given sound currently playing on the given object? virtual void pauseSounds(int types=Play_TypeMask) = 0; diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 06b1df7b9..c36278b6d 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -86,17 +86,23 @@ namespace MWBase */ virtual void update() = 0; + virtual void setNewGame(bool newgame) = 0; + virtual void pushGuiMode (MWGui::GuiMode mode) = 0; virtual void popGuiMode() = 0; virtual void removeGuiMode (MWGui::GuiMode mode) = 0; ///< can be anywhere in the stack + virtual void updatePlayer() = 0; + virtual MWGui::GuiMode getMode() const = 0; virtual bool containsMode(MWGui::GuiMode) const = 0; virtual bool isGuiMode() const = 0; + virtual bool isConsoleMode() const = 0; + virtual void toggleVisible (MWGui::GuiWindow wnd) = 0; /// Disallow all inventory mode windows @@ -186,8 +192,8 @@ namespace MWBase virtual void activateQuickKey (int index) = 0; virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0; - virtual void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) = 0; - virtual void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) = 0; + virtual void setSelectedEnchantItem(const MWWorld::Ptr& item) = 0; + virtual void setSelectedWeapon(const MWWorld::Ptr& item) = 0; virtual void unsetSelectedSpell() = 0; virtual void unsetSelectedWeapon() = 0; @@ -204,7 +210,11 @@ 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) = 0; + 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 int readPressedButton() = 0; ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) @@ -234,13 +244,24 @@ namespace MWBase virtual void enableRest() = 0; virtual bool getRestEnabled() = 0; + virtual bool getJournalAllowed() = 0; virtual bool getPlayerSleeping() = 0; virtual void wakeUpPlayer() = 0; + virtual void showCompanionWindow(MWWorld::Ptr actor) = 0; virtual void startSpellMaking(MWWorld::Ptr actor) = 0; virtual void startEnchanting(MWWorld::Ptr actor) = 0; + virtual void startSelfEnchanting(MWWorld::Ptr soulgem) = 0; virtual void startTraining(MWWorld::Ptr actor) = 0; + virtual void startRepair(MWWorld::Ptr actor) = 0; + virtual void startRepairItem(MWWorld::Ptr item) = 0; + + virtual void showSoulgemDialog (MWWorld::Ptr item) = 0; + + virtual void frameStarted(float dt) = 0; + + virtual void changePointer (const std::string& name) = 0; virtual const Translation::Storage& getTranslationDataStorage() const = 0; }; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index a576912c0..86a6a89d2 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -6,6 +6,7 @@ #include #include "../mwworld/globals.hpp" +#include "../mwworld/ptr.hpp" namespace Ogre { @@ -19,6 +20,11 @@ namespace OEngine { class Fader; } + + namespace Physic + { + class PhysicEngine; + } } namespace ESM @@ -35,16 +41,25 @@ namespace ESM namespace MWRender { class ExternalRendering; + class Animation; +} + +namespace MWMechanics +{ + class Movement; } namespace MWWorld { + class Fallback; class CellStore; class Player; class LocalScripts; - class Ptr; class TimeStamp; class ESMStore; + class RefData; + + typedef std::vector > PtrMovementList; } namespace MWBase @@ -79,6 +94,8 @@ namespace MWBase virtual ~World() {} + virtual void startNewGame() = 0; + virtual OEngine::Render::Fader* getFader() = 0; ///< \ŧodo remove this function. Rendering details should not be exposed. @@ -94,17 +111,13 @@ namespace MWBase virtual void getTriangleBatchCount(unsigned int &triangles, unsigned int &batches) = 0; - virtual void setFallbackValues (const std::map& fallbackMap) = 0; - - virtual std::string getFallback (const std::string& key) const = 0; - - virtual std::string getFallback (const std::string& key, const std::string& def) const = 0; + virtual const MWWorld::Fallback *getFallback () const = 0; virtual MWWorld::Player& getPlayer() = 0; virtual const MWWorld::ESMStore& getStore() const = 0; - virtual ESM::ESMReader& getEsmReader() = 0; + virtual std::vector& getEsmReader() = 0; virtual MWWorld::LocalScripts& getLocalScripts() = 0; @@ -133,11 +146,14 @@ 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 getCurrentCellName() const = 0; + virtual void removeRefScript (MWWorld::RefData *ref) = 0; + //< Remove the script attached to ref from mLocalScripts + virtual MWWorld::Ptr getPtr (const std::string& name, bool activeOnly) = 0; ///< Return a pointer to a liveCellRef with the given name. /// \param activeOnly do non search inactive cells. @@ -202,6 +218,9 @@ namespace MWBase virtual MWWorld::Ptr getFacedObject() = 0; ///< Return pointer to the object the player is looking at, if it is within activation range + virtual void adjustPosition (const MWWorld::Ptr& ptr) = 0; + ///< Adjust position after load to be on ground. Must be called after model load. + virtual void deleteObject (const MWWorld::Ptr& ptr) = 0; virtual void moveObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; @@ -213,6 +232,8 @@ namespace MWBase virtual void rotateObject(const MWWorld::Ptr& ptr,float x,float y,float z, bool adjust = false) = 0; + virtual void localRotateObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; + virtual void safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos) = 0; ///< place an object in a "safe" location (ie not in the void, etc). @@ -223,10 +244,12 @@ namespace MWBase virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const = 0; ///< Convert position to cell numbers - virtual void doPhysics (const std::vector >& actors, - float duration) = 0; + virtual void doPhysics (const MWWorld::PtrMovementList &actors, float duration) = 0; ///< Run physics simulation and modify \a world accordingly. + virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0; + ///< cast a Ray and return true if there is an object in the ray path. + virtual bool toggleCollisionMode() = 0; ///< Toggle collision mode for player. If disabled player object should ignore /// collisions and gravity. @@ -236,40 +259,45 @@ namespace MWBase ///< Toggle a render mode. ///< \return Resulting mode - virtual const ESM::Potion *createRecord (const ESM::Potion& record) - = 0; - ///< Create a new recrod (of type potion) in the ESM store. + virtual const ESM::Potion *createRecord (const ESM::Potion& record) = 0; + ///< Create a new record (of type potion) in the ESM store. /// \return pointer to created record - virtual const ESM::Spell *createRecord (const ESM::Spell& record) - = 0; - ///< Create a new recrod (of type spell) in the ESM store. + virtual const ESM::Spell *createRecord (const ESM::Spell& record) = 0; + ///< Create a new record (of type spell) in the ESM store. /// \return pointer to created record - virtual const ESM::Class *createRecord (const ESM::Class& record) - = 0; - ///< Create a new recrod (of type class) in the ESM store. + virtual const ESM::Class *createRecord (const ESM::Class& record) = 0; + ///< Create a new record (of type class) in the ESM store. /// \return pointer to created record virtual const ESM::Cell *createRecord (const ESM::Cell& record) = 0; - ///< Create a new recrod (of type cell) in the ESM store. + ///< Create a new record (of type cell) in the ESM store. /// \return pointer to created record virtual const ESM::NPC *createRecord(const ESM::NPC &record) = 0; - ///< Create a new recrod (of type npc) in the ESM store. + ///< Create a new record (of type npc) in the ESM store. + /// \return pointer to created record + + virtual const ESM::Armor *createRecord (const ESM::Armor& record) = 0; + ///< Create a new record (of type armor) in the ESM store. /// \return pointer to created record - 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 rendered scene should be ignored. - /// - /// \param mode: 0 normal, 1 immediate start, 2 immediate loop - /// \param number How offen the animation should be run + virtual const ESM::Weapon *createRecord (const ESM::Weapon& record) = 0; + ///< Create a new record (of type weapon) in the ESM store. + /// \return pointer to created record - 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 rendered scene should be ignored. + virtual const ESM::Clothing *createRecord (const ESM::Clothing& record) = 0; + ///< Create a new record (of type clothing) in the ESM store. + /// \return pointer to created record + + virtual const ESM::Enchantment *createRecord (const ESM::Enchantment& record) = 0; + ///< Create a new record (of type enchantment) in the ESM store. + /// \return pointer to created record + + virtual const ESM::Book *createRecord (const ESM::Book& record) = 0; + ///< Create a new record (of type book) in the ESM store. + /// \return pointer to created record virtual void update (float duration, bool paused) = 0; @@ -287,17 +315,36 @@ namespace MWBase virtual void processChangedSettings (const Settings::CategorySettingVector& settings) = 0; - virtual bool isSwimming(const MWWorld::Ptr &object) = 0; - virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) = 0; + virtual bool isFlying(const MWWorld::Ptr &ptr) const = 0; + virtual bool isSwimming(const MWWorld::Ptr &object) const = 0; + virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const = 0; + virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0; virtual void togglePOV() = 0; virtual void togglePreviewMode(bool enable) = 0; - virtual bool toggleVanityMode(bool enable, bool force) = 0; + virtual bool toggleVanityMode(bool enable) = 0; virtual void allowVanityMode(bool allow) = 0; virtual void togglePlayerLooking(bool enable) = 0; + virtual void changeVanityModeScale(float factor) = 0; + virtual bool vanityRotateCamera(float * rot) = 0; + virtual void setupPlayer() = 0; virtual void renderPlayer() = 0; - + + virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door) = 0; + ///< if activated, should this door be opened or closed? + virtual void activateDoor(const MWWorld::Ptr& door) = 0; + ///< activate (open or close) an non-teleport door + + virtual bool getPlayerStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if the player is standing on \a object + virtual bool getActorStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if any actor is standing on \a object + virtual float getWindSpeed() = 0; + + virtual void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector& out) = 0; + ///< get all containers in active cells owned by this Npc + virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector& out) = 0; + ///< get all items in active cells owned by this Npc + virtual void setupExternalRendering (MWRender::ExternalRendering& rendering) = 0; virtual int canRest() = 0; @@ -307,10 +354,13 @@ namespace MWBase /// 2 - player is underwater \n /// 3 - enemies are nearby (not implemented) + /// \todo Probably shouldn't be here + virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0; /// \todo this does not belong here virtual void playVideo(const std::string& name, bool allowSkipping) = 0; virtual void stopVideo() = 0; + virtual void frameStarted (float dt) = 0; }; } diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 26d286aa1..3a60d9c39 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -5,12 +5,13 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld//cellstore.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwrender/objects.hpp" +#include "../mwrender/actors.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwgui/tooltips.hpp" @@ -21,9 +22,8 @@ namespace MWClass { const std::string model = getModel(ptr); if (!model.empty()) { - MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, model); + MWRender::Actors& actors = renderingInterface.getActors(); + actors.insertActivator(ptr); } } @@ -32,6 +32,7 @@ namespace MWClass const std::string model = getModel(ptr); if(!model.empty()) physics.addObject(ptr); + MWBase::Environment::get().getMechanicsManager()->add(ptr); } std::string Activator::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 06467bb21..b56ed118f 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -12,6 +12,7 @@ #include "../mwworld/actionalchemy.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" +#include "../mwworld/nullaction.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" @@ -34,7 +35,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Apparatus::getModel(const MWWorld::Ptr &ptr) const @@ -61,6 +62,9 @@ namespace MWClass boost::shared_ptr Apparatus::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { + if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) + return boost::shared_ptr (new MWWorld::NullAction ()); + boost::shared_ptr action( new MWWorld::ActionTake (ptr)); @@ -155,4 +159,16 @@ namespace MWClass return MWWorld::Ptr(&cell.mAppas.insert(*ref), &cell); } + + bool Apparatus::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Apparatus; + } + + float Apparatus::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/apparatus.hpp b/apps/openmw/mwclass/apparatus.hpp index 7045f62d6..17b8b9254 100644 --- a/apps/openmw/mwclass/apparatus.hpp +++ b/apps/openmw/mwclass/apparatus.hpp @@ -13,6 +13,8 @@ namespace MWClass public: + virtual float getWeight (const MWWorld::Ptr& ptr) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering @@ -54,6 +56,8 @@ namespace MWClass ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; } diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 704173b1c..f8e4dc40a 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -15,6 +15,9 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" +#include "../mwworld/nullaction.hpp" +#include "../mwworld/containerstore.hpp" +#include "../mwworld/player.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" @@ -37,7 +40,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Armor::getModel(const MWWorld::Ptr &ptr) const @@ -64,6 +67,9 @@ namespace MWClass boost::shared_ptr Armor::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { + if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) + return boost::shared_ptr (new MWWorld::NullAction ()); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); action->setSound(getUpSoundId(ptr)); @@ -243,8 +249,9 @@ namespace MWClass text += "\n#{sArmorRating}: " + MWGui::ToolTips::toString(ref->mBase->mData.mArmor); - /// \todo store the current armor health somewhere - text += "\n#{sCondition}: " + MWGui::ToolTips::toString(ref->mBase->mData.mHealth); + int remainingHealth = (ptr.getCellRef().mCharge != -1) ? ptr.getCellRef().mCharge : ref->mBase->mData.mHealth; + text += "\n#{sCondition}: " + MWGui::ToolTips::toString(remainingHealth) + "/" + + 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}"); @@ -255,6 +262,8 @@ namespace MWClass } info.enchant = ref->mBase->mEnchant; + if (!info.enchant.empty()) + info.remainingEnchantCharge = ptr.getCellRef().mEnchantmentCharge; info.text = text; @@ -269,6 +278,86 @@ namespace MWClass return ref->mBase->mEnchant; } + void Armor::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + ESM::Armor newItem = *ref->mBase; + newItem.mId=""; + newItem.mName=newName; + newItem.mData.mEnchant=enchCharge; + newItem.mEnchant=enchId; + const ESM::Armor *record = MWBase::Environment::get().getWorld()->createRecord (newItem); + ref->mBase = record; + } + + std::pair Armor::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const + { + MWWorld::InventoryStore& invStore = MWWorld::Class::get(npc).getInventoryStore(npc); + + // slots that this item can be equipped in + std::pair, bool> slots = MWWorld::Class::get(ptr).getEquipmentSlots(ptr); + + 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; + + 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(*slot == MWWorld::InventoryStore::Slot_CarriedLeft) + { + MWWorld::ContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + + if(weapon == invStore.end()) + return std::make_pair(1,""); + + if(weapon->getTypeName() == typeid(ESM::Weapon).name() && + (weapon->get()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand || + weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoClose || + weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoWide || + weapon->get()->mBase->mData.mType == ESM::Weapon::SpearTwoWide || + weapon->get()->mBase->mData.mType == ESM::Weapon::AxeTwoHand || + weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanBow || + weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)) + { + return std::make_pair(3,""); + } + return std::make_pair(1,""); + } + } + return std::make_pair(1,""); + } + boost::shared_ptr Armor::use (const MWWorld::Ptr& ptr) const { boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); @@ -286,4 +375,24 @@ namespace MWClass return MWWorld::Ptr(&cell.mArmors.insert(*ref), &cell); } + + float Armor::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mData.mEnchant/10.f; + } + + bool Armor::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Armor; + } + + float Armor::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index 51c0ea21c..d8d09d5bb 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -12,6 +12,8 @@ namespace MWClass public: + virtual float getWeight (const MWWorld::Ptr& ptr) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering @@ -65,11 +67,21 @@ namespace MWClass virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; ///< @return the enchantment ID if the object is enchanted, otherwise an empty string + virtual void applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + + virtual std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; + ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. \n + /// Second item in the pair specifies the error message + virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual float 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 6c3b7b86c..40ba21733 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -33,7 +33,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Book::getModel(const MWWorld::Ptr &ptr) const @@ -147,6 +147,21 @@ namespace MWClass return ref->mBase->mEnchant; } + void Book::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + ESM::Book newItem = *ref->mBase; + newItem.mId=""; + newItem.mName=newName; + newItem.mData.mIsScroll = 1; + newItem.mData.mEnchant=enchCharge; + newItem.mEnchant=enchId; + const ESM::Book *record = MWBase::Environment::get().getWorld()->createRecord (newItem); + ref->mBase = record; + } + boost::shared_ptr Book::use (const MWWorld::Ptr& ptr) const { return boost::shared_ptr(new MWWorld::ActionRead(ptr)); @@ -160,4 +175,24 @@ namespace MWClass return MWWorld::Ptr(&cell.mBooks.insert(*ref), &cell); } + + float Book::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mData.mEnchant/10.f; + } + + bool Book::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Books; + } + + float Book::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/book.hpp b/apps/openmw/mwclass/book.hpp index acb1aac06..7fb8a9507 100644 --- a/apps/openmw/mwclass/book.hpp +++ b/apps/openmw/mwclass/book.hpp @@ -51,10 +51,18 @@ namespace MWClass virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; ///< @return the enchantment ID if the object is enchanted, otherwise an empty string + virtual void applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + + virtual float getWeight (const MWWorld::Ptr& ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index c411bb193..df05a7910 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -13,6 +13,8 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" +#include "../mwworld/nullaction.hpp" +#include "../mwworld/player.hpp" #include "../mwgui/tooltips.hpp" @@ -35,7 +37,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Clothing::getModel(const MWWorld::Ptr &ptr) const @@ -62,6 +64,9 @@ namespace MWClass boost::shared_ptr Clothing::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { + if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) + return boost::shared_ptr (new MWWorld::NullAction ()); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); action->setSound(getUpSoundId(ptr)); @@ -203,6 +208,8 @@ namespace MWClass } info.enchant = ref->mBase->mEnchant; + if (!info.enchant.empty()) + info.remainingEnchantCharge = ptr.getCellRef().mEnchantmentCharge; info.text = text; @@ -217,6 +224,59 @@ namespace MWClass return ref->mBase->mEnchant; } + void Clothing::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + ESM::Clothing newItem = *ref->mBase; + newItem.mId=""; + newItem.mName=newName; + newItem.mData.mEnchant=enchCharge; + newItem.mEnchant=enchId; + const ESM::Clothing *record = MWBase::Environment::get().getWorld()->createRecord (newItem); + ref->mBase = record; + } + + std::pair Clothing::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const + { + // slots that this item can be equipped in + std::pair, bool> slots = MWWorld::Class::get(ptr).getEquipmentSlots(ptr); + + 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; + + 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}"); + } + } + } + } + return std::make_pair (1, ""); + } + boost::shared_ptr Clothing::use (const MWWorld::Ptr& ptr) const { boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); @@ -234,4 +294,24 @@ namespace MWClass return MWWorld::Ptr(&cell.mClothes.insert(*ref), &cell); } + + float Clothing::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mData.mEnchant/10.f; + } + + bool Clothing::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Clothing; + } + + float Clothing::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index f7801848f..e2e1188a1 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -59,11 +59,23 @@ namespace MWClass virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; ///< @return the enchantment ID if the object is enchanted, otherwise an empty string + virtual void applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + + virtual std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; + ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. + /// Second item in the pair specifies the error message + virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; + + virtual float getWeight (const MWWorld::Ptr& ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; } diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index bbe005955..4ee95b96e 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -9,6 +9,7 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/failedaction.hpp" +#include "../mwworld/nullaction.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/customdata.hpp" #include "../mwworld/cellstore.hpp" @@ -85,6 +86,9 @@ namespace MWClass boost::shared_ptr Container::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { + if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) + return boost::shared_ptr (new MWWorld::NullAction ()); + const std::string lockedSound = "LockedChest"; const std::string trapActivationSound = "Disarm Trap Fail"; @@ -111,7 +115,7 @@ namespace MWClass if (needKey && hasKey) { - MWBase::Environment::get().getWindowManager ()->messageBox (keyName + " #{sKeyUsed}", std::vector()); + MWBase::Environment::get().getWindowManager ()->messageBox (keyName + " #{sKeyUsed}"); ptr.getCellRef().mLockLevel = 0; // using a key disarms the trap ptr.getCellRef().mTrap = ""; diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index edf5dd25d..a263af464 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -5,10 +5,12 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/magiceffects.hpp" +#include "../mwmechanics/movement.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" @@ -18,6 +20,7 @@ #include "../mwworld/physicssystem.hpp" #include "../mwrender/renderinginterface.hpp" +#include "../mwrender/actors.hpp" #include "../mwgui/tooltips.hpp" @@ -27,6 +30,7 @@ namespace { MWMechanics::CreatureStats mCreatureStats; MWWorld::ContainerStore mContainerStore; + MWMechanics::Movement mMovement; virtual MWWorld::CustomData *clone() const; }; @@ -45,6 +49,18 @@ namespace MWClass { std::auto_ptr data (new CustomData); + static bool inited = false; + if(!inited) + { + const MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Store &gmst = world->getStore().get(); + + fMinWalkSpeedCreature = gmst.find("fMinWalkSpeedCreature"); + fMaxWalkSpeedCreature = gmst.find("fMaxWalkSpeedCreature"); + + inited = true; + } + MWWorld::LiveCellRef *ref = ptr.get(); // creature stats @@ -85,6 +101,11 @@ namespace MWClass return ref->mBase->mId; } + void Creature::adjustPosition(const MWWorld::Ptr& ptr) const + { + MWBase::Environment::get().getWorld()->adjustPosition(ptr); + } + void Creature::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { MWRender::Actors& actors = renderingInterface.getActors(); @@ -96,7 +117,7 @@ namespace MWClass const std::string model = getModel(ptr); if(!model.empty()) physics.addActor(ptr); - MWBase::Environment::get().getMechanicsManager()->addActor (ptr); + MWBase::Environment::get().getMechanicsManager()->add(ptr); } std::string Creature::getModel(const MWWorld::Ptr &ptr) const @@ -131,7 +152,7 @@ namespace MWClass const MWWorld::Ptr& actor) const { if (MWWorld::Class::get (ptr).getCreatureStats (ptr).isDead()) - return boost::shared_ptr (new MWWorld::ActionOpen(ptr)); + return boost::shared_ptr (new MWWorld::ActionOpen(ptr, true)); else return boost::shared_ptr (new MWWorld::ActionTalk (ptr)); } @@ -174,6 +195,42 @@ namespace MWClass return true; } + 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; + } + + MWMechanics::Movement& Creature::getMovementSettings (const MWWorld::Ptr& ptr) const + { + ensureCustomData (ptr); + + return dynamic_cast (*ptr.getRefData().getCustomData()).mMovement; + } + + Ogre::Vector3 Creature::getMovementVector (const MWWorld::Ptr& ptr) const + { + MWMechanics::Movement &movement = getMovementSettings(ptr); + Ogre::Vector3 vec(movement.mPosition); + movement.mPosition[0] = 0.0f; + movement.mPosition[1] = 0.0f; + movement.mPosition[2] = 0.0f; + return vec; + } + + Ogre::Vector3 Creature::getRotationVector (const MWWorld::Ptr& ptr) const + { + MWMechanics::Movement &movement = getMovementSettings(ptr); + Ogre::Vector3 vec(movement.mRotation); + movement.mRotation[0] = 0.0f; + movement.mRotation[1] = 0.0f; + movement.mRotation[2] = 0.0f; + return vec; + } + MWGui::ToolTipInfo Creature::getToolTipInfo (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = @@ -190,6 +247,12 @@ namespace MWClass return info; } + float Creature::getArmorRating (const MWWorld::Ptr& ptr) const + { + /// \todo add Shield magic effect magnitude here, controlled by a GMST (Vanilla vs MCP behaviour) + return 0.f; + } + float Creature::getCapacity (const MWWorld::Ptr& ptr) const { const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); @@ -202,9 +265,9 @@ namespace MWClass const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); - weight -= stats.getMagicEffects().get (MWMechanics::EffectKey (8)).mMagnitude; // feather + weight -= stats.getMagicEffects().get (MWMechanics::EffectKey (ESM::MagicEffect::Feather)).mMagnitude; - weight += stats.getMagicEffects().get (MWMechanics::EffectKey (7)).mMagnitude; // burden + weight += stats.getMagicEffects().get (MWMechanics::EffectKey (ESM::MagicEffect::Burden)).mMagnitude; if (weight<0) weight = 0; @@ -212,6 +275,22 @@ namespace MWClass return weight; } + + int Creature::getServices(const MWWorld::Ptr &actor) const + { + MWWorld::LiveCellRef* ref = actor.get(); + if (ref->mBase->mHasAI) + return ref->mBase->mAiData.mServices; + else + return 0; + } + + bool Creature::isPersistent(const MWWorld::Ptr &actor) const + { + MWWorld::LiveCellRef* ref = actor.get(); + return ref->mBase->mPersistent; + } + MWWorld::Ptr Creature::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { @@ -220,4 +299,7 @@ namespace MWClass return MWWorld::Ptr(&cell.mCreatures.insert(*ref), &cell); } + + 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 a158fa743..5c30004cd 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -1,9 +1,7 @@ #ifndef GAME_MWCLASS_CREATURE_H #define GAME_MWCLASS_CREATURE_H -#include "../mwrender/renderinginterface.hpp" -#include "../mwrender/actors.hpp" - +#include "../mwworld/class.hpp" namespace MWClass { @@ -14,6 +12,9 @@ namespace MWClass virtual MWWorld::Ptr copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + static const ESM::GameSetting *fMinWalkSpeedCreature; + static const ESM::GameSetting *fMaxWalkSpeedCreature; + public: virtual std::string getId (const MWWorld::Ptr& ptr) const; @@ -24,6 +25,8 @@ namespace MWClass virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void adjustPosition(const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. @@ -56,9 +59,28 @@ namespace MWClass ///< Returns total weight of objects inside this object (including modifications from magic /// effects). Throws an exception, if the object can't hold other objects. + virtual float getArmorRating (const MWWorld::Ptr& ptr) const; + ///< @return combined armor rating of this actor + 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; + + virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const; + ///< Return desired movement. + + virtual Ogre::Vector3 getMovementVector (const MWWorld::Ptr& ptr) const; + ///< Return desired movement vector (determined based on movement settings, + /// stance and stats). + + virtual Ogre::Vector3 getRotationVector (const MWWorld::Ptr& ptr) const; + ///< Return desired rotations, as euler angles. + + float getSpeed (const MWWorld::Ptr& ptr) const; + static void registerSelf(); virtual std::string getModel(const MWWorld::Ptr &ptr) const; diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index fb6329939..91d38b8ef 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -12,6 +12,7 @@ #include "../mwworld/nullaction.hpp" #include "../mwworld/failedaction.hpp" #include "../mwworld/actionteleport.hpp" +#include "../mwworld/actiondoor.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/inventorystore.hpp" @@ -71,7 +72,7 @@ namespace MWClass ptr.get(); const std::string &openSound = ref->mBase->mOpenSound; - //const std::string &closeSound = ref->mBase->closeSound; + const std::string &closeSound = ref->mBase->mCloseSound; const std::string lockedSound = "LockedDoor"; const std::string trapActivationSound = "Disarm Trap Fail"; @@ -98,7 +99,7 @@ namespace MWClass if (needKey && hasKey) { - MWBase::Environment::get().getWindowManager ()->messageBox (keyName + " #{sKeyUsed}", std::vector()); + MWBase::Environment::get().getWindowManager ()->messageBox (keyName + " #{sKeyUsed}"); ptr.getCellRef().mLockLevel = 0; // using a key disarms the trap ptr.getCellRef().mTrap = ""; @@ -139,12 +140,11 @@ namespace MWClass else { // animated door - // TODO return action for rotating the door - - // This is a little pointless, but helps with testing - boost::shared_ptr action(new MWWorld::NullAction); - - action->setSound(openSound); + boost::shared_ptr action(new MWWorld::ActionDoor(ptr)); + if (MWBase::Environment::get().getWorld()->getOpenOrCloseDoor(ptr)) + action->setSound(openSound); + else + action->setSound(closeSound); return action; } diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 7ad8f1b47..2cc607f5f 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -13,6 +13,7 @@ #include "../mwworld/physicssystem.hpp" #include "../mwworld/actioneat.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/nullaction.hpp" #include "../mwmechanics/npcstats.hpp" @@ -45,7 +46,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Ingredient::getModel(const MWWorld::Ptr &ptr) const @@ -72,6 +73,9 @@ namespace MWClass boost::shared_ptr Ingredient::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { + if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) + return boost::shared_ptr (new MWWorld::NullAction ()); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); action->setSound(getUpSoundId(ptr)); @@ -193,4 +197,17 @@ namespace MWClass return MWWorld::Ptr(&cell.mIngreds.insert(*ref), &cell); } + + bool Ingredient::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Ingredients; + } + + + float Ingredient::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/ingredient.hpp b/apps/openmw/mwclass/ingredient.hpp index 0afd202fb..690dd601a 100644 --- a/apps/openmw/mwclass/ingredient.hpp +++ b/apps/openmw/mwclass/ingredient.hpp @@ -56,6 +56,10 @@ namespace MWClass ///< Return name of inventory icon. virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual float getWeight (const MWWorld::Ptr& ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index b94b0d395..ccc9d1de6 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -36,14 +36,9 @@ namespace MWClass objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); if (!model.empty()) - objects.insertMesh(ptr, "meshes\\" + model); - - const int color = ref->mBase->mData.mColor; - const float r = ((color >> 0) & 0xFF) / 255.0f; - const float g = ((color >> 8) & 0xFF) / 255.0f; - const float b = ((color >> 16) & 0xFF) / 255.0f; - const float radius = float (ref->mBase->mData.mRadius); - objects.insertLight (ptr, r, g, b, radius); + objects.insertMesh(ptr, "meshes\\" + model, true); + else + objects.insertLight(ptr); } void Light::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const @@ -55,7 +50,7 @@ namespace MWClass const std::string &model = ref->mBase->mModel; if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,ref->mBase->mData.mFlags & ESM::Light::Carry); if (!ref->mBase->mSound.empty()) MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->mBase->mSound, 1.0, 1.0, MWBase::SoundManager::Play_Loop); @@ -88,6 +83,9 @@ namespace MWClass boost::shared_ptr Light::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { + if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) + return boost::shared_ptr (new MWWorld::NullAction ()); + MWWorld::LiveCellRef *ref = ptr.get(); @@ -205,4 +203,16 @@ namespace MWClass return MWWorld::Ptr(&cell.mLights.insert(*ref), &cell); } + + bool Light::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Lights; + } + + float Light::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index 640e1705b..7f747ef48 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -57,6 +57,10 @@ namespace MWClass ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual float getWeight (const MWWorld::Ptr& ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; } diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index a667fefb2..084490742 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -1,7 +1,7 @@ #include "lockpick.hpp" -#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -13,6 +13,7 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" +#include "../mwworld/nullaction.hpp" #include "../mwgui/tooltips.hpp" @@ -35,13 +36,13 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Lockpick::getModel(const MWWorld::Ptr &ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = + ptr.get(); assert(ref->mBase != NULL); const std::string &model = ref->mBase->mModel; @@ -53,8 +54,8 @@ namespace MWClass std::string Lockpick::getName (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = + ptr.get(); return ref->mBase->mName; } @@ -62,6 +63,9 @@ namespace MWClass boost::shared_ptr Lockpick::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { + if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) + return boost::shared_ptr (new MWWorld::NullAction ()); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); action->setSound(getUpSoundId(ptr)); @@ -71,8 +75,8 @@ namespace MWClass std::string Lockpick::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = + ptr.get(); return ref->mBase->mScript; } @@ -88,8 +92,8 @@ namespace MWClass int Lockpick::getValue (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = + ptr.get(); return ref->mBase->mData.mValue; } @@ -98,7 +102,7 @@ namespace MWClass { boost::shared_ptr instance (new Lockpick); - registerClass (typeid (ESM::Tool).name(), instance); + registerClass (typeid (ESM::Lockpick).name(), instance); } std::string Lockpick::getUpSoundId (const MWWorld::Ptr& ptr) const @@ -113,24 +117,24 @@ namespace MWClass std::string Lockpick::getInventoryIcon (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = + ptr.get(); return ref->mBase->mIcon; } bool Lockpick::hasToolTip (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = + ptr.get(); return (ref->mBase->mName != ""); } MWGui::ToolTipInfo Lockpick::getToolTipInfo (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = + ptr.get(); MWGui::ToolTipInfo info; info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); @@ -138,9 +142,9 @@ namespace MWClass std::string text; - /// \todo store remaining uses somewhere + int remainingUses = (ptr.getCellRef().mCharge != -1) ? ptr.getCellRef().mCharge : ref->mBase->mData.mUses; - text += "\n#{sUses}: " + MWGui::ToolTips::toString(ref->mBase->mData.mUses); + 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}"); @@ -167,9 +171,29 @@ namespace MWClass MWWorld::Ptr Lockpick::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = + ptr.get(); return MWWorld::Ptr(&cell.mLockpicks.insert(*ref), &cell); } + + bool Lockpick::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Picks; + } + + int Lockpick::getItemMaxHealth (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mData.mUses; + } + + float Lockpick::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/lockpick.hpp b/apps/openmw/mwclass/lockpick.hpp index 0961b55b2..a7cf3791f 100644 --- a/apps/openmw/mwclass/lockpick.hpp +++ b/apps/openmw/mwclass/lockpick.hpp @@ -57,6 +57,13 @@ namespace MWClass ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + + virtual float getWeight (const MWWorld::Ptr& ptr) const; + + virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const; + ///< Return item max health or throw an exception, if class does not have item health }; } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 94d40bcb7..6b384be99 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -14,6 +14,8 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/manualref.hpp" +#include "../mwworld/nullaction.hpp" +#include "../mwworld/actionsoulgem.hpp" #include "../mwgui/tooltips.hpp" @@ -22,6 +24,18 @@ #include +namespace +{ +bool isGold (const MWWorld::Ptr& ptr) +{ + return Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_001") + || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_005") + || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_010") + || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_025") + || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_100"); +} +} + namespace MWClass { void Miscellaneous::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -38,7 +52,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Miscellaneous::getModel(const MWWorld::Ptr &ptr) const @@ -65,6 +79,9 @@ namespace MWClass boost::shared_ptr Miscellaneous::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { + if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) + return boost::shared_ptr (new MWWorld::NullAction ()); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); action->setSound(getUpSoundId(ptr)); @@ -85,7 +102,15 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return ref->mBase->mData.mValue; + int value = (ptr.getCellRef().mGoldValue == 1) ? ref->mBase->mData.mValue : ptr.getCellRef().mGoldValue; + + if (ptr.getCellRef().mSoul != "") + { + const ESM::Creature *creature = MWBase::Environment::get().getWorld()->getStore().get().find(ref->mRef.mSoul); + value *= creature->mData.mSoul; + } + + return value; } void Miscellaneous::registerSelf() @@ -97,25 +122,15 @@ namespace MWClass std::string Miscellaneous::getUpSoundId (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - if (ref->mBase->mName == MWBase::Environment::get().getWorld()->getStore().get().find("sGold")->getString()) - { + if (isGold(ptr)) return std::string("Item Gold Up"); - } return std::string("Item Misc Up"); } std::string Miscellaneous::getDownSoundId (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - if (ref->mBase->mName == MWBase::Environment::get().getWorld()->getStore().get().find("sGold")->getString()) - { + if (isGold(ptr)) return std::string("Item Gold Down"); - } return std::string("Item Misc Down"); } @@ -146,12 +161,15 @@ namespace MWClass int count = ptr.getRefData().getCount(); - bool isGold = (ref->mBase->mName == store.get().find("sGold")->getString()); - if (isGold && count == 1) - count = ref->mBase->mData.mValue; + bool gold = isGold(ptr); + + if (gold && ptr.getCellRef().mGoldValue != 1) + count = ptr.getCellRef().mGoldValue; + else if (gold) + count *= ref->mBase->mData.mValue; std::string countString; - if (!isGold) + if (!gold) countString = MWGui::ToolTips::getCountString(count); else // gold displays its count also if it's 1. countString = " (" + boost::lexical_cast(count) + ")"; @@ -167,10 +185,10 @@ namespace MWClass std::string text; - if (!isGold) + if (!gold) { 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()) { @@ -191,7 +209,7 @@ namespace MWClass const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - if (MWWorld::Class::get(ptr).getName(ptr) == store.get().find("sGold")->getString()) { + if (isGold(ptr)) { int goldAmount = ptr.getRefData().getCount(); std::string base = "Gold_001"; @@ -210,6 +228,8 @@ namespace MWClass MWWorld::LiveCellRef *ref = newRef.getPtr().get(); newPtr = MWWorld::Ptr(&cell.mMiscItems.insert(*ref), &cell); + newPtr.getRefData ().setCount(1); + newPtr.getCellRef().mGoldValue = goldAmount; } else { MWWorld::LiveCellRef *ref = ptr.get(); @@ -217,4 +237,28 @@ namespace MWClass } return newPtr; } + + boost::shared_ptr Miscellaneous::use (const MWWorld::Ptr& ptr) const + { + if (ptr.getCellRef().mSoul == "") + return boost::shared_ptr(new MWWorld::NullAction()); + else + return boost::shared_ptr(new MWWorld::ActionSoulgem(ptr)); + } + + bool Miscellaneous::canSell (const MWWorld::Ptr& item, int npcServices) const + { + MWWorld::LiveCellRef *ref = + item.get(); + + return !ref->mBase->mData.mIsKey && (npcServices & ESM::NPC::Misc); + } + + float Miscellaneous::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } + } diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index a5a79a8f6..16a8e8c05 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -49,6 +49,14 @@ namespace MWClass ///< Return name of inventory icon. virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) + const; + ///< Generate action for using via inventory menu + + virtual float getWeight (const MWWorld::Ptr& ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 586f9638d..4574792ae 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -49,15 +49,101 @@ namespace { return new CustomData (*this); } + + void autoCalculateAttributes (const ESM::NPC* npc, MWMechanics::CreatureStats& creatureStats) + { + // race bonus + const ESM::Race *race = + MWBase::Environment::get().getWorld()->getStore().get().find(npc->mRace); + + bool male = (npc->mFlags & ESM::NPC::Female) == 0; + + int level = creatureStats.getLevel(); + + for (int i=0; imData.mAttributeValues[i]; + creatureStats.getAttribute(i).setBase (male ? attribute.mMale : attribute.mFemale); + } + + // class bonus + const ESM::Class *class_ = + MWBase::Environment::get().getWorld()->getStore().get().find(npc->mClass); + + for (int i=0; i<2; ++i) + { + int attribute = class_->mData.mAttribute[i]; + if (attribute>=0 && attribute<8) + { + creatureStats.getAttribute(attribute).setBase ( + creatureStats.getAttribute(attribute).getBase() + 10); + } + } + + // skill bonus + for (int attribute=0; attributegetStore().get().find(j); + + if (skill->mData.mAttribute != attribute) + continue; + + // is this a minor or major skill? + float add=0.2; + for (int k=0; k<5; ++k) + { + if (class_->mData.mSkills[k][0] == j) + add=0.5; + } + for (int k=0; k<5; ++k) + { + if (class_->mData.mSkills[k][1] == j) + add=1.0; + } + modifierSum += add; + } + creatureStats.getAttribute(attribute).setBase ( std::min(creatureStats.getAttribute(attribute).getBase() + + static_cast((level-1) * modifierSum+0.5), 100) ); + } + } } namespace MWClass { void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const { + static bool inited = false; + if(!inited) + { + const MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Store &gmst = world->getStore().get(); + + fMinWalkSpeed = gmst.find("fMinWalkSpeed"); + fMaxWalkSpeed = gmst.find("fMaxWalkSpeed"); + 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"); + fJumpEncumbranceBase = gmst.find("fJumpEncumbranceBase"); + fJumpEncumbranceMultiplier = gmst.find("fJumpEncumbranceMultiplier"); + fJumpAcrobaticsBase = gmst.find("fJumpAcrobaticsBase"); + fJumpAcroMultiplier = gmst.find("fJumpAcroMultiplier"); + fJumpRunMultiplier = gmst.find("fJumpRunMultiplier"); + fWereWolfRunMult = gmst.find("fWereWolfRunMult"); + + inited = true; + } if (!ptr.getRefData().getCustomData()) { - std::auto_ptr data (new CustomData); + std::auto_ptr data(new CustomData); MWWorld::LiveCellRef *ref = ptr.get(); @@ -100,14 +186,14 @@ namespace MWClass } else { - /// \todo do something with mNpdt12 maybe:p - for (int i=0; i<8; ++i) - data->mCreatureStats.getAttribute (i).set (10); - for (int i=0; i<3; ++i) data->mCreatureStats.setDynamic (i, 10); - data->mCreatureStats.setLevel (1); + data->mCreatureStats.setLevel(ref->mBase->mNpdt12.mLevel); + data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt12.mDisposition); + data->mNpcStats.setReputation(ref->mBase->mNpdt12.mReputation); + + autoCalculateAttributes(ref->mBase, data->mCreatureStats); } data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello); @@ -133,6 +219,11 @@ namespace MWClass return ref->mBase->mId; } + void Npc::adjustPosition(const MWWorld::Ptr& ptr) const + { + MWBase::Environment::get().getWorld()->adjustPosition(ptr); + } + void Npc::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { renderingInterface.getActors().insertNPC(ptr, getInventoryStore(ptr)); @@ -141,7 +232,13 @@ namespace MWClass void Npc::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { physics.addActor(ptr); - MWBase::Environment::get().getMechanicsManager()->addActor(ptr); + MWBase::Environment::get().getMechanicsManager()->add(ptr); + } + + bool Npc::isPersistent(const MWWorld::Ptr &actor) const + { + MWWorld::LiveCellRef* ref = actor.get(); + return ref->mBase->mPersistent; } std::string Npc::getModel(const MWWorld::Ptr &ptr) const @@ -156,13 +253,10 @@ namespace MWClass std::string bodyRaceID = headID.substr(0, end); std::string model = "meshes\\base_anim.nif"; - if (bodyRaceID == "b_n_khajiit_m_" || - bodyRaceID == "b_n_khajiit_f_" || - bodyRaceID == "b_n_argonian_m_" || - bodyRaceID == "b_n_argonian_f_") - { + const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(ref->mBase->mRace); + if(race->mData.mFlags & ESM::Race::Beast) model = "meshes\\base_animkna.nif"; - } + return model; } @@ -193,7 +287,9 @@ namespace MWClass const MWWorld::Ptr& actor) const { if (MWWorld::Class::get (ptr).getCreatureStats (ptr).isDead()) - return boost::shared_ptr (new MWWorld::ActionOpen(ptr)); + return boost::shared_ptr (new MWWorld::ActionOpen(ptr, true)); + else if (MWWorld::Class::get(actor).getStance(actor, MWWorld::Class::Sneak)) + return boost::shared_ptr (new MWWorld::ActionOpen(ptr)); // stealing else return boost::shared_ptr (new MWWorld::ActionTalk (ptr)); } @@ -296,9 +392,87 @@ namespace MWClass return false; } - float Npc::getSpeed (const MWWorld::Ptr& ptr) const + float Npc::getSpeed(const MWWorld::Ptr& ptr) const + { + const MWBase::World *world = MWBase::Environment::get().getWorld(); + const CustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); + const MWMechanics::MagicEffects &mageffects = npcdata->mCreatureStats.getMagicEffects(); + + const float normalizedEncumbrance = Npc::getEncumbrance(ptr) / Npc::getCapacity(ptr); + + float walkSpeed = fMinWalkSpeed->getFloat() + 0.01f*npcdata->mCreatureStats.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)) + walkSpeed *= fSneakSpeedMultiplier->getFloat(); + + float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() * + fAthleticsRunBonus->getFloat() + fBaseRunMultiplier->getFloat()); + if(npcdata->mNpcStats.isWerewolf()) + runSpeed *= fWereWolfRunMult->getFloat(); + + float moveSpeed; + if(normalizedEncumbrance >= 1.0f) + moveSpeed = 0.0f; + else if(mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude > 0) + { + float flySpeed = 0.01f*(npcdata->mCreatureStats.getAttribute(ESM::Attribute::Speed).getModified() + + mageffects.get(MWMechanics::EffectKey(10/*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(Npc::getStance(ptr, Run, false)) + swimSpeed = runSpeed; + swimSpeed *= 1.0f + 0.01f * mageffects.get(MWMechanics::EffectKey(1/*swift swim*/)).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)) + moveSpeed = runSpeed; + else + moveSpeed = walkSpeed; + if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) + moveSpeed *= 0.75f; + + return moveSpeed; + } + + float Npc::getJump(const MWWorld::Ptr &ptr) const { - return getStance (ptr, Run) ? 600 : 300; // TODO calculate these values from stats + const CustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); + const MWMechanics::MagicEffects &mageffects = npcdata->mCreatureStats.getMagicEffects(); + const float encumbranceTerm = fJumpEncumbranceBase->getFloat() + + fJumpEncumbranceMultiplier->getFloat() * + (1.0f - Npc::getEncumbrance(ptr)/Npc::getCapacity(ptr)); + + float a = npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified(); + float b = 0.0f; + if(a > 50.0f) + { + b = a - 50.0f; + a = 50.0f; + } + + float x = fJumpAcrobaticsBase->getFloat() + + std::pow(a / 15.0f, fJumpAcroMultiplier->getFloat()); + x += 3 * b * fJumpAcroMultiplier->getFloat(); + x += mageffects.get(MWMechanics::EffectKey(9/*jump*/)).mMagnitude * 64; + x *= encumbranceTerm; + + if(Npc::getStance(ptr, Run, false)) + x *= fJumpRunMultiplier->getFloat(); + x *= 1.25f;//fatigueTerm; + x -= -627.2/*gravity constant*/; + x /= 3; + + return x; } MWMechanics::Movement& Npc::getMovementSettings (const MWWorld::Ptr& ptr) const @@ -310,18 +484,24 @@ namespace MWClass Ogre::Vector3 Npc::getMovementVector (const MWWorld::Ptr& ptr) const { - Ogre::Vector3 vector (0, 0, 0); - - vector.x = getMovementSettings (ptr).mLeftRight * 127; - vector.y = getMovementSettings (ptr).mForwardBackward * 127; - vector.z = getMovementSettings(ptr).mUpDown * 127; - - //if (getStance (ptr, Run, false)) - // vector *= 2; + MWMechanics::Movement &movement = getMovementSettings(ptr); + Ogre::Vector3 vec(movement.mPosition); + movement.mPosition[0] = 0.0f; + movement.mPosition[1] = 0.0f; + movement.mPosition[2] = 0.0f; + return vec; + } - return vector; + Ogre::Vector3 Npc::getRotationVector (const MWWorld::Ptr& ptr) const + { + MWMechanics::Movement &movement = getMovementSettings(ptr); + Ogre::Vector3 vec(movement.mRotation); + movement.mRotation[0] = 0.0f; + movement.mRotation[1] = 0.0f; + movement.mRotation[2] = 0.0f; + return vec; } - + bool Npc::isEssential (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = @@ -371,9 +551,9 @@ namespace MWClass const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); - weight -= stats.getMagicEffects().get (MWMechanics::EffectKey (8)).mMagnitude; // feather + weight -= stats.getMagicEffects().get (MWMechanics::EffectKey (ESM::MagicEffect::Feather)).mMagnitude; - weight += stats.getMagicEffects().get (MWMechanics::EffectKey (7)).mMagnitude; // burden + weight += stats.getMagicEffects().get (MWMechanics::EffectKey (ESM::MagicEffect::Burden)).mMagnitude; if (weight<0) weight = 0; @@ -405,12 +585,84 @@ namespace MWClass stats.useSkill (skill, *class_, usageType); } + float Npc::getArmorRating (const MWWorld::Ptr& ptr) const + { + MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore(ptr); + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + + int ratings[MWWorld::InventoryStore::Slots]; + + int iBaseArmorSkill = gmst.find("iBaseArmorSkill")->getInt(); + float fUnarmoredBase1 = gmst.find("fUnarmoredBase1")->getFloat(); + float fUnarmoredBase2 = gmst.find("fUnarmoredBase2")->getFloat(); + int unarmoredSkill = MWWorld::Class::get(ptr).getNpcStats(ptr).getSkill(ESM::Skill::Unarmored).getModified(); + + for (int i = 0; i < MWWorld::InventoryStore::Slots; ++i) + { + MWWorld::ContainerStoreIterator it = invStore.getSlot(i); + if (it == invStore.end() || it->getTypeName() != typeid(ESM::Armor).name()) + { + // unarmored + ratings[i] = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill); + } + else + { + MWWorld::LiveCellRef *ref = + it->get(); + + int armorSkillType = MWWorld::Class::get(*it).getEquipmentSkill(*it); + int armorSkill = MWWorld::Class::get(ptr).getNpcStats(ptr).getSkill(armorSkillType).getModified(); + + if (ref->mBase->mData.mWeight == 0) + ratings[i] = ref->mBase->mData.mArmor; + else + ratings[i] = ref->mBase->mData.mArmor * armorSkill / iBaseArmorSkill; + } + } + + float shield = MWWorld::Class::get(ptr).getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Shield).mMagnitude; + + return ratings[MWWorld::InventoryStore::Slot_Cuirass] * 0.3 + + (ratings[MWWorld::InventoryStore::Slot_CarriedLeft] + ratings[MWWorld::InventoryStore::Slot_Helmet] + + ratings[MWWorld::InventoryStore::Slot_Greaves] + ratings[MWWorld::InventoryStore::Slot_Boots] + + ratings[MWWorld::InventoryStore::Slot_LeftPauldron] + ratings[MWWorld::InventoryStore::Slot_RightPauldron] + ) * 0.1 + + (ratings[MWWorld::InventoryStore::Slot_LeftGauntlet] + MWWorld::InventoryStore::Slot_RightGauntlet) + * 0.05 + + shield; + } + + void Npc::adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const { y = 0; x = 0; } + void Npc::adjustScale(const MWWorld::Ptr &ptr, float &scale) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + const ESM::Race* race = + MWBase::Environment::get().getWorld()->getStore().get().find(ref->mBase->mRace); + + if (ref->mBase->isMale()) + scale *= race->mData.mHeight.mMale; + else + scale *= race->mData.mHeight.mFemale; + } + + int Npc::getServices(const MWWorld::Ptr &actor) const + { + MWWorld::LiveCellRef* ref = actor.get(); + if (ref->mBase->mHasAI) + return ref->mBase->mAiData.mServices; + else + return 0; + } + MWWorld::Ptr Npc::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { @@ -419,4 +671,21 @@ namespace MWClass return MWWorld::Ptr(&cell.mNpcs.insert(*ref), &cell); } + + const ESM::GameSetting *Npc::fMinWalkSpeed; + const ESM::GameSetting *Npc::fMaxWalkSpeed; + const ESM::GameSetting *Npc::fEncumberedMoveEffect; + const ESM::GameSetting *Npc::fSneakSpeedMultiplier; + const ESM::GameSetting *Npc::fAthleticsRunBonus; + const ESM::GameSetting *Npc::fBaseRunMultiplier; + const ESM::GameSetting *Npc::fMinFlySpeed; + const ESM::GameSetting *Npc::fMaxFlySpeed; + const ESM::GameSetting *Npc::fSwimRunBase; + const ESM::GameSetting *Npc::fSwimRunAthleticsMult; + const ESM::GameSetting *Npc::fJumpEncumbranceBase; + const ESM::GameSetting *Npc::fJumpEncumbranceMultiplier; + const ESM::GameSetting *Npc::fJumpAcrobaticsBase; + const ESM::GameSetting *Npc::fJumpAcroMultiplier; + const ESM::GameSetting *Npc::fJumpRunMultiplier; + const ESM::GameSetting *Npc::fWereWolfRunMult; } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 20c2da4b1..83bfbdcbf 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -3,6 +3,11 @@ #include "../mwworld/class.hpp" +namespace ESM +{ + class GameSetting; +} + namespace MWClass { class Npc : public MWWorld::Class @@ -12,6 +17,23 @@ namespace MWClass virtual MWWorld::Ptr copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + static const ESM::GameSetting *fMinWalkSpeed; + static const ESM::GameSetting *fMaxWalkSpeed; + 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; + static const ESM::GameSetting *fJumpEncumbranceBase; + static const ESM::GameSetting *fJumpEncumbranceMultiplier; + static const ESM::GameSetting *fJumpAcrobaticsBase; + static const ESM::GameSetting *fJumpAcroMultiplier; + static const ESM::GameSetting *fJumpRunMultiplier; + static const ESM::GameSetting *fWereWolfRunMult; + public: virtual std::string getId (const MWWorld::Ptr& ptr) const; @@ -22,6 +44,8 @@ namespace MWClass virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; + virtual void adjustPosition(const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. @@ -59,11 +83,14 @@ namespace MWClass virtual bool getStance (const MWWorld::Ptr& ptr, Stance stance, bool ignoreForce = false) const; - ////< Check if a stance is active or not. + ///< Check if a stance is active or not. virtual float getSpeed (const MWWorld::Ptr& ptr) const; ///< Return movement speed. + virtual float getJump(const MWWorld::Ptr &ptr) const; + ///< Return jump velocity (not accounting for movement) + virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const; ///< Return desired movement. @@ -71,6 +98,9 @@ namespace MWClass ///< Return desired movement vector (determined based on movement settings, /// stance and stats). + virtual Ogre::Vector3 getRotationVector (const MWWorld::Ptr& ptr) const; + ///< Return desired rotations, as euler angles. + virtual float getCapacity (const MWWorld::Ptr& ptr) const; ///< Return total weight that fits into the object. Throws an exception, if the object can't /// hold other objects. @@ -79,12 +109,17 @@ namespace MWClass ///< Returns total weight of objects inside this object (including modifications from magic /// effects). Throws an exception, if the object can't hold other objects. + virtual float getArmorRating (const MWWorld::Ptr& ptr) const; + ///< @return combined armor rating of this actor + virtual bool apply (const MWWorld::Ptr& ptr, const std::string& id, const MWWorld::Ptr& actor) const; ///< Apply \a id on \a ptr. /// \param actor Actor that is resposible for the ID being applied to \a ptr. /// \return Any effect? + virtual void adjustScale (const MWWorld::Ptr &ptr, float &scale) const; + virtual void skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType) const; ///< Inform actor \a ptr that a skill use has succeeded. @@ -92,7 +127,11 @@ 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; + static void registerSelf(); virtual std::string getModel(const MWWorld::Ptr &ptr) const; diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 09d152de7..c2d2920d0 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -13,12 +13,15 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/nullaction.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" +#include "../mwmechanics/npcstats.hpp" + namespace MWClass { void Potion::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -35,7 +38,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Potion::getModel(const MWWorld::Ptr &ptr) const @@ -62,6 +65,9 @@ namespace MWClass boost::shared_ptr Potion::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { + if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) + return boost::shared_ptr (new MWWorld::NullAction ()); + boost::shared_ptr action( new MWWorld::ActionTake (ptr)); @@ -134,6 +140,23 @@ namespace MWClass text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); 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(); + MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats (player); + int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase(); + int i=0; + for (MWGui::Widgets::SpellEffectList::iterator it = info.effects.begin(); it != info.effects.end(); ++it) + { + /// \todo this code is duplicated from mwclass/ingredient, put it in a helper function + it->mKnown = ( (i == 0 && alchemySkill >= 15) + || (i == 1 && alchemySkill >= 30) + || (i == 2 && alchemySkill >= 45) + || (i == 3 && alchemySkill >= 60)); + + ++i; + } + info.isPotion = true; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { @@ -171,4 +194,16 @@ namespace MWClass return MWWorld::Ptr(&cell.mPotions.insert(*ref), &cell); } + + bool Potion::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Potions; + } + + float Potion::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/potion.hpp b/apps/openmw/mwclass/potion.hpp index d595f7e69..0f0578ca0 100644 --- a/apps/openmw/mwclass/potion.hpp +++ b/apps/openmw/mwclass/potion.hpp @@ -52,6 +52,10 @@ namespace MWClass ///< Return name of inventory icon. virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual float getWeight (const MWWorld::Ptr& ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; } diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 0d8653aa8..6b7e43230 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -1,7 +1,7 @@ #include "probe.hpp" -#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -13,6 +13,7 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" +#include "../mwworld/nullaction.hpp" #include "../mwgui/tooltips.hpp" @@ -35,7 +36,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Probe::getModel(const MWWorld::Ptr &ptr) const @@ -61,6 +62,9 @@ namespace MWClass boost::shared_ptr Probe::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { + if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) + return boost::shared_ptr (new MWWorld::NullAction ()); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); action->setSound(getUpSoundId(ptr)); @@ -137,9 +141,9 @@ namespace MWClass std::string text; - /// \todo store remaining uses somewhere + int remainingUses = (ptr.getCellRef().mCharge != -1) ? ptr.getCellRef().mCharge : ref->mBase->mData.mUses; - text += "\n#{sUses}: " + MWGui::ToolTips::toString(ref->mBase->mData.mUses); + 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}"); @@ -171,4 +175,24 @@ namespace MWClass return MWWorld::Ptr(&cell.mProbes.insert(*ref), &cell); } + + bool Probe::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Probes; + } + + int Probe::getItemMaxHealth (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mData.mUses; + } + + float Probe::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/probe.hpp b/apps/openmw/mwclass/probe.hpp index d9f90baf6..832169f8b 100644 --- a/apps/openmw/mwclass/probe.hpp +++ b/apps/openmw/mwclass/probe.hpp @@ -57,6 +57,13 @@ namespace MWClass ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + + virtual float getWeight (const MWWorld::Ptr& ptr) const; + + virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const; + ///< Return item max health or throw an exception, if class does not have item health }; } diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index edb28d16c..e2993029b 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -1,7 +1,7 @@ #include "repair.hpp" -#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -11,6 +11,8 @@ #include "../mwworld/actiontake.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" +#include "../mwworld/nullaction.hpp" +#include "../mwworld/actionrepair.hpp" #include "../mwgui/tooltips.hpp" @@ -33,7 +35,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Repair::getModel(const MWWorld::Ptr &ptr) const @@ -60,6 +62,9 @@ namespace MWClass boost::shared_ptr Repair::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { + if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) + return boost::shared_ptr (new MWWorld::NullAction ()); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); action->setSound(getUpSoundId(ptr)); @@ -116,6 +121,19 @@ namespace MWClass return (ref->mBase->mName != ""); } + bool Repair::hasItemHealth (const MWWorld::Ptr& ptr) const + { + return true; + } + + int Repair::getItemMaxHealth (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mData.mUses; + } + MWGui::ToolTipInfo Repair::getToolTipInfo (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = @@ -127,9 +145,9 @@ namespace MWClass std::string text; - /// \todo store remaining uses somewhere + int remainingUses = (ptr.getCellRef().mCharge != -1) ? ptr.getCellRef().mCharge : ref->mBase->mData.mUses; - text += "\n#{sUses}: " + MWGui::ToolTips::toString(ref->mBase->mData.mUses); + 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}"); @@ -152,4 +170,21 @@ namespace MWClass return MWWorld::Ptr(&cell.mRepairs.insert(*ref), &cell); } + + boost::shared_ptr Repair::use (const MWWorld::Ptr& ptr) const + { + return boost::shared_ptr(new MWWorld::ActionRepair(ptr)); + } + + bool Repair::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::RepairItem; + } + + float Repair::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/repair.hpp b/apps/openmw/mwclass/repair.hpp index c58e38f96..28ca5ad4c 100644 --- a/apps/openmw/mwclass/repair.hpp +++ b/apps/openmw/mwclass/repair.hpp @@ -49,6 +49,22 @@ namespace MWClass ///< Return name of inventory icon. virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) + const; + ///< Generate action for using via inventory menu (default implementation: return a + /// null action). + + virtual bool hasItemHealth (const MWWorld::Ptr& ptr) const; + ///< \return Item health data available? (default implementation: false) + + virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const; + ///< Return item max health or throw an exception, if class does not have item health + /// (default implementation: throw an exceoption) + + virtual float getWeight (const MWWorld::Ptr& ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; }; } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index c8fe0d276..cb394d089 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -13,6 +13,7 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" +#include "../mwworld/nullaction.hpp" #include "../mwgui/tooltips.hpp" @@ -35,7 +36,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) - physics.addObject(ptr); + physics.addObject(ptr,true); } std::string Weapon::getModel(const MWWorld::Ptr &ptr) const @@ -62,6 +63,9 @@ namespace MWClass boost::shared_ptr Weapon::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { + if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) + return boost::shared_ptr (new MWWorld::NullAction ()); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); action->setSound(getUpSoundId(ptr)); @@ -71,7 +75,10 @@ namespace MWClass bool Weapon::hasItemHealth (const MWWorld::Ptr& ptr) const { - return true; + MWWorld::LiveCellRef *ref = + ptr.get(); + + return (ref->mBase->mData.mType < 11); // thrown weapons and arrows/bolts don't have health, only quantity } int Weapon::getItemMaxHealth (const MWWorld::Ptr& ptr) const @@ -330,15 +337,21 @@ namespace MWClass } } - /// \todo store the current weapon health somewhere if (ref->mBase->mData.mType < 11) // thrown weapons and arrows/bolts don't have health, only quantity - text += "\n#{sCondition}: " + MWGui::ToolTips::toString(ref->mBase->mData.mHealth); + { + int remainingHealth = (ptr.getCellRef().mCharge != -1) ? ptr.getCellRef().mCharge : ref->mBase->mData.mHealth; + text += "\n#{sCondition}: " + MWGui::ToolTips::toString(remainingHealth) + "/" + + MWGui::ToolTips::toString(ref->mBase->mData.mHealth); + } text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); info.enchant = ref->mBase->mEnchant; + if (!info.enchant.empty()) + info.remainingEnchantCharge = ptr.getCellRef().mEnchantmentCharge; + if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); @@ -357,6 +370,46 @@ namespace MWClass return ref->mBase->mEnchant; } + void Weapon::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + ESM::Weapon newItem = *ref->mBase; + newItem.mId=""; + newItem.mName=newName; + newItem.mData.mEnchant=enchCharge; + newItem.mEnchant=enchId; + const ESM::Weapon *record = MWBase::Environment::get().getWorld()->createRecord (newItem); + ref->mBase = record; + } + + std::pair Weapon::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const + { + 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(*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 (0, ""); + } + boost::shared_ptr Weapon::use (const MWWorld::Ptr& ptr) const { boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); @@ -374,4 +427,24 @@ namespace MWClass return MWWorld::Ptr(&cell.mWeapons.insert(*ref), &cell); } + + float Weapon::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mData.mEnchant/10.f; + } + + bool Weapon::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return npcServices & ESM::NPC::Weapon; + } + + float Weapon::getWeight(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mWeight; + } } diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index 06cf88c5f..3902ef612 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -65,11 +65,23 @@ namespace MWClass virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; ///< @return the enchantment ID if the object is enchanted, otherwise an empty string + virtual void applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + + virtual std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; + ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. + /// Second item in the pair specifies the error message + virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + + virtual float getWeight (const MWWorld::Ptr& ptr) const; + + virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index f9d8c3459..7093ff91e 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -2,6 +2,7 @@ #include "dialoguemanagerimp.hpp" #include +#include #include #include @@ -20,6 +21,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/journal.hpp" #include "../mwbase/scriptmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -48,12 +50,11 @@ namespace MWDialogue , mTemporaryDispositionChange(0.f) , mPermanentDispositionChange(0.f), mScriptVerbose (scriptVerbose) , mTranslationDataStorage(translationDataStorage) + , mTalkedTo(false) { mChoice = -1; mIsInChoice = false; mCompilerContext.setExtensions (&extensions); - mDialogueMap.clear(); - mActorKnownTopics.clear(); const MWWorld::Store &dialogs = MWBase::Environment::get().getWorld()->getStore().get(); @@ -65,6 +66,14 @@ namespace MWDialogue } } + void DialogueManager::clear() + { + mKnownTopics.clear(); + mTalkedTo = false; + mTemporaryDispositionChange = 0; + mPermanentDispositionChange = 0; + } + void DialogueManager::addTopic (const std::string& topic) { mKnownTopics[Misc::StringUtils::lowerCase(topic)] = true; @@ -115,6 +124,8 @@ namespace MWDialogue void DialogueManager::startDialogue (const MWWorld::Ptr& actor) { + mLastTopic = ""; + mChoice = -1; mIsInChoice = false; @@ -122,12 +133,9 @@ namespace MWDialogue MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get (actor).getCreatureStats (actor); mTalkedTo = creatureStats.hasTalkedToPlayer(); - creatureStats.talkedToPlayer(); mActorKnownTopics.clear(); - //initialise the GUI - MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue); MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); win->startDialogue(actor, MWWorld::Class::get (actor).getName (actor)); @@ -144,8 +152,14 @@ namespace MWDialogue { if(it->mType == ESM::Dialogue::Greeting) { - if (const ESM::DialInfo *info = filter.search (*it)) + // Search a response (we do not accept a fallback to "Info refusal" here) + if (const ESM::DialInfo *info = filter.search (*it, false)) { + //initialise the GUI + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue); + + creatureStats.talkedToPlayer(); + if (!info->mSound.empty()) { // TODO play sound @@ -154,9 +168,9 @@ namespace MWDialogue parseText (info->mResponse); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); - win->addText (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); + win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); executeScript (info->mResultScript); - mLastTopic = it->mId; + mLastTopic = Misc::StringUtils::lowerCase(it->mId); mLastDialogue = *info; break; } @@ -237,7 +251,7 @@ namespace MWDialogue } } - void DialogueManager::executeTopic (const std::string& topic) + void DialogueManager::executeTopic (const std::string& topic, bool randomResponse) { Filter filter (mActor, mChoice, mTalkedTo); @@ -246,12 +260,17 @@ namespace MWDialogue const ESM::Dialogue& dialogue = *dialogues.find (topic); - if (const ESM::DialInfo *info = filter.search (dialogue)) + MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); + + std::vector infos = filter.list (dialogue, true, true); + + if (!infos.empty()) { - parseText (info->mResponse); + const ESM::DialInfo* info = infos[randomResponse ? std::rand() % infos.size() : 0]; - MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); + parseText (info->mResponse); + std::string title; if (dialogue.mType==ESM::Dialogue::Persuasion) { std::string modifiedTopic = "s" + topic; @@ -261,19 +280,25 @@ namespace MWDialogue const MWWorld::Store& gmsts = MWBase::Environment::get().getWorld()->getStore().get(); - win->addTitle (gmsts.find (modifiedTopic)->getString()); + title = gmsts.find (modifiedTopic)->getString(); } else - win->addTitle (topic); + title = topic; MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); - win->addText (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); + win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), title); + MWBase::Environment::get().getJournal()->addTopic (topic, info->mId); executeScript (info->mResultScript); mLastTopic = topic; mLastDialogue = *info; } + else + { + // no response found, print a fallback text + win->addResponse ("…", topic); + } } void DialogueManager::updateTopics() @@ -292,7 +317,7 @@ namespace MWDialogue { if (iter->mType == ESM::Dialogue::Topic) { - if (filter.search (*iter)) + if (filter.responseAvailable (*iter)) { std::string lower = Misc::StringUtils::lowerCase(iter->mId); mActorKnownTopics.push_back (lower); @@ -351,6 +376,9 @@ namespace MWDialogue if (services & ESM::NPC::Enchanting) windowServices |= MWGui::DialogueWindow::Service_Enchant; + if (services & ESM::NPC::Repair) + windowServices |= MWGui::DialogueWindow::Service_Repair; + MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); win->setServices (windowServices); @@ -379,8 +407,17 @@ namespace MWDialogue updateTopics(); } + bool DialogueManager::isInChoice() const + { + return mIsInChoice; + } + 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 @@ -393,50 +430,42 @@ namespace MWDialogue mTemporaryDispositionChange = 0; } - void DialogueManager::questionAnswered (const std::string& answer) + void DialogueManager::questionAnswered (int answer) { - if (mChoiceMap.find(answer) != mChoiceMap.end()) + mChoice = answer; + + if (mDialogueMap.find(mLastTopic) != mDialogueMap.end()) { - mChoice = mChoiceMap[answer]; + Filter filter (mActor, mChoice, mTalkedTo); - if (mDialogueMap.find(mLastTopic) != mDialogueMap.end()) + if (mDialogueMap[mLastTopic].mType == ESM::Dialogue::Topic + || mDialogueMap[mLastTopic].mType == ESM::Dialogue::Greeting) { - if (mDialogueMap[mLastTopic].mType == ESM::Dialogue::Topic) + if (const ESM::DialInfo *info = filter.search (mDialogueMap[mLastTopic], true)) { - Filter filter (mActor, mChoice, mTalkedTo); + std::string text = info->mResponse; + parseText (text); - if (const ESM::DialInfo *info = filter.search (mDialogueMap[mLastTopic])) - { - mChoiceMap.clear(); - mChoice = -1; - mIsInChoice = false; - std::string text = info->mResponse; - parseText (text); - - MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); - MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addText (Interpreter::fixDefinesDialog(text, interpreterContext)); - executeScript (info->mResultScript); - mLastTopic = mLastTopic; - mLastDialogue = *info; - } + mChoice = -1; + mIsInChoice = false; + MWBase::Environment::get().getWindowManager()->getDialogueWindow()->clearChoices(); + + 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); + executeScript (info->mResultScript); + mLastDialogue = *info; } } - - updateTopics(); } - } - void DialogueManager::printError (const std::string& error) - { - MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); - win->addText(error); + updateTopics(); } void DialogueManager::askQuestion (const std::string& question, int choice) { MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); - win->askQuestion(question); - mChoiceMap[Misc::StringUtils::lowerCase(question)] = choice; + win->addChoice(question, choice); mIsInChoice = true; } @@ -484,7 +513,7 @@ namespace MWDialogue text = "Bribe"; } - executeTopic (text + (success ? " Success" : " Fail")); + executeTopic (text + (success ? " Success" : " Fail"), true); } int DialogueManager::getTemporaryDispositionChange() const @@ -497,12 +526,41 @@ namespace MWDialogue mTemporaryDispositionChange += delta; } + bool DialogueManager::checkServiceRefused() + { + Filter filter (mActor, mChoice, mTalkedTo); + + const MWWorld::Store &dialogues = + MWBase::Environment::get().getWorld()->getStore().get(); + + const ESM::Dialogue& dialogue = *dialogues.find ("Service Refusal"); + MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); + + std::vector infos = filter.list (dialogue, false, false, true); + if (!infos.empty()) + { + const ESM::DialInfo* info = infos[0]; + + parseText (info->mResponse); + + const MWWorld::Store& gmsts = + MWBase::Environment::get().getWorld()->getStore().get(); + + MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); + + win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), + gmsts.find ("sServiceRefusal")->getString()); + + executeScript (info->mResultScript); + return true; + } + return false; + } + std::vector ParseHyperText(const std::string& text) { std::vector result; - MyGUI::UString utext(text); - size_t pos_begin, pos_end, iteration_pos = 0; for(;;) { diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 1ca2ae5eb..de1ac77c6 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -30,7 +30,6 @@ namespace MWDialogue bool mTalkedTo; int mChoice; - std::map mChoiceMap; std::string mLastTopic; ESM::DialInfo mLastDialogue; bool mIsInChoice; @@ -46,14 +45,16 @@ namespace MWDialogue bool compile (const std::string& cmd,std::vector& code); void executeScript (const std::string& script); - void printError (const std::string& error); - - void executeTopic (const std::string& topic); + void executeTopic (const std::string& topic, bool randomResponse=false); public: DialogueManager (const Compiler::Extensions& extensions, bool scriptVerbose, Translation::Storage& translationDataStorage); + virtual void clear(); + + virtual bool isInChoice() const; + virtual void startDialogue (const MWWorld::Ptr& actor); virtual void addTopic (const std::string& topic); @@ -65,10 +66,12 @@ namespace MWDialogue virtual MWWorld::Ptr getActor() const; ///< Return the actor the player is currently talking to. + virtual bool checkServiceRefused (); + //calbacks for the GUI virtual void keywordSelected (const std::string& keyword); virtual void goodbyeSelected(); - virtual void questionAnswered (const std::string& answer); + virtual void questionAnswered (int answer); virtual void persuade (int type); virtual int getTemporaryDispositionChange () const; diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 7c590c8ef..52c7bd4f3 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -19,12 +19,19 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const { + bool isCreature = (mActor.getTypeName() != typeid (ESM::NPC).name()); + // actor id if (!info.mActor.empty()) + { if ( Misc::StringUtils::lowerCase (info.mActor)!=MWWorld::Class::get (mActor).getId (mActor)) return false; - - bool isCreature = (mActor.getTypeName() != typeid (ESM::NPC).name()); + } + else if (isCreature) + { + // Creatures must not have topics aside of those specific to their id + return false; + } // NPC race if (!info.mRace.empty()) @@ -66,6 +73,11 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const if (iter->second < info.mData.mRank) return false; } + else if (info.mData.mRank != -1) + { + // if there is a rank condition, but the NPC is not in a faction, always fail + return false; + } // Gender if (!isCreature) @@ -114,10 +126,27 @@ bool MWDialogue::Filter::testSelectStructs (const ESM::DialInfo& info) const return true; } +bool MWDialogue::Filter::testDisposition (const ESM::DialInfo& info, bool invert) const +{ + bool isCreature = (mActor.getTypeName() != typeid (ESM::NPC).name()); + + if (isCreature) + return true; + + int actorDisposition = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor); + // 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); +} + bool MWDialogue::Filter::testSelectStruct (const SelectWrapper& select) const { - if (select.isNpcOnly() && mActor.getTypeName()!=typeid (ESM::NPC).name()) - return select.isInverted(); + if (select.isNpcOnly() && (mActor.getTypeName() != typeid (ESM::NPC).name())) + // If the actor is a creature, we do not test the conditions applicable + // only to NPCs. Such conditions can never be satisfied, apart + // inverted ones (NotClass, NotRace, NotFaction return true + // because creatures are not of any race, class or faction). + return select.getType() == SelectWrapper::Type_Inverted; switch (select.getType()) { @@ -125,6 +154,9 @@ bool MWDialogue::Filter::testSelectStruct (const SelectWrapper& select) const case SelectWrapper::Type_Integer: return select.selectCompare (getSelectStructInteger (select)); case SelectWrapper::Type_Numeric: return testSelectStructNumeric (select); case SelectWrapper::Type_Boolean: return select.selectCompare (getSelectStructBoolean (select)); + + // We must not do the comparison for inverted functions (eg. Function_NotClass) + case SelectWrapper::Type_Inverted: return getSelectStructBoolean (select); } return true; @@ -155,7 +187,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c int i = 0; for (; i (script->mVarNames.size()); ++i) - if (script->mVarNames[i]==name) + if (Misc::StringUtils::lowerCase(script->mVarNames[i]) == name) break; if (i>=static_cast (script->mVarNames.size())) @@ -171,7 +203,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c if (imData.mNumLongs) return select.selectCompare (locals.mLongs[i]); - i -= script->mData.mNumShorts; + i -= script->mData.mNumLongs; return select.selectCompare (locals.mFloats.at (i)); } @@ -270,7 +302,7 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con case SelectWrapper::Function_PcGender: - return player.get()->mBase->mFlags & ESM::NPC::Female ? 0 : 1; + return player.get()->mBase->isMale() ? 0 : 1; case SelectWrapper::Function_PcClothingModifier: { @@ -393,25 +425,49 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co return false; - case SelectWrapper::Function_Id: + case SelectWrapper::Function_NotId: - return select.getName()==Misc::StringUtils::lowerCase (MWWorld::Class::get (mActor).getId (mActor)); + return select.getName()!=Misc::StringUtils::lowerCase (MWWorld::Class::get (mActor).getId (mActor)); - case SelectWrapper::Function_Faction: + case SelectWrapper::Function_NotFaction: - return Misc::StringUtils::lowerCase (mActor.get()->mBase->mFaction)==select.getName(); + return Misc::StringUtils::lowerCase (mActor.get()->mBase->mFaction)!=select.getName(); - case SelectWrapper::Function_Class: + case SelectWrapper::Function_NotClass: - return Misc::StringUtils::lowerCase (mActor.get()->mBase->mClass)==select.getName(); + return Misc::StringUtils::lowerCase (mActor.get()->mBase->mClass)!=select.getName(); - case SelectWrapper::Function_Race: + case SelectWrapper::Function_NotRace: - return Misc::StringUtils::lowerCase (mActor.get()->mBase->mRace)==select.getName(); + return Misc::StringUtils::lowerCase (mActor.get()->mBase->mRace)!=select.getName(); - case SelectWrapper::Function_Cell: + case SelectWrapper::Function_NotCell: - return Misc::StringUtils::lowerCase (mActor.getCell()->mCell->mName)==select.getName(); + return Misc::StringUtils::lowerCase (mActor.getCell()->mCell->mName)!=select.getName(); + + case SelectWrapper::Function_NotLocal: + { + std::string scriptName = MWWorld::Class::get (mActor).getScript (mActor); + + if (scriptName.empty()) + // This actor has no attached script, so there is no local variable + return true; + + const ESM::Script *script = + MWBase::Environment::get().getWorld()->getStore().get().find (scriptName); + + std::string name = select.getName(); + + int i = 0; + for (; i < static_cast (script->mVarNames.size()); ++i) + if (Misc::StringUtils::lowerCase(script->mVarNames[i]) == name) + break; + + if (i >= static_cast (script->mVarNames.size())) + return true; // script does not have a variable of this name + + return false; + } case SelectWrapper::Function_SameGender: @@ -439,7 +495,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co case SelectWrapper::Function_PcCorprus: return MWWorld::Class::get (player).getCreatureStats (player). - getMagicEffects().get (132).mMagnitude!=0; + getMagicEffects().get (ESM::MagicEffect::Corprus).mMagnitude!=0; case SelectWrapper::Function_PcExpelled: { @@ -518,8 +574,8 @@ bool MWDialogue::Filter::hasFactionRankSkillRequirements (const MWWorld::Ptr& ac MWMechanics::CreatureStats& stats = MWWorld::Class::get (actor).getCreatureStats (actor); - return stats.getAttribute (faction.mData.mAttribute1).getBase()>=faction.mData.mRankData[rank].mAttribute1 && - stats.getAttribute (faction.mData.mAttribute2).getBase()>=faction.mData.mRankData[rank].mAttribute2; + return stats.getAttribute (faction.mData.mAttribute[0]).getBase()>=faction.mData.mRankData[rank].mAttribute1 && + stats.getAttribute (faction.mData.mAttribute[1]).getBase()>=faction.mData.mRankData[rank].mAttribute2; } bool MWDialogue::Filter::hasFactionRankReputationRequirements (const MWWorld::Ptr& actor, @@ -540,18 +596,69 @@ MWDialogue::Filter::Filter (const MWWorld::Ptr& actor, int choice, bool talkedTo : mActor (actor), mChoice (choice), mTalkedToPlayer (talkedToPlayer) {} -bool MWDialogue::Filter::operator() (const ESM::DialInfo& info) const +const ESM::DialInfo* MWDialogue::Filter::search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const { - return testActor (info) && testPlayer (info) && testSelectStructs (info); + std::vector suitableInfos = list (dialogue, fallbackToInfoRefusal, false); + + if (suitableInfos.empty()) + return NULL; + else + return suitableInfos[0]; } -const ESM::DialInfo *MWDialogue::Filter::search (const ESM::Dialogue& dialogue) const +std::vector MWDialogue::Filter::list (const ESM::Dialogue& dialogue, + bool fallbackToInfoRefusal, bool searchAll, bool invertDisposition) const { + std::vector infos; + + bool infoRefusal = false; + + // Iterate over topic responses to find a matching one for (std::vector::const_iterator iter = dialogue.mInfo.begin(); iter!=dialogue.mInfo.end(); ++iter) - if ((*this) (*iter)) - return &*iter; + { + if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter)) + { + if (testDisposition (*iter, invertDisposition)) { + infos.push_back(&*iter); + if (!searchAll) + break; + } + else + infoRefusal = true; + } + } - return 0; + if (infos.empty() && infoRefusal && fallbackToInfoRefusal) + { + // No response is valid because of low NPC disposition, + // search a response in the topic "Info Refusal" + + const MWWorld::Store &dialogues = + MWBase::Environment::get().getWorld()->getStore().get(); + + const ESM::Dialogue& infoRefusalDialogue = *dialogues.find ("Info Refusal"); + + for (std::vector::const_iterator iter = infoRefusalDialogue.mInfo.begin(); + iter!=infoRefusalDialogue.mInfo.end(); ++iter) + if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter) && testDisposition(*iter, invertDisposition)) { + infos.push_back(&*iter); + if (!searchAll) + break; + } + } + + return infos; } +bool MWDialogue::Filter::responseAvailable (const ESM::Dialogue& dialogue) const +{ + for (std::vector::const_iterator iter = dialogue.mInfo.begin(); + iter!=dialogue.mInfo.end(); ++iter) + { + if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter)) + return true; + } + + return false; +} diff --git a/apps/openmw/mwdialogue/filter.hpp b/apps/openmw/mwdialogue/filter.hpp index 7c8f1116f..5c6d092ad 100644 --- a/apps/openmw/mwdialogue/filter.hpp +++ b/apps/openmw/mwdialogue/filter.hpp @@ -1,6 +1,8 @@ #ifndef GAME_MWDIALOGUE_FILTER_H #define GAME_MWDIALOGUE_FILTER_H +#include + #include "../mwworld/ptr.hpp" namespace ESM @@ -18,40 +20,48 @@ namespace MWDialogue MWWorld::Ptr mActor; int mChoice; bool mTalkedToPlayer; - + bool testActor (const ESM::DialInfo& info) const; ///< Is this the right actor for this \a info? - + bool testPlayer (const ESM::DialInfo& info) const; ///< Do the player and the cell the player is currently in match \a info? - + bool testSelectStructs (const ESM::DialInfo& info) const; ///< Are all select structs matching? - + + bool testDisposition (const ESM::DialInfo& info, bool invert=false) const; + ///< Is the actor disposition toward the player high enough (or low enough, if \a invert is true)? + bool testSelectStruct (const SelectWrapper& select) const; - + bool testSelectStructNumeric (const SelectWrapper& select) const; - + int getSelectStructInteger (const SelectWrapper& select) const; - + bool getSelectStructBoolean (const SelectWrapper& select) const; - + int getFactionRank (const MWWorld::Ptr& actor, const std::string& factionId) const; - + bool hasFactionRankSkillRequirements (const MWWorld::Ptr& actor, const std::string& factionId, int rank) const; bool hasFactionRankReputationRequirements (const MWWorld::Ptr& actor, const std::string& factionId, int rank) const; - + public: - - Filter (const MWWorld::Ptr& actor, int choice, bool talkedToPlayer); - bool operator() (const ESM::DialInfo& info) const; - ///< \return does the dialogue match? - - const ESM::DialInfo *search (const ESM::Dialogue& dialogue) const; + Filter (const MWWorld::Ptr& actor, int choice, bool talkedToPlayer); + + std::vector list (const ESM::Dialogue& dialogue, + bool fallbackToInfoRefusal, bool searchAll, bool invertDisposition=false) const; + + const ESM::DialInfo* search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const; + ///< Get a matching response for the requested dialogue. + /// Redirect to "Info Refusal" topic if a response fulfills all conditions but disposition. + + bool responseAvailable (const ESM::Dialogue& dialogue) const; + ///< Does a matching response exist? (disposition is ignored for this check) }; } diff --git a/apps/openmw/mwdialogue/journalentry.cpp b/apps/openmw/mwdialogue/journalentry.cpp index e6141884c..5ffde5499 100644 --- a/apps/openmw/mwdialogue/journalentry.cpp +++ b/apps/openmw/mwdialogue/journalentry.cpp @@ -61,8 +61,8 @@ 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 ("day").mLong; - int dayOfMonth = MWBase::Environment::get().getWorld()->getGlobalVariable ("month").mLong; + int month = MWBase::Environment::get().getWorld()->getGlobalVariable ("month").mLong; + int dayOfMonth = MWBase::Environment::get().getWorld()->getGlobalVariable ("day").mLong; return StampedJournalEntry (topic, idFromIndex (topic, index), day, month, dayOfMonth); } diff --git a/apps/openmw/mwdialogue/journalentry.hpp b/apps/openmw/mwdialogue/journalentry.hpp index 19a9f42b5..9d009b48b 100644 --- a/apps/openmw/mwdialogue/journalentry.hpp +++ b/apps/openmw/mwdialogue/journalentry.hpp @@ -3,7 +3,7 @@ #include -namespace MWWorld +namespace MWWorld { struct ESMStore; } @@ -27,7 +27,7 @@ namespace MWDialogue static std::string idFromIndex (const std::string& topic, int index); }; - /// \biref A quest entry with a timestamp. + /// \brief A quest entry with a timestamp. struct StampedJournalEntry : public JournalEntry { int mDay; diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index 2b2c60381..23cfb5fdd 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -29,8 +29,21 @@ namespace MWDialogue Journal::Journal() {} + void Journal::clear() + { + mJournal.clear(); + mQuests.clear(); + mTopics.clear(); + } + void Journal::addEntry (const std::string& id, int index) { + // bail out of we already have heard this... + std::string infoId = JournalEntry::idFromIndex (id, index); + for (TEntryIter i = mJournal.begin (); i != mJournal.end (); ++i) + if (i->mTopic == id && i->mInfoId == infoId) + return; + StampedJournalEntry entry = StampedJournalEntry::makeFromQuest (id, index); mJournal.push_back (entry); diff --git a/apps/openmw/mwdialogue/journalimp.hpp b/apps/openmw/mwdialogue/journalimp.hpp index 7d71fd7d0..f4f8eb1c2 100644 --- a/apps/openmw/mwdialogue/journalimp.hpp +++ b/apps/openmw/mwdialogue/journalimp.hpp @@ -21,6 +21,8 @@ namespace MWDialogue Journal(); + virtual void clear(); + virtual void addEntry (const std::string& id, int index); ///< Add a journal entry. diff --git a/apps/openmw/mwdialogue/selectwrapper.cpp b/apps/openmw/mwdialogue/selectwrapper.cpp index 9cc528a11..64bd1d244 100644 --- a/apps/openmw/mwdialogue/selectwrapper.cpp +++ b/apps/openmw/mwdialogue/selectwrapper.cpp @@ -31,14 +31,13 @@ namespace template bool selectCompareImp (const ESM::DialInfo::SelectStruct& select, T value1) { - if (select.mType==ESM::VT_Short || select.mType==ESM::VT_Int || - select.mType==ESM::VT_Long) + if (select.mValue.getType()==ESM::VT_Int) { - return selectCompareImp (select.mSelectRule[4], value1, select.mI); + return selectCompareImp (select.mSelectRule[4], value1, select.mValue.getInteger()); } - else if (select.mType==ESM::VT_Float) + else if (select.mValue.getType()==ESM::VT_Float) { - return selectCompareImp (select.mSelectRule[4], value1, select.mF); + return selectCompareImp (select.mSelectRule[4], value1, select.mValue.getFloat()); } else throw std::runtime_error ( @@ -113,12 +112,12 @@ MWDialogue::SelectWrapper::Function MWDialogue::SelectWrapper::getFunction() con case '4': return Function_Journal; case '5': return Function_Item; case '6': return Function_Dead; - case '7': return Function_Id; - case '8': return Function_Faction; - case '9': return Function_Class; - case 'A': return Function_Race; - case 'B': return Function_Cell; - case 'C': return Function_Local; + case '7': return Function_NotId; + case '8': return Function_NotFaction; + case '9': return Function_NotClass; + case 'A': return Function_NotRace; + case 'B': return Function_NotCell; + case 'C': return Function_NotLocal; } return Function_None; @@ -220,7 +219,6 @@ MWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const static const Function booleanFunctions[] = { Function_False, - Function_Id, Function_Faction, Function_Class, Function_Race, Function_Cell, Function_SameGender, Function_SameRace, Function_SameFaction, Function_PcCommonDisease, Function_PcBlightDisease, Function_PcCorprus, Function_PcExpelled, @@ -232,6 +230,13 @@ MWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const Function_None // end marker }; + static const Function invertedBooleanFunctions[] = + { + Function_NotId, Function_NotFaction, Function_NotClass, + Function_NotRace, Function_NotCell, Function_NotLocal, + Function_None // end marker + }; + Function function = getFunction(); for (int i=0; integerFunctions[i]!=Function_None; ++i) @@ -246,21 +251,18 @@ MWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const if (booleanFunctions[i]==function) return Type_Boolean; - return Type_None; -} - -bool MWDialogue::SelectWrapper::isInverted() const -{ - char type = mSelect.mSelectRule[1]; + for (int i=0; invertedBooleanFunctions[i]!=Function_None; ++i) + if (invertedBooleanFunctions[i]==function) + return Type_Inverted; - return type=='7' || type=='8' || type=='9' || type=='A' || type=='B' || type=='C'; + return Type_None; } bool MWDialogue::SelectWrapper::isNpcOnly() const { static const Function functions[] = { - Function_Faction, SelectWrapper::Function_Class, SelectWrapper::Function_Race, + Function_NotFaction, Function_NotClass, Function_NotRace, Function_SameGender, Function_SameRace, Function_SameFaction, Function_PcSkill, Function_PcExpelled, @@ -284,17 +286,17 @@ bool MWDialogue::SelectWrapper::isNpcOnly() const bool MWDialogue::SelectWrapper::selectCompare (int value) const { - return selectCompareImp (mSelect, value)!=isInverted(); // logic XOR + return selectCompareImp (mSelect, value); } bool MWDialogue::SelectWrapper::selectCompare (float value) const { - return selectCompareImp (mSelect, value)!=isInverted(); // logic XOR + return selectCompareImp (mSelect, value); } bool MWDialogue::SelectWrapper::selectCompare (bool value) const { - return selectCompareImp (mSelect, static_cast (value))!=isInverted(); // logic XOR + return selectCompareImp (mSelect, static_cast (value)); } std::string MWDialogue::SelectWrapper::getName() const diff --git a/apps/openmw/mwdialogue/selectwrapper.hpp b/apps/openmw/mwdialogue/selectwrapper.hpp index b77ed7985..f4bc898a4 100644 --- a/apps/openmw/mwdialogue/selectwrapper.hpp +++ b/apps/openmw/mwdialogue/selectwrapper.hpp @@ -17,11 +17,12 @@ namespace MWDialogue Function_Journal, Function_Item, Function_Dead, - Function_Id, - Function_Faction, - Function_Class, - Function_Race, - Function_Cell, + Function_NotId, + Function_NotFaction, + Function_NotClass, + Function_NotRace, + Function_NotCell, + Function_NotLocal, Function_Local, Function_Global, Function_SameGender, Function_SameRace, Function_SameFaction, @@ -50,7 +51,8 @@ namespace MWDialogue Type_None, Type_Integer, Type_Numeric, - Type_Boolean + Type_Boolean, + Type_Inverted }; private: @@ -67,8 +69,6 @@ namespace MWDialogue Type getType() const; - bool isInverted() const; - bool isNpcOnly() const; ///< \attention Do not call any of the select functions for this select struct! diff --git a/apps/openmw/mwdialogue/topic.cpp b/apps/openmw/mwdialogue/topic.cpp index b6e7c07ae..3253b20d6 100644 --- a/apps/openmw/mwdialogue/topic.cpp +++ b/apps/openmw/mwdialogue/topic.cpp @@ -27,17 +27,17 @@ namespace MWDialogue mEntries.push_back (entry.mInfoId); } - Topic::TEntryIter Topic::begin() + Topic::TEntryIter Topic::begin() const { return mEntries.begin(); } - Topic::TEntryIter Topic::end() + Topic::TEntryIter Topic::end() const { return mEntries.end(); } - JournalEntry Topic::getEntry (const std::string& infoId) + JournalEntry Topic::getEntry (const std::string& infoId) const { return JournalEntry (mTopic, infoId); } diff --git a/apps/openmw/mwdialogue/topic.hpp b/apps/openmw/mwdialogue/topic.hpp index 566f60ab0..c3f0baabc 100644 --- a/apps/openmw/mwdialogue/topic.hpp +++ b/apps/openmw/mwdialogue/topic.hpp @@ -34,13 +34,15 @@ namespace MWDialogue /// /// \note Redundant entries are ignored. - TEntryIter begin(); + std::string const & getName () const { return mTopic; } + + TEntryIter begin() const; ///< Iterator pointing to the begin of the journal for this topic. - TEntryIter end(); + TEntryIter end() const; ///< Iterator pointing past the end of the journal for this topic. - JournalEntry getEntry (const std::string& infoId); + JournalEntry getEntry (const std::string& infoId) const; }; } diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index db1a81c2c..136cb7c98 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -1,6 +1,7 @@ #include "alchemywindow.hpp" #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -8,8 +9,11 @@ #include "../mwbase/windowmanager.hpp" #include "../mwworld/player.hpp" -#include "../mwworld/manualref.hpp" -#include "../mwworld/containerstore.hpp" +#include "../mwworld/class.hpp" + +#include "inventoryitemmodel.hpp" +#include "sortfilteritemmodel.hpp" +#include "itemview.hpp" namespace { @@ -22,13 +26,26 @@ namespace path.append(".dds"); return path; } + + std::string getCountString(const int count) + { + if (count == 1) + return ""; + if (count > 9999) + return boost::lexical_cast(int(count/1000.f)) + "k"; + else + return boost::lexical_cast(count); + } + } namespace MWGui { - AlchemyWindow::AlchemyWindow(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_alchemy_window.layout", parWindowManager) - , ContainerBase(0), mApparatus (4), mIngredients (4) + AlchemyWindow::AlchemyWindow() + : WindowBase("openmw_alchemy_window.layout") + , mApparatus (4) + , mIngredients (4) + , mSortModel(NULL) { getWidget(mCreateButton, "CreateButton"); getWidget(mCancelButton, "CancelButton"); @@ -42,6 +59,10 @@ namespace MWGui getWidget(mApparatus[3], "Apparatus4"); getWidget(mEffectsBox, "CreatedEffects"); getWidget(mNameEdit, "NameEdit"); + getWidget(mItemView, "ItemView"); + + + mItemView->eventItemClicked += MyGUI::newDelegate(this, &AlchemyWindow::onSelectedItem); mIngredients[0]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected); mIngredients[1]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected); @@ -51,12 +72,6 @@ namespace MWGui mCreateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCreateButtonClicked); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCancelButtonClicked); - MyGUI::ScrollView* itemView; - MyGUI::Widget* containerWidget; - getWidget(containerWidget, "Items"); - getWidget(itemView, "ItemView"); - setWidgets(containerWidget, itemView); - center(); } @@ -64,8 +79,8 @@ namespace MWGui { mAlchemy.clear(); - mWindowManager.removeGuiMode(GM_Alchemy); - mWindowManager.removeGuiMode(GM_Inventory); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Alchemy); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Inventory); } void AlchemyWindow::onCreateButtonClicked(MyGUI::Widget* _sender) @@ -77,40 +92,40 @@ namespace MWGui if (result == MWMechanics::Alchemy::Result_NoName) { - mWindowManager.messageBox("#{sNotifyMessage37}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage37}"); return; } // check if mortar & pestle is available (always needed) if (result == MWMechanics::Alchemy::Result_NoMortarAndPestle) { - mWindowManager.messageBox("#{sNotifyMessage45}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage45}"); return; } // make sure 2 or more ingredients were selected if (result == MWMechanics::Alchemy::Result_LessThanTwoIngredients) { - mWindowManager.messageBox("#{sNotifyMessage6a}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage6a}"); return; } if (result == MWMechanics::Alchemy::Result_NoEffects) { - mWindowManager.messageBox("#{sNotifyMessage8}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage8}"); MWBase::Environment::get().getSoundManager()->playSound("potion fail", 1.f, 1.f); return; } if (result == MWMechanics::Alchemy::Result_Success) { - mWindowManager.messageBox("#{sPotionSuccess}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox("#{sPotionSuccess}"); MWBase::Environment::get().getSoundManager()->playSound("potion success", 1.f, 1.f); } else if (result == MWMechanics::Alchemy::Result_RandomFailure) { // potion failed - mWindowManager.messageBox("#{sNotifyMessage8}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage8}"); MWBase::Environment::get().getSoundManager()->playSound("potion fail", 1.f, 1.f); } @@ -128,12 +143,14 @@ namespace MWGui void AlchemyWindow::open() { - openContainer (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); // this sets mPtr - setFilter (ContainerBase::Filter_Ingredients); + InventoryItemModel* model = new InventoryItemModel(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + mSortModel = new SortFilterItemModel(model); + mSortModel->setFilter(SortFilterItemModel::Filter_OnlyIngredients); + mItemView->setModel (mSortModel); mNameEdit->setCaption(""); - mAlchemy.setAlchemist (mPtr); + mAlchemy.setAlchemist (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); int index = 0; @@ -157,8 +174,9 @@ namespace MWGui update(); } - void AlchemyWindow::onSelectedItemImpl(MWWorld::Ptr item) + void AlchemyWindow::onSelectedItem(int index) { + MWWorld::Ptr item = mSortModel->getItem(index).mBase; int res = mAlchemy.addIngredient(item); if (res != -1) @@ -170,19 +188,10 @@ namespace MWGui } } - std::vector AlchemyWindow::itemsToIgnore() - { - std::vector ignore; - // don't show ingredients that are currently selected in the "available ingredients" box. - for (int i=0; i<4; ++i) - if (mIngredients[i]->isUserString("ToolTipType")) - ignore.push_back(*mIngredients[i]->getUserData()); - - return ignore; - } - void AlchemyWindow::update() { + mSortModel->clearDragItems(); + MWMechanics::Alchemy::TIngredientsIterator it = mAlchemy.beginIngredients (); for (int i=0; i<4; ++i) { @@ -195,6 +204,9 @@ namespace MWGui ++it; } + if (!item.isEmpty()) + mSortModel->addDragItem(item, item.getRefData().getCount()); + if (ingredient->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(ingredient->getChildAt(0)); @@ -216,7 +228,7 @@ namespace MWGui text->setCaption(getCountString(ingredient->getUserData()->getRefData().getCount())); } - drawItems(); + mItemView->update(); std::vector effects; ESM::EffectList list; @@ -232,12 +244,11 @@ namespace MWGui MyGUI::IntCoord coord(0, 0, mEffectsBox->getWidth(), 24); Widgets::MWEffectListPtr effectsWidget = mEffectsBox->createWidget ("MW_StatName", coord, MyGUI::Align::Left | MyGUI::Align::Top); - effectsWidget->setWindowManager(&mWindowManager); Widgets::SpellEffectList _list = Widgets::MWEffectList::effectListFromESM(&list); effectsWidget->setEffectList(_list); - std::vector effectItems; + std::vector effectItems; effectsWidget->createEffectWidgets(effectItems, mEffectsBox, coord, false, 0); effectsWidget->setCoord(coord); } diff --git a/apps/openmw/mwgui/alchemywindow.hpp b/apps/openmw/mwgui/alchemywindow.hpp index 5f84e73e9..3a58ebf06 100644 --- a/apps/openmw/mwgui/alchemywindow.hpp +++ b/apps/openmw/mwgui/alchemywindow.hpp @@ -5,20 +5,25 @@ #include "../mwmechanics/alchemy.hpp" -#include "window_base.hpp" -#include "container.hpp" #include "widgets.hpp" +#include "windowbase.hpp" namespace MWGui { - class AlchemyWindow : public WindowBase, public ContainerBase + class ItemView; + class SortFilterItemModel; + + class AlchemyWindow : public WindowBase { public: - AlchemyWindow(MWBase::WindowManager& parWindowManager); + AlchemyWindow(); virtual void open(); - protected: + private: + ItemView* mItemView; + SortFilterItemModel* mSortModel; + MyGUI::Button* mCreateButton; MyGUI::Button* mCancelButton; @@ -30,16 +35,11 @@ namespace MWGui void onCreateButtonClicked(MyGUI::Widget* _sender); void onIngredientSelected(MyGUI::Widget* _sender); - virtual void onSelectedItemImpl(MWWorld::Ptr item); - virtual std::vector itemsToIgnore(); + void onSelectedItem(int index); void removeIngredient(MyGUI::Widget* ingredient); - virtual void onReferenceUnavailable() { ; } - void update(); - - private: MWMechanics::Alchemy mAlchemy; diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 4837821e0..965606709 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -3,243 +3,245 @@ #include #include -#include "../mwworld/esmstore.hpp" - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "widgets.hpp" -using namespace MWGui; -using namespace Widgets; - namespace { -bool sortBirthSigns(const std::pair& left, const std::pair& right) -{ - return left.second->mName.compare (right.second->mName) < 0; -} + bool sortBirthSigns(const std::pair& left, const std::pair& right) + { + return left.second->mName.compare (right.second->mName) < 0; + } } -BirthDialog::BirthDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_chargen_birth.layout", parWindowManager) +namespace MWGui { - // Centre dialog - center(); - getWidget(mSpellArea, "SpellArea"); + BirthDialog::BirthDialog() + : WindowModal("openmw_chargen_birth.layout") + { + // Centre dialog + center(); - getWidget(mBirthImage, "BirthsignImage"); + getWidget(mSpellArea, "SpellArea"); - getWidget(mBirthList, "BirthsignList"); - mBirthList->setScrollVisible(true); - mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); - mBirthList->eventListMouseItemActivate += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); - mBirthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); + getWidget(mBirthImage, "BirthsignImage"); - MyGUI::ButtonPtr backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onBackClicked); + getWidget(mBirthList, "BirthsignList"); + mBirthList->setScrollVisible(true); + mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); + mBirthList->eventListMouseItemActivate += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); + mBirthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); - MyGUI::ButtonPtr okButton; - getWidget(okButton, "OKButton"); - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onOkClicked); - okButton->setEnabled(false); + MyGUI::Button* backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onBackClicked); - updateBirths(); - updateSpells(); -} + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onOkClicked); -void BirthDialog::setNextButtonShow(bool shown) -{ - MyGUI::ButtonPtr okButton; - getWidget(okButton, "OKButton"); + updateBirths(); + updateSpells(); + } - if (shown) - okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); - else - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); -} + void BirthDialog::setNextButtonShow(bool shown) + { + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); -void BirthDialog::open() -{ - WindowBase::open(); - updateBirths(); - updateSpells(); -} + if (shown) + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); + else + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + } + + void BirthDialog::open() + { + WindowModal::open(); + updateBirths(); + updateSpells(); + } -void BirthDialog::setBirthId(const std::string &birthId) -{ - mCurrentBirthId = birthId; - mBirthList->setIndexSelected(MyGUI::ITEM_NONE); - size_t count = mBirthList->getItemCount(); - for (size_t i = 0; i < count; ++i) + void BirthDialog::setBirthId(const std::string &birthId) { - if (boost::iequals(*mBirthList->getItemDataAt(i), birthId)) + mCurrentBirthId = birthId; + mBirthList->setIndexSelected(MyGUI::ITEM_NONE); + size_t count = mBirthList->getItemCount(); + for (size_t i = 0; i < count; ++i) { - mBirthList->setIndexSelected(i); - MyGUI::ButtonPtr okButton; - getWidget(okButton, "OKButton"); - okButton->setEnabled(true); - break; + if (boost::iequals(*mBirthList->getItemDataAt(i), birthId)) + { + mBirthList->setIndexSelected(i); + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + break; + } } - } - updateSpells(); -} + updateSpells(); + } -// widget controls + // widget controls -void BirthDialog::onOkClicked(MyGUI::Widget* _sender) -{ - if(mBirthList->getIndexSelected() == MyGUI::ITEM_NONE) - return; - eventDone(this); -} - -void BirthDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} + void BirthDialog::onOkClicked(MyGUI::Widget* _sender) + { + if(mBirthList->getIndexSelected() == MyGUI::ITEM_NONE) + return; + eventDone(this); + } -void BirthDialog::onSelectBirth(MyGUI::ListBox* _sender, size_t _index) -{ - if (_index == MyGUI::ITEM_NONE) - return; + void BirthDialog::onBackClicked(MyGUI::Widget* _sender) + { + eventBack(); + } - MyGUI::ButtonPtr okButton; - getWidget(okButton, "OKButton"); - okButton->setEnabled(true); + void BirthDialog::onSelectBirth(MyGUI::ListBox* _sender, size_t _index) + { + if (_index == MyGUI::ITEM_NONE) + return; - const std::string *birthId = mBirthList->getItemDataAt(_index); - if (boost::iequals(mCurrentBirthId, *birthId)) - return; + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); - mCurrentBirthId = *birthId; - updateSpells(); -} + const std::string *birthId = mBirthList->getItemDataAt(_index); + if (boost::iequals(mCurrentBirthId, *birthId)) + return; -// update widget content + mCurrentBirthId = *birthId; + updateSpells(); + } -void BirthDialog::updateBirths() -{ - mBirthList->removeAllItems(); + // update widget content - const MWWorld::Store &signs = - MWBase::Environment::get().getWorld()->getStore().get(); + void BirthDialog::updateBirths() + { + mBirthList->removeAllItems(); - int index = 0; + const MWWorld::Store &signs = + MWBase::Environment::get().getWorld()->getStore().get(); - // sort by name - std::vector < std::pair > birthSigns; + // sort by name + std::vector < std::pair > birthSigns; - MWWorld::Store::iterator it = signs.begin(); - for (; it != signs.end(); ++it) - { - birthSigns.push_back(std::make_pair(it->mId, &(*it))); - } - std::sort(birthSigns.begin(), birthSigns.end(), sortBirthSigns); + MWWorld::Store::iterator it = signs.begin(); + for (; it != signs.end(); ++it) + { + birthSigns.push_back(std::make_pair(it->mId, &(*it))); + } + std::sort(birthSigns.begin(), birthSigns.end(), sortBirthSigns); - for (std::vector < std::pair >::const_iterator it2 = birthSigns.begin(); it2 != birthSigns.end(); ++it2) - { - mBirthList->addItem(it2->second->mName, it2->first); - if (boost::iequals(it2->first, mCurrentBirthId)) - mBirthList->setIndexSelected(index); - ++index; + int index = 0; + for (std::vector >::const_iterator it2 = birthSigns.begin(); + it2 != birthSigns.end(); ++it2, ++index) + { + mBirthList->addItem(it2->second->mName, it2->first); + if (mCurrentBirthId.empty()) + { + mBirthList->setIndexSelected(index); + mCurrentBirthId = it2->first; + } + else if (boost::iequals(it2->first, mCurrentBirthId)) + { + mBirthList->setIndexSelected(index); + } + } } -} -void BirthDialog::updateSpells() -{ - for (std::vector::iterator it = mSpellItems.begin(); it != mSpellItems.end(); ++it) + void BirthDialog::updateSpells() { - MyGUI::Gui::getInstance().destroyWidget(*it); - } - mSpellItems.clear(); + for (std::vector::iterator it = mSpellItems.begin(); it != mSpellItems.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + mSpellItems.clear(); - if (mCurrentBirthId.empty()) - return; + if (mCurrentBirthId.empty()) + return; - MWSpellPtr spellWidget; - const int lineHeight = 18; - MyGUI::IntCoord coord(0, 0, mSpellArea->getWidth(), 18); + Widgets::MWSpellPtr spellWidget; + const int lineHeight = 18; + MyGUI::IntCoord coord(0, 0, mSpellArea->getWidth(), 18); - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); - const ESM::BirthSign *birth = - store.get().find(mCurrentBirthId); + const ESM::BirthSign *birth = + store.get().find(mCurrentBirthId); - std::string texturePath = std::string("textures\\") + birth->mTexture; - fixTexturePath(texturePath); - mBirthImage->setImageTexture(texturePath); + std::string texturePath = std::string("textures\\") + birth->mTexture; + Widgets::fixTexturePath(texturePath); + mBirthImage->setImageTexture(texturePath); - std::vector abilities, powers, spells; + std::vector abilities, powers, spells; - std::vector::const_iterator it = birth->mPowers.mList.begin(); - std::vector::const_iterator end = birth->mPowers.mList.end(); - for (; it != end; ++it) - { - const std::string &spellId = *it; - const ESM::Spell *spell = store.get().search(spellId); - if (!spell) - continue; // Skip spells which cannot be found - ESM::Spell::SpellType type = static_cast(spell->mData.mType); - if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Ability && type != ESM::Spell::ST_Power) - continue; // We only want spell, ability and powers. - - if (type == ESM::Spell::ST_Ability) - abilities.push_back(spellId); - else if (type == ESM::Spell::ST_Power) - powers.push_back(spellId); - else if (type == ESM::Spell::ST_Spell) - spells.push_back(spellId); - } + std::vector::const_iterator it = birth->mPowers.mList.begin(); + std::vector::const_iterator end = birth->mPowers.mList.end(); + for (; it != end; ++it) + { + const std::string &spellId = *it; + const ESM::Spell *spell = store.get().search(spellId); + if (!spell) + continue; // Skip spells which cannot be found + ESM::Spell::SpellType type = static_cast(spell->mData.mType); + if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Ability && type != ESM::Spell::ST_Power) + continue; // We only want spell, ability and powers. + + if (type == ESM::Spell::ST_Ability) + abilities.push_back(spellId); + else if (type == ESM::Spell::ST_Power) + powers.push_back(spellId); + else if (type == ESM::Spell::ST_Spell) + spells.push_back(spellId); + } - int i = 0; + int i = 0; - struct { - const std::vector &spells; - const char *label; - } - categories[3] = { - {abilities, "sBirthsignmenu1"}, - {powers, "sPowers"}, - {spells, "sBirthsignmenu2"} - }; + struct { + const std::vector &spells; + const char *label; + } + categories[3] = { + {abilities, "sBirthsignmenu1"}, + {powers, "sPowers"}, + {spells, "sBirthsignmenu2"} + }; - for (int category = 0; category < 3; ++category) - { - if (!categories[category].spells.empty()) + for (int category = 0; category < 3; ++category) { - MyGUI::TextBox* label = mSpellArea->createWidget("SandBrightText", coord, MyGUI::Align::Default, std::string("Label")); - label->setCaption(mWindowManager.getGameSettingString(categories[category].label, "")); - mSpellItems.push_back(label); - coord.top += lineHeight; - - std::vector::const_iterator end = categories[category].spells.end(); - for (std::vector::const_iterator it = categories[category].spells.begin(); it != end; ++it) + if (!categories[category].spells.empty()) { - const std::string &spellId = *it; - spellWidget = mSpellArea->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("Spell") + boost::lexical_cast(i)); - spellWidget->setWindowManager(&mWindowManager); - spellWidget->setSpellId(spellId); - - mSpellItems.push_back(spellWidget); + MyGUI::TextBox* label = mSpellArea->createWidget("SandBrightText", coord, MyGUI::Align::Default, std::string("Label")); + label->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(categories[category].label, "")); + mSpellItems.push_back(label); coord.top += lineHeight; - MyGUI::IntCoord spellCoord = coord; - spellCoord.height = 24; // TODO: This should be fetched from the skin somehow, or perhaps a widget in the layout as a template? - spellWidget->createEffectWidgets(mSpellItems, mSpellArea, spellCoord, (category == 0) ? MWEffectList::EF_Constant : 0); - coord.top = spellCoord.top; + std::vector::const_iterator end = categories[category].spells.end(); + for (std::vector::const_iterator it = categories[category].spells.begin(); it != end; ++it) + { + const std::string &spellId = *it; + spellWidget = mSpellArea->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("Spell") + boost::lexical_cast(i)); + spellWidget->setSpellId(spellId); + + mSpellItems.push_back(spellWidget); + coord.top += lineHeight; - ++i; + MyGUI::IntCoord spellCoord = coord; + spellCoord.height = 24; // TODO: This should be fetched from the skin somehow, or perhaps a widget in the layout as a template? + spellWidget->createEffectWidgets(mSpellItems, mSpellArea, spellCoord, (category == 0) ? Widgets::MWEffectList::EF_Constant : 0); + coord.top = spellCoord.top; + + ++i; + } } } } + } diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index f16f92325..cc958ddca 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_BIRTH_H #define MWGUI_BIRTH_H -#include "window_base.hpp" +#include "windowbase.hpp" /* This file contains the dialog for choosing a birth sign. @@ -10,10 +10,10 @@ namespace MWGui { - class BirthDialog : public WindowBase + class BirthDialog : public WindowModal { public: - BirthDialog(MWBase::WindowManager& parWindowManager); + BirthDialog(); enum Gender { @@ -46,9 +46,9 @@ namespace MWGui void updateSpells(); MyGUI::ListBox* mBirthList; - MyGUI::WidgetPtr mSpellArea; + MyGUI::Widget* mSpellArea; MyGUI::ImageBox* mBirthImage; - std::vector mSpellItems; + std::vector mSpellItems; std::string mCurrentBirthId; }; diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp new file mode 100644 index 000000000..94f834a4d --- /dev/null +++ b/apps/openmw/mwgui/bookpage.cpp @@ -0,0 +1,1236 @@ +#include "bookpage.hpp" + +#include "MyGUI_FontManager.h" +#include "MyGUI_RenderItem.h" +#include "MyGUI_RenderManager.h" +#include "MyGUI_TextureUtility.h" +#include "MyGUI_FactoryManager.h" + +#include +#include +#include +#include + +#include + +namespace MWGui +{ +struct TypesetBookImpl; +struct PageDisplay; +struct BookPageImpl; + +static bool ucsSpace (int codePoint); +static bool ucsLineBreak (int codePoint); +static bool ucsBreakingSpace (int codePoint); + +struct BookTypesetter::Style { virtual ~Style () {} }; + +struct TypesetBookImpl : TypesetBook +{ + typedef std::vector Content; + typedef std::list Contents; + typedef Utf8Stream::Point Utf8Point; + typedef std::pair Range; + + struct StyleImpl : BookTypesetter::Style + { + MyGUI::IFont* mFont; + MyGUI::Colour mHotColour; + MyGUI::Colour mActiveColour; + MyGUI::Colour mNormalColour; + InteractiveId mInteractiveId; + + bool match (MyGUI::IFont* tstFont, MyGUI::Colour tstHotColour, MyGUI::Colour tstActiveColour, + MyGUI::Colour tstNormalColour, intptr_t tstInteractiveId) + { + return (mFont == tstFont) && + partal_match (tstHotColour, tstActiveColour, tstNormalColour, tstInteractiveId); + } + + bool match (char const * tstFont, MyGUI::Colour tstHotColour, MyGUI::Colour tstActiveColour, + MyGUI::Colour tstNormalColour, intptr_t tstInteractiveId) + { + return (mFont->getResourceName () == tstFont) && + partal_match (tstHotColour, tstActiveColour, tstNormalColour, tstInteractiveId); + } + + bool partal_match (MyGUI::Colour tstHotColour, MyGUI::Colour tstActiveColour, + MyGUI::Colour tstNormalColour, intptr_t tstInteractiveId) + { + return + (mHotColour == tstHotColour ) && + (mActiveColour == tstActiveColour ) && + (mNormalColour == tstNormalColour ) && + (mInteractiveId == tstInteractiveId ) ; + } + }; + + typedef std::list Styles; + + struct Run + { + StyleImpl* mStyle; + Range mRange; + int mLeft, mRight; + int mPrintableChars; + }; + + typedef std::vector Runs; + + struct Line + { + Runs mRuns; + MyGUI::IntRect mRect; + }; + + typedef std::vector Lines; + + struct Section + { + Lines mLines; + MyGUI::IntRect mRect; + }; + + typedef std::vector
Sections; + + typedef std::pair Page; + typedef std::vector Pages; + + Pages mPages; + Sections mSections; + Contents mContents; + Styles mStyles; + MyGUI::IntRect mRect; + + virtual ~TypesetBookImpl () {} + + Range addContent (BookTypesetter::Utf8Span text) + { + Contents::iterator i = mContents.insert (mContents.end (), Content (text.first, text.second)); + + if (i->size () == 0) + return Range (Utf8Point (NULL), Utf8Point (NULL)); + + Utf8Point begin = &i->front (); + Utf8Point end = &i->front () + i->size (); + + return Range (begin, end); + } + + size_t pageCount () const { return mPages.size (); } + + std::pair getSize () const + { + return std::make_pair (mRect.width (), mRect.height ()); + } + + template + void visitRuns (int top, int bottom, MyGUI::IFont* Font, Visitor const & visitor) const + { + for (Sections::const_iterator i = mSections.begin (); i != mSections.end (); ++i) + { + if (top >= mRect.bottom || bottom <= i->mRect.top) + continue; + + for (Lines::const_iterator j = i->mLines.begin (); j != i->mLines.end (); ++j) + { + if (top >= j->mRect.bottom || bottom <= j->mRect.top) + continue; + + for (Runs::const_iterator k = j->mRuns.begin (); k != j->mRuns.end (); ++k) + if (!Font || k->mStyle->mFont == Font) + visitor (*i, *j, *k); + } + } + } + + template + void visitRuns (int top, int bottom, Visitor const & visitor) const + { + visitRuns (top, bottom, NULL, visitor); + } + + StyleImpl * hitTest (int left, int top) const + { + for (Sections::const_iterator i = mSections.begin (); i != mSections.end (); ++i) + { + if (top < i->mRect.top || top >= i->mRect.bottom) + continue; + + int left1 = left - i->mRect.left; + + for (Lines::const_iterator j = i->mLines.begin (); j != i->mLines.end (); ++j) + { + if (top < j->mRect.top || top >= j->mRect.bottom) + continue; + + int left2 = left1 - j->mRect.left; + + for (Runs::const_iterator k = j->mRuns.begin (); k != j->mRuns.end (); ++k) + { + if (left2 < k->mLeft || left2 >= k->mRight) + continue; + + return k->mStyle; + } + } + } + + return nullptr; + } + + MyGUI::IFont* affectedFont (StyleImpl* style) + { + for (Styles::iterator i = mStyles.begin (); i != mStyles.end (); ++i) + if (&*i == style) + return i->mFont; + return NULL; + } + + struct Typesetter; +}; + +struct TypesetBookImpl::Typesetter : BookTypesetter +{ + typedef TypesetBookImpl Book; + typedef boost::shared_ptr BookPtr; + + int mPageWidth; + int mPageHeight; + + BookPtr mBook; + Section * mSection; + Line * mLine; + Run * mRun; + + std::vector mSectionAlignment; + + Book::Content const * mCurrentContent; + Alignment mCurrentAlignment; + + Typesetter (size_t width, size_t height) : + mPageWidth (width), mPageHeight(height), + mSection (NULL), mLine (NULL), mRun (NULL), + mCurrentAlignment (AlignLeft), + mCurrentContent (NULL) + { + mBook = boost::make_shared (); + } + + virtual ~Typesetter () + { + } + + Style * createStyle (char const * fontName, Colour fontColour) + { + if (strcmp(fontName, "") == 0) + return createStyle(MyGUI::FontManager::getInstance().getDefaultFont().c_str(), fontColour); + + for (Styles::iterator i = mBook->mStyles.begin (); i != mBook->mStyles.end (); ++i) + if (i->match (fontName, fontColour, fontColour, fontColour, 0)) + return &*i; + + StyleImpl & style = *mBook->mStyles.insert (mBook->mStyles.end (), StyleImpl ()); + + style.mFont = MyGUI::FontManager::getInstance().getByName(fontName); + style.mHotColour = fontColour; + style.mActiveColour = fontColour; + style.mNormalColour = fontColour; + style.mInteractiveId = 0; + + return &style; + } + + Style* createHotStyle (Style* baseStyle, Colour normalColour, Colour hoverColour, Colour activeColour, InteractiveId id, bool unique) + { + StyleImpl* BaseStyle = dynamic_cast (baseStyle); + + if (!unique) + for (Styles::iterator i = mBook->mStyles.begin (); i != mBook->mStyles.end (); ++i) + if (i->match (BaseStyle->mFont, hoverColour, activeColour, normalColour, id)) + return &*i; + + StyleImpl & style = *mBook->mStyles.insert (mBook->mStyles.end (), StyleImpl ()); + + style.mFont = BaseStyle->mFont; + style.mHotColour = hoverColour; + style.mActiveColour = activeColour; + style.mNormalColour = normalColour; + style.mInteractiveId = id; + + return &style; + } + + void write (Style * style, Utf8Span text) + { + Range range = mBook->addContent (text); + + writeImpl (dynamic_cast (style), range.first, range.second); + } + + intptr_t addContent (Utf8Span text, bool select) + { + Contents::iterator i = mBook->mContents.insert (mBook->mContents.end (), Content (text.first, text.second)); + + if (select) + mCurrentContent = &(*i); + + return reinterpret_cast (&(*i)); + } + + void selectContent (intptr_t contentHandle) + { + mCurrentContent = reinterpret_cast (contentHandle); + } + + void write (Style * style, size_t begin, size_t end) + { + assert (mCurrentContent != NULL); + assert (end <= mCurrentContent->size ()); + assert (begin <= mCurrentContent->size ()); + + Utf8Point begin_ = &mCurrentContent->front () + begin; + Utf8Point end_ = &mCurrentContent->front () + end ; + + writeImpl (dynamic_cast (style), begin_, end_); + } + + void lineBreak (float margin) + { + assert (margin == 0); //TODO: figure out proper behavior here... + + mRun = NULL; + mLine = NULL; + } + + void sectionBreak (float margin) + { + if (mBook->mSections.size () > 0) + { + mRun = NULL; + mLine = NULL; + mSection = NULL; + + if (mBook->mRect.bottom < (mBook->mSections.back ().mRect.bottom + margin)) + mBook->mRect.bottom = (mBook->mSections.back ().mRect.bottom + margin); + } + } + + void setSectionAlignment (Alignment sectionAlignment) + { + if (mSection != NULL) + mSectionAlignment.back () = sectionAlignment; + mCurrentAlignment = sectionAlignment; + } + + TypesetBook::Ptr complete () + { + int curPageStart = 0; + int curPageStop = 0; + + std::vector ::iterator sa = mSectionAlignment.begin (); + for (Sections::iterator i = mBook->mSections.begin (); i != mBook->mSections.end (); ++i, ++sa) + { + // apply alignment to individual lines... + for (Lines::iterator j = i->mLines.begin (); j != i->mLines.end (); ++j) + { + int width = j->mRect.width (); + int excess = mPageWidth - width; + + switch (*sa) + { + default: + case AlignLeft: j->mRect.left = 0; break; + case AlignCenter: j->mRect.left = excess/2; break; + case AlignRight: j->mRect.left = excess; break; + } + + j->mRect.right = j->mRect.left + width; + } + + if (curPageStop == curPageStart) + { + curPageStart = i->mRect.top; + curPageStop = i->mRect.top; + } + + int spaceLeft = mPageHeight - (curPageStop - curPageStart); + int sectionHeight = i->mRect.height (); + + if (sectionHeight <= mPageHeight) + { + if (sectionHeight > spaceLeft) + { + assert (curPageStart != curPageStop); + + mBook->mPages.push_back (Page (curPageStart, curPageStop)); + + curPageStart = i->mRect.top; + curPageStop = i->mRect.bottom; + } + else + curPageStop = i->mRect.bottom; + } + else + { + //split section + } + } + + if (curPageStart != curPageStop) + mBook->mPages.push_back (Page (curPageStart, curPageStop)); + + return mBook; + } + + void writeImpl (StyleImpl * style, Utf8Stream::Point _begin, Utf8Stream::Point _end) + { + int line_height = style->mFont->getDefaultHeight (); + + Utf8Stream stream (_begin, _end); + + while (!stream.eof ()) + { + if (ucsLineBreak (stream.peek ())) + { + stream.consume (); + mLine = NULL, mRun = NULL; + continue; + } + + int word_width = 0; + int word_height = 0; + int space_width = 0; + int character_count = 0; + + Utf8Stream::Point lead = stream.current (); + + while (!stream.eof () && !ucsLineBreak (stream.peek ()) && ucsBreakingSpace (stream.peek ())) + { + MyGUI::GlyphInfo* gi = style->mFont->getGlyphInfo (stream.peek ()); + if (gi) + space_width += gi->advance + gi->bearingX; + stream.consume (); + } + + Utf8Stream::Point origin = stream.current (); + + while (!stream.eof () && !ucsLineBreak (stream.peek ()) && !ucsBreakingSpace (stream.peek ())) + { + MyGUI::GlyphInfo* gi = style->mFont->getGlyphInfo (stream.peek ()); + if (gi) + word_width += gi->advance + gi->bearingX; + word_height = line_height; + ++character_count; + stream.consume (); + } + + Utf8Stream::Point extent = stream.current (); + + if (lead == extent) + break; + + int left = mLine ? mLine->mRect.right : 0; + + if (left + space_width + word_width > mPageWidth) + { + mLine = NULL, mRun = NULL; + + append_run (style, origin, extent, extent - origin, word_width, mBook->mRect.bottom + word_height); + } + else + { + int top = mLine ? mLine->mRect.top : mBook->mRect.bottom; + + append_run (style, lead, extent, extent - origin, left + space_width + word_width, top + word_height); + } + } + } + + void append_run (StyleImpl * style, Utf8Stream::Point begin, Utf8Stream::Point end, int pc, int right, int bottom) + { + if (mSection == NULL) + { + mBook->mSections.push_back (Section ()); + mSection = &mBook->mSections.back (); + mSection->mRect = MyGUI::IntRect (0, mBook->mRect.bottom, 0, mBook->mRect.bottom); + mSectionAlignment.push_back (mCurrentAlignment); + } + + if (mLine == NULL) + { + mSection->mLines.push_back (Line ()); + mLine = &mSection->mLines.back (); + mLine->mRect = MyGUI::IntRect (0, mSection->mRect.bottom, 0, mBook->mRect.bottom); + } + + if (mBook->mRect.right < right) + mBook->mRect.right = right; + + if (mBook->mRect.bottom < bottom) + mBook->mRect.bottom = bottom; + + if (mSection->mRect.right < right) + mSection->mRect.right = right; + + if (mSection->mRect.bottom < bottom) + mSection->mRect.bottom = bottom; + + if (mLine->mRect.right < right) + mLine->mRect.right = right; + + if (mLine->mRect.bottom < bottom) + mLine->mRect.bottom = bottom; + + if (mRun == NULL || mRun->mStyle != style || mRun->mRange.second != begin) + { + int left = mRun ? mRun->mRight : mLine->mRect.left; + + mLine->mRuns.push_back (Run ()); + mRun = &mLine->mRuns.back (); + mRun->mStyle = style; + mRun->mLeft = left; + mRun->mRight = right; + mRun->mRange.first = begin; + mRun->mRange.second = end; + mRun->mPrintableChars = pc; + //Run->Locale = Locale; + } + else + { + mRun->mRight = right; + mRun->mRange.second = end; + mRun->mPrintableChars += pc; + } + } +}; + +BookTypesetter::Ptr BookTypesetter::create (int pageWidth, int pageHeight) +{ + return boost::make_shared (pageWidth, pageHeight); +} + +namespace +{ + struct RenderXform + { + public: + + float clipTop; + float clipLeft; + float clipRight; + float clipBottom; + + float absoluteLeft; + float absoluteTop; + float leftOffset; + float topOffset; + + float pixScaleX; + float pixScaleY; + float hOffset; + float vOffset; + + RenderXform (MyGUI::ICroppedRectangle* croppedParent, MyGUI::RenderTargetInfo const & renderTargetInfo) + { + clipTop = croppedParent->_getMarginTop (); + clipLeft = croppedParent->_getMarginLeft (); + clipRight = croppedParent->getWidth () - croppedParent->_getMarginRight (); + clipBottom = croppedParent->getHeight () - croppedParent->_getMarginBottom (); + + absoluteLeft = croppedParent->getAbsoluteLeft(); + absoluteTop = croppedParent->getAbsoluteTop(); + leftOffset = renderTargetInfo.leftOffset; + topOffset = renderTargetInfo.topOffset; + + pixScaleX = renderTargetInfo.pixScaleX; + pixScaleY = renderTargetInfo.pixScaleY; + hOffset = renderTargetInfo.hOffset; + vOffset = renderTargetInfo.vOffset; + } + + bool clip (MyGUI::FloatRect & vr, MyGUI::FloatRect & tr) + { + if (vr.bottom <= clipTop || vr.right <= clipLeft || + vr.left >= clipRight || vr.top >= clipBottom ) + return false; + + if (vr.top < clipTop) + { + tr.top += tr.height () * (clipTop - vr.top) / vr.height (); + vr.top = clipTop; + } + + if (vr.left < clipLeft) + { + tr.left += tr.width () * (clipLeft - vr.left) / vr.width (); + vr.left = clipLeft; + } + + if (vr.right > clipRight) + { + tr.right -= tr.width () * (vr.right - clipRight) / vr.width (); + vr.right = clipRight; + } + + if (vr.bottom > clipBottom) + { + tr.bottom -= tr.height () * (vr.bottom - clipBottom) / vr.height (); + vr.bottom = clipBottom; + } + + return true; + } + + MyGUI::FloatPoint operator () (MyGUI::FloatPoint pt) + { + pt.left = absoluteLeft - leftOffset + pt.left; + pt.top = absoluteTop - topOffset + pt.top; + + pt.left = +(((pixScaleX * pt.left + hOffset) * 2.0f) - 1.0f); + pt.top = -(((pixScaleY * pt.top + vOffset) * 2.0f) - 1.0f); + + return pt; + } + }; + + struct GlyphStream + { + float mZ; + uint32_t mC; + MyGUI::IFont* mFont; + MyGUI::FloatPoint mOrigin; + MyGUI::FloatPoint mCursor; + MyGUI::Vertex* mVertices; + RenderXform mRenderXform; + MyGUI::VertexColourType mVertexColourType; + + GlyphStream (MyGUI::IFont* font, float left, float top, float Z, + MyGUI::Vertex* vertices, RenderXform const & renderXform) : + mZ(Z), mOrigin (left, top), + mFont (font), mVertices (vertices), + mRenderXform (renderXform) + { + mVertexColourType = MyGUI::RenderManager::getInstance().getVertexFormat(); + } + + ~GlyphStream () + { + } + + MyGUI::Vertex* end () const { return mVertices; } + + void reset (float left, float top, MyGUI::Colour colour) + { + mC = MyGUI::texture_utility::toColourARGB(colour) | 0xFF000000; + MyGUI::texture_utility::convertColour(mC, mVertexColourType); + + mCursor.left = mOrigin.left + left; + mCursor.top = mOrigin.top + top; + } + + void emitGlyph (wchar_t ch) + { + MyGUI::GlyphInfo* gi = mFont->getGlyphInfo (ch); + + if (!gi) + return; + + MyGUI::FloatRect vr; + + vr.left = mCursor.left + gi->bearingX; + vr.top = mCursor.top + gi->bearingY; + vr.right = vr.left + gi->width; + vr.bottom = vr.top + gi->height; + + MyGUI::FloatRect tr = gi->uvRect; + + if (mRenderXform.clip (vr, tr)) + quad (vr, tr); + + mCursor.left += gi->bearingX + gi->advance; + } + + void emitSpace (wchar_t ch) + { + MyGUI::GlyphInfo* gi = mFont->getGlyphInfo (ch); + + if (gi) + mCursor.left += gi->bearingX + gi->advance; + } + + private: + + void quad (const MyGUI::FloatRect& vr, const MyGUI::FloatRect& tr) + { + vertex (vr.left, vr.top, tr.left, tr.top); + vertex (vr.right, vr.top, tr.right, tr.top); + vertex (vr.left, vr.bottom, tr.left, tr.bottom); + vertex (vr.right, vr.top, tr.right, tr.top); + vertex (vr.left, vr.bottom, tr.left, tr.bottom); + vertex (vr.right, vr.bottom, tr.right, tr.bottom); + } + + void vertex (float x, float y, float u, float v) + { + MyGUI::FloatPoint pt = mRenderXform (MyGUI::FloatPoint (x, y)); + + mVertices->x = pt.left; + mVertices->y = pt.top ; + mVertices->z = mZ; + mVertices->u = u; + mVertices->v = v; + mVertices->colour = mC; + + ++mVertices; + } + }; +} + +class PageDisplay : public MyGUI::ISubWidgetText +{ + MYGUI_RTTI_DERIVED(PageDisplay) +protected: + + typedef TypesetBookImpl::Section Section; + typedef TypesetBookImpl::Line Line; + typedef TypesetBookImpl::Run Run; + + struct TextFormat : ISubWidget + { + typedef MyGUI::IFont* Id; + + Id mFont; + int mCountVertex; + MyGUI::ITexture* mTexture; + MyGUI::RenderItem* mRenderItem; + PageDisplay * mDisplay; + + TextFormat (MyGUI::IFont* id, PageDisplay * display) : + mFont (id), + mTexture (NULL), + mRenderItem (NULL), + mDisplay (display), + mCountVertex (0) + { + } + + void createDrawItem (MyGUI::ILayerNode* node) + { + assert (mRenderItem == NULL); + + if (mTexture != NULL) + { + mRenderItem = node->addToRenderItem(mTexture, false, false); + mRenderItem->addDrawItem(this, mCountVertex); + } + } + + void destroyDrawItem (MyGUI::ILayerNode* node) + { + assert (mTexture != NULL ? mRenderItem != NULL : mRenderItem == NULL); + + if (mTexture != NULL) + { + mRenderItem->removeDrawItem (this); + mRenderItem = NULL; + } + } + + void doRender() { mDisplay->doRender (*this); } + + // this isn't really a sub-widget, its just a "drawitem" which + // should have its own interface + void createDrawItem(MyGUI::ITexture* _texture, MyGUI::ILayerNode* _node) {} + void destroyDrawItem() {}; + }; + +public: + + typedef TypesetBookImpl::StyleImpl Style; + typedef std::map ActiveTextFormats; + + int mViewTop; + int mViewBottom; + + Style* mFocusItem; + bool mItemActive; + MyGUI::MouseButton mLastDown; + boost::function mLinkClicked; + + + boost::shared_ptr mBook; + size_t mPage; + + MyGUI::ILayerNode* mNode; + ActiveTextFormats mActiveTextFormats; + + PageDisplay () + { + mPage = -1; + } + + void dirtyFocusItem () + { + if (mFocusItem != 0) + { + MyGUI::IFont* Font = mBook->affectedFont (mFocusItem); + + ActiveTextFormats::iterator i = mActiveTextFormats.find (Font); + + mNode->outOfDate (i->second->mRenderItem); + } + } + + void onMouseLostFocus () + { + if (!mBook) + return; + + dirtyFocusItem (); + + mFocusItem = 0; + mItemActive = false; + } + + void onMouseMove (int left, int top) + { + if (!mBook) + return; + + left -= mCroppedParent->getAbsoluteLeft (); + top -= mCroppedParent->getAbsoluteTop (); + + Style * Hit = mBook->hitTest (left, mViewTop + top); + + if (mLastDown == MyGUI::MouseButton::None) + { + if (Hit != mFocusItem) + { + dirtyFocusItem (); + + mFocusItem = Hit; + mItemActive = false; + + dirtyFocusItem (); + } + } + else + if (mFocusItem != 0) + { + bool newItemActive = Hit == mFocusItem; + + if (newItemActive != mItemActive) + { + mItemActive = newItemActive; + + dirtyFocusItem (); + } + } + } + + void onMouseButtonPressed (int left, int top, MyGUI::MouseButton id) + { + if (!mBook) + return; + + left -= mCroppedParent->getAbsoluteLeft (); + top -= mCroppedParent->getAbsoluteTop (); + + if (mLastDown == MyGUI::MouseButton::None) + { + mFocusItem = mBook->hitTest (left, mViewTop + top); + mItemActive = true; + + dirtyFocusItem (); + + mLastDown = id; + } + } + + void onMouseButtonReleased(int left, int top, MyGUI::MouseButton id) + { + if (!mBook) + return; + + left -= mCroppedParent->getAbsoluteLeft (); + top -= mCroppedParent->getAbsoluteTop (); + + if (mLastDown == id) + { + Style * mItem = mBook->hitTest (left, mViewTop + top); + + bool clicked = mFocusItem == mItem; + + mItemActive = false; + + dirtyFocusItem (); + + mLastDown = MyGUI::MouseButton::None; + + if (clicked && mLinkClicked && mItem && mItem->mInteractiveId != 0) + mLinkClicked (mItem->mInteractiveId); + } + } + + void showPage (TypesetBook::Ptr book, size_t newPage) + { + boost::shared_ptr newBook = boost::dynamic_pointer_cast (book); + + if (mBook != newBook) + { + mFocusItem = nullptr; + mItemActive = 0; + + for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) + { + if (mNode != NULL) + i->second->destroyDrawItem (mNode); + delete i->second; + } + + mActiveTextFormats.clear (); + + if (newBook != NULL) + { + createActiveFormats (newBook); + + mBook = newBook; + mPage = newPage; + + if (newPage < mBook->mPages.size ()) + { + mViewTop = mBook->mPages [newPage].first; + mViewBottom = mBook->mPages [newPage].second; + } + else + { + mViewTop = 0; + mViewBottom = 0; + } + } + else + { + mBook.reset (); + mPage = -1; + mViewTop = 0; + mViewBottom = 0; + } + } + else + if (mBook && mPage != newPage) + { + if (mNode != NULL) + for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) + mNode->outOfDate(i->second->mRenderItem); + + mPage = newPage; + + if (newPage < mBook->mPages.size ()) + { + mViewTop = mBook->mPages [newPage].first; + mViewBottom = mBook->mPages [newPage].second; + } + else + { + mViewTop = 0; + mViewBottom = 0; + } + } + } + + struct CreateActiveFormat + { + PageDisplay * this_; + + CreateActiveFormat (PageDisplay * this_) : this_ (this_) {} + + void operator () (Section const & section, Line const & line, Run const & run) const + { + MyGUI::IFont* Font = run.mStyle->mFont; + + ActiveTextFormats::iterator j = this_->mActiveTextFormats.find (Font); + + if (j == this_->mActiveTextFormats.end ()) + { + TextFormat * textFormat = new TextFormat (Font, this_); + + textFormat->mTexture = Font->getTextureFont (); + + j = this_->mActiveTextFormats.insert (std::make_pair (Font, textFormat)).first; + } + + j->second->mCountVertex += run.mPrintableChars * 6; + } + }; + + void createActiveFormats (boost::shared_ptr newBook) + { + newBook->visitRuns (0, 0x7FFFFFFF, CreateActiveFormat (this)); + + if (mNode != NULL) + for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) + i->second->createDrawItem (mNode); + } + + void setVisible (bool newVisible) + { + if (mVisible == newVisible) + return; + + mVisible = newVisible; + + if (mVisible) + { + // reset input state + mLastDown = MyGUI::MouseButton::None; + mFocusItem = nullptr; + mItemActive = 0; + } + + if (nullptr != mNode) + { + for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) + mNode->outOfDate(i->second->mRenderItem); + } + } + + void createDrawItem(MyGUI::ITexture* texture, MyGUI::ILayerNode* node) + { + //test (); + + mNode = node; + + for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) + i->second->createDrawItem (node); + } + + struct RenderRun + { + PageDisplay * this_; + GlyphStream &glyphStream; + + RenderRun (PageDisplay * this_, GlyphStream &glyphStream) : + this_(this_), glyphStream (glyphStream) + { + } + + void operator () (Section const & section, Line const & line, Run const & run) const + { + bool isActive = run.mStyle->mInteractiveId && (run.mStyle == this_->mFocusItem); + + MyGUI::Colour colour = isActive ? (this_->mItemActive ? run.mStyle->mActiveColour: run.mStyle->mHotColour) : run.mStyle->mNormalColour; + + glyphStream.reset (section.mRect.left + line.mRect.left + run.mLeft, line.mRect.top, colour); + + Utf8Stream stream (run.mRange); + + while (!stream.eof ()) + { + Utf8Stream::UnicodeChar code_point = stream.consume (); + + if (!ucsSpace (code_point)) + glyphStream.emitGlyph (code_point); + else + glyphStream.emitSpace (code_point); + } + } + }; + + /* + queue up rendering operations for this text format + */ + void doRender(TextFormat & textFormat) + { + if (!mVisible) + return; + + MyGUI::Vertex* vertices = textFormat.mRenderItem->getCurrentVertexBuffer(); + + RenderXform renderXform (mCroppedParent, textFormat.mRenderItem->getRenderTarget()->getInfo()); + + GlyphStream glyphStream (textFormat.mFont, mCoord.left, mCoord.top-mViewTop, + -1 /*mNode->getNodeDepth()*/, vertices, renderXform); + + int visit_top = (std::max) (mViewTop, mViewTop + int (renderXform.clipTop )); + int visit_bottom = (std::min) (mViewBottom, mViewTop + int (renderXform.clipBottom)); + + mBook->visitRuns (visit_top, visit_bottom, textFormat.mFont, RenderRun (this, glyphStream)); + + textFormat.mRenderItem->setLastVertexCount(glyphStream.end () - vertices); + } + + // ISubWidget should not necessarily be a drawitem + // in this case, it is not... + void doRender() { } + + void _updateView () + { + } + + void _correctView() + { + _checkMargin (); + + if (mNode != NULL) + for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) + mNode->outOfDate (i->second->mRenderItem); + + } + + void destroyDrawItem() + { + for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) + i->second->destroyDrawItem (mNode); + + mNode = NULL; + } +}; + + +class BookPageImpl : public BookPage +{ +MYGUI_RTTI_DERIVED(BookPage) +public: + + + void showPage (TypesetBook::Ptr book, size_t page) + { + if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) + pd->showPage (book, page); + else + throw std::runtime_error ("The main sub-widget for a BookPage must be a PageDisplay."); + } + + void adviseLinkClicked (boost::function linkClicked) + { + if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) + { + pd->mLinkClicked = linkClicked; + } + } + + void unadviseLinkClicked () + { + if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) + { + pd->mLinkClicked = boost::function (); + } + } + +protected: + void onMouseLostFocus(Widget* _new) + { + if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) + { + pd->onMouseLostFocus (); + } + else + Widget::onMouseLostFocus (_new); + } + + void onMouseMove(int left, int top) + { + if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) + { + pd->onMouseMove (left, top); + } + else + Widget::onMouseMove (left, top); + } + + void onMouseButtonPressed (int left, int top, MyGUI::MouseButton id) + { + if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) + { + pd->onMouseButtonPressed (left, top, id); + } + else + Widget::onMouseButtonPressed (left, top, id); + } + + void onMouseButtonReleased(int left, int top, MyGUI::MouseButton id) + { + if (PageDisplay* pd = dynamic_cast (getSubWidgetText ())) + { + pd->onMouseButtonReleased (left, top, id); + } + else + Widget::onMouseButtonReleased (left, top, id); + } +}; + +void BookPage::registerMyGUIComponents () +{ + MyGUI::FactoryManager & factory = MyGUI::FactoryManager::getInstance(); + + factory.registerFactory("Widget"); + factory.registerFactory("BasisSkin"); +} + +static bool ucsLineBreak (int codePoint) +{ + return codePoint == '\n'; +} + +static bool ucsSpace (int codePoint) +{ + switch (codePoint) + { + case 0x0020: // SPACE + case 0x00A0: // NO-BREAK SPACE + case 0x1680: // OGHAM SPACE MARK + case 0x180E: // MONGOLIAN VOWEL SEPARATOR + case 0x2000: // EN QUAD + case 0x2001: // EM QUAD + case 0x2002: // EN SPACE + case 0x2003: // EM SPACE + case 0x2004: // THREE-PER-EM SPACE + case 0x2005: // FOUR-PER-EM SPACE + case 0x2006: // SIX-PER-EM SPACE + case 0x2007: // FIGURE SPACE + case 0x2008: // PUNCTUATION SPACE + case 0x2009: // THIN SPACE + case 0x200A: // HAIR SPACE + case 0x200B: // ZERO WIDTH SPACE + case 0x202F: // NARROW NO-BREAK SPACE + case 0x205F: // MEDIUM MATHEMATICAL SPACE + case 0x3000: // IDEOGRAPHIC SPACE + case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE + return true; + default: + return false; + } +} + +static bool ucsBreakingSpace (int codePoint) +{ + switch (codePoint) + { + case 0x0020: // SPACE + //case 0x00A0: // NO-BREAK SPACE + case 0x1680: // OGHAM SPACE MARK + case 0x180E: // MONGOLIAN VOWEL SEPARATOR + case 0x2000: // EN QUAD + case 0x2001: // EM QUAD + case 0x2002: // EN SPACE + case 0x2003: // EM SPACE + case 0x2004: // THREE-PER-EM SPACE + case 0x2005: // FOUR-PER-EM SPACE + case 0x2006: // SIX-PER-EM SPACE + case 0x2007: // FIGURE SPACE + case 0x2008: // PUNCTUATION SPACE + case 0x2009: // THIN SPACE + case 0x200A: // HAIR SPACE + case 0x200B: // ZERO WIDTH SPACE + case 0x202F: // NARROW NO-BREAK SPACE + case 0x205F: // MEDIUM MATHEMATICAL SPACE + case 0x3000: // IDEOGRAPHIC SPACE + //case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE + return true; + default: + return false; + } +} + +} diff --git a/apps/openmw/mwgui/bookpage.hpp b/apps/openmw/mwgui/bookpage.hpp new file mode 100644 index 000000000..28aa371cf --- /dev/null +++ b/apps/openmw/mwgui/bookpage.hpp @@ -0,0 +1,122 @@ +#ifndef MWGUI_BOOKPAGE_HPP +#define MWGUI_BOOKPAGE_HPP + +#include "MyGUI_Colour.h" +#include "MyGUI_Widget.h" + +#include +#include +#include +#include + +namespace MWGui +{ + /// A formatted and paginated document to be used with + /// the book page widget. + struct TypesetBook + { + typedef boost::shared_ptr Ptr; + typedef intptr_t InteractiveId; + + /// Returns the number of pages in the document. + virtual size_t pageCount () const = 0; + + /// Return the area covered by the document. The first + /// integer is the maximum with of any line. This is not + /// the largest coordinate of the right edge of any line, + /// it is the largest distance from the left edge to the + /// right edge. The second integer is the height of all + /// text combined prior to pagination. + virtual std::pair getSize () const = 0; + }; + + /// A factory class for creating a typeset book instance. + struct BookTypesetter + { + typedef boost::shared_ptr Ptr; + typedef TypesetBook::InteractiveId InteractiveId; + typedef MyGUI::Colour Colour; + typedef uint8_t const * Utf8Point; + typedef std::pair Utf8Span; + + enum Alignment { + AlignLeft = -1, + AlignCenter = 0, + AlignRight = +1 + }; + + /// Styles are used to control the character level formatting + /// of text added to a typeset book. Their lifetime is equal + /// to the lifetime of the book-typesetter instance that created + /// them. + struct Style; + + /// A factory function for creating the default implementation of a book typesetter + static Ptr create (int pageWidth, int pageHeight); + + /// Create a simple text style consisting of a font and a text color. + virtual Style* createStyle (char const * Font, Colour Colour) = 0; + + /// Create a hyper-link style with a user-defined identifier based on an + /// existing style. The unique flag forces a new instance of this style + /// to be created even if an existing instance is present. + virtual Style* createHotStyle (Style * BaseStyle, Colour NormalColour, Colour HoverColour, Colour ActiveColour, InteractiveId Id, bool Unique = true) = 0; + + /// Insert a line break into the document. Newline characters in the input + /// text have the same affect. The margin parameter adds additional space + /// before the next line of text. + virtual void lineBreak (float margin = 0) = 0; + + /// Insert a section break into the document. This causes a new section + /// to begin when additional text is inserted. Pagination attempts to keep + /// sections together on a single page. The margin parameter adds additional space + /// before the next line of text. + virtual void sectionBreak (float margin = 0) = 0; + + /// Changes the alignment for the current section of text. + virtual void setSectionAlignment (Alignment sectionAlignment) = 0; + + // Layout a block of text with the specified style into the document. + virtual void write (Style * Style, Utf8Span Text) = 0; + + /// Adds a content block to the document without laying it out. An + /// identifier is returned that can be used to refer to it. If select + /// is true, the block is activated to be references by future writes. + virtual intptr_t addContent (Utf8Span Text, bool Select = true) = 0; + + /// Select a previously created content block for future writes. + virtual void selectContent (intptr_t contentHandle) = 0; + + /// Layout a span of the selected content block into the document + /// using the specified style. + virtual void write (Style * Style, size_t Begin, size_t End) = 0; + + /// Finalize the document layout, and return a pointer to it. + virtual TypesetBook::Ptr complete () = 0; + }; + + /// An interface to the BookPage widget. + class BookPage : public MyGUI::Widget + { + MYGUI_RTTI_DERIVED(BookPage) + public: + + typedef TypesetBook::InteractiveId InteractiveId; + typedef boost::function ClickCallback; + + /// Make the widget display the specified page from the specified book. + virtual void showPage (TypesetBook::Ptr Book, size_t Page) = 0; + + /// Set the callback for a clicking a hyper-link in the document. + virtual void adviseLinkClicked (ClickCallback callback) = 0; + + /// Clear the hyper-link click callback. + virtual void unadviseLinkClicked () = 0; + + /// Register the widget and associated sub-widget with MyGUI. Should be + /// called once near the beginning of the program. + static void registerMyGUIComponents (); + }; +} + +#endif // MWGUI_BOOKPAGE_HPP diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 659795e18..3b5deb5ac 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -12,139 +12,187 @@ #include "formatting.hpp" -using namespace MWGui; - -BookWindow::BookWindow (MWBase::WindowManager& parWindowManager) : - WindowBase("openmw_book.layout", parWindowManager) +namespace MWGui { - getWidget(mCloseButton, "CloseButton"); - mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onCloseButtonClicked); - getWidget(mTakeButton, "TakeButton"); - mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onTakeButtonClicked); + BookWindow::BookWindow () + : WindowBase("openmw_book.layout") + , mTakeButtonShow(true) + , mTakeButtonAllowed(true) + { + getWidget(mCloseButton, "CloseButton"); + mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onCloseButtonClicked); - getWidget(mNextPageButton, "NextPageBTN"); - mNextPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onNextPageButtonClicked); + getWidget(mTakeButton, "TakeButton"); + mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onTakeButtonClicked); - getWidget(mPrevPageButton, "PrevPageBTN"); - mPrevPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onPrevPageButtonClicked); + getWidget(mNextPageButton, "NextPageBTN"); + mNextPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onNextPageButtonClicked); - getWidget(mLeftPageNumber, "LeftPageNumber"); - getWidget(mRightPageNumber, "RightPageNumber"); + getWidget(mPrevPageButton, "PrevPageBTN"); + mPrevPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onPrevPageButtonClicked); - getWidget(mLeftPage, "LeftPage"); - getWidget(mRightPage, "RightPage"); + getWidget(mLeftPageNumber, "LeftPageNumber"); + getWidget(mRightPageNumber, "RightPageNumber"); - center(); -} + getWidget(mLeftPage, "LeftPage"); + getWidget(mRightPage, "RightPage"); -void BookWindow::clearPages() -{ - for (std::vector::iterator it=mPages.begin(); - it!=mPages.end(); ++it) + adjustButton(mCloseButton); + adjustButton(mTakeButton); + adjustButton(mNextPageButton); + adjustButton(mPrevPageButton); + + if (mNextPageButton->getSize().width == 64) + { + // english button has a 7 pixel wide strip of garbage on its right edge + mNextPageButton->setSize(64-7, mNextPageButton->getSize().height); + mNextPageButton->setImageCoord(MyGUI::IntCoord(0,0,64-7,mNextPageButton->getSize().height)); + } + + center(); + } + + void BookWindow::clearPages() { - MyGUI::Gui::getInstance().destroyWidget(*it); + for (std::vector::iterator it=mPages.begin(); + it!=mPages.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + mPages.clear(); } - mPages.clear(); -} -void BookWindow::open (MWWorld::Ptr book) -{ - mBook = book; + void BookWindow::open (MWWorld::Ptr book) + { + mBook = book; - clearPages(); - mCurrentPage = 0; + clearPages(); + mCurrentPage = 0; - MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); + MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); - MWWorld::LiveCellRef *ref = mBook.get(); + MWWorld::LiveCellRef *ref = mBook.get(); - BookTextParser parser; - std::vector results = parser.split(ref->mBase->mText, mLeftPage->getSize().width, mLeftPage->getSize().height); + BookTextParser parser; + std::vector results = parser.split(ref->mBase->mText, mLeftPage->getSize().width, mLeftPage->getSize().height); - int i=0; - for (std::vector::iterator it=results.begin(); - it!=results.end(); ++it) - { - MyGUI::Widget* parent; - if (i%2 == 0) - parent = mLeftPage; - else - 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)); - parser.parse(*it, pageWidget, mLeftPage->getSize().width); - mPages.push_back(pageWidget); - ++i; - } + int i=0; + for (std::vector::iterator it=results.begin(); + it!=results.end(); ++it) + { + MyGUI::Widget* parent; + if (i%2 == 0) + parent = mLeftPage; + else + 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)); + parser.parsePage(*it, pageWidget, mLeftPage->getSize().width); + mPages.push_back(pageWidget); + ++i; + } - updatePages(); + updatePages(); - setTakeButtonShow(true); -} + setTakeButtonShow(true); + } -void BookWindow::setTakeButtonShow(bool show) -{ - mTakeButton->setVisible(show); -} + void BookWindow::setTakeButtonShow(bool show) + { + mTakeButtonShow = show; + mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); + } -void BookWindow::onCloseButtonClicked (MyGUI::Widget* sender) -{ - // no 3d sounds because the object could be in a container. - MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); + void BookWindow::setInventoryAllowed(bool allowed) + { + mTakeButtonAllowed = allowed; + mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); + } - mWindowManager.removeGuiMode(GM_Book); -} + void BookWindow::onCloseButtonClicked (MyGUI::Widget* sender) + { + // no 3d sounds because the object could be in a container. + MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); -void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender) -{ - MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book); + } - MWWorld::ActionTake take(mBook); - take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender) + { + MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); - mWindowManager.removeGuiMode(GM_Book); -} + MWWorld::ActionTake take(mBook); + take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); -void BookWindow::onNextPageButtonClicked (MyGUI::Widget* sender) -{ - if ((mCurrentPage+1)*2 < mPages.size()) + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book); + } + + void BookWindow::onNextPageButtonClicked (MyGUI::Widget* sender) { - MWBase::Environment::get().getSoundManager()->playSound ("book page2", 1.0, 1.0); + if ((mCurrentPage+1)*2 < mPages.size()) + { + MWBase::Environment::get().getSoundManager()->playSound ("book page2", 1.0, 1.0); - ++mCurrentPage; + ++mCurrentPage; - updatePages(); + updatePages(); + } } -} -void BookWindow::onPrevPageButtonClicked (MyGUI::Widget* sender) -{ - if (mCurrentPage > 0) + void BookWindow::onPrevPageButtonClicked (MyGUI::Widget* sender) { - MWBase::Environment::get().getSoundManager()->playSound ("book page", 1.0, 1.0); + if (mCurrentPage > 0) + { + MWBase::Environment::get().getSoundManager()->playSound ("book page", 1.0, 1.0); - --mCurrentPage; + --mCurrentPage; - updatePages(); + updatePages(); + } } -} -void BookWindow::updatePages() -{ - mLeftPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 1) ); - mRightPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 2) ); - - unsigned int i=0; - for (std::vector::iterator it = mPages.begin(); - it != mPages.end(); ++it) + void BookWindow::updatePages() { - if (mCurrentPage*2 == i || mCurrentPage*2+1 == i) - (*it)->setVisible(true); - else + mLeftPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 1) ); + mRightPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 2) ); + + unsigned int i=0; + for (std::vector::iterator it = mPages.begin(); + it != mPages.end(); ++it) + { + if (mCurrentPage*2 == i || mCurrentPage*2+1 == i) + (*it)->setVisible(true); + else + { + (*it)->setVisible(false); + } + ++i; + } + + //If it is the last page, hide the button "Next Page" + if ( (mCurrentPage+1)*2 == mPages.size() + || (mCurrentPage+1)*2 == mPages.size() + 1) { - (*it)->setVisible(false); + mNextPageButton->setVisible(false); + } else { + mNextPageButton->setVisible(true); + } + //If it is the fist page, hide the button "Prev Page" + if (mCurrentPage == 0) { + mPrevPageButton->setVisible(false); + } else { + mPrevPageButton->setVisible(true); } - ++i; } + + void BookWindow::adjustButton (MWGui::ImageButton* button) + { + MyGUI::IntSize diff = button->getSize() - button->getRequestedSize(); + button->setSize(button->getRequestedSize()); + + if (button->getAlign().isRight()) + button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0)); + } + } diff --git a/apps/openmw/mwgui/bookwindow.hpp b/apps/openmw/mwgui/bookwindow.hpp index 5887975ea..c6ea486d4 100644 --- a/apps/openmw/mwgui/bookwindow.hpp +++ b/apps/openmw/mwgui/bookwindow.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_BOOKWINDOW_H #define MWGUI_BOOKWINDOW_H -#include "window_base.hpp" +#include "windowbase.hpp" #include "../mwworld/ptr.hpp" @@ -12,11 +12,13 @@ namespace MWGui class BookWindow : public WindowBase { public: - BookWindow(MWBase::WindowManager& parWindowManager); + BookWindow(); void open(MWWorld::Ptr book); void setTakeButtonShow(bool show); + void setInventoryAllowed(bool allowed); + protected: void onNextPageButtonClicked (MyGUI::Widget* sender); void onPrevPageButtonClicked (MyGUI::Widget* sender); @@ -25,6 +27,7 @@ namespace MWGui void updatePages(); void clearPages(); + void adjustButton(MWGui::ImageButton* button); private: MWGui::ImageButton* mCloseButton; @@ -40,6 +43,9 @@ namespace MWGui std::vector mPages; MWWorld::Ptr mBook; + + bool mTakeButtonShow; + bool mTakeButtonAllowed; }; } diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 1719c5768..fc8c24484 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -1,110 +1,38 @@ #include "charactercreation.hpp" -#include "text_input.hpp" +#include "textinput.hpp" #include "race.hpp" #include "class.hpp" #include "birth.hpp" #include "review.hpp" -#include "dialogue.hpp" -#include "mode.hpp" #include "inventorywindow.hpp" - +#include #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwworld/fallback.hpp" namespace { struct Step { - const char* mText; - const char* mButtons[3]; - const char* mSound; - ESM::Class::Specialization mSpecializations[3]; // The specialization for each answer + const std::string mText; + const std::string mButtons[3]; + const std::string mSound; }; - static boost::array sGenerateClassSteps = { { - // Question 1 - {"On a clear day you chance upon a strange animal, its legs trapped in a hunter's clawsnare. Judging from the bleeding, it will not survive long.", - {"Draw your dagger, mercifully endings its life with a single thrust.", - "Use herbs from your pack to put it to sleep.", - "Do not interfere in the natural evolution of events, but rather take the opportunity to learn more about a strange animal that you have never seen before."}, - "vo\\misc\\chargen qa1.wav", - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 2 - {"One Summer afternoon your father gives you a choice of chores.", - {"Work in the forge with him casting iron for a new plow.", - "Gather herbs for your mother who is preparing dinner.", - "Go catch fish at the stream using a net and line."}, - "vo\\misc\\chargen qa2.wav", - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 3 - {"Your cousin has given you a very embarrassing nickname and, even worse, likes to call you it in front of your friends. You asked him to stop, but he finds it very amusing to watch you blush.", - {"Beat up your cousin, then tell him that if he ever calls you that nickname again, you will bloody him worse than this time.", - "Make up a story that makes your nickname a badge of honor instead of something humiliating.", - "Make up an even more embarrassing nickname for him and use it constantly until he learns his lesson."}, - "vo\\misc\\chargen qa3.wav", - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 4 - {"There is a lot of heated discussion at the local tavern over a grouped of people called 'Telepaths'. They have been hired by certain City-State kings. Rumor has it these Telepaths read a person's mind and tell their lord whether a follower is telling the truth or not.", - {"This is a terrible practice. A person's thoughts are his own and no one, not even a king, has the right to make such an invasion into another human's mind.", - "Loyal followers to the king have nothing to fear from a Telepath. It is important to have a method of finding assassins and spies before it is too late.", - "In these times, it is a necessary evil. Although you do not necessarily like the idea, a Telepath could have certain advantages during a time of war or in finding someone innocent of a crime."}, - "vo\\misc\\chargen qa4.wav", - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 5 - {"Your mother sends you to the market with a list of goods to buy. After you finish you find that by mistake a shopkeeper has given you too much money back in exchange for one of the items.", - {"Return to the store and give the shopkeeper his hard-earned money, explaining to him the mistake?", - "Decide to put the extra money to good use and purchase items that would help your family?", - "Pocket the extra money, knowing that shopkeepers in general tend to overcharge customers anyway?"}, - "vo\\misc\\chargen qa5.wav", - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 6 - {"While in the market place you witness a thief cut a purse from a noble. Even as he does so, the noble notices and calls for the city guards. In his haste to get away, the thief drops the purse near you. Surprisingly no one seems to notice the bag of coins at your feet.", - {"Pick up the bag and signal to the guard, knowing that the only honorable thing to do is return the money to its rightful owner.", - "Leave the bag there, knowing that it is better not to get involved.", - "Pick up the bag and pocket it, knowing that the extra windfall will help your family in times of trouble."}, - "vo\\misc\\chargen qa6.wav", - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 7 - {"Your father sends you on a task which you loathe, cleaning the stables. On the way there, pitchfork in hand, you run into your friend from the homestead near your own. He offers to do it for you, in return for a future favor of his choosing.", - {"Decline his offer, knowing that your father expects you to do the work, and it is better not to be in debt.", - "Ask him to help you, knowing that two people can do the job faster than one, and agree to help him with one task of his choosing in the future.", - "Accept his offer, reasoning that as long as the stables are cleaned, it matters not who does the cleaning."}, - "vo\\misc\\chargen qa7.wav", - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 8 - {"Your mother asks you to help fix the stove. While you are working, a very hot pipe slips its mooring and falls towards her.", - {"Position yourself between the pipe and your mother.", - "Grab the hot pipe and try to push it away.", - "Push your mother out of the way."}, - "vo\\misc\\chargen qa8.wav", - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 9 - {"While in town the baker gives you a sweetroll. Delighted, you take it into an alley to enjoy only to be intercepted by a gang of three other kids your age. The leader demands the sweetroll, or else he and his friends will beat you and take it.", - {"Drop the sweetroll and step on it, then get ready for the fight.", - "Give him the sweetroll now without argument, knowing that later this afternoon you will have all your friends with you and can come and take whatever he owes you.", - "Act like you're going to give him the sweetroll, but at the last minute throw it in the air, hoping that they'll pay attention to it long enough for you to get a shot in on the leader."}, - "vo\\misc\\chargen qa9.wav", - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 10 - {"Entering town you find that you are witness to a very well-dressed man running from a crowd. He screams to you for help. The crowd behind him seem very angry.", - {"Rush to the town's aid immediately, despite your lack of knowledge of the circumstances.", - "Stand aside and allow the man and the mob to pass, realizing it is probably best not to get involved.", - "Rush to the man's aid immediately, despite your lack of knowledge of the circumstances."}, - "vo\\misc\\chargen qa10.wav", - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - } - } }; + const ESM::Class::Specialization mSpecializations[3]={ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth}; // The specialization for each answer + Step sGenerateClassSteps(int number) { + number++; + const MWWorld::Fallback* fallback=MWBase::Environment::get().getWorld()->getFallback(); + Step step = {fallback->getFallbackString("Question_"+boost::lexical_cast(number)+"_Question"), + {fallback->getFallbackString("Question_"+boost::lexical_cast(number)+"_AnswerOne"), + fallback->getFallbackString("Question_"+boost::lexical_cast(number)+"_AnswerTwo"), + fallback->getFallbackString("Question_"+boost::lexical_cast(number)+"_AnswerThree")}, + "vo\\misc\\chargen qa"+boost::lexical_cast(number)+".wav" + }; + return step; + } struct ClassPoint { @@ -115,672 +43,685 @@ namespace }; } -using namespace MWGui; - -CharacterCreation::CharacterCreation(MWBase::WindowManager* _wm) - : mNameDialog(0) - , mRaceDialog(0) - , mClassChoiceDialog(0) - , mGenerateClassQuestionDialog(0) - , mGenerateClassResultDialog(0) - , mPickClassDialog(0) - , mCreateClassDialog(0) - , mBirthSignDialog(0) - , mReviewDialog(0) - , mGenerateClassStep(0) - , mWM(_wm) +namespace MWGui { - mCreationStage = CSE_NotStarted; -} -void CharacterCreation::setValue (const std::string& id, const MWMechanics::Stat& value) -{ - if (mReviewDialog) - { - static const char *ids[] = - { - "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", - "AttribVal6", "AttribVal7", "AttribVal8", - 0 - }; - - for (int i=0; ids[i]; ++i) - { - if (ids[i]==id) - mReviewDialog->setAttribute(ESM::Attribute::AttributeID(i), value); - } + CharacterCreation::CharacterCreation() + : mNameDialog(0) + , mRaceDialog(0) + , mClassChoiceDialog(0) + , mGenerateClassQuestionDialog(0) + , mGenerateClassResultDialog(0) + , mPickClassDialog(0) + , mCreateClassDialog(0) + , mBirthSignDialog(0) + , mReviewDialog(0) + , mGenerateClassStep(0) + { + mCreationStage = CSE_NotStarted; } -} -void CharacterCreation::setValue (const std::string& id, const MWMechanics::DynamicStat& value) -{ - if (mReviewDialog) + void CharacterCreation::setValue (const std::string& id, const MWMechanics::Stat& value) { - if (id == "HBar") - { - mReviewDialog->setHealth (value); - } - else if (id == "MBar") + if (mReviewDialog) { - mReviewDialog->setMagicka (value); - } - else if (id == "FBar") - { - mReviewDialog->setFatigue (value); + static const char *ids[] = + { + "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", + "AttribVal6", "AttribVal7", "AttribVal8", + 0 + }; + + for (int i=0; ids[i]; ++i) + { + if (ids[i]==id) + mReviewDialog->setAttribute(ESM::Attribute::AttributeID(i), value); + } } } -} - -void CharacterCreation::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value) -{ - if (mReviewDialog) - mReviewDialog->setSkillValue(parSkill, value); -} - -void CharacterCreation::configureSkills (const SkillList& major, const SkillList& minor) -{ - if (mReviewDialog) - mReviewDialog->configureSkills(major, minor); -} -void CharacterCreation::spawnDialog(const char id) -{ - switch (id) + void CharacterCreation::setValue (const std::string& id, const MWMechanics::DynamicStat& value) { - case GM_Name: - mWM->removeDialog(mNameDialog); - mNameDialog = 0; - mNameDialog = new TextInputDialog(*mWM); - mNameDialog->setTextLabel(mWM->getGameSettingString("sName", "Name")); - mNameDialog->setTextInput(mPlayerName); - mNameDialog->setNextButtonShow(mCreationStage >= CSE_NameChosen); - mNameDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onNameDialogDone); - mNameDialog->setVisible(true); - break; - - case GM_Race: - mWM->removeDialog(mRaceDialog); - mRaceDialog = 0; - mRaceDialog = new RaceDialog(*mWM); - mRaceDialog->setNextButtonShow(mCreationStage >= CSE_RaceChosen); - mRaceDialog->setRaceId(mPlayerRaceId); - mRaceDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogDone); - mRaceDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogBack); - mRaceDialog->setVisible(true);; - break; - - case GM_Class: - mWM->removeDialog(mClassChoiceDialog); - mClassChoiceDialog = 0; - mClassChoiceDialog = new ClassChoiceDialog(*mWM); - mClassChoiceDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassChoice); - mClassChoiceDialog->setVisible(true); - break; - - case GM_ClassPick: - mWM->removeDialog(mPickClassDialog); - mPickClassDialog = 0; - mPickClassDialog = new PickClassDialog(*mWM); - mPickClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); - mPickClassDialog->setClassId(mPlayerClass.mName); - mPickClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogDone); - mPickClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogBack); - mPickClassDialog->setVisible(true); - break; - - case GM_Birth: - mWM->removeDialog(mBirthSignDialog); - mBirthSignDialog = 0; - mBirthSignDialog = new BirthDialog(*mWM); - mBirthSignDialog->setNextButtonShow(mCreationStage >= CSE_BirthSignChosen); - mBirthSignDialog->setBirthId(mPlayerBirthSignId); - mBirthSignDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogDone); - mBirthSignDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogBack); - mBirthSignDialog->setVisible(true); - break; - - case GM_ClassCreate: - mWM->removeDialog(mCreateClassDialog); - mCreateClassDialog = 0; - mCreateClassDialog = new CreateClassDialog(*mWM); - mCreateClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); - mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone); - mCreateClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack); - mCreateClassDialog->setVisible(true); - break; - case GM_ClassGenerate: - mGenerateClassStep = 0; - mGenerateClass = ""; - mGenerateClassSpecializations[0] = 0; - mGenerateClassSpecializations[1] = 0; - mGenerateClassSpecializations[2] = 0; - showClassQuestionDialog(); - break; - case GM_Review: - mWM->removeDialog(mReviewDialog); - mReviewDialog = 0; - mReviewDialog = new ReviewDialog(*mWM); - mReviewDialog->setPlayerName(mPlayerName); - mReviewDialog->setRace(mPlayerRaceId); - mReviewDialog->setClass(mPlayerClass); - mReviewDialog->setBirthSign(mPlayerBirthSignId); - - mReviewDialog->setHealth(mPlayerHealth); - mReviewDialog->setMagicka(mPlayerMagicka); - mReviewDialog->setFatigue(mPlayerFatigue); - + if (mReviewDialog) + { + if (id == "HBar") { - std::map > attributes = mWM->getPlayerAttributeValues(); - for (std::map >::iterator it = attributes.begin(); - it != attributes.end(); ++it) - { - mReviewDialog->setAttribute(static_cast (it->first), it->second); - } + mReviewDialog->setHealth (value); } - + else if (id == "MBar") { - std::map > skills = mWM->getPlayerSkillValues(); - for (std::map >::iterator it = skills.begin(); - it != skills.end(); ++it) - { - mReviewDialog->setSkillValue(static_cast (it->first), it->second); - } - mReviewDialog->configureSkills(mWM->getPlayerMajorSkills(), mWM->getPlayerMinorSkills()); + mReviewDialog->setMagicka (value); } - - mReviewDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogDone); - mReviewDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogBack); - mReviewDialog->eventActivateDialog += MyGUI::newDelegate(this, &CharacterCreation::onReviewActivateDialog); - mReviewDialog->setVisible(true); - break; + else if (id == "FBar") + { + mReviewDialog->setFatigue (value); + } + } } -} - -void CharacterCreation::setPlayerHealth (const MWMechanics::DynamicStat& value) -{ - mPlayerHealth = value; -} -void CharacterCreation::setPlayerMagicka (const MWMechanics::DynamicStat& value) -{ - mPlayerMagicka = value; -} + void CharacterCreation::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value) + { + if (mReviewDialog) + mReviewDialog->setSkillValue(parSkill, value); + } -void CharacterCreation::setPlayerFatigue (const MWMechanics::DynamicStat& value) -{ - mPlayerFatigue = value; -} + void CharacterCreation::configureSkills (const SkillList& major, const SkillList& minor) + { + if (mReviewDialog) + mReviewDialog->configureSkills(major, minor); + } -void CharacterCreation::onReviewDialogDone(WindowBase* parWindow) -{ - mWM->removeDialog(mReviewDialog); - mReviewDialog = 0; + void CharacterCreation::spawnDialog(const char id) + { + switch (id) + { + case GM_Name: + MWBase::Environment::get().getWindowManager()->removeDialog(mNameDialog); + mNameDialog = 0; + mNameDialog = new TextInputDialog(); + mNameDialog->setTextLabel(MWBase::Environment::get().getWindowManager()->getGameSettingString("sName", "Name")); + mNameDialog->setTextInput(mPlayerName); + mNameDialog->setNextButtonShow(mCreationStage >= CSE_NameChosen); + mNameDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onNameDialogDone); + mNameDialog->setVisible(true); + break; - mWM->popGuiMode(); -} + case GM_Race: + MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog); + mRaceDialog = 0; + mRaceDialog = new RaceDialog(); + mRaceDialog->setNextButtonShow(mCreationStage >= CSE_RaceChosen); + mRaceDialog->setRaceId(mPlayerRaceId); + mRaceDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogDone); + mRaceDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogBack); + mRaceDialog->setVisible(true); + if (mCreationStage < CSE_NameChosen) + mCreationStage = CSE_NameChosen; + break; -void CharacterCreation::onReviewDialogBack() -{ - mWM->removeDialog(mReviewDialog); - mReviewDialog = 0; + case GM_Class: + MWBase::Environment::get().getWindowManager()->removeDialog(mClassChoiceDialog); + mClassChoiceDialog = 0; + mClassChoiceDialog = new ClassChoiceDialog(); + mClassChoiceDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassChoice); + mClassChoiceDialog->setVisible(true); + if (mCreationStage < CSE_RaceChosen) + mCreationStage = CSE_RaceChosen; + break; - mWM->pushGuiMode(GM_Birth); -} + case GM_ClassPick: + MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog); + mPickClassDialog = 0; + mPickClassDialog = new PickClassDialog(); + mPickClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); + mPickClassDialog->setClassId(mPlayerClass.mName); + mPickClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogDone); + mPickClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogBack); + mPickClassDialog->setVisible(true); + if (mCreationStage < CSE_RaceChosen) + mCreationStage = CSE_RaceChosen; + break; -void CharacterCreation::onReviewActivateDialog(int parDialog) -{ - mWM->removeDialog(mReviewDialog); - mReviewDialog = 0; - mCreationStage = CSE_ReviewNext; + case GM_Birth: + MWBase::Environment::get().getWindowManager()->removeDialog(mBirthSignDialog); + mBirthSignDialog = 0; + mBirthSignDialog = new BirthDialog(); + mBirthSignDialog->setNextButtonShow(mCreationStage >= CSE_BirthSignChosen); + mBirthSignDialog->setBirthId(mPlayerBirthSignId); + mBirthSignDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogDone); + mBirthSignDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogBack); + mBirthSignDialog->setVisible(true); + if (mCreationStage < CSE_ClassChosen) + mCreationStage = CSE_ClassChosen; + break; - mWM->popGuiMode(); + case GM_ClassCreate: + MWBase::Environment::get().getWindowManager()->removeDialog(mCreateClassDialog); + mCreateClassDialog = 0; + mCreateClassDialog = new CreateClassDialog(); + mCreateClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); + mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone); + mCreateClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack); + mCreateClassDialog->setVisible(true); + if (mCreationStage < CSE_RaceChosen) + mCreationStage = CSE_RaceChosen; + break; + case GM_ClassGenerate: + mGenerateClassStep = 0; + mGenerateClass = ""; + mGenerateClassSpecializations[0] = 0; + mGenerateClassSpecializations[1] = 0; + mGenerateClassSpecializations[2] = 0; + showClassQuestionDialog(); + if (mCreationStage < CSE_RaceChosen) + mCreationStage = CSE_RaceChosen; + break; + case GM_Review: + MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); + mReviewDialog = 0; + mReviewDialog = new ReviewDialog(); + mReviewDialog->setPlayerName(mPlayerName); + mReviewDialog->setRace(mPlayerRaceId); + mReviewDialog->setClass(mPlayerClass); + mReviewDialog->setBirthSign(mPlayerBirthSignId); + + mReviewDialog->setHealth(mPlayerHealth); + mReviewDialog->setMagicka(mPlayerMagicka); + mReviewDialog->setFatigue(mPlayerFatigue); - switch(parDialog) - { - case ReviewDialog::NAME_DIALOG: - mWM->pushGuiMode(GM_Name); - break; - case ReviewDialog::RACE_DIALOG: - mWM->pushGuiMode(GM_Race); - break; - case ReviewDialog::CLASS_DIALOG: - mWM->pushGuiMode(GM_Class); - break; - case ReviewDialog::BIRTHSIGN_DIALOG: - mWM->pushGuiMode(GM_Birth); - }; -} + { + 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); + } + } -void CharacterCreation::onPickClassDialogDone(WindowBase* parWindow) -{ - if (mPickClassDialog) - { - const std::string &classId = mPickClassDialog->getClassId(); - if (!classId.empty()) - MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId); + { + 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); + } + mReviewDialog->configureSkills(MWBase::Environment::get().getWindowManager()->getPlayerMajorSkills(), MWBase::Environment::get().getWindowManager()->getPlayerMinorSkills()); + } - const ESM::Class *klass = - MWBase::Environment::get().getWorld()->getStore().get().find(classId); - if (klass) - { - mPlayerClass = *klass; - mWM->setPlayerClass(mPlayerClass); + mReviewDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogDone); + mReviewDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogBack); + mReviewDialog->eventActivateDialog += MyGUI::newDelegate(this, &CharacterCreation::onReviewActivateDialog); + mReviewDialog->setVisible(true); + if (mCreationStage < CSE_BirthSignChosen) + mCreationStage = CSE_BirthSignChosen; + break; } - mWM->removeDialog(mPickClassDialog); - mPickClassDialog = 0; } - //TODO This bit gets repeated a few times; wrap it in a function - if (mCreationStage == CSE_ReviewNext) + void CharacterCreation::setPlayerHealth (const MWMechanics::DynamicStat& value) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Review); + mPlayerHealth = value; } - else if (mCreationStage >= CSE_ClassChosen) + + void CharacterCreation::setPlayerMagicka (const MWMechanics::DynamicStat& value) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Birth); + mPlayerMagicka = value; } - else + + void CharacterCreation::setPlayerFatigue (const MWMechanics::DynamicStat& value) { - mCreationStage = CSE_ClassChosen; - mWM->popGuiMode(); + mPlayerFatigue = value; } -} -void CharacterCreation::onPickClassDialogBack() -{ - if (mPickClassDialog) + void CharacterCreation::onReviewDialogDone(WindowBase* parWindow) { - const std::string classId = mPickClassDialog->getClassId(); - if (!classId.empty()) - MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId); - mWM->removeDialog(mPickClassDialog); - mPickClassDialog = 0; - } + MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); + mReviewDialog = 0; - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); -} + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } -void CharacterCreation::onClassChoice(int _index) -{ - mWM->removeDialog(mClassChoiceDialog); - mClassChoiceDialog = 0; + void CharacterCreation::onReviewDialogBack() + { + MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); + mReviewDialog = 0; - mWM->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); + } - switch(_index) + void CharacterCreation::onReviewActivateDialog(int parDialog) { - case ClassChoiceDialog::Class_Generate: - mWM->pushGuiMode(GM_ClassGenerate); - break; - case ClassChoiceDialog::Class_Pick: - mWM->pushGuiMode(GM_ClassPick); - break; - case ClassChoiceDialog::Class_Create: - mWM->pushGuiMode(GM_ClassCreate); - break; - case ClassChoiceDialog::Class_Back: - mWM->pushGuiMode(GM_Race); - break; + MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); + mReviewDialog = 0; + mCreationStage = CSE_ReviewNext; - }; -} + MWBase::Environment::get().getWindowManager()->popGuiMode(); -void CharacterCreation::onNameDialogDone(WindowBase* parWindow) -{ - if (mNameDialog) - { - mPlayerName = mNameDialog->getTextInput(); - mWM->setValue("name", mPlayerName); - MWBase::Environment::get().getMechanicsManager()->setPlayerName(mPlayerName); - mWM->removeDialog(mNameDialog); - mNameDialog = 0; + switch(parDialog) + { + case ReviewDialog::NAME_DIALOG: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Name); + break; + case ReviewDialog::RACE_DIALOG: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race); + break; + case ReviewDialog::CLASS_DIALOG: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + break; + case ReviewDialog::BIRTHSIGN_DIALOG: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); + }; } - if (mCreationStage == CSE_ReviewNext) - { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Review); - } - else if (mCreationStage >= CSE_NameChosen) + void CharacterCreation::onPickClassDialogDone(WindowBase* parWindow) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Race); - } - else - { - mCreationStage = CSE_NameChosen; - mWM->popGuiMode(); - } -} + if (mPickClassDialog) + { + const std::string &classId = mPickClassDialog->getClassId(); + if (!classId.empty()) + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId); -void CharacterCreation::onRaceDialogBack() -{ - if (mRaceDialog) - { - const ESM::NPC &data = mRaceDialog->getResult(); - mPlayerRaceId = data.mRace; - if (!mPlayerRaceId.empty()) { - MWBase::Environment::get().getMechanicsManager()->setPlayerRace( - data.mRace, - data.isMale(), - data.mHead, - data.mHair - ); + const ESM::Class *klass = + MWBase::Environment::get().getWorld()->getStore().get().find(classId); + if (klass) + { + mPlayerClass = *klass; + MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass); + } + MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog); + mPickClassDialog = 0; } - mWM->removeDialog(mRaceDialog); - mRaceDialog = 0; - } - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Name); -} + //TODO This bit gets repeated a few times; wrap it in a function + if (mCreationStage == CSE_ReviewNext) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); + } + else if (mCreationStage >= CSE_ClassChosen) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); + } + else + { + mCreationStage = CSE_ClassChosen; + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } + } -void CharacterCreation::onRaceDialogDone(WindowBase* parWindow) -{ - if (mRaceDialog) + void CharacterCreation::onPickClassDialogBack() { - const ESM::NPC &data = mRaceDialog->getResult(); - mPlayerRaceId = data.mRace; - if (!mPlayerRaceId.empty()) { - MWBase::Environment::get().getMechanicsManager()->setPlayerRace( - data.mRace, - data.isMale(), - data.mHead, - data.mHair - ); + if (mPickClassDialog) + { + const std::string classId = mPickClassDialog->getClassId(); + if (!classId.empty()) + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId); + MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog); + mPickClassDialog = 0; } - mWM->getInventoryWindow()->rebuildAvatar(); - mWM->removeDialog(mRaceDialog); - mRaceDialog = 0; + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); } - if (mCreationStage == CSE_ReviewNext) + void CharacterCreation::onClassChoice(int _index) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Review); + MWBase::Environment::get().getWindowManager()->removeDialog(mClassChoiceDialog); + mClassChoiceDialog = 0; + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + + switch(_index) + { + case ClassChoiceDialog::Class_Generate: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassGenerate); + break; + case ClassChoiceDialog::Class_Pick: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassPick); + break; + case ClassChoiceDialog::Class_Create: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassCreate); + break; + case ClassChoiceDialog::Class_Back: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race); + break; + + }; } - else if (mCreationStage >= CSE_RaceChosen) + + void CharacterCreation::onNameDialogDone(WindowBase* parWindow) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); + if (mNameDialog) + { + mPlayerName = mNameDialog->getTextInput(); + MWBase::Environment::get().getWindowManager()->setValue("name", mPlayerName); + MWBase::Environment::get().getMechanicsManager()->setPlayerName(mPlayerName); + MWBase::Environment::get().getWindowManager()->removeDialog(mNameDialog); + mNameDialog = 0; + } + + if (mCreationStage == CSE_ReviewNext) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); + } + else if (mCreationStage >= CSE_NameChosen) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race); + } + else + { + mCreationStage = CSE_NameChosen; + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } } - else + + void CharacterCreation::onRaceDialogBack() { - mCreationStage = CSE_RaceChosen; - mWM->popGuiMode(); + if (mRaceDialog) + { + const ESM::NPC &data = mRaceDialog->getResult(); + mPlayerRaceId = data.mRace; + if (!mPlayerRaceId.empty()) { + MWBase::Environment::get().getMechanicsManager()->setPlayerRace( + data.mRace, + data.isMale(), + data.mHead, + data.mHair + ); + } + MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog); + mRaceDialog = 0; + } + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Name); } -} -void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow) -{ - if (mBirthSignDialog) + void CharacterCreation::onRaceDialogDone(WindowBase* parWindow) { - mPlayerBirthSignId = mBirthSignDialog->getBirthId(); - if (!mPlayerBirthSignId.empty()) - MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mPlayerBirthSignId); - mWM->removeDialog(mBirthSignDialog); - mBirthSignDialog = 0; + if (mRaceDialog) + { + const ESM::NPC &data = mRaceDialog->getResult(); + mPlayerRaceId = data.mRace; + if (!mPlayerRaceId.empty()) { + MWBase::Environment::get().getMechanicsManager()->setPlayerRace( + data.mRace, + data.isMale(), + data.mHead, + data.mHair + ); + } + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->rebuildAvatar(); + + MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog); + mRaceDialog = 0; + } + + if (mCreationStage == CSE_ReviewNext) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); + } + else if (mCreationStage >= CSE_RaceChosen) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + } + else + { + mCreationStage = CSE_RaceChosen; + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } } - if (mCreationStage >= CSE_BirthSignChosen) + void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow) { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Review); + if (mBirthSignDialog) + { + mPlayerBirthSignId = mBirthSignDialog->getBirthId(); + if (!mPlayerBirthSignId.empty()) + MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mPlayerBirthSignId); + MWBase::Environment::get().getWindowManager()->removeDialog(mBirthSignDialog); + mBirthSignDialog = 0; + } + + if (mCreationStage >= CSE_BirthSignChosen) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); + } + else + { + mCreationStage = CSE_BirthSignChosen; + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } } - else + + void CharacterCreation::onBirthSignDialogBack() { - mCreationStage = CSE_BirthSignChosen; - mWM->popGuiMode(); + if (mBirthSignDialog) + { + MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mBirthSignDialog->getBirthId()); + MWBase::Environment::get().getWindowManager()->removeDialog(mBirthSignDialog); + mBirthSignDialog = 0; + } + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); } -} -void CharacterCreation::onBirthSignDialogBack() -{ - if (mBirthSignDialog) + void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) { - MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mBirthSignDialog->getBirthId()); - mWM->removeDialog(mBirthSignDialog); - mBirthSignDialog = 0; - } + if (mCreateClassDialog) + { + ESM::Class klass; + klass.mName = mCreateClassDialog->getName(); + klass.mDescription = mCreateClassDialog->getDescription(); + klass.mData.mSpecialization = mCreateClassDialog->getSpecializationId(); + klass.mData.mIsPlayable = 0x1; + + std::vector attributes = mCreateClassDialog->getFavoriteAttributes(); + assert(attributes.size() == 2); + klass.mData.mAttribute[0] = attributes[0]; + klass.mData.mAttribute[1] = attributes[1]; + + std::vector majorSkills = mCreateClassDialog->getMajorSkills(); + std::vector minorSkills = mCreateClassDialog->getMinorSkills(); + assert(majorSkills.size() >= sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0])); + assert(minorSkills.size() >= sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0])); + for (size_t i = 0; i < sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0]); ++i) + { + klass.mData.mSkills[i][1] = majorSkills[i]; + klass.mData.mSkills[i][0] = minorSkills[i]; + } - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); -} + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(klass); + mPlayerClass = klass; + MWBase::Environment::get().getWindowManager()->setPlayerClass(klass); -void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) -{ - if (mCreateClassDialog) - { - ESM::Class klass; - klass.mName = mCreateClassDialog->getName(); - klass.mDescription = mCreateClassDialog->getDescription(); - klass.mData.mSpecialization = mCreateClassDialog->getSpecializationId(); - klass.mData.mIsPlayable = 0x1; - - std::vector attributes = mCreateClassDialog->getFavoriteAttributes(); - assert(attributes.size() == 2); - klass.mData.mAttribute[0] = attributes[0]; - klass.mData.mAttribute[1] = attributes[1]; - - std::vector majorSkills = mCreateClassDialog->getMajorSkills(); - std::vector minorSkills = mCreateClassDialog->getMinorSkills(); - assert(majorSkills.size() >= sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0])); - assert(minorSkills.size() >= sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0])); - for (size_t i = 0; i < sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0]); ++i) - { - klass.mData.mSkills[i][1] = majorSkills[i]; - klass.mData.mSkills[i][0] = minorSkills[i]; + MWBase::Environment::get().getWindowManager()->removeDialog(mCreateClassDialog); + mCreateClassDialog = 0; } - MWBase::Environment::get().getMechanicsManager()->setPlayerClass(klass); - mPlayerClass = klass; - mWM->setPlayerClass(klass); - - mWM->removeDialog(mCreateClassDialog); - mCreateClassDialog = 0; + if (mCreationStage == CSE_ReviewNext) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); + } + else if (mCreationStage >= CSE_ClassChosen) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); + } + else + { + mCreationStage = CSE_ClassChosen; + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } } - if (mCreationStage == CSE_ReviewNext) - { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Review); - } - else if (mCreationStage >= CSE_ClassChosen) - { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Birth); - } - else + void CharacterCreation::onCreateClassDialogBack() { - mCreationStage = CSE_ClassChosen; - mWM->popGuiMode(); - } -} + MWBase::Environment::get().getWindowManager()->removeDialog(mCreateClassDialog); + mCreateClassDialog = 0; -void CharacterCreation::onCreateClassDialogBack() -{ - mWM->removeDialog(mCreateClassDialog); - mCreateClassDialog = 0; + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + } - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); -} + void CharacterCreation::onClassQuestionChosen(int _index) + { + MWBase::Environment::get().getSoundManager()->stopSay(); -void CharacterCreation::onClassQuestionChosen(int _index) -{ - MWBase::Environment::get().getSoundManager()->stopSay(); + MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassQuestionDialog); + mGenerateClassQuestionDialog = 0; - mWM->removeDialog(mGenerateClassQuestionDialog); - mGenerateClassQuestionDialog = 0; + if (_index < 0 || _index >= 3) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + return; + } - if (_index < 0 || _index >= 3) - { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); - return; + ESM::Class::Specialization specialization = mSpecializations[_index]; + if (specialization == ESM::Class::Stealth) + ++mGenerateClassSpecializations[0]; + else if (specialization == ESM::Class::Combat) + ++mGenerateClassSpecializations[1]; + else if (specialization == ESM::Class::Magic) + ++mGenerateClassSpecializations[2]; + ++mGenerateClassStep; + showClassQuestionDialog(); } - ESM::Class::Specialization specialization = sGenerateClassSteps[mGenerateClassStep].mSpecializations[_index]; - if (specialization == ESM::Class::Stealth) - ++mGenerateClassSpecializations[0]; - else if (specialization == ESM::Class::Combat) - ++mGenerateClassSpecializations[1]; - else if (specialization == ESM::Class::Magic) - ++mGenerateClassSpecializations[2]; - ++mGenerateClassStep; - showClassQuestionDialog(); -} - -void CharacterCreation::showClassQuestionDialog() -{ - if (mGenerateClassStep == sGenerateClassSteps.size()) + void CharacterCreation::showClassQuestionDialog() { - static boost::array classes = { { - {"Acrobat", {6, 2, 2}}, - {"Agent", {6, 1, 3}}, - {"Archer", {3, 5, 2}}, - {"Archer", {5, 5, 0}}, - {"Assassin", {6, 3, 1}}, - {"Barbarian", {3, 6, 1}}, - {"Bard", {3, 3, 3}}, - {"Battlemage", {1, 3, 6}}, - {"Crusader", {1, 6, 3}}, - {"Healer", {3, 1, 6}}, - {"Knight", {2, 6, 2}}, - {"Monk", {5, 3, 2}}, - {"Nightblade", {4, 2, 4}}, - {"Pilgrim", {5, 2, 3}}, - {"Rogue", {3, 4, 3}}, - {"Rogue", {4, 4, 2}}, - {"Rogue", {5, 4, 1}}, - {"Scout", {2, 5, 3}}, - {"Sorcerer", {2, 2, 6}}, - {"Spellsword", {2, 4, 4}}, - {"Spellsword", {5, 1, 4}}, - {"Witchhunter", {2, 3, 5}}, - {"Witchhunter", {5, 0, 5}} - } }; - - int match = -1; - for (unsigned i = 0; i < classes.size(); ++i) - { - if (mGenerateClassSpecializations[0] == classes[i].points[0] && - mGenerateClassSpecializations[1] == classes[i].points[1] && - mGenerateClassSpecializations[2] == classes[i].points[2]) + if (mGenerateClassStep == 10) + { + static boost::array classes = { { + {"Acrobat", {6, 2, 2}}, + {"Agent", {6, 1, 3}}, + {"Archer", {3, 5, 2}}, + {"Archer", {5, 5, 0}}, + {"Assassin", {6, 3, 1}}, + {"Barbarian", {3, 6, 1}}, + {"Bard", {3, 3, 3}}, + {"Battlemage", {1, 3, 6}}, + {"Crusader", {1, 6, 3}}, + {"Healer", {3, 1, 6}}, + {"Knight", {2, 6, 2}}, + {"Monk", {5, 3, 2}}, + {"Nightblade", {4, 2, 4}}, + {"Pilgrim", {5, 2, 3}}, + {"Rogue", {3, 4, 3}}, + {"Rogue", {4, 4, 2}}, + {"Rogue", {5, 4, 1}}, + {"Scout", {2, 5, 3}}, + {"Sorcerer", {2, 2, 6}}, + {"Spellsword", {2, 4, 4}}, + {"Spellsword", {5, 1, 4}}, + {"Witchhunter", {2, 3, 5}}, + {"Witchhunter", {5, 0, 5}} + } }; + + int match = -1; + for (unsigned i = 0; i < classes.size(); ++i) { - match = i; - mGenerateClass = classes[i].id; - break; + if (mGenerateClassSpecializations[0] == classes[i].points[0] && + mGenerateClassSpecializations[1] == classes[i].points[1] && + mGenerateClassSpecializations[2] == classes[i].points[2]) + { + match = i; + mGenerateClass = classes[i].id; + break; + } } - } - if (match == -1) - { - if (mGenerateClassSpecializations[0] >= 7) - mGenerateClass = "Thief"; - else if (mGenerateClassSpecializations[1] >= 7) - mGenerateClass = "Warrior"; - else if (mGenerateClassSpecializations[2] >= 7) - mGenerateClass = "Mage"; - else + if (match == -1) { - std::cerr << "Failed to deduce class from chosen answers in generate class dialog" << std::endl; - mGenerateClass = "Thief"; + if (mGenerateClassSpecializations[0] >= 7) + mGenerateClass = "Thief"; + else if (mGenerateClassSpecializations[1] >= 7) + mGenerateClass = "Warrior"; + else if (mGenerateClassSpecializations[2] >= 7) + mGenerateClass = "Mage"; + else + { + std::cerr << "Failed to deduce class from chosen answers in generate class dialog" << std::endl; + mGenerateClass = "Thief"; + } } - } - mWM->removeDialog(mGenerateClassResultDialog); - mGenerateClassResultDialog = 0; - - mGenerateClassResultDialog = new GenerateClassResultDialog(*mWM); - mGenerateClassResultDialog->setClassId(mGenerateClass); - mGenerateClassResultDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassBack); - mGenerateClassResultDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassDone); - mGenerateClassResultDialog->setVisible(true); - return; - } + MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog); + mGenerateClassResultDialog = 0; - if (mGenerateClassStep > sGenerateClassSteps.size()) - { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); - return; - } + mGenerateClassResultDialog = new GenerateClassResultDialog(); + mGenerateClassResultDialog->setClassId(mGenerateClass); + mGenerateClassResultDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassBack); + mGenerateClassResultDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassDone); + mGenerateClassResultDialog->setVisible(true); + return; + } - mWM->removeDialog(mGenerateClassQuestionDialog); - mGenerateClassQuestionDialog = 0; + if (mGenerateClassStep > 10) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + return; + } - mGenerateClassQuestionDialog = new InfoBoxDialog(*mWM); + MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassQuestionDialog); + mGenerateClassQuestionDialog = 0; - InfoBoxDialog::ButtonList buttons; - mGenerateClassQuestionDialog->setText(sGenerateClassSteps[mGenerateClassStep].mText); - buttons.push_back(sGenerateClassSteps[mGenerateClassStep].mButtons[0]); - buttons.push_back(sGenerateClassSteps[mGenerateClassStep].mButtons[1]); - buttons.push_back(sGenerateClassSteps[mGenerateClassStep].mButtons[2]); - mGenerateClassQuestionDialog->setButtons(buttons); - mGenerateClassQuestionDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassQuestionChosen); - mGenerateClassQuestionDialog->setVisible(true); + mGenerateClassQuestionDialog = new InfoBoxDialog(); - MWBase::Environment::get().getSoundManager()->say(sGenerateClassSteps[mGenerateClassStep].mSound); -} + InfoBoxDialog::ButtonList buttons; + mGenerateClassQuestionDialog->setText(sGenerateClassSteps(mGenerateClassStep).mText); + buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[0]); + buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[1]); + buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[2]); + mGenerateClassQuestionDialog->setButtons(buttons); + mGenerateClassQuestionDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassQuestionChosen); + mGenerateClassQuestionDialog->setVisible(true); -void CharacterCreation::onGenerateClassBack() -{ - if(mCreationStage < CSE_ClassChosen) - mCreationStage = CSE_ClassChosen; + MWBase::Environment::get().getSoundManager()->say(sGenerateClassSteps(mGenerateClassStep).mSound); + } - mWM->removeDialog(mGenerateClassResultDialog); - mGenerateClassResultDialog = 0; + void CharacterCreation::onGenerateClassBack() + { + MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog); + mGenerateClassResultDialog = 0; - MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Class); -} + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + } -void CharacterCreation::onGenerateClassDone(WindowBase* parWindow) -{ - mWM->removeDialog(mGenerateClassResultDialog); - mGenerateClassResultDialog = 0; + void CharacterCreation::onGenerateClassDone(WindowBase* parWindow) + { + MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog); + mGenerateClassResultDialog = 0; - MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); - const ESM::Class *klass = - MWBase::Environment::get().getWorld()->getStore().get().find(mGenerateClass); + const ESM::Class *klass = + MWBase::Environment::get().getWorld()->getStore().get().find(mGenerateClass); - mPlayerClass = *klass; - mWM->setPlayerClass(mPlayerClass); + mPlayerClass = *klass; + MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass); - if (mCreationStage == CSE_ReviewNext) - { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Review); - } - else if (mCreationStage >= CSE_ClassChosen) - { - mWM->popGuiMode(); - mWM->pushGuiMode(GM_Birth); + if (mCreationStage == CSE_ReviewNext) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); + } + else if (mCreationStage >= CSE_ClassChosen) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); + } + else + { + mCreationStage = CSE_ClassChosen; + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } } - else - { - mCreationStage = CSE_ClassChosen; - mWM->popGuiMode(); + + CharacterCreation::~CharacterCreation() + { + delete mNameDialog; + delete mRaceDialog; + delete mClassChoiceDialog; + delete mGenerateClassQuestionDialog; + delete mGenerateClassResultDialog; + delete mPickClassDialog; + delete mCreateClassDialog; + delete mBirthSignDialog; + delete mReviewDialog; } -} -CharacterCreation::~CharacterCreation() -{ - delete mNameDialog; - delete mRaceDialog; - delete mClassChoiceDialog; - delete mGenerateClassQuestionDialog; - delete mGenerateClassResultDialog; - delete mPickClassDialog; - delete mCreateClassDialog; - delete mBirthSignDialog; - delete mReviewDialog; } diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index 9653aeede..586faf966 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -1,13 +1,9 @@ #ifndef CHARACTER_CREATION_HPP #define CHARACTER_CREATION_HPP -#include "../mwworld/esmstore.hpp" - #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwmechanics/stat.hpp" - namespace MWGui { class WindowBase; @@ -29,7 +25,7 @@ namespace MWGui public: typedef std::vector SkillList; - CharacterCreation(MWBase::WindowManager* _wm); + CharacterCreation(); ~CharacterCreation(); //Show a dialog @@ -58,8 +54,6 @@ namespace MWGui BirthDialog* mBirthSignDialog; ReviewDialog* mReviewDialog; - MWBase::WindowManager* mWM; - //Player data std::string mPlayerName; std::string mPlayerRaceId; diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index c14c7f74b..ab8103868 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -1,11 +1,6 @@ #include "class.hpp" -#include - #include -#include - -#include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -16,873 +11,873 @@ #undef min #undef max -using namespace MWGui; +namespace MWGui +{ -/* GenerateClassResultDialog */ + /* GenerateClassResultDialog */ -GenerateClassResultDialog::GenerateClassResultDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_chargen_generate_class_result.layout", parWindowManager) -{ - // Centre dialog - center(); + GenerateClassResultDialog::GenerateClassResultDialog() + : WindowModal("openmw_chargen_generate_class_result.layout") + { + // Centre dialog + center(); - setText("ReflectT", mWindowManager.getGameSettingString("sMessageQuestionAnswer1", "")); + setText("ReflectT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sMessageQuestionAnswer1", "")); - getWidget(mClassImage, "ClassImage"); - getWidget(mClassName, "ClassName"); + getWidget(mClassImage, "ClassImage"); + getWidget(mClassName, "ClassName"); - MyGUI::ButtonPtr backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onBackClicked); + MyGUI::Button* backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onBackClicked); - MyGUI::ButtonPtr okButton; - getWidget(okButton, "OKButton"); - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked); -} + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked); + } -std::string GenerateClassResultDialog::getClassId() const -{ - return mClassName->getCaption(); -} + std::string GenerateClassResultDialog::getClassId() const + { + return mClassName->getCaption(); + } -void GenerateClassResultDialog::setClassId(const std::string &classId) -{ - mCurrentClassId = classId; - mClassImage->setImageTexture(std::string("textures\\levelup\\") + mCurrentClassId + ".dds"); - mClassName->setCaption(MWBase::Environment::get().getWorld()->getStore().get().find(mCurrentClassId)->mName); -} + void GenerateClassResultDialog::setClassId(const std::string &classId) + { + mCurrentClassId = classId; + mClassImage->setImageTexture(std::string("textures\\levelup\\") + mCurrentClassId + ".dds"); + mClassName->setCaption(MWBase::Environment::get().getWorld()->getStore().get().find(mCurrentClassId)->mName); + } -// widget controls + // widget controls -void GenerateClassResultDialog::onOkClicked(MyGUI::Widget* _sender) -{ - eventDone(this); -} + void GenerateClassResultDialog::onOkClicked(MyGUI::Widget* _sender) + { + eventDone(this); + } -void GenerateClassResultDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} + void GenerateClassResultDialog::onBackClicked(MyGUI::Widget* _sender) + { + eventBack(); + } -/* PickClassDialog */ + /* PickClassDialog */ -PickClassDialog::PickClassDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_chargen_class.layout", parWindowManager) -{ - // Centre dialog - center(); + PickClassDialog::PickClassDialog() + : WindowModal("openmw_chargen_class.layout") + { + // Centre dialog + center(); - getWidget(mSpecializationName, "SpecializationName"); + getWidget(mSpecializationName, "SpecializationName"); - getWidget(mFavoriteAttribute[0], "FavoriteAttribute0"); - getWidget(mFavoriteAttribute[1], "FavoriteAttribute1"); - mFavoriteAttribute[0]->setWindowManager(&mWindowManager); - mFavoriteAttribute[1]->setWindowManager(&mWindowManager); + getWidget(mFavoriteAttribute[0], "FavoriteAttribute0"); + getWidget(mFavoriteAttribute[1], "FavoriteAttribute1"); - for(int i = 0; i < 5; i++) - { - char theIndex = '0'+i; - getWidget(mMajorSkill[i], std::string("MajorSkill").append(1, theIndex)); - getWidget(mMinorSkill[i], std::string("MinorSkill").append(1, theIndex)); - mMajorSkill[i]->setWindowManager(&mWindowManager); - mMinorSkill[i]->setWindowManager(&mWindowManager); - } + for(int i = 0; i < 5; i++) + { + char theIndex = '0'+i; + getWidget(mMajorSkill[i], std::string("MajorSkill").append(1, theIndex)); + getWidget(mMinorSkill[i], std::string("MinorSkill").append(1, theIndex)); + } - getWidget(mClassList, "ClassList"); - mClassList->setScrollVisible(true); - mClassList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); - mClassList->eventListMouseItemActivate += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); - mClassList->eventListChangePosition += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); + getWidget(mClassList, "ClassList"); + mClassList->setScrollVisible(true); + mClassList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); + mClassList->eventListMouseItemActivate += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); + mClassList->eventListChangePosition += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); - getWidget(mClassImage, "ClassImage"); + getWidget(mClassImage, "ClassImage"); - MyGUI::ButtonPtr backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onBackClicked); + MyGUI::Button* backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onBackClicked); - MyGUI::ButtonPtr okButton; - getWidget(okButton, "OKButton"); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onOkClicked); - okButton->setEnabled(false); + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onOkClicked); - updateClasses(); - updateStats(); -} + updateClasses(); + updateStats(); + } -void PickClassDialog::setNextButtonShow(bool shown) -{ - MyGUI::ButtonPtr okButton; - getWidget(okButton, "OKButton"); + void PickClassDialog::setNextButtonShow(bool shown) + { + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); - if (shown) - okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); - else - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); -} + if (shown) + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); + else + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + } -void PickClassDialog::open() -{ - updateClasses(); - updateStats(); -} + void PickClassDialog::open() + { + WindowModal::open (); + updateClasses(); + updateStats(); + } -void PickClassDialog::setClassId(const std::string &classId) -{ - mCurrentClassId = classId; - mClassList->setIndexSelected(MyGUI::ITEM_NONE); - size_t count = mClassList->getItemCount(); - for (size_t i = 0; i < count; ++i) + void PickClassDialog::setClassId(const std::string &classId) { - if (boost::iequals(*mClassList->getItemDataAt(i), classId)) + mCurrentClassId = classId; + mClassList->setIndexSelected(MyGUI::ITEM_NONE); + size_t count = mClassList->getItemCount(); + for (size_t i = 0; i < count; ++i) { - mClassList->setIndexSelected(i); - MyGUI::ButtonPtr okButton; - getWidget(okButton, "OKButton"); - okButton->setEnabled(true); - break; + if (boost::iequals(*mClassList->getItemDataAt(i), classId)) + { + mClassList->setIndexSelected(i); + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + break; + } } + + updateStats(); } - updateStats(); -} + // widget controls -// widget controls + void PickClassDialog::onOkClicked(MyGUI::Widget* _sender) + { + if(mClassList->getIndexSelected() == MyGUI::ITEM_NONE) + return; + eventDone(this); + } -void PickClassDialog::onOkClicked(MyGUI::Widget* _sender) -{ - if(mClassList->getIndexSelected() == MyGUI::ITEM_NONE) - return; - eventDone(this); -} + void PickClassDialog::onBackClicked(MyGUI::Widget* _sender) + { + eventBack(); + } -void PickClassDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} + void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index) + { + if (_index == MyGUI::ITEM_NONE) + return; -void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index) -{ - if (_index == MyGUI::ITEM_NONE) - return; + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); - MyGUI::ButtonPtr okButton; - getWidget(okButton, "OKButton"); - okButton->setEnabled(true); + const std::string *classId = mClassList->getItemDataAt(_index); + if (boost::iequals(mCurrentClassId, *classId)) + return; - const std::string *classId = mClassList->getItemDataAt(_index); - if (boost::iequals(mCurrentClassId, *classId)) - return; + mCurrentClassId = *classId; + updateStats(); + } - mCurrentClassId = *classId; - updateStats(); -} + // update widget content -// update widget content + void PickClassDialog::updateClasses() + { + mClassList->removeAllItems(); -void PickClassDialog::updateClasses() -{ - mClassList->removeAllItems(); + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + int index = 0; + MWWorld::Store::iterator it = store.get().begin(); + for (; it != store.get().end(); ++it) + { + bool playable = (it->mData.mIsPlayable != 0); + if (!playable) // Only display playable classes + continue; + + const std::string &id = it->mId; + mClassList->addItem(it->mName, id); + if (mCurrentClassId.empty()) + { + mCurrentClassId = id; + mClassList->setIndexSelected(index); + } + else if (boost::iequals(id, mCurrentClassId)) + { + mClassList->setIndexSelected(index); + } + ++index; + } + } - int index = 0; - MWWorld::Store::iterator it = store.get().begin(); - for (; it != store.get().end(); ++it) + void PickClassDialog::updateStats() { - bool playable = (it->mData.mIsPlayable != 0); - if (!playable) // Only display playable classes - continue; + if (mCurrentClassId.empty()) + return; + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Class *klass = store.get().search(mCurrentClassId); + if (!klass) + return; - const std::string &id = it->mId; - mClassList->addItem(it->mName, id); - if (boost::iequals(id, mCurrentClassId)) - mClassList->setIndexSelected(index); - ++index; - } -} + ESM::Class::Specialization specialization = static_cast(klass->mData.mSpecialization); -void PickClassDialog::updateStats() -{ - if (mCurrentClassId.empty()) - return; - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - const ESM::Class *klass = store.get().search(mCurrentClassId); - if (!klass) - return; - - ESM::Class::Specialization specialization = static_cast(klass->mData.mSpecialization); - - static const char *specIds[3] = { - "sSpecializationCombat", - "sSpecializationMagic", - "sSpecializationStealth" - }; - std::string specName = mWindowManager.getGameSettingString(specIds[specialization], specIds[specialization]); - mSpecializationName->setCaption(specName); - ToolTips::createSpecializationToolTip(mSpecializationName, specName, specialization); - - mFavoriteAttribute[0]->setAttributeId(klass->mData.mAttribute[0]); - mFavoriteAttribute[1]->setAttributeId(klass->mData.mAttribute[1]); - ToolTips::createAttributeToolTip(mFavoriteAttribute[0], mFavoriteAttribute[0]->getAttributeId()); - ToolTips::createAttributeToolTip(mFavoriteAttribute[1], mFavoriteAttribute[1]->getAttributeId()); - - for (int i = 0; i < 5; ++i) - { - mMinorSkill[i]->setSkillNumber(klass->mData.mSkills[i][0]); - mMajorSkill[i]->setSkillNumber(klass->mData.mSkills[i][1]); - ToolTips::createSkillToolTip(mMinorSkill[i], klass->mData.mSkills[i][0]); - ToolTips::createSkillToolTip(mMajorSkill[i], klass->mData.mSkills[i][1]); - } - - mClassImage->setImageTexture(std::string("textures\\levelup\\") + mCurrentClassId + ".dds"); -} + static const char *specIds[3] = { + "sSpecializationCombat", + "sSpecializationMagic", + "sSpecializationStealth" + }; + std::string specName = MWBase::Environment::get().getWindowManager()->getGameSettingString(specIds[specialization], specIds[specialization]); + mSpecializationName->setCaption(specName); + ToolTips::createSpecializationToolTip(mSpecializationName, specName, specialization); -/* InfoBoxDialog */ + mFavoriteAttribute[0]->setAttributeId(klass->mData.mAttribute[0]); + mFavoriteAttribute[1]->setAttributeId(klass->mData.mAttribute[1]); + ToolTips::createAttributeToolTip(mFavoriteAttribute[0], mFavoriteAttribute[0]->getAttributeId()); + ToolTips::createAttributeToolTip(mFavoriteAttribute[1], mFavoriteAttribute[1]->getAttributeId()); -void InfoBoxDialog::fitToText(MyGUI::TextBox* widget) -{ - MyGUI::IntCoord inner = widget->getTextRegion(); - MyGUI::IntCoord outer = widget->getCoord(); - MyGUI::IntSize size = widget->getTextSize(); - size.width += outer.width - inner.width; - size.height += outer.height - inner.height; - widget->setSize(size); -} + for (int i = 0; i < 5; ++i) + { + mMinorSkill[i]->setSkillNumber(klass->mData.mSkills[i][0]); + mMajorSkill[i]->setSkillNumber(klass->mData.mSkills[i][1]); + ToolTips::createSkillToolTip(mMinorSkill[i], klass->mData.mSkills[i][0]); + ToolTips::createSkillToolTip(mMajorSkill[i], klass->mData.mSkills[i][1]); + } -void InfoBoxDialog::layoutVertically(MyGUI::WidgetPtr widget, int margin) -{ - size_t count = widget->getChildCount(); - int pos = 0; - pos += margin; - int width = 0; - for (unsigned i = 0; i < count; ++i) - { - MyGUI::WidgetPtr child = widget->getChildAt(i); - if (!child->getVisible()) - continue; - - child->setPosition(child->getLeft(), pos); - width = std::max(width, child->getWidth()); - pos += child->getHeight() + margin; - } - width += margin*2; - widget->setSize(width, pos); -} + mClassImage->setImageTexture(std::string("textures\\levelup\\") + mCurrentClassId + ".dds"); + } -InfoBoxDialog::InfoBoxDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_infobox.layout", parWindowManager) - , mCurrentButton(-1) -{ - getWidget(mTextBox, "TextBox"); - getWidget(mText, "Text"); - mText->getSubWidgetText()->setWordWrap(true); - getWidget(mButtonBar, "ButtonBar"); + /* InfoBoxDialog */ - center(); -} + void InfoBoxDialog::fitToText(MyGUI::TextBox* widget) + { + MyGUI::IntCoord inner = widget->getTextRegion(); + MyGUI::IntCoord outer = widget->getCoord(); + MyGUI::IntSize size = widget->getTextSize(); + size.width += outer.width - inner.width; + size.height += outer.height - inner.height; + widget->setSize(size); + } -void InfoBoxDialog::setText(const std::string &str) -{ - mText->setCaption(str); - mTextBox->setVisible(!str.empty()); - fitToText(mText); -} + void InfoBoxDialog::layoutVertically(MyGUI::Widget* widget, int margin) + { + size_t count = widget->getChildCount(); + int pos = 0; + pos += margin; + int width = 0; + for (unsigned i = 0; i < count; ++i) + { + MyGUI::Widget* child = widget->getChildAt(i); + if (!child->getVisible()) + continue; -std::string InfoBoxDialog::getText() const -{ - return mText->getCaption(); -} + child->setPosition(child->getLeft(), pos); + width = std::max(width, child->getWidth()); + pos += child->getHeight() + margin; + } + width += margin*2; + widget->setSize(width, pos); + } -void InfoBoxDialog::setButtons(ButtonList &buttons) -{ - for (std::vector::iterator it = this->mButtons.begin(); it != this->mButtons.end(); ++it) + InfoBoxDialog::InfoBoxDialog() + : WindowModal("openmw_infobox.layout") + , mCurrentButton(-1) { - MyGUI::Gui::getInstance().destroyWidget(*it); + getWidget(mTextBox, "TextBox"); + getWidget(mText, "Text"); + mText->getSubWidgetText()->setWordWrap(true); + getWidget(mButtonBar, "ButtonBar"); + + center(); } - this->mButtons.clear(); - mCurrentButton = -1; - // TODO: The buttons should be generated from a template in the layout file, ie. cloning an existing widget - MyGUI::ButtonPtr button; - MyGUI::IntCoord coord = MyGUI::IntCoord(0, 0, mButtonBar->getWidth(), 10); - ButtonList::const_iterator end = buttons.end(); - for (ButtonList::const_iterator it = buttons.begin(); it != end; ++it) + void InfoBoxDialog::setText(const std::string &str) { - const std::string &text = *it; - button = mButtonBar->createWidget("MW_Button", coord, MyGUI::Align::Top | MyGUI::Align::HCenter, ""); - button->getSubWidgetText()->setWordWrap(true); - button->setCaption(text); - fitToText(button); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &InfoBoxDialog::onButtonClicked); - coord.top += button->getHeight(); - this->mButtons.push_back(button); + mText->setCaption(str); + mTextBox->setVisible(!str.empty()); + fitToText(mText); } -} -void InfoBoxDialog::open() -{ - // Fix layout - layoutVertically(mTextBox, 4); - layoutVertically(mButtonBar, 6); - layoutVertically(mMainWidget, 4 + 6); - - center(); -} - -int InfoBoxDialog::getChosenButton() const -{ - return mCurrentButton; -} + std::string InfoBoxDialog::getText() const + { + return mText->getCaption(); + } -void InfoBoxDialog::onButtonClicked(MyGUI::WidgetPtr _sender) -{ - std::vector::const_iterator end = mButtons.end(); - int i = 0; - for (std::vector::const_iterator it = mButtons.begin(); it != end; ++it) + void InfoBoxDialog::setButtons(ButtonList &buttons) { - if (*it == _sender) + for (std::vector::iterator it = this->mButtons.begin(); it != this->mButtons.end(); ++it) { - mCurrentButton = i; - eventButtonSelected(i); - return; + MyGUI::Gui::getInstance().destroyWidget(*it); + } + this->mButtons.clear(); + mCurrentButton = -1; + + // TODO: The buttons should be generated from a template in the layout file, ie. cloning an existing widget + MyGUI::Button* button; + MyGUI::IntCoord coord = MyGUI::IntCoord(0, 0, mButtonBar->getWidth(), 10); + ButtonList::const_iterator end = buttons.end(); + for (ButtonList::const_iterator it = buttons.begin(); it != end; ++it) + { + const std::string &text = *it; + button = mButtonBar->createWidget("MW_Button", coord, MyGUI::Align::Top | MyGUI::Align::HCenter, ""); + button->getSubWidgetText()->setWordWrap(true); + button->setCaption(text); + fitToText(button); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &InfoBoxDialog::onButtonClicked); + coord.top += button->getHeight(); + this->mButtons.push_back(button); } - ++i; } -} -/* ClassChoiceDialog */ - -ClassChoiceDialog::ClassChoiceDialog(MWBase::WindowManager& parWindowManager) - : InfoBoxDialog(parWindowManager) -{ - setText(""); - ButtonList buttons; - buttons.push_back(mWindowManager.getGameSettingString("sClassChoiceMenu1", "")); - buttons.push_back(mWindowManager.getGameSettingString("sClassChoiceMenu2", "")); - buttons.push_back(mWindowManager.getGameSettingString("sClassChoiceMenu3", "")); - buttons.push_back(mWindowManager.getGameSettingString("sBack", "")); - setButtons(buttons); -} + void InfoBoxDialog::open() + { + WindowModal::open(); + // Fix layout + layoutVertically(mTextBox, 4); + layoutVertically(mButtonBar, 6); + layoutVertically(mMainWidget, 4 + 6); -/* CreateClassDialog */ + center(); + } -CreateClassDialog::CreateClassDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_chargen_create_class.layout", parWindowManager) - , mSpecDialog(nullptr) - , mAttribDialog(nullptr) - , mSkillDialog(nullptr) - , mDescDialog(nullptr) -{ - // Centre dialog - center(); + int InfoBoxDialog::getChosenButton() const + { + return mCurrentButton; + } - setText("SpecializationT", mWindowManager.getGameSettingString("sChooseClassMenu1", "Specialization")); - getWidget(mSpecializationName, "SpecializationName"); - mSpecializationName->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationClicked); + void InfoBoxDialog::onButtonClicked(MyGUI::Widget* _sender) + { + std::vector::const_iterator end = mButtons.end(); + int i = 0; + for (std::vector::const_iterator it = mButtons.begin(); it != end; ++it) + { + if (*it == _sender) + { + mCurrentButton = i; + eventButtonSelected(i); + return; + } + ++i; + } + } - setText("FavoriteAttributesT", mWindowManager.getGameSettingString("sChooseClassMenu2", "Favorite Attributes:")); - getWidget(mFavoriteAttribute0, "FavoriteAttribute0"); - getWidget(mFavoriteAttribute1, "FavoriteAttribute1"); - mFavoriteAttribute0->setWindowManager(&mWindowManager); - mFavoriteAttribute1->setWindowManager(&mWindowManager); - mFavoriteAttribute0->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked); - mFavoriteAttribute1->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked); + /* ClassChoiceDialog */ - setText("MajorSkillT", mWindowManager.getGameSettingString("sSkillClassMajor", "")); - setText("MinorSkillT", mWindowManager.getGameSettingString("sSkillClassMinor", "")); - for(int i = 0; i < 5; i++) + ClassChoiceDialog::ClassChoiceDialog() + : InfoBoxDialog() { - char theIndex = '0'+i; - getWidget(mMajorSkill[i], std::string("MajorSkill").append(1, theIndex)); - getWidget(mMinorSkill[i], std::string("MinorSkill").append(1, theIndex)); - mSkills.push_back(mMajorSkill[i]); - mSkills.push_back(mMinorSkill[i]); + setText(""); + ButtonList buttons; + buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString("sClassChoiceMenu1", "")); + buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString("sClassChoiceMenu2", "")); + buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString("sClassChoiceMenu3", "")); + buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString("sBack", "")); + setButtons(buttons); } - std::vector::const_iterator end = mSkills.end(); - for (std::vector::const_iterator it = mSkills.begin(); it != end; ++it) + /* CreateClassDialog */ + + CreateClassDialog::CreateClassDialog() + : WindowModal("openmw_chargen_create_class.layout") + , mSpecDialog(NULL) + , mAttribDialog(NULL) + , mSkillDialog(NULL) + , mDescDialog(NULL) { - (*it)->setWindowManager(&mWindowManager); - (*it)->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onSkillClicked); - } + // Centre dialog + center(); + + setText("SpecializationT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sChooseClassMenu1", "Specialization")); + getWidget(mSpecializationName, "SpecializationName"); + mSpecializationName->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationClicked); + + setText("FavoriteAttributesT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sChooseClassMenu2", "Favorite Attributes:")); + getWidget(mFavoriteAttribute0, "FavoriteAttribute0"); + getWidget(mFavoriteAttribute1, "FavoriteAttribute1"); + mFavoriteAttribute0->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked); + mFavoriteAttribute1->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked); + + setText("MajorSkillT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSkillClassMajor", "")); + setText("MinorSkillT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSkillClassMinor", "")); + for(int i = 0; i < 5; i++) + { + char theIndex = '0'+i; + getWidget(mMajorSkill[i], std::string("MajorSkill").append(1, theIndex)); + getWidget(mMinorSkill[i], std::string("MinorSkill").append(1, theIndex)); + mSkills.push_back(mMajorSkill[i]); + mSkills.push_back(mMinorSkill[i]); + } - setText("LabelT", mWindowManager.getGameSettingString("sName", "")); - getWidget(mEditName, "EditName"); + std::vector::const_iterator end = mSkills.end(); + for (std::vector::const_iterator it = mSkills.begin(); it != end; ++it) + { + (*it)->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onSkillClicked); + } - // Make sure the edit box has focus - MyGUI::InputManager::getInstance().setKeyFocusWidget(mEditName); + setText("LabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sName", "")); + getWidget(mEditName, "EditName"); - MyGUI::ButtonPtr descriptionButton; - getWidget(descriptionButton, "DescriptionButton"); - descriptionButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionClicked); + // Make sure the edit box has focus + MyGUI::InputManager::getInstance().setKeyFocusWidget(mEditName); - MyGUI::ButtonPtr backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onBackClicked); + MyGUI::Button* descriptionButton; + getWidget(descriptionButton, "DescriptionButton"); + descriptionButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionClicked); - MyGUI::ButtonPtr okButton; - getWidget(okButton, "OKButton"); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onOkClicked); + MyGUI::Button* backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onBackClicked); - // Set default skills, attributes + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onOkClicked); - mFavoriteAttribute0->setAttributeId(ESM::Attribute::Strength); - mFavoriteAttribute1->setAttributeId(ESM::Attribute::Agility); + // Set default skills, attributes - mMajorSkill[0]->setSkillId(ESM::Skill::Block); - mMajorSkill[1]->setSkillId(ESM::Skill::Armorer); - mMajorSkill[2]->setSkillId(ESM::Skill::MediumArmor); - mMajorSkill[3]->setSkillId(ESM::Skill::HeavyArmor); - mMajorSkill[4]->setSkillId(ESM::Skill::BluntWeapon); + mFavoriteAttribute0->setAttributeId(ESM::Attribute::Strength); + mFavoriteAttribute1->setAttributeId(ESM::Attribute::Agility); - mMinorSkill[0]->setSkillId(ESM::Skill::LongBlade); - mMinorSkill[1]->setSkillId(ESM::Skill::Axe); - mMinorSkill[2]->setSkillId(ESM::Skill::Spear); - mMinorSkill[3]->setSkillId(ESM::Skill::Athletics); - mMinorSkill[4]->setSkillId(ESM::Skill::Enchant); + mMajorSkill[0]->setSkillId(ESM::Skill::Block); + mMajorSkill[1]->setSkillId(ESM::Skill::Armorer); + mMajorSkill[2]->setSkillId(ESM::Skill::MediumArmor); + mMajorSkill[3]->setSkillId(ESM::Skill::HeavyArmor); + mMajorSkill[4]->setSkillId(ESM::Skill::BluntWeapon); - setSpecialization(0); - update(); -} + mMinorSkill[0]->setSkillId(ESM::Skill::LongBlade); + mMinorSkill[1]->setSkillId(ESM::Skill::Axe); + mMinorSkill[2]->setSkillId(ESM::Skill::Spear); + mMinorSkill[3]->setSkillId(ESM::Skill::Athletics); + mMinorSkill[4]->setSkillId(ESM::Skill::Enchant); -CreateClassDialog::~CreateClassDialog() -{ - delete mSpecDialog; - delete mAttribDialog; - delete mSkillDialog; - delete mDescDialog; -} + setSpecialization(0); + update(); + } -void CreateClassDialog::update() -{ - for (int i = 0; i < 5; ++i) + CreateClassDialog::~CreateClassDialog() { - ToolTips::createSkillToolTip(mMajorSkill[i], mMajorSkill[i]->getSkillId()); - ToolTips::createSkillToolTip(mMinorSkill[i], mMinorSkill[i]->getSkillId()); + delete mSpecDialog; + delete mAttribDialog; + delete mSkillDialog; + delete mDescDialog; } - ToolTips::createAttributeToolTip(mFavoriteAttribute0, mFavoriteAttribute0->getAttributeId()); - ToolTips::createAttributeToolTip(mFavoriteAttribute1, mFavoriteAttribute1->getAttributeId()); -} - -std::string CreateClassDialog::getName() const -{ - return mEditName->getOnlyText(); -} + void CreateClassDialog::update() + { + for (int i = 0; i < 5; ++i) + { + ToolTips::createSkillToolTip(mMajorSkill[i], mMajorSkill[i]->getSkillId()); + ToolTips::createSkillToolTip(mMinorSkill[i], mMinorSkill[i]->getSkillId()); + } -std::string CreateClassDialog::getDescription() const -{ - return mDescription; -} + ToolTips::createAttributeToolTip(mFavoriteAttribute0, mFavoriteAttribute0->getAttributeId()); + ToolTips::createAttributeToolTip(mFavoriteAttribute1, mFavoriteAttribute1->getAttributeId()); + } -ESM::Class::Specialization CreateClassDialog::getSpecializationId() const -{ - return mSpecializationId; -} + std::string CreateClassDialog::getName() const + { + return mEditName->getOnlyText(); + } -std::vector CreateClassDialog::getFavoriteAttributes() const -{ - std::vector v; - v.push_back(mFavoriteAttribute0->getAttributeId()); - v.push_back(mFavoriteAttribute1->getAttributeId()); - return v; -} + std::string CreateClassDialog::getDescription() const + { + return mDescription; + } -std::vector CreateClassDialog::getMajorSkills() const -{ - std::vector v; - for(int i = 0; i < 5; i++) + ESM::Class::Specialization CreateClassDialog::getSpecializationId() const { - v.push_back(mMajorSkill[i]->getSkillId()); + return mSpecializationId; } - return v; -} -std::vector CreateClassDialog::getMinorSkills() const -{ - std::vector v; - for(int i=0; i < 5; i++) + std::vector CreateClassDialog::getFavoriteAttributes() const { - v.push_back(mMinorSkill[i]->getSkillId()); + std::vector v; + v.push_back(mFavoriteAttribute0->getAttributeId()); + v.push_back(mFavoriteAttribute1->getAttributeId()); + return v; } - return v; -} -void CreateClassDialog::setNextButtonShow(bool shown) -{ - MyGUI::ButtonPtr okButton; - getWidget(okButton, "OKButton"); + std::vector CreateClassDialog::getMajorSkills() const + { + std::vector v; + for(int i = 0; i < 5; i++) + { + v.push_back(mMajorSkill[i]->getSkillId()); + } + return v; + } - if (shown) - okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); - else - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); -} + std::vector CreateClassDialog::getMinorSkills() const + { + std::vector v; + for(int i=0; i < 5; i++) + { + v.push_back(mMinorSkill[i]->getSkillId()); + } + return v; + } -// widget controls + void CreateClassDialog::setNextButtonShow(bool shown) + { + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); -void CreateClassDialog::onDialogCancel() -{ - mWindowManager.removeDialog(mSpecDialog); - mSpecDialog = 0; + if (shown) + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); + else + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + } - mWindowManager.removeDialog(mAttribDialog); - mAttribDialog = 0; + // widget controls - mWindowManager.removeDialog(mSkillDialog); - mSkillDialog = 0; + void CreateClassDialog::onDialogCancel() + { + MWBase::Environment::get().getWindowManager()->removeDialog(mSpecDialog); + mSpecDialog = 0; - mWindowManager.removeDialog(mDescDialog); - mDescDialog = 0; -} + MWBase::Environment::get().getWindowManager()->removeDialog(mAttribDialog); + mAttribDialog = 0; -void CreateClassDialog::onSpecializationClicked(MyGUI::WidgetPtr _sender) -{ - delete mSpecDialog; - mSpecDialog = new SelectSpecializationDialog(mWindowManager); - mSpecDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); - mSpecDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationSelected); - mSpecDialog->setVisible(true); -} + MWBase::Environment::get().getWindowManager()->removeDialog(mSkillDialog); + mSkillDialog = 0; -void CreateClassDialog::onSpecializationSelected() -{ - mSpecializationId = mSpecDialog->getSpecializationId(); - setSpecialization(mSpecializationId); + MWBase::Environment::get().getWindowManager()->removeDialog(mDescDialog); + mDescDialog = 0; + } - mWindowManager.removeDialog(mSpecDialog); - mSpecDialog = 0; -} + void CreateClassDialog::onSpecializationClicked(MyGUI::Widget* _sender) + { + delete mSpecDialog; + mSpecDialog = new SelectSpecializationDialog(); + mSpecDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); + mSpecDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationSelected); + mSpecDialog->setVisible(true); + } -void CreateClassDialog::setSpecialization(int id) -{ - mSpecializationId = (ESM::Class::Specialization) id; - static const char *specIds[3] = { - "sSpecializationCombat", - "sSpecializationMagic", - "sSpecializationStealth" - }; - std::string specName = mWindowManager.getGameSettingString(specIds[mSpecializationId], specIds[mSpecializationId]); - mSpecializationName->setCaption(specName); - ToolTips::createSpecializationToolTip(mSpecializationName, specName, mSpecializationId); -} + void CreateClassDialog::onSpecializationSelected() + { + mSpecializationId = mSpecDialog->getSpecializationId(); + setSpecialization(mSpecializationId); -void CreateClassDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) -{ - delete mAttribDialog; - mAttribDialog = new SelectAttributeDialog(mWindowManager); - mAffectedAttribute = _sender; - mAttribDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); - mAttribDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeSelected); - mAttribDialog->setVisible(true); -} + MWBase::Environment::get().getWindowManager()->removeDialog(mSpecDialog); + mSpecDialog = 0; + } -void CreateClassDialog::onAttributeSelected() -{ - ESM::Attribute::AttributeID id = mAttribDialog->getAttributeId(); - if (mAffectedAttribute == mFavoriteAttribute0) + void CreateClassDialog::setSpecialization(int id) { - if (mFavoriteAttribute1->getAttributeId() == id) - mFavoriteAttribute1->setAttributeId(mFavoriteAttribute0->getAttributeId()); + mSpecializationId = (ESM::Class::Specialization) id; + static const char *specIds[3] = { + "sSpecializationCombat", + "sSpecializationMagic", + "sSpecializationStealth" + }; + std::string specName = MWBase::Environment::get().getWindowManager()->getGameSettingString(specIds[mSpecializationId], specIds[mSpecializationId]); + mSpecializationName->setCaption(specName); + ToolTips::createSpecializationToolTip(mSpecializationName, specName, mSpecializationId); } - else if (mAffectedAttribute == mFavoriteAttribute1) + + void CreateClassDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) { - if (mFavoriteAttribute0->getAttributeId() == id) - mFavoriteAttribute0->setAttributeId(mFavoriteAttribute1->getAttributeId()); + delete mAttribDialog; + mAttribDialog = new SelectAttributeDialog(); + mAffectedAttribute = _sender; + mAttribDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); + mAttribDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeSelected); + mAttribDialog->setVisible(true); } - mAffectedAttribute->setAttributeId(id); - mWindowManager.removeDialog(mAttribDialog); - mAttribDialog = 0; - - update(); -} - -void CreateClassDialog::onSkillClicked(Widgets::MWSkillPtr _sender) -{ - delete mSkillDialog; - mSkillDialog = new SelectSkillDialog(mWindowManager); - mAffectedSkill = _sender; - mSkillDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); - mSkillDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSkillSelected); - mSkillDialog->setVisible(true); -} -void CreateClassDialog::onSkillSelected() -{ - ESM::Skill::SkillEnum id = mSkillDialog->getSkillId(); - - // Avoid duplicate skills by swapping any skill field that matches the selected one - std::vector::const_iterator end = mSkills.end(); - for (std::vector::const_iterator it = mSkills.begin(); it != end; ++it) + void CreateClassDialog::onAttributeSelected() { - if (*it == mAffectedSkill) - continue; - if ((*it)->getSkillId() == id) + ESM::Attribute::AttributeID id = mAttribDialog->getAttributeId(); + if (mAffectedAttribute == mFavoriteAttribute0) { - (*it)->setSkillId(mAffectedSkill->getSkillId()); - break; + if (mFavoriteAttribute1->getAttributeId() == id) + mFavoriteAttribute1->setAttributeId(mFavoriteAttribute0->getAttributeId()); } + else if (mAffectedAttribute == mFavoriteAttribute1) + { + if (mFavoriteAttribute0->getAttributeId() == id) + mFavoriteAttribute0->setAttributeId(mFavoriteAttribute1->getAttributeId()); + } + mAffectedAttribute->setAttributeId(id); + MWBase::Environment::get().getWindowManager()->removeDialog(mAttribDialog); + mAttribDialog = 0; + + update(); } - mAffectedSkill->setSkillId(mSkillDialog->getSkillId()); - mWindowManager.removeDialog(mSkillDialog); - mSkillDialog = 0; - update(); -} + void CreateClassDialog::onSkillClicked(Widgets::MWSkillPtr _sender) + { + delete mSkillDialog; + mSkillDialog = new SelectSkillDialog(); + mAffectedSkill = _sender; + mSkillDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); + mSkillDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSkillSelected); + mSkillDialog->setVisible(true); + } -void CreateClassDialog::onDescriptionClicked(MyGUI::Widget* _sender) -{ - mDescDialog = new DescriptionDialog(mWindowManager); - mDescDialog->setTextInput(mDescription); - mDescDialog->eventDone += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionEntered); - mDescDialog->setVisible(true); -} + void CreateClassDialog::onSkillSelected() + { + ESM::Skill::SkillEnum id = mSkillDialog->getSkillId(); -void CreateClassDialog::onDescriptionEntered(WindowBase* parWindow) -{ - mDescription = mDescDialog->getTextInput(); - mWindowManager.removeDialog(mDescDialog); - mDescDialog = 0; -} + // Avoid duplicate skills by swapping any skill field that matches the selected one + std::vector::const_iterator end = mSkills.end(); + for (std::vector::const_iterator it = mSkills.begin(); it != end; ++it) + { + if (*it == mAffectedSkill) + continue; + if ((*it)->getSkillId() == id) + { + (*it)->setSkillId(mAffectedSkill->getSkillId()); + break; + } + } -void CreateClassDialog::onOkClicked(MyGUI::Widget* _sender) -{ - if(getName().size() <= 0) - return; - eventDone(this); -} + mAffectedSkill->setSkillId(mSkillDialog->getSkillId()); + MWBase::Environment::get().getWindowManager()->removeDialog(mSkillDialog); + mSkillDialog = 0; + update(); + } -void CreateClassDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} + void CreateClassDialog::onDescriptionClicked(MyGUI::Widget* _sender) + { + mDescDialog = new DescriptionDialog(); + mDescDialog->setTextInput(mDescription); + mDescDialog->eventDone += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionEntered); + mDescDialog->setVisible(true); + } -/* SelectSpecializationDialog */ + void CreateClassDialog::onDescriptionEntered(WindowBase* parWindow) + { + mDescription = mDescDialog->getTextInput(); + MWBase::Environment::get().getWindowManager()->removeDialog(mDescDialog); + mDescDialog = 0; + } -SelectSpecializationDialog::SelectSpecializationDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_select_specialization.layout", parWindowManager) -{ - // Centre dialog - center(); - - setText("LabelT", mWindowManager.getGameSettingString("sSpecializationMenu1", "")); - - getWidget(mSpecialization0, "Specialization0"); - getWidget(mSpecialization1, "Specialization1"); - getWidget(mSpecialization2, "Specialization2"); - std::string combat = mWindowManager.getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Combat], ""); - std::string magic = mWindowManager.getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Magic], ""); - std::string stealth = mWindowManager.getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Stealth], ""); - - mSpecialization0->setCaption(combat); - mSpecialization0->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); - mSpecialization1->setCaption(magic); - mSpecialization1->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); - mSpecialization2->setCaption(stealth); - mSpecialization2->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); - mSpecializationId = ESM::Class::Combat; - - ToolTips::createSpecializationToolTip(mSpecialization0, combat, ESM::Class::Combat); - ToolTips::createSpecializationToolTip(mSpecialization1, magic, ESM::Class::Magic); - ToolTips::createSpecializationToolTip(mSpecialization2, stealth, ESM::Class::Stealth); - - MyGUI::ButtonPtr cancelButton; - getWidget(cancelButton, "CancelButton"); - cancelButton->setCaption(mWindowManager.getGameSettingString("sCancel", "")); - cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onCancelClicked); -} + void CreateClassDialog::onOkClicked(MyGUI::Widget* _sender) + { + if(getName().size() <= 0) + return; + eventDone(this); + } -SelectSpecializationDialog::~SelectSpecializationDialog() -{ -} + void CreateClassDialog::onBackClicked(MyGUI::Widget* _sender) + { + eventBack(); + } -// widget controls + /* SelectSpecializationDialog */ -void SelectSpecializationDialog::onSpecializationClicked(MyGUI::WidgetPtr _sender) -{ - if (_sender == mSpecialization0) + SelectSpecializationDialog::SelectSpecializationDialog() + : WindowModal("openmw_chargen_select_specialization.layout") + { + // Centre dialog + center(); + + setText("LabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSpecializationMenu1", "")); + + getWidget(mSpecialization0, "Specialization0"); + getWidget(mSpecialization1, "Specialization1"); + getWidget(mSpecialization2, "Specialization2"); + std::string combat = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Combat], ""); + std::string magic = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Magic], ""); + std::string stealth = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Stealth], ""); + + mSpecialization0->setCaption(combat); + mSpecialization0->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); + mSpecialization1->setCaption(magic); + mSpecialization1->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); + mSpecialization2->setCaption(stealth); + mSpecialization2->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); mSpecializationId = ESM::Class::Combat; - else if (_sender == mSpecialization1) - mSpecializationId = ESM::Class::Magic; - else if (_sender == mSpecialization2) - mSpecializationId = ESM::Class::Stealth; - else - return; - - eventItemSelected(); -} -void SelectSpecializationDialog::onCancelClicked(MyGUI::Widget* _sender) -{ - eventCancel(); -} + ToolTips::createSpecializationToolTip(mSpecialization0, combat, ESM::Class::Combat); + ToolTips::createSpecializationToolTip(mSpecialization1, magic, ESM::Class::Magic); + ToolTips::createSpecializationToolTip(mSpecialization2, stealth, ESM::Class::Stealth); -/* SelectAttributeDialog */ + MyGUI::Button* cancelButton; + getWidget(cancelButton, "CancelButton"); + cancelButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sCancel", "")); + cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onCancelClicked); + } -SelectAttributeDialog::SelectAttributeDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_select_attribute.layout", parWindowManager) -{ - // Centre dialog - center(); + SelectSpecializationDialog::~SelectSpecializationDialog() + { + } - setText("LabelT", mWindowManager.getGameSettingString("sAttributesMenu1", "")); + // widget controls - for (int i = 0; i < 8; ++i) + void SelectSpecializationDialog::onSpecializationClicked(MyGUI::Widget* _sender) { - Widgets::MWAttributePtr attribute; - char theIndex = '0'+i; + if (_sender == mSpecialization0) + mSpecializationId = ESM::Class::Combat; + else if (_sender == mSpecialization1) + mSpecializationId = ESM::Class::Magic; + else if (_sender == mSpecialization2) + mSpecializationId = ESM::Class::Stealth; + else + return; - getWidget(attribute, std::string("Attribute").append(1, theIndex)); - attribute->setWindowManager(&parWindowManager); - attribute->setAttributeId(ESM::Attribute::sAttributeIds[i]); - attribute->eventClicked += MyGUI::newDelegate(this, &SelectAttributeDialog::onAttributeClicked); - ToolTips::createAttributeToolTip(attribute, attribute->getAttributeId()); + eventItemSelected(); } - MyGUI::ButtonPtr cancelButton; - getWidget(cancelButton, "CancelButton"); - cancelButton->setCaption(mWindowManager.getGameSettingString("sCancel", "")); - cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectAttributeDialog::onCancelClicked); -} + void SelectSpecializationDialog::onCancelClicked(MyGUI::Widget* _sender) + { + eventCancel(); + } -SelectAttributeDialog::~SelectAttributeDialog() -{ -} + /* SelectAttributeDialog */ -// widget controls + SelectAttributeDialog::SelectAttributeDialog() + : WindowModal("openmw_chargen_select_attribute.layout") + { + // Centre dialog + center(); -void SelectAttributeDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) -{ - // TODO: Change MWAttribute to set and get AttributeID enum instead of int - mAttributeId = static_cast(_sender->getAttributeId()); - eventItemSelected(); -} + setText("LabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sAttributesMenu1", "")); -void SelectAttributeDialog::onCancelClicked(MyGUI::Widget* _sender) -{ - eventCancel(); -} + for (int i = 0; i < 8; ++i) + { + Widgets::MWAttributePtr attribute; + char theIndex = '0'+i; + getWidget(attribute, std::string("Attribute").append(1, theIndex)); + attribute->setAttributeId(ESM::Attribute::sAttributeIds[i]); + attribute->eventClicked += MyGUI::newDelegate(this, &SelectAttributeDialog::onAttributeClicked); + ToolTips::createAttributeToolTip(attribute, attribute->getAttributeId()); + } -/* SelectSkillDialog */ + MyGUI::Button* cancelButton; + getWidget(cancelButton, "CancelButton"); + cancelButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sCancel", "")); + cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectAttributeDialog::onCancelClicked); + } -SelectSkillDialog::SelectSkillDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_select_skill.layout", parWindowManager) -{ - // Centre dialog - center(); + SelectAttributeDialog::~SelectAttributeDialog() + { + } - setText("LabelT", mWindowManager.getGameSettingString("sSkillsMenu1", "")); - setText("CombatLabelT", mWindowManager.getGameSettingString("sSpecializationCombat", "")); - setText("MagicLabelT", mWindowManager.getGameSettingString("sSpecializationMagic", "")); - setText("StealthLabelT", mWindowManager.getGameSettingString("sSpecializationStealth", "")); + // widget controls - for(int i = 0; i < 9; i++) + void SelectAttributeDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) { - char theIndex = '0'+i; - getWidget(mCombatSkill[i], std::string("CombatSkill").append(1, theIndex)); - getWidget(mMagicSkill[i], std::string("MagicSkill").append(1, theIndex)); - getWidget(mStealthSkill[i], std::string("StealthSkill").append(1, theIndex)); + // TODO: Change MWAttribute to set and get AttributeID enum instead of int + mAttributeId = static_cast(_sender->getAttributeId()); + eventItemSelected(); } - struct {Widgets::MWSkillPtr widget; ESM::Skill::SkillEnum skillId;} mSkills[3][9] = { - { - {mCombatSkill[0], ESM::Skill::Block}, - {mCombatSkill[1], ESM::Skill::Armorer}, - {mCombatSkill[2], ESM::Skill::MediumArmor}, - {mCombatSkill[3], ESM::Skill::HeavyArmor}, - {mCombatSkill[4], ESM::Skill::BluntWeapon}, - {mCombatSkill[5], ESM::Skill::LongBlade}, - {mCombatSkill[6], ESM::Skill::Axe}, - {mCombatSkill[7], ESM::Skill::Spear}, - {mCombatSkill[8], ESM::Skill::Athletics} - }, - { - {mMagicSkill[0], ESM::Skill::Enchant}, - {mMagicSkill[1], ESM::Skill::Destruction}, - {mMagicSkill[2], ESM::Skill::Alteration}, - {mMagicSkill[3], ESM::Skill::Illusion}, - {mMagicSkill[4], ESM::Skill::Conjuration}, - {mMagicSkill[5], ESM::Skill::Mysticism}, - {mMagicSkill[6], ESM::Skill::Restoration}, - {mMagicSkill[7], ESM::Skill::Alchemy}, - {mMagicSkill[8], ESM::Skill::Unarmored} - }, + void SelectAttributeDialog::onCancelClicked(MyGUI::Widget* _sender) + { + eventCancel(); + } + + + /* SelectSkillDialog */ + + SelectSkillDialog::SelectSkillDialog() + : WindowModal("openmw_chargen_select_skill.layout") + { + // Centre dialog + center(); + + setText("LabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSkillsMenu1", "")); + setText("CombatLabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSpecializationCombat", "")); + setText("MagicLabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSpecializationMagic", "")); + setText("StealthLabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSpecializationStealth", "")); + + for(int i = 0; i < 9; i++) { - {mStealthSkill[0], ESM::Skill::Security}, - {mStealthSkill[1], ESM::Skill::Sneak}, - {mStealthSkill[2], ESM::Skill::Acrobatics}, - {mStealthSkill[3], ESM::Skill::LightArmor}, - {mStealthSkill[4], ESM::Skill::ShortBlade}, - {mStealthSkill[5] ,ESM::Skill::Marksman}, - {mStealthSkill[6] ,ESM::Skill::Mercantile}, - {mStealthSkill[7] ,ESM::Skill::Speechcraft}, - {mStealthSkill[8] ,ESM::Skill::HandToHand} + char theIndex = '0'+i; + getWidget(mCombatSkill[i], std::string("CombatSkill").append(1, theIndex)); + getWidget(mMagicSkill[i], std::string("MagicSkill").append(1, theIndex)); + getWidget(mStealthSkill[i], std::string("StealthSkill").append(1, theIndex)); } - }; - for (int spec = 0; spec < 3; ++spec) - { - for (int i = 0; i < 9; ++i) + struct {Widgets::MWSkillPtr widget; ESM::Skill::SkillEnum skillId;} mSkills[3][9] = { + { + {mCombatSkill[0], ESM::Skill::Block}, + {mCombatSkill[1], ESM::Skill::Armorer}, + {mCombatSkill[2], ESM::Skill::MediumArmor}, + {mCombatSkill[3], ESM::Skill::HeavyArmor}, + {mCombatSkill[4], ESM::Skill::BluntWeapon}, + {mCombatSkill[5], ESM::Skill::LongBlade}, + {mCombatSkill[6], ESM::Skill::Axe}, + {mCombatSkill[7], ESM::Skill::Spear}, + {mCombatSkill[8], ESM::Skill::Athletics} + }, + { + {mMagicSkill[0], ESM::Skill::Enchant}, + {mMagicSkill[1], ESM::Skill::Destruction}, + {mMagicSkill[2], ESM::Skill::Alteration}, + {mMagicSkill[3], ESM::Skill::Illusion}, + {mMagicSkill[4], ESM::Skill::Conjuration}, + {mMagicSkill[5], ESM::Skill::Mysticism}, + {mMagicSkill[6], ESM::Skill::Restoration}, + {mMagicSkill[7], ESM::Skill::Alchemy}, + {mMagicSkill[8], ESM::Skill::Unarmored} + }, + { + {mStealthSkill[0], ESM::Skill::Security}, + {mStealthSkill[1], ESM::Skill::Sneak}, + {mStealthSkill[2], ESM::Skill::Acrobatics}, + {mStealthSkill[3], ESM::Skill::LightArmor}, + {mStealthSkill[4], ESM::Skill::ShortBlade}, + {mStealthSkill[5] ,ESM::Skill::Marksman}, + {mStealthSkill[6] ,ESM::Skill::Mercantile}, + {mStealthSkill[7] ,ESM::Skill::Speechcraft}, + {mStealthSkill[8] ,ESM::Skill::HandToHand} + } + }; + + for (int spec = 0; spec < 3; ++spec) { - mSkills[spec][i].widget->setWindowManager(&mWindowManager); - mSkills[spec][i].widget->setSkillId(mSkills[spec][i].skillId); - mSkills[spec][i].widget->eventClicked += MyGUI::newDelegate(this, &SelectSkillDialog::onSkillClicked); - ToolTips::createSkillToolTip(mSkills[spec][i].widget, mSkills[spec][i].widget->getSkillId()); + for (int i = 0; i < 9; ++i) + { + mSkills[spec][i].widget->setSkillId(mSkills[spec][i].skillId); + mSkills[spec][i].widget->eventClicked += MyGUI::newDelegate(this, &SelectSkillDialog::onSkillClicked); + ToolTips::createSkillToolTip(mSkills[spec][i].widget, mSkills[spec][i].widget->getSkillId()); + } } + + MyGUI::Button* cancelButton; + getWidget(cancelButton, "CancelButton"); + cancelButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sCancel", "")); + cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSkillDialog::onCancelClicked); } - MyGUI::ButtonPtr cancelButton; - getWidget(cancelButton, "CancelButton"); - cancelButton->setCaption(mWindowManager.getGameSettingString("sCancel", "")); - cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSkillDialog::onCancelClicked); -} + SelectSkillDialog::~SelectSkillDialog() + { + } -SelectSkillDialog::~SelectSkillDialog() -{ -} + // widget controls -// widget controls + void SelectSkillDialog::onSkillClicked(Widgets::MWSkillPtr _sender) + { + mSkillId = _sender->getSkillId(); + eventItemSelected(); + } -void SelectSkillDialog::onSkillClicked(Widgets::MWSkillPtr _sender) -{ - mSkillId = _sender->getSkillId(); - eventItemSelected(); -} + void SelectSkillDialog::onCancelClicked(MyGUI::Widget* _sender) + { + eventCancel(); + } -void SelectSkillDialog::onCancelClicked(MyGUI::Widget* _sender) -{ - eventCancel(); -} + /* DescriptionDialog */ -/* DescriptionDialog */ + DescriptionDialog::DescriptionDialog() + : WindowModal("openmw_chargen_class_description.layout") + { + // Centre dialog + center(); -DescriptionDialog::DescriptionDialog(MWBase::WindowManager& parWindowManager) - : WindowModal("openmw_chargen_class_description.layout", parWindowManager) -{ - // Centre dialog - center(); + getWidget(mTextEdit, "TextEdit"); - getWidget(mTextEdit, "TextEdit"); + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DescriptionDialog::onOkClicked); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sInputMenu1", "")); - MyGUI::ButtonPtr okButton; - getWidget(okButton, "OKButton"); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DescriptionDialog::onOkClicked); - okButton->setCaption(mWindowManager.getGameSettingString("sInputMenu1", "")); + // Make sure the edit box has focus + MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); + } - // Make sure the edit box has focus - MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); -} + DescriptionDialog::~DescriptionDialog() + { + } -DescriptionDialog::~DescriptionDialog() -{ -} + // widget controls -// widget controls + void DescriptionDialog::onOkClicked(MyGUI::Widget* _sender) + { + eventDone(this); + } -void DescriptionDialog::onOkClicked(MyGUI::Widget* _sender) -{ - eventDone(this); } diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index c7699b308..15fc89658 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -1,9 +1,9 @@ #ifndef MWGUI_CLASS_H #define MWGUI_CLASS_H -#include + #include "widgets.hpp" -#include "window_base.hpp" +#include "windowbase.hpp" /* This file contains the dialogs for choosing a class. @@ -12,10 +12,10 @@ namespace MWGui { - class InfoBoxDialog : public WindowBase + class InfoBoxDialog : public WindowModal { public: - InfoBoxDialog(MWBase::WindowManager& parWindowManager); + InfoBoxDialog(); typedef std::vector ButtonList; @@ -35,17 +35,17 @@ namespace MWGui EventHandle_Int eventButtonSelected; protected: - void onButtonClicked(MyGUI::WidgetPtr _sender); + void onButtonClicked(MyGUI::Widget* _sender); private: void fitToText(MyGUI::TextBox* widget); - void layoutVertically(MyGUI::WidgetPtr widget, int margin); + void layoutVertically(MyGUI::Widget* widget, int margin); int mCurrentButton; - MyGUI::WidgetPtr mTextBox; + MyGUI::Widget* mTextBox; MyGUI::TextBox* mText; - MyGUI::WidgetPtr mButtonBar; - std::vector mButtons; + MyGUI::Widget* mButtonBar; + std::vector mButtons; }; // Lets the player choose between 3 ways of creating a class @@ -60,13 +60,13 @@ namespace MWGui Class_Create = 2, Class_Back = 3 }; - ClassChoiceDialog(MWBase::WindowManager& parWindowManager); + ClassChoiceDialog(); }; - class GenerateClassResultDialog : public WindowBase + class GenerateClassResultDialog : public WindowModal { public: - GenerateClassResultDialog(MWBase::WindowManager& parWindowManager); + GenerateClassResultDialog(); std::string getClassId() const; void setClassId(const std::string &classId); @@ -90,10 +90,10 @@ namespace MWGui std::string mCurrentClassId; }; - class PickClassDialog : public WindowBase + class PickClassDialog : public WindowModal { public: - PickClassDialog(MWBase::WindowManager& parWindowManager); + PickClassDialog(); const std::string &getClassId() const { return mCurrentClassId; } void setClassId(const std::string &classId); @@ -132,7 +132,7 @@ namespace MWGui class SelectSpecializationDialog : public WindowModal { public: - SelectSpecializationDialog(MWBase::WindowManager& parWindowManager); + SelectSpecializationDialog(); ~SelectSpecializationDialog(); ESM::Class::Specialization getSpecializationId() const { return mSpecializationId; } @@ -163,7 +163,7 @@ namespace MWGui class SelectAttributeDialog : public WindowModal { public: - SelectAttributeDialog(MWBase::WindowManager& parWindowManager); + SelectAttributeDialog(); ~SelectAttributeDialog(); ESM::Attribute::AttributeID getAttributeId() const { return mAttributeId; } @@ -192,7 +192,7 @@ namespace MWGui class SelectSkillDialog : public WindowModal { public: - SelectSkillDialog(MWBase::WindowManager& parWindowManager); + SelectSkillDialog(); ~SelectSkillDialog(); ESM::Skill::SkillEnum getSkillId() const { return mSkillId; } @@ -225,7 +225,7 @@ namespace MWGui class DescriptionDialog : public WindowModal { public: - DescriptionDialog(MWBase::WindowManager& parWindowManager); + DescriptionDialog(); ~DescriptionDialog(); std::string getTextInput() const { return mTextEdit ? mTextEdit->getOnlyText() : ""; } @@ -235,13 +235,13 @@ namespace MWGui void onOkClicked(MyGUI::Widget* _sender); private: - MyGUI::EditPtr mTextEdit; + MyGUI::EditBox* mTextEdit; }; - class CreateClassDialog : public WindowBase + class CreateClassDialog : public WindowModal { public: - CreateClassDialog(MWBase::WindowManager& parWindowManager); + CreateClassDialog(); virtual ~CreateClassDialog(); std::string getName() const; @@ -265,7 +265,7 @@ namespace MWGui void onOkClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender); - void onSpecializationClicked(MyGUI::WidgetPtr _sender); + void onSpecializationClicked(MyGUI::Widget* _sender); void onSpecializationSelected(); void onAttributeClicked(Widgets::MWAttributePtr _sender); void onAttributeSelected(); @@ -280,7 +280,7 @@ namespace MWGui void update(); private: - MyGUI::EditPtr mEditName; + MyGUI::EditBox* mEditName; MyGUI::TextBox* mSpecializationName; Widgets::MWAttributePtr mFavoriteAttribute0, mFavoriteAttribute1; Widgets::MWSkillPtr mMajorSkill[5]; diff --git a/apps/openmw/mwgui/companionitemmodel.cpp b/apps/openmw/mwgui/companionitemmodel.cpp new file mode 100644 index 000000000..3212ed701 --- /dev/null +++ b/apps/openmw/mwgui/companionitemmodel.cpp @@ -0,0 +1,34 @@ +#include "companionitemmodel.hpp" + +#include "../mwmechanics/npcstats.hpp" +#include "../mwworld/class.hpp" + +namespace MWGui +{ + CompanionItemModel::CompanionItemModel(const MWWorld::Ptr &actor) + : InventoryItemModel(actor) + { + } + + void CompanionItemModel::copyItem (const ItemStack& item, size_t count) + { + if (mActor.getTypeName() == typeid(ESM::NPC).name()) + { + MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor); + stats.modifyProfit(MWWorld::Class::get(item.mBase).getValue(item.mBase) * count); + } + + InventoryItemModel::copyItem(item, count); + } + + void CompanionItemModel::removeItem (const ItemStack& item, size_t count) + { + if (mActor.getTypeName() == typeid(ESM::NPC).name()) + { + MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor); + stats.modifyProfit(-MWWorld::Class::get(item.mBase).getValue(item.mBase) * count); + } + + InventoryItemModel::removeItem(item, count); + } +} diff --git a/apps/openmw/mwgui/companionitemmodel.hpp b/apps/openmw/mwgui/companionitemmodel.hpp new file mode 100644 index 000000000..c11e11c32 --- /dev/null +++ b/apps/openmw/mwgui/companionitemmodel.hpp @@ -0,0 +1,22 @@ +#ifndef MWGUI_COMPANION_ITEM_MODEL_H +#define MWGUI_COMPANION_ITEM_MODEL_H + +#include "inventoryitemmodel.hpp" + +namespace MWGui +{ + + /// @brief The companion item model keeps track of the companion's profit by + /// monitoring which items are being added to and removed from the model. + class CompanionItemModel : public InventoryItemModel + { + public: + CompanionItemModel (const MWWorld::Ptr& actor); + + virtual void copyItem (const ItemStack& item, size_t count); + virtual void removeItem (const ItemStack& item, size_t count); + }; + +} + +#endif diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp new file mode 100644 index 000000000..9698608d6 --- /dev/null +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -0,0 +1,152 @@ +#include "companionwindow.hpp" + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/dialoguemanager.hpp" + +#include "../mwmechanics/npcstats.hpp" + +#include "../mwworld/class.hpp" + +#include "messagebox.hpp" +#include "itemview.hpp" +#include "sortfilteritemmodel.hpp" +#include "companionitemmodel.hpp" +#include "container.hpp" +#include "countdialog.hpp" + +namespace MWGui +{ + +CompanionWindow::CompanionWindow(DragAndDrop *dragAndDrop, MessageBoxManager* manager) + : WindowBase("openmw_companion_window.layout") + , mDragAndDrop(dragAndDrop) + , mMessageBoxManager(manager) + , mSelectedItem(-1) + , mModel(NULL) + , mSortModel(NULL) +{ + getWidget(mCloseButton, "CloseButton"); + getWidget(mProfitLabel, "ProfitLabel"); + getWidget(mEncumbranceBar, "EncumbranceBar"); + getWidget(mItemView, "ItemView"); + mItemView->eventBackgroundClicked += MyGUI::newDelegate(this, &CompanionWindow::onBackgroundSelected); + mItemView->eventItemClicked += MyGUI::newDelegate(this, &CompanionWindow::onItemSelected); + + mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CompanionWindow::onCloseButtonClicked); + + setCoord(200,0,600,300); +} + +void CompanionWindow::onItemSelected(int index) +{ + if (mDragAndDrop->mIsOnDragAndDrop) + { + mDragAndDrop->drop(mModel, mItemView); + updateEncumbranceBar(); + return; + } + + const ItemStack& item = mSortModel->getItem(index); + + MWWorld::Ptr object = item.mBase; + int count = item.mCount; + bool shift = MyGUI::InputManager::getInstance().isShiftPressed(); + if (MyGUI::InputManager::getInstance().isControlPressed()) + count = 1; + + mSelectedItem = mSortModel->mapToSource(index); + + if (count > 1 && !shift) + { + CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); + dialog->open(MWWorld::Class::get(object).getName(object), "#{sTake}", count); + dialog->eventOkClicked.clear(); + dialog->eventOkClicked += MyGUI::newDelegate(this, &CompanionWindow::dragItem); + } + else + dragItem (NULL, count); +} + +void CompanionWindow::dragItem(MyGUI::Widget* sender, int count) +{ + mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count); +} + +void CompanionWindow::onBackgroundSelected() +{ + if (mDragAndDrop->mIsOnDragAndDrop) + { + mDragAndDrop->drop(mModel, mItemView); + updateEncumbranceBar(); + } +} + +void CompanionWindow::open(const MWWorld::Ptr& npc) +{ + mPtr = npc; + setTitle(MWWorld::Class::get(npc).getName(npc)); + updateEncumbranceBar(); + + mModel = new CompanionItemModel(npc); + mSortModel = new SortFilterItemModel(mModel); + mItemView->setModel(mSortModel); +} + +void CompanionWindow::onFrame() +{ + updateEncumbranceBar(); +} + +void CompanionWindow::updateEncumbranceBar() +{ + if (mPtr.isEmpty()) + return; + float capacity = MWWorld::Class::get(mPtr).getCapacity(mPtr); + float encumbrance = MWWorld::Class::get(mPtr).getEncumbrance(mPtr); + mEncumbranceBar->setValue(encumbrance, capacity); + + if (mPtr.getTypeName() != typeid(ESM::NPC).name()) + mProfitLabel->setCaption(""); + else + { + MWMechanics::NpcStats& stats = MWWorld::Class::get(mPtr).getNpcStats(mPtr); + mProfitLabel->setCaptionWithReplacing("#{sProfitValue} " + boost::lexical_cast(stats.getProfit())); + } +} + +void CompanionWindow::onCloseButtonClicked(MyGUI::Widget* _sender) +{ + if (mPtr.getTypeName() == typeid(ESM::NPC).name() && MWWorld::Class::get(mPtr).getNpcStats(mPtr).getProfit() < 0) + { + std::vector buttons; + buttons.push_back("#{sCompanionWarningButtonOne}"); + buttons.push_back("#{sCompanionWarningButtonTwo}"); + mMessageBoxManager->createInteractiveMessageBox("#{sCompanionWarningMessage}", buttons); + mMessageBoxManager->eventButtonPressed += MyGUI::newDelegate(this, &CompanionWindow::onMessageBoxButtonClicked); + } + else + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion); +} + +void CompanionWindow::onMessageBoxButtonClicked(int button) +{ + if (button == 0) + { + mPtr.getRefData().getLocals().setVarByInt(MWWorld::Class::get(mPtr).getScript(mPtr), + "minimumProfit", MWWorld::Class::get(mPtr).getNpcStats(mPtr).getProfit()); + + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion); + MWBase::Environment::get().getDialogueManager()->startDialogue (mPtr); + } +} + +void CompanionWindow::onReferenceUnavailable() +{ + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion); +} + + + +} diff --git a/apps/openmw/mwgui/companionwindow.hpp b/apps/openmw/mwgui/companionwindow.hpp new file mode 100644 index 000000000..7fdfc069f --- /dev/null +++ b/apps/openmw/mwgui/companionwindow.hpp @@ -0,0 +1,52 @@ +#ifndef OPENMW_MWGUI_COMPANIONWINDOW_H +#define OPENMW_MWGUI_COMPANIONWINDOW_H + +#include "widgets.hpp" +#include "windowbase.hpp" +#include "referenceinterface.hpp" + +namespace MWGui +{ + class MessageBoxManager; + class ItemView; + class DragAndDrop; + class SortFilterItemModel; + class CompanionItemModel; + + class CompanionWindow : public WindowBase, public ReferenceInterface + { + public: + CompanionWindow(DragAndDrop* dragAndDrop, MessageBoxManager* manager); + + void open(const MWWorld::Ptr& npc); + void onFrame (); + + private: + ItemView* mItemView; + SortFilterItemModel* mSortModel; + CompanionItemModel* mModel; + size_t mSelectedItem; + + DragAndDrop* mDragAndDrop; + + MyGUI::Button* mCloseButton; + MyGUI::TextBox* mProfitLabel; + Widgets::MWDynamicStat* mEncumbranceBar; + MessageBoxManager* mMessageBoxManager; + + void onItemSelected(int index); + void onBackgroundSelected(); + void dragItem(MyGUI::Widget* sender, int count); + + void onMessageBoxButtonClicked(int button); + + void updateEncumbranceBar(); + + void onCloseButtonClicked(MyGUI::Widget* _sender); + + virtual void onReferenceUnavailable(); + }; + +} + +#endif diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index 31f4989e0..f431f2f64 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -1,14 +1,9 @@ #include "confirmationdialog.hpp" -#include - -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" - namespace MWGui { - ConfirmationDialog::ConfirmationDialog(MWBase::WindowManager& parWindowManager) : - WindowModal("openmw_confirmation_dialog.layout", parWindowManager) + ConfirmationDialog::ConfirmationDialog() : + WindowModal("openmw_confirmation_dialog.layout") { getWidget(mMessage, "Message"); getWidget(mOkButton, "OkButton"); diff --git a/apps/openmw/mwgui/confirmationdialog.hpp b/apps/openmw/mwgui/confirmationdialog.hpp index 45941f2ad..47b256017 100644 --- a/apps/openmw/mwgui/confirmationdialog.hpp +++ b/apps/openmw/mwgui/confirmationdialog.hpp @@ -1,14 +1,14 @@ #ifndef MWGUI_CONFIRMATIONDIALOG_H #define MWGUI_CONFIRMATIONDIALOG_H -#include "window_base.hpp" +#include "windowbase.hpp" namespace MWGui { class ConfirmationDialog : public WindowModal { public: - ConfirmationDialog(MWBase::WindowManager& parWindowManager); + ConfirmationDialog(); void open(const std::string& message); typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index b2281d87e..b69d2d6af 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -1,13 +1,7 @@ - #include "console.hpp" -#include -#include - #include -#include "../mwworld/esmstore.hpp" - #include "../mwscript/extensions.hpp" #include "../mwbase/environment.hpp" @@ -143,7 +137,7 @@ namespace MWGui void Console::disable() { setVisible(false); - setSelectedObject(MWWorld::Ptr()); + // Remove keyboard focus from the console input whenever the // console is turned off MyGUI::InputManager::getInstance().setKeyFocusWidget(NULL); @@ -216,7 +210,7 @@ namespace MWGui } } - void Console::keyPress(MyGUI::WidgetPtr _sender, + void Console::keyPress(MyGUI::Widget* _sender, MyGUI::KeyCode key, MyGUI::Char _char) { @@ -266,7 +260,7 @@ namespace MWGui } } - void Console::acceptCommand(MyGUI::EditPtr _sender) + void Console::acceptCommand(MyGUI::EditBox* _sender) { const std::string &cm = command->getCaption(); if(cm.empty()) return; @@ -284,16 +278,15 @@ namespace MWGui std::string Console::complete( std::string input, std::vector &matches ) { - using namespace std; - string output=input; - string tmp=input; + std::string output = input; + std::string tmp = input; bool has_front_quote = false; /* Does the input string contain things that don't have to be completed? If yes erase them. */ /* Are there quotation marks? */ - if( tmp.find('"') != string::npos ) { + if( tmp.find('"') != std::string::npos ) { int numquotes=0; - for(string::iterator it=tmp.begin(); it < tmp.end(); ++it) { + for(std::string::iterator it=tmp.begin(); it < tmp.end(); ++it) { if( *it == '"' ) numquotes++; } @@ -305,7 +298,7 @@ namespace MWGui } else { size_t pos; - if( ( ((pos = tmp.rfind(' ')) != string::npos ) ) && ( pos > tmp.rfind('"') ) ) { + if( ( ((pos = tmp.rfind(' ')) != std::string::npos ) ) && ( pos > tmp.rfind('"') ) ) { tmp.erase( 0, tmp.rfind(' ')+1); } else { @@ -317,7 +310,7 @@ namespace MWGui /* No quotation marks. Are there spaces?*/ else { size_t rpos; - if( (rpos=tmp.rfind(' ')) != string::npos ) { + if( (rpos=tmp.rfind(' ')) != std::string::npos ) { if( rpos == 0 ) { tmp.clear(); } @@ -336,7 +329,7 @@ namespace MWGui } /* Iterate through the vector. */ - for(vector::iterator it=mNames.begin(); it < mNames.end();++it) { + for(std::vector::iterator it=mNames.begin(); it < mNames.end();++it) { bool string_different=false; /* Is the string shorter than the input string? If yes skip it. */ @@ -344,7 +337,7 @@ namespace MWGui continue; /* Is the beginning of the string different from the input string? If yes skip it. */ - for( string::iterator iter=tmp.begin(), iter2=(*it).begin(); iter < tmp.end();iter++, iter2++) { + for( std::string::iterator iter=tmp.begin(), iter2=(*it).begin(); iter < tmp.end();iter++, iter2++) { if( tolower(*iter) != tolower(*iter2) ) { string_different=true; break; @@ -367,24 +360,24 @@ namespace MWGui /* Only one match. We're done. */ if( matches.size() == 1 ) { /* Adding quotation marks when the input string started with a quotation mark or has spaces in it*/ - if( ( matches.front().find(' ') != string::npos ) ) { + if( ( matches.front().find(' ') != std::string::npos ) ) { if( !has_front_quote ) - output.append(string("\"")); - return output.append(matches.front() + string("\" ")); + output.append(std::string("\"")); + return output.append(matches.front() + std::string("\" ")); } else if( has_front_quote ) { - return output.append(matches.front() + string("\" ")); + return output.append(matches.front() + std::string("\" ")); } else { - return output.append(matches.front() + string(" ")); + return output.append(matches.front() + std::string(" ")); } } /* Check if all matching strings match further than input. If yes complete to this match. */ int i = tmp.length(); - for(string::iterator iter=matches.front().begin()+tmp.length(); iter < matches.front().end(); iter++, i++) { - for(vector::iterator it=matches.begin(); it < matches.end();++it) { + for(std::string::iterator iter=matches.front().begin()+tmp.length(); iter < matches.front().end(); iter++, i++) { + for(std::vector::iterator it=matches.begin(); it < matches.end();++it) { if( tolower((*it)[i]) != tolower(*iter) ) { /* Append the longest match to the end of the output string*/ output.append(matches.front().substr( 0, i)); @@ -404,11 +397,24 @@ namespace MWGui void Console::setSelectedObject(const MWWorld::Ptr& object) { - mPtr = object; - if (!mPtr.isEmpty()) - setTitle("#{sConsoleTitle} (" + mPtr.getCellRef().mRefID + ")"); + if (!object.isEmpty()) + { + if (object == mPtr) + { + setTitle("#{sConsoleTitle}"); + mPtr=MWWorld::Ptr(); + } + else + { + setTitle("#{sConsoleTitle} (" + object.getCellRef().mRefID + ")"); + mPtr = object; + } + } else + { setTitle("#{sConsoleTitle}"); + mPtr = MWWorld::Ptr(); + } MyGUI::InputManager::getInstance().setKeyFocusWidget(command); } diff --git a/apps/openmw/mwgui/console.hpp b/apps/openmw/mwgui/console.hpp index 1893b0148..b1d961ed2 100644 --- a/apps/openmw/mwgui/console.hpp +++ b/apps/openmw/mwgui/console.hpp @@ -55,8 +55,8 @@ namespace MWGui public: - MyGUI::EditPtr command; - MyGUI::EditPtr history; + MyGUI::EditBox* command; + MyGUI::EditBox* history; typedef std::list StringList; @@ -95,11 +95,11 @@ namespace MWGui private: - void keyPress(MyGUI::WidgetPtr _sender, + void keyPress(MyGUI::Widget* _sender, MyGUI::KeyCode key, MyGUI::Char _char); - void acceptCommand(MyGUI::EditPtr _sender); + void acceptCommand(MyGUI::EditBox* _sender); std::string complete( std::string input, std::vector &matches ); }; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 479a82efa..85c2cf5fb 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -1,11 +1,5 @@ #include "container.hpp" -#include -#include -#include -#include -#include - #include #include "../mwbase/environment.hpp" @@ -13,665 +7,269 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/manualref.hpp" -#include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" -#include "../mwclass/container.hpp" - -#include "widgets.hpp" #include "countdialog.hpp" #include "tradewindow.hpp" #include "inventorywindow.hpp" -using namespace MWGui; -using namespace Widgets; - +#include "itemview.hpp" +#include "inventoryitemmodel.hpp" +#include "sortfilteritemmodel.hpp" +#include "pickpocketitemmodel.hpp" namespace { - bool compareType(std::string type1, std::string type2) + std::string getCountString(const int count) { - // this defines the sorting order of types. types that are first in the vector, appear before other types. - std::vector mapping; - mapping.push_back( typeid(ESM::Weapon).name() ); - mapping.push_back( typeid(ESM::Armor).name() ); - mapping.push_back( typeid(ESM::Clothing).name() ); - mapping.push_back( typeid(ESM::Potion).name() ); - mapping.push_back( typeid(ESM::Ingredient).name() ); - mapping.push_back( typeid(ESM::Apparatus).name() ); - mapping.push_back( typeid(ESM::Book).name() ); - mapping.push_back( typeid(ESM::Light).name() ); - mapping.push_back( typeid(ESM::Miscellaneous).name() ); - mapping.push_back( typeid(ESM::Tool).name() ); - mapping.push_back( typeid(ESM::Repair).name() ); - mapping.push_back( typeid(ESM::Probe).name() ); - - assert( std::find(mapping.begin(), mapping.end(), type1) != mapping.end() ); - assert( std::find(mapping.begin(), mapping.end(), type2) != mapping.end() ); - - return std::find(mapping.begin(), mapping.end(), type1) < std::find(mapping.begin(), mapping.end(), type2); - } - - bool sortItems(MWWorld::Ptr left, MWWorld::Ptr right) - { - if (left.getTypeName() == right.getTypeName()) - { - int cmp = MWWorld::Class::get(left).getName(left).compare( - MWWorld::Class::get(right).getName(right)); - return cmp < 0; - } + if (count == 1) + return ""; + if (count > 9999) + return boost::lexical_cast(int(count/1000.f)) + "k"; else - { - return compareType(left.getTypeName(), right.getTypeName()); - } + return boost::lexical_cast(count); } } - -ContainerBase::ContainerBase(DragAndDrop* dragAndDrop) : - mDragAndDrop(dragAndDrop), - mFilter(ContainerBase::Filter_All) -{ -} - -void ContainerBase::setWidgets(MyGUI::Widget* containerWidget, MyGUI::ScrollView* itemView) +namespace MWGui { - mContainerWidget = containerWidget; - mItemView = itemView; - mContainerWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerBase::onContainerClicked); - mContainerWidget->eventMouseWheel += MyGUI::newDelegate(this, &ContainerWindow::onMouseWheel); -} - -ContainerBase::~ContainerBase() -{ -} - -void ContainerBase::onSelectedItem(MyGUI::Widget* _sender) -{ - mSelectedItem = _sender; - - if (mDragAndDrop && !isTrading()) - { - if(!mDragAndDrop->mIsOnDragAndDrop) - { - MWWorld::Ptr object = (*_sender->getUserData()); - int count = object.getRefData().getCount(); - - if (MyGUI::InputManager::getInstance().isShiftPressed() || count == 1) - { - startDragItem(_sender, count); - } - else if (MyGUI::InputManager::getInstance().isControlPressed()) - { - startDragItem(_sender, 1); - } - else - { - std::string message = "#{sTake}"; - CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); - dialog->open(MWWorld::Class::get(object).getName(object), message, count); - dialog->eventOkClicked.clear(); - dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerBase::startDragItem); - } - } - else - onContainerClicked(mContainerWidget); - } - else if (isTrading()) + void DragAndDrop::startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count) { - MWWorld::Ptr object = (*_sender->getUserData()); - int count = object.getRefData().getCount(); + mItem = sourceModel->getItem(index); + mDraggedCount = count; + mSourceModel = sourceModel; + mSourceView = sourceView; + mSourceSortModel = sortModel; + mIsOnDragAndDrop = true; + mDragAndDropWidget->setVisible(true); + + std::string sound = MWWorld::Class::get(mItem.mBase).getUpSoundId(mItem.mBase); + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - if (isInventory()) + if (mSourceSortModel) { - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); - - // the player is trying to sell an item, check if the merchant accepts it - // also, don't allow selling gold (let's be better than Morrowind at this, can we?) - if (!MWBase::Environment::get().getWindowManager()->getTradeWindow()->npcAcceptsItem(object) || - MWWorld::Class::get(object).getName(object) == gmst.find("sGold")->getString()) - { - // user notification "i don't buy this item" - MWBase::Environment::get().getWindowManager()-> - messageBox("#{sBarterDialog4}", std::vector()); - return; - } + mSourceSortModel->clearDragItems(); + mSourceSortModel->addDragItem(mItem.mBase, count); } - bool buying = isTradeWindow(); // buying or selling? - std::string message = buying ? "#{sQuanityMenuMessage02}" : "#{sQuanityMenuMessage01}"; - - if (std::find(mBoughtItems.begin(), mBoughtItems.end(), object) != mBoughtItems.end()) - { - if (MyGUI::InputManager::getInstance().isShiftPressed() || count == 1) - { - sellAlreadyBoughtItem(NULL, count); - } - else if (MyGUI::InputManager::getInstance().isControlPressed()) - { - sellAlreadyBoughtItem(NULL, 1); - } - else - { - CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); - dialog->open(MWWorld::Class::get(object).getName(object), message, count); - dialog->eventOkClicked.clear(); - dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerBase::sellAlreadyBoughtItem); - } - } - else - { - if (MyGUI::InputManager::getInstance().isShiftPressed() || count == 1) - { - sellItem(NULL, count); - } - else if (MyGUI::InputManager::getInstance().isControlPressed()) - { - sellItem(NULL, 1); - } - else - { - CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); - dialog->open(MWWorld::Class::get(object).getName(object), message, count); - dialog->eventOkClicked.clear(); - dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerBase::sellItem); - } - } + std::string path = std::string("icons\\"); + path += MWWorld::Class::get(mItem.mBase).getInventoryIcon(mItem.mBase); + MyGUI::ImageBox* baseWidget = mDragAndDropWidget->createWidget + ("ImageBox", MyGUI::IntCoord(0, 0, 42, 42), MyGUI::Align::Default); + mDraggedWidget = baseWidget; + MyGUI::ImageBox* image = baseWidget->createWidget("ImageBox", + MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + image->setImageTexture(path); + image->setNeedMouseFocus(false); + + // text widget that shows item count + MyGUI::TextBox* text = image->createWidget("SandBrightText", + MyGUI::IntCoord(0, 14, 32, 18), MyGUI::Align::Default, std::string("Label")); + text->setTextAlign(MyGUI::Align::Right); + text->setNeedMouseFocus(false); + text->setTextShadow(true); + text->setTextShadowColour(MyGUI::Colour(0,0,0)); + text->setCaption(getCountString(count)); + + sourceView->update(); + + MWBase::Environment::get().getWindowManager()->setDragDrop(true); } - else - { - onSelectedItemImpl(*_sender->getUserData()); - } -} -void ContainerBase::sellAlreadyBoughtItem(MyGUI::Widget* _sender, int count) -{ - MWWorld::Ptr object = *mSelectedItem->getUserData(); - - if (isInventory()) - { - MWBase::Environment::get().getWindowManager()->getTradeWindow()->addItem(object, count); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->sellToNpc(object, count, true); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->drawItems(); - } - else + void DragAndDrop::drop(ItemModel *targetModel, ItemView *targetView) { - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->addItem(object, count); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->buyFromNpc(object, count, true); - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems(); - } + std::string sound = MWWorld::Class::get(mItem.mBase).getDownSoundId(mItem.mBase); + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - std::string sound = MWWorld::Class::get(object).getUpSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); + mDragAndDropWidget->setVisible(false); - drawItems(); -} + targetModel->copyItem(mItem, mDraggedCount); + mSourceModel->removeItem(mItem, mDraggedCount); -void ContainerBase::sellItem(MyGUI::Widget* _sender, int count) -{ - MWWorld::Ptr object = *mSelectedItem->getUserData(); + mSourceModel->update(); - if (isInventory()) - { - MWBase::Environment::get().getWindowManager()->getTradeWindow()->addBarteredItem(object, count); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->sellToNpc(object, count, false); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->drawItems(); + finish(); + targetView->update(); } - else + + void DragAndDrop::finish() { - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->addBarteredItem(object, count); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->buyFromNpc(object, count, false); - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems(); - } + mIsOnDragAndDrop = false; + mSourceSortModel->clearDragItems(); - std::string sound = MWWorld::Class::get(object).getUpSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); + MyGUI::Gui::getInstance().destroyWidget(mDraggedWidget); + mDraggedWidget = 0; + MWBase::Environment::get().getWindowManager()->setDragDrop(false); + } - drawItems(); -} -void ContainerBase::startDragItem(MyGUI::Widget* _sender, int count) -{ - mDragAndDrop->mIsOnDragAndDrop = true; - mSelectedItem->detachFromWidget(); - mSelectedItem->attachToWidget(mDragAndDrop->mDragAndDropWidget); + ContainerWindow::ContainerWindow(DragAndDrop* dragAndDrop) + : WindowBase("openmw_container_window.layout") + , mDragAndDrop(dragAndDrop) + , mSelectedItem(-1) + , mModel(NULL) + , mSortModel(NULL) + { + getWidget(mDisposeCorpseButton, "DisposeCorpseButton"); + getWidget(mTakeButton, "TakeButton"); + getWidget(mCloseButton, "CloseButton"); - MWWorld::Ptr object = *mSelectedItem->getUserData(); - _unequipItem(object); + getWidget(mItemView, "ItemView"); + mItemView->eventBackgroundClicked += MyGUI::newDelegate(this, &ContainerWindow::onBackgroundSelected); + mItemView->eventItemClicked += MyGUI::newDelegate(this, &ContainerWindow::onItemSelected); - mDragAndDrop->mDraggedCount = count; + mDisposeCorpseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onDisposeCorpseButtonClicked); + mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onCloseButtonClicked); + mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked); - mDragAndDrop->mDraggedFrom = this; + setCoord(200,0,600,300); + } - std::string sound = MWWorld::Class::get(object).getUpSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); + void ContainerWindow::onItemSelected(int index) + { + if (mDragAndDrop->mIsOnDragAndDrop) + { + if (!dynamic_cast(mModel)) + dropItem(); + return; + } - mDragAndDrop->mDraggedWidget = mSelectedItem; - static_cast(mSelectedItem)->setImageTexture(""); // remove the background texture (not visible during drag) - static_cast(mSelectedItem->getChildAt(0)->getChildAt(0))->setCaption( - getCountString(mDragAndDrop->mDraggedCount)); + const ItemStack& item = mSortModel->getItem(index); - drawItems(); + MWWorld::Ptr object = item.mBase; + int count = item.mCount; + bool shift = MyGUI::InputManager::getInstance().isShiftPressed(); + if (MyGUI::InputManager::getInstance().isControlPressed()) + count = 1; - MWBase::Environment::get().getWindowManager()->setDragDrop(true); -} + mSelectedItem = mSortModel->mapToSource(index); -void ContainerBase::onContainerClicked(MyGUI::Widget* _sender) -{ - if (mDragAndDrop == NULL) return; + if (count > 1 && !shift) + { + CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); + dialog->open(MWWorld::Class::get(object).getName(object), "#{sTake}", count); + dialog->eventOkClicked.clear(); + dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::dragItem); + } + else + dragItem (NULL, count); + } - if(mDragAndDrop->mIsOnDragAndDrop) //drop item here + void ContainerWindow::dragItem(MyGUI::Widget* sender, int count) { - MWWorld::Ptr object = *mDragAndDrop->mDraggedWidget->getUserData(); - MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); + mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count); + } - if (mDragAndDrop->mDraggedFrom != this) + void ContainerWindow::dropItem() + { + if (mPtr.getTypeName() == typeid(ESM::Container).name()) { - assert(object.getContainerStore() && "Item is not in a container!"); - - // check the container's Organic flag (if this is a container). container with Organic flag doesn't allow putting items inside - if (mPtr.getTypeName() == typeid(ESM::Container).name()) + // check that we don't exceed container capacity + MWWorld::Ptr item = mDragAndDrop->mItem.mBase; + float weight = MWWorld::Class::get(item).getWeight(item) * mDragAndDrop->mDraggedCount; + if (MWWorld::Class::get(mPtr).getCapacity(mPtr) < MWWorld::Class::get(mPtr).getEncumbrance(mPtr) + weight) { - MWWorld::LiveCellRef* ref = mPtr.get(); - if (ref->mBase->mFlags & ESM::Container::Organic) - { - // user notification - MWBase::Environment::get().getWindowManager()-> - messageBox("#{sContentsMessage2}", std::vector()); - - return; - } + MWBase::Environment::get().getWindowManager()->messageBox("#{sContentsMessage3}"); + return; } - int origCount = object.getRefData().getCount(); - - // check that we don't exceed the allowed weight (only for containers, not for inventory) - if (!isInventory()) + // check container organic flag + MWWorld::LiveCellRef* ref = mPtr.get(); + if (ref->mBase->mFlags & ESM::Container::Organic) { - float capacity = MWWorld::Class::get(mPtr).getCapacity(mPtr); - - // try adding the item, and if weight is exceeded, just remove it again. - object.getRefData().setCount(mDragAndDrop->mDraggedCount); - MWWorld::ContainerStoreIterator it = containerStore.add(object); - - float curWeight = MWWorld::Class::get(mPtr).getEncumbrance(mPtr); - if (curWeight > capacity) - { - it->getRefData().setCount(0); - object.getRefData().setCount(origCount); - // user notification - MWBase::Environment::get().getWindowManager()-> - messageBox("#{sContentsMessage3}", std::vector()); - - return; - } - else - { - object.getRefData().setCount(origCount - mDragAndDrop->mDraggedCount); - } - std::cout << "container weight " << curWeight << "/" << capacity << std::endl; - } - else - { - object.getRefData().setCount (mDragAndDrop->mDraggedCount); - containerStore.add(object); - object.getRefData().setCount (origCount - mDragAndDrop->mDraggedCount); + MWBase::Environment::get().getWindowManager()-> + messageBox("#{sContentsMessage2}"); + return; } } - mDragAndDrop->mIsOnDragAndDrop = false; - MyGUI::Gui::getInstance().destroyWidget(mDragAndDrop->mDraggedWidget); - drawItems(); - mDragAndDrop->mDraggedFrom->drawItems(); - - MWBase::Environment::get().getWindowManager()->setDragDrop(false); - - std::string sound = MWWorld::Class::get(object).getDownSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); + mDragAndDrop->drop(mModel, mItemView); } -} - -void ContainerBase::onMouseWheel(MyGUI::Widget* _sender, int _rel) -{ - if (mItemView->getViewOffset().left + _rel*0.3 > 0) - mItemView->setViewOffset(MyGUI::IntPoint(0, 0)); - else - mItemView->setViewOffset(MyGUI::IntPoint(mItemView->getViewOffset().left + _rel*0.3, 0)); -} - -void ContainerBase::setFilter(ContainerBase::Filter filter) -{ - mFilter = filter; - drawItems(); -} -void ContainerBase::openContainer(MWWorld::Ptr container) -{ - mPtr = container; -} - -void ContainerBase::drawItems() -{ - while (mContainerWidget->getChildCount()) - { - MyGUI::Gui::getInstance().destroyWidget(mContainerWidget->getChildAt(0)); - } - MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - - int x = 0; - int y = 0; - int maxHeight = mItemView->getSize().height - 58; - - bool onlyMagic = false; - int categories = 0; - if (mFilter == Filter_All) - categories = MWWorld::ContainerStore::Type_All; - else if (mFilter == Filter_Weapon) - categories = MWWorld::ContainerStore::Type_Weapon; - else if (mFilter == Filter_Apparel) - categories = MWWorld::ContainerStore::Type_Clothing + MWWorld::ContainerStore::Type_Armor; - else if (mFilter == Filter_Magic) + void ContainerWindow::onBackgroundSelected() { - categories = MWWorld::ContainerStore::Type_Clothing + MWWorld::ContainerStore::Type_Armor - + MWWorld::ContainerStore::Type_Weapon + MWWorld::ContainerStore::Type_Book - + MWWorld::ContainerStore::Type_Potion; - onlyMagic = true; + if (mDragAndDrop->mIsOnDragAndDrop && !dynamic_cast(mModel)) + dropItem(); } - else if (mFilter == Filter_Misc) + + void ContainerWindow::open(const MWWorld::Ptr& container, bool loot) { - categories = MWWorld::ContainerStore::Type_Miscellaneous + MWWorld::ContainerStore::Type_Book - + MWWorld::ContainerStore::Type_Ingredient + MWWorld::ContainerStore::Type_Repair - + MWWorld::ContainerStore::Type_Lockpick + MWWorld::ContainerStore::Type_Light - + MWWorld::ContainerStore::Type_Apparatus + MWWorld::ContainerStore::Type_Probe; - } - else if (mFilter == Filter_Ingredients) - categories = MWWorld::ContainerStore::Type_Ingredient; + mPtr = container; - /// \todo performance improvement: don't create/destroy all the widgets everytime the container window changes size, only reposition them + if (mPtr.getTypeName() == typeid(ESM::NPC).name() && !loot) + { + // we are stealing stuff + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + mModel = new PickpocketItemModel(player, new InventoryItemModel(container)); + } + else + mModel = new InventoryItemModel(container); - std::vector< std::pair > items; + mDisposeCorpseButton->setVisible(loot); - std::vector equippedItems = getEquippedItems(); + setTitle(MWWorld::Class::get(container).getName(container)); - // add bought items (always at the beginning) - std::vector boughtItems; - for (MWWorld::ContainerStoreIterator it (mBoughtItems.begin()); it!=mBoughtItems.end(); ++it) - { - boughtItems.push_back(*it); - } - std::sort(boughtItems.begin(), boughtItems.end(), sortItems); + mSortModel = new SortFilterItemModel(mModel); - for (std::vector::iterator it=boughtItems.begin(); - it != boughtItems.end(); ++it) - { - items.push_back( std::make_pair(*it, ItemState_Barter) ); + mItemView->setModel (mSortModel); } - // filter out the equipped items of categories we don't want - std::vector unwantedItems = equippedItems; - for (MWWorld::ContainerStoreIterator iter (containerStore.begin(categories)); iter!=containerStore.end(); ++iter) - { - std::vector::iterator found = std::find(unwantedItems.begin(), unwantedItems.end(), *iter); - if (found != unwantedItems.end()) - { - unwantedItems.erase(found); - } - } - // now erase everything that's still in unwantedItems. - for (std::vector::iterator it=unwantedItems.begin(); - it != unwantedItems.end(); ++it) - { - std::vector::iterator found = std::find(equippedItems.begin(), equippedItems.end(), *it); - assert(found != equippedItems.end()); - equippedItems.erase(found); - } - // and add the items that are left (= have the correct category) - if (!ignoreEquippedItems()) + void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender) { - for (std::vector::const_iterator it=equippedItems.begin(); - it != equippedItems.end(); ++it) + if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) { - items.push_back( std::make_pair(*it, ItemState_Equipped) ); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } } - std::vector ignoreItems = itemsToIgnore(); - - // now add the regular items - std::vector regularItems; - for (MWWorld::ContainerStoreIterator iter (containerStore.begin(categories)); iter!=containerStore.end(); ++iter) - { - if (std::find(equippedItems.begin(), equippedItems.end(), *iter) == equippedItems.end() - && std::find(ignoreItems.begin(), ignoreItems.end(), *iter) == ignoreItems.end() - && std::find(mBoughtItems.begin(), mBoughtItems.end(), *iter) == mBoughtItems.end()) - regularItems.push_back(*iter); - } - - // sort them and add - std::sort(regularItems.begin(), regularItems.end(), sortItems); - for (std::vector::const_iterator it=regularItems.begin(); it!=regularItems.end(); ++it) - { - items.push_back( std::make_pair(*it, ItemState_Normal) ); - } - - for (std::vector< std::pair >::const_iterator it=items.begin(); - it != items.end(); ++it) + void ContainerWindow::onTakeAllButtonClicked(MyGUI::Widget* _sender) { - const MWWorld::Ptr* iter = &((*it).first); - - int displayCount = iter->getRefData().getCount(); - if (mDragAndDrop != NULL && mDragAndDrop->mIsOnDragAndDrop && *iter == *mDragAndDrop->mDraggedWidget->getUserData()) - { - displayCount -= mDragAndDrop->mDraggedCount; - } - if(displayCount > 0 && !(onlyMagic && it->second != ItemState_Barter && MWWorld::Class::get(*iter).getEnchantment(*iter) == "" && iter->getTypeName() != typeid(ESM::Potion).name())) + if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) { - std::string path = std::string("icons\\"); - path += MWWorld::Class::get(*iter).getInventoryIcon(*iter); - - // background widget (for the "equipped" frame and magic item background image) - bool isMagic = (MWWorld::Class::get(*iter).getEnchantment(*iter) != ""); - MyGUI::ImageBox* backgroundWidget = mContainerWidget->createWidget("ImageBox", MyGUI::IntCoord(x, y, 42, 42), MyGUI::Align::Default); - backgroundWidget->setUserString("ToolTipType", "ItemPtr"); - backgroundWidget->setUserData(*iter); - - std::string backgroundTex = "textures\\menu_icon"; - if (isMagic) - backgroundTex += "_magic"; - if (it->second == ItemState_Normal) - { - if (!isMagic) - backgroundTex = ""; - } - else if (it->second == ItemState_Equipped) - { - backgroundTex += "_equip"; - } - else if (it->second == ItemState_Barter) + // transfer everything into the player's inventory + ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel(); + mModel->update(); + for (size_t i=0; igetItemCount(); ++i) { - backgroundTex += "_barter"; - } - if (backgroundTex != "") - backgroundTex += ".dds"; + if (i==0) + { + // play the sound of the first object + MWWorld::Ptr item = mModel->getItem(i).mBase; + std::string sound = MWWorld::Class::get(item).getUpSoundId(item); + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); + } - backgroundWidget->setImageTexture(backgroundTex); - if (it->second == ItemState_Barter && !isMagic) - backgroundWidget->setProperty("ImageCoord", "2 2 42 42"); - else - backgroundWidget->setProperty("ImageCoord", "0 0 42 42"); - backgroundWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerBase::onSelectedItem); - backgroundWidget->eventMouseWheel += MyGUI::newDelegate(this, &ContainerBase::onMouseWheel); - - // image - MyGUI::ImageBox* image = backgroundWidget->createWidget("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); - int pos = path.rfind("."); - path.erase(pos); - path.append(".dds"); - image->setImageTexture(path); - image->setNeedMouseFocus(false); - - // text widget that shows item count - MyGUI::TextBox* text = image->createWidget("SandBrightText", MyGUI::IntCoord(0, 14, 32, 18), MyGUI::Align::Default, std::string("Label")); - text->setTextAlign(MyGUI::Align::Right); - text->setNeedMouseFocus(false); - text->setTextShadow(true); - text->setTextShadowColour(MyGUI::Colour(0,0,0)); - text->setCaption(getCountString(displayCount)); - - y += 42; - if (y > maxHeight) - { - x += 42; - y = 0; + playerModel->copyItem(mModel->getItem(i), mModel->getItem(i).mCount); + mModel->removeItem(mModel->getItem(i), mModel->getItem(i).mCount); } + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } } - MyGUI::IntSize size = MyGUI::IntSize(std::max(mItemView->getSize().width, x+42), mItemView->getSize().height); - mItemView->setCanvasSize(size); - mContainerWidget->setSize(size); - - notifyContentChanged(); -} - -std::string ContainerBase::getCountString(const int count) -{ - if (count == 1) - return ""; - if (count > 9999) - return boost::lexical_cast(int(count/1000.f)) + "k"; - else - return boost::lexical_cast(count); -} - -void ContainerBase::addBarteredItem(MWWorld::Ptr item, int count) -{ - int origCount = item.getRefData().getCount(); - item.getRefData().setCount(count); - MWWorld::ContainerStoreIterator it = mBoughtItems.add(item); - item.getRefData().setCount(origCount - count); -} - -void ContainerBase::addItem(MWWorld::Ptr item, int count) -{ - MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - - int origCount = item.getRefData().getCount(); - - item.getRefData().setCount(count); - MWWorld::ContainerStoreIterator it = containerStore.add(item); - - item.getRefData().setCount(origCount - count); -} - -void ContainerBase::transferBoughtItems() -{ - MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - - for (MWWorld::ContainerStoreIterator it(mBoughtItems.begin()); it != mBoughtItems.end(); ++it) - { - containerStore.add(*it); - } -} - -void ContainerBase::returnBoughtItems(MWWorld::ContainerStore& store) -{ - for (MWWorld::ContainerStoreIterator it(mBoughtItems.begin()); it != mBoughtItems.end(); ++it) + void ContainerWindow::onDisposeCorpseButtonClicked(MyGUI::Widget *sender) { - store.add(*it); - } -} - -MWWorld::ContainerStore& ContainerBase::getContainerStore() -{ - MWWorld::ContainerStore& store = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - return store; -} - -// ------------------------------------------------------------------------------------------------ - -ContainerWindow::ContainerWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop) - : ContainerBase(dragAndDrop) - , WindowBase("openmw_container_window.layout", parWindowManager) -{ - getWidget(mTakeButton, "TakeButton"); - getWidget(mCloseButton, "CloseButton"); - - MyGUI::ScrollView* itemView; - MyGUI::Widget* containerWidget; - getWidget(containerWidget, "Items"); - getWidget(itemView, "ItemView"); - setWidgets(containerWidget, itemView); - - mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onCloseButtonClicked); - mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked); - - static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &ContainerWindow::onWindowResize); - - setCoord(200,0,600,300); -} - -ContainerWindow::~ContainerWindow() -{ -} - -void ContainerWindow::onWindowResize(MyGUI::Window* window) -{ - drawItems(); -} - -void ContainerWindow::open(MWWorld::Ptr container) -{ - openContainer(container); - setTitle(MWWorld::Class::get(container).getName(container)); - drawItems(); -} - -void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender) -{ - if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) - { - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); - } -} - -void ContainerWindow::onTakeAllButtonClicked(MyGUI::Widget* _sender) -{ - if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) - { - // transfer everything into the player's inventory - MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWWorld::ContainerStore& playerStore = MWWorld::Class::get(player).getContainerStore(player); - - int i=0; - for (MWWorld::ContainerStoreIterator iter (containerStore.begin()); iter!=containerStore.end(); ++iter) + if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) { - playerStore.add(*iter); + onTakeAllButtonClicked(mTakeButton); - if (i==0) - { - // play the sound of the first object - std::string sound = MWWorld::Class::get(*iter).getUpSoundId(*iter); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - } + if (MWWorld::Class::get(mPtr).isPersistent(mPtr)) + MWBase::Environment::get().getWindowManager()->messageBox("#{sDisposeCorpseFail}"); + else + MWBase::Environment::get().getWorld()->deleteObject(mPtr); - ++i; + mPtr = MWWorld::Ptr(); } + } - containerStore.clear(); - + void ContainerWindow::onReferenceUnavailable() + { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } -} -void ContainerWindow::onReferenceUnavailable() -{ - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 08d425032..243f77aa5 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -1,15 +1,10 @@ #ifndef MGUI_CONTAINER_H #define MGUI_CONTAINER_H -#include "../mwworld/esmstore.hpp" - -#include "window_base.hpp" +#include "windowbase.hpp" #include "referenceinterface.hpp" -#include "../mwclass/container.hpp" -#include "../mwworld/ptr.hpp" -#include "../mwworld/containerstore.hpp" - +#include "itemmodel.hpp" namespace MWWorld { @@ -26,7 +21,8 @@ namespace MWGui { class WindowManager; class ContainerWindow; - class ContainerBase; + class ItemView; + class SortFilterItemModel; } @@ -38,108 +34,44 @@ namespace MWGui bool mIsOnDragAndDrop; MyGUI::Widget* mDraggedWidget; MyGUI::Widget* mDragAndDropWidget; - ContainerBase* mDraggedFrom; + ItemModel* mSourceModel; + ItemView* mSourceView; + SortFilterItemModel* mSourceSortModel; + ItemStack mItem; int mDraggedCount; - }; - - class ContainerBase : public ReferenceInterface - { - public: - ContainerBase(DragAndDrop* dragAndDrop); - virtual ~ContainerBase(); - - enum Filter - { - Filter_All = 0x01, - Filter_Weapon = 0x02, - Filter_Apparel = 0x03, - Filter_Magic = 0x04, - Filter_Misc = 0x05, - - Filter_Ingredients = 0x06 - }; - - enum ItemState - { - ItemState_Normal = 0x01, - ItemState_Equipped = 0x02, - ItemState_Barter = 0x03 - }; - - void setWidgets(MyGUI::Widget* containerWidget, MyGUI::ScrollView* itemView); ///< only call once - - void addBarteredItem(MWWorld::Ptr item, int count); - void addItem(MWWorld::Ptr item, int count); - - void transferBoughtItems(); ///< transfer bought items into the inventory - void returnBoughtItems(MWWorld::ContainerStore& store); ///< return bought items into the specified ContainerStore - - MWWorld::ContainerStore& getContainerStore(); - MWWorld::ContainerStore& getBoughtItems() { return mBoughtItems; } - - void openContainer(MWWorld::Ptr container); - void setFilter(Filter filter); ///< set category filter - void drawItems(); - - protected: - MyGUI::ScrollView* mItemView; - MyGUI::Widget* mContainerWidget; - MyGUI::Widget* mSelectedItem; + void startDrag (int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count); + void drop (ItemModel* targetModel, ItemView* targetView); - DragAndDrop* mDragAndDrop; - - Filter mFilter; - - // bought items are put in a separate ContainerStore so that they don't stack with other (not bought) items. - MWWorld::ContainerStore mBoughtItems; - - void onSelectedItem(MyGUI::Widget* _sender); - void onContainerClicked(MyGUI::Widget* _sender); - void onMouseWheel(MyGUI::Widget* _sender, int _rel); - - /// start dragging an item (drag & drop) - void startDragItem(MyGUI::Widget* _sender, int count); - - /// sell an item from this container - void sellItem(MyGUI::Widget* _sender, int count); - - /// sell an item from this container, that was previously just bought - void sellAlreadyBoughtItem(MyGUI::Widget* _sender, int count); - - std::string getCountString(const int count); - - virtual bool isTradeWindow() { return false; } - virtual bool isInventory() { return false; } - virtual std::vector getEquippedItems() { return std::vector(); } - virtual void _unequipItem(MWWorld::Ptr item) { ; } - - virtual bool isTrading() { return false; } - - virtual void onSelectedItemImpl(MWWorld::Ptr item) { ; } - - virtual bool ignoreEquippedItems() { return false; } - virtual std::vector itemsToIgnore() { return std::vector(); } - - virtual void notifyContentChanged() { ; } + void finish(); }; - class ContainerWindow : public ContainerBase, public WindowBase + class ContainerWindow : public WindowBase, public ReferenceInterface { public: - ContainerWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop); + ContainerWindow(DragAndDrop* dragAndDrop); + + void open(const MWWorld::Ptr& container, bool loot=false); - virtual ~ContainerWindow(); + private: + DragAndDrop* mDragAndDrop; - void open(MWWorld::Ptr container); + MWGui::ItemView* mItemView; + SortFilterItemModel* mSortModel; + ItemModel* mModel; + size_t mSelectedItem; - protected: + MyGUI::Button* mDisposeCorpseButton; MyGUI::Button* mTakeButton; MyGUI::Button* mCloseButton; - void onWindowResize(MyGUI::Window* window); + void onItemSelected(int index); + void onBackgroundSelected(); + void dragItem(MyGUI::Widget* sender, int count); + void dropItem(); void onCloseButtonClicked(MyGUI::Widget* _sender); void onTakeAllButtonClicked(MyGUI::Widget* _sender); + void onDisposeCorpseButtonClicked(MyGUI::Widget* sender); virtual void onReferenceUnavailable(); }; diff --git a/apps/openmw/mwgui/containeritemmodel.cpp b/apps/openmw/mwgui/containeritemmodel.cpp new file mode 100644 index 000000000..5d23744b2 --- /dev/null +++ b/apps/openmw/mwgui/containeritemmodel.cpp @@ -0,0 +1,174 @@ +#include "containeritemmodel.hpp" + +#include "../mwworld/containerstore.hpp" +#include "../mwworld/class.hpp" + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + +namespace +{ + + bool stacks (const MWWorld::Ptr& left, const MWWorld::Ptr& right) + { + if (left == right) + return true; + + // If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure + if (left.getContainerStore() && right.getContainerStore()) + return left.getContainerStore()->stacks(left, right) + && right.getContainerStore()->stacks(left, right); + + if (left.getContainerStore()) + return left.getContainerStore()->stacks(left, right); + if (right.getContainerStore()) + return right.getContainerStore()->stacks(left, right); + + MWWorld::ContainerStore store; + return store.stacks(left, right); + } + +} + +namespace MWGui +{ + +ContainerItemModel::ContainerItemModel(const std::vector& itemSources, const std::vector& worldItems) + : mItemSources(itemSources) + , mWorldItems(worldItems) +{ + assert (mItemSources.size()); +} + +ContainerItemModel::ContainerItemModel (const MWWorld::Ptr& source) +{ + mItemSources.push_back(source); +} + +ItemStack ContainerItemModel::getItem (ModelIndex index) +{ + if (index < 0) + throw std::runtime_error("Invalid index supplied"); + if (mItems.size() <= static_cast(index)) + throw std::runtime_error("Item index out of range"); + return mItems[index]; +} + +size_t ContainerItemModel::getItemCount() +{ + return mItems.size(); +} + +ItemModel::ModelIndex ContainerItemModel::getIndex (ItemStack item) +{ + size_t i = 0; + for (std::vector::iterator it = mItems.begin(); it != mItems.end(); ++it) + { + if (*it == item) + return i; + ++i; + } + return -1; +} + +void ContainerItemModel::copyItem (const ItemStack& item, size_t count) +{ + const MWWorld::Ptr& source = mItemSources[mItemSources.size()-1]; + int origCount = item.mBase.getRefData().getCount(); + item.mBase.getRefData().setCount(count); + MWWorld::ContainerStoreIterator it = MWWorld::Class::get(source).getContainerStore(source).add(item.mBase); + if (*it != item.mBase) + item.mBase.getRefData().setCount(origCount); + else + item.mBase.getRefData().setCount(origCount + count); // item copied onto itself +} + +void ContainerItemModel::removeItem (const ItemStack& item, size_t count) +{ + int toRemove = count; + + for (std::vector::iterator source = mItemSources.begin(); source != mItemSources.end(); ++source) + { + MWWorld::ContainerStore& store = MWWorld::Class::get(*source).getContainerStore(*source); + + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + if (stacks(*it, item.mBase)) + { + int refCount = it->getRefData().getCount(); + it->getRefData().setCount(std::max(0, refCount - toRemove)); + toRemove -= refCount; + if (toRemove <= 0) + return; + } + } + } + for (std::vector::iterator source = mWorldItems.begin(); source != mWorldItems.end(); ++source) + { + if (stacks(*source, item.mBase)) + { + int refCount = source->getRefData().getCount(); + if (refCount - toRemove <= 0) + MWBase::Environment::get().getWorld()->deleteObject(*source); + else + source->getRefData().setCount(std::max(0, refCount - toRemove)); + toRemove -= refCount; + if (toRemove <= 0) + return; + } + } + + throw std::runtime_error("Not enough items to remove could be found"); +} + +void ContainerItemModel::update() +{ + mItems.clear(); + for (std::vector::iterator source = mItemSources.begin(); source != mItemSources.end(); ++source) + { + MWWorld::ContainerStore& store = MWWorld::Class::get(*source).getContainerStore(*source); + + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + std::vector::iterator itemStack = mItems.begin(); + for (; itemStack != mItems.end(); ++itemStack) + { + if (stacks(*it, itemStack->mBase)) + { + // we already have an item stack of this kind, add to it + itemStack->mCount += it->getRefData().getCount(); + break; + } + } + + if (itemStack == mItems.end()) + { + // no stack yet, create one + ItemStack newItem (*it, this, it->getRefData().getCount()); + mItems.push_back(newItem); + } + } + } + for (std::vector::iterator source = mWorldItems.begin(); source != mWorldItems.end(); ++source) + { + std::vector::iterator itemStack = mItems.begin(); + for (; itemStack != mItems.end(); ++itemStack) + { + if (stacks(*source, itemStack->mBase)) + { + // we already have an item stack of this kind, add to it + itemStack->mCount += source->getRefData().getCount(); + break; + } + } + + if (itemStack == mItems.end()) + { + // no stack yet, create one + ItemStack newItem (*source, this, source->getRefData().getCount()); + mItems.push_back(newItem); + } + } +} + +} diff --git a/apps/openmw/mwgui/containeritemmodel.hpp b/apps/openmw/mwgui/containeritemmodel.hpp new file mode 100644 index 000000000..22595582e --- /dev/null +++ b/apps/openmw/mwgui/containeritemmodel.hpp @@ -0,0 +1,38 @@ +#ifndef MWGUI_CONTAINER_ITEM_MODEL_H +#define MWGUI_CONTAINER_ITEM_MODEL_H + +#include "itemmodel.hpp" + +namespace MWGui +{ + + /// @brief The container item model supports multiple item sources, which are needed for + /// making NPCs sell items from containers owned by them + class ContainerItemModel : public ItemModel + { + public: + ContainerItemModel (const std::vector& itemSources, const std::vector& worldItems); + ///< @note The order of elements \a itemSources matters here. The first element has the highest priority for removal, + /// while the last element will be used to add new items to. + + ContainerItemModel (const MWWorld::Ptr& source); + + virtual ItemStack getItem (ModelIndex index); + virtual ModelIndex getIndex (ItemStack item); + virtual size_t getItemCount(); + + virtual void copyItem (const ItemStack& item, size_t count); + virtual void removeItem (const ItemStack& item, size_t count); + + virtual void update(); + + private: + std::vector mItemSources; + std::vector mWorldItems; + + std::vector mItems; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 61c3c358a..354de561d 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -2,13 +2,10 @@ #include -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" - namespace MWGui { - CountDialog::CountDialog(MWBase::WindowManager& parWindowManager) : - WindowModal("openmw_count_window.layout", parWindowManager) + CountDialog::CountDialog() : + WindowModal("openmw_count_window.layout") { getWidget(mSlider, "CountSlider"); getWidget(mItemEdit, "ItemEdit"); @@ -21,6 +18,8 @@ namespace MWGui mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CountDialog::onOkButtonClicked); mItemEdit->eventEditTextChange += MyGUI::newDelegate(this, &CountDialog::onEditTextChange); mSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &CountDialog::onSliderMoved); + // make sure we read the enter key being pressed to accept multiple items + mItemEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &CountDialog::onEnterKeyPressed); } void CountDialog::open(const std::string& item, const std::string& message, const int maxCount) @@ -40,6 +39,7 @@ namespace MWGui width, mMainWidget->getHeight()); + // by default, the text edit field has the focus of the keyboard MyGUI::InputManager::getInstance().setKeyFocusWidget(mItemEdit); mSlider->setScrollPosition(maxCount-1); @@ -57,7 +57,16 @@ namespace MWGui setVisible(false); } - + + // essentially duplicating what the OK button does if user presses + // Enter key + void CountDialog::onEnterKeyPressed(MyGUI::EditBox* _sender) + { + eventOkClicked(NULL, mSlider->getScrollPosition()+1); + + setVisible(false); + } + void CountDialog::onEditTextChange(MyGUI::EditBox* _sender) { if (_sender->getCaption() == "") diff --git a/apps/openmw/mwgui/countdialog.hpp b/apps/openmw/mwgui/countdialog.hpp index 80da6eea0..82a833bb7 100644 --- a/apps/openmw/mwgui/countdialog.hpp +++ b/apps/openmw/mwgui/countdialog.hpp @@ -1,14 +1,14 @@ #ifndef MWGUI_COUNTDIALOG_H #define MWGUI_COUNTDIALOG_H -#include "window_base.hpp" +#include "windowbase.hpp" namespace MWGui { class CountDialog : public WindowModal { public: - CountDialog(MWBase::WindowManager& parWindowManager); + CountDialog(); void open(const std::string& item, const std::string& message, const int maxCount); typedef MyGUI::delegates::CMultiDelegate2 EventHandle_WidgetInt; @@ -25,11 +25,12 @@ namespace MWGui MyGUI::TextBox* mLabelText; MyGUI::Button* mOkButton; MyGUI::Button* mCancelButton; - + void onCancelButtonClicked(MyGUI::Widget* _sender); void onOkButtonClicked(MyGUI::Widget* _sender); void onEditTextChange(MyGUI::EditBox* _sender); void onSliderMoved(MyGUI::ScrollBar* _sender, size_t _position); + void onEnterKeyPressed(MyGUI::EditBox* _sender); }; } diff --git a/apps/openmw/mwgui/cursor.cpp b/apps/openmw/mwgui/cursor.cpp new file mode 100644 index 000000000..c069eca15 --- /dev/null +++ b/apps/openmw/mwgui/cursor.cpp @@ -0,0 +1,129 @@ +#include "cursor.hpp" + +#include +#include +#include +#include + +#include + +namespace MWGui +{ + + + ResourceImageSetPointerFix::ResourceImageSetPointerFix() + : mImageSet(NULL) + , mRotation(0) + { + } + + ResourceImageSetPointerFix::~ResourceImageSetPointerFix() + { + } + + void ResourceImageSetPointerFix::deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version) + { + Base::deserialization(_node, _version); + + MyGUI::xml::ElementEnumerator info = _node->getElementEnumerator(); + while (info.next("Property")) + { + const std::string& key = info->findAttribute("key"); + const std::string& value = info->findAttribute("value"); + + if (key == "Point") + mPoint = MyGUI::IntPoint::parse(value); + else if (key == "Size") + mSize = MyGUI::IntSize::parse(value); + else if (key == "Rotation") + mRotation = MyGUI::utility::parseInt(value); + else if (key == "Resource") + mImageSet = MyGUI::ResourceManager::getInstance().getByName(value)->castType(); + } + } + + int ResourceImageSetPointerFix::getRotation() + { + return mRotation; + } + + void ResourceImageSetPointerFix::setImage(MyGUI::ImageBox* _image) + { + if (mImageSet != NULL) + _image->setItemResourceInfo(mImageSet->getIndexInfo(0, 0)); + } + + void ResourceImageSetPointerFix::setPosition(MyGUI::ImageBox* _image, const MyGUI::IntPoint& _point) + { + _image->setCoord(_point.left - mPoint.left, _point.top - mPoint.top, mSize.width, mSize.height); + } + + MyGUI::ResourceImageSetPtr ResourceImageSetPointerFix:: getImageSet() + { + return mImageSet; + } + + MyGUI::IntPoint ResourceImageSetPointerFix::getHotSpot() + { + return mPoint; + } + + MyGUI::IntSize ResourceImageSetPointerFix::getSize() + { + return mSize; + } + + // ---------------------------------------------------------------------------------------- + + Cursor::Cursor() + { + // hide mygui's pointer since we're rendering it ourselves (because mygui's pointer doesn't support rotation) + MyGUI::PointerManager::getInstance().setVisible(false); + + MyGUI::PointerManager::getInstance().eventChangeMousePointer += MyGUI::newDelegate(this, &Cursor::onCursorChange); + + mWidget = MyGUI::Gui::getInstance().createWidget("RotatingSkin",0,0,0,0,MyGUI::Align::Default,"Pointer",""); + + onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer()); + } + + Cursor::~Cursor() + { + } + + void Cursor::onCursorChange(const std::string &name) + { + ResourceImageSetPointerFix* imgSetPtr = dynamic_cast( + MyGUI::PointerManager::getInstance().getByName(name)); + assert(imgSetPtr != NULL); + + MyGUI::ResourceImageSet* imgSet = imgSetPtr->getImageSet(); + + std::string texture = imgSet->getIndexInfo(0,0).texture; + + mSize = imgSetPtr->getSize(); + mHotSpot = imgSetPtr->getHotSpot(); + + int rotation = imgSetPtr->getRotation(); + + mWidget->setImageTexture(texture); + MyGUI::ISubWidget* main = mWidget->getSubWidgetMain(); + MyGUI::RotatingSkin* rotatingSubskin = main->castType(); + rotatingSubskin->setCenter(MyGUI::IntPoint(mSize.width/2,mSize.height/2)); + rotatingSubskin->setAngle(Ogre::Degree(rotation).valueRadians()); + } + + void Cursor::update() + { + MyGUI::IntPoint position = MyGUI::InputManager::getInstance().getMousePosition(); + + mWidget->setPosition(position - mHotSpot); + mWidget->setSize(mSize); + } + + void Cursor::setVisible(bool visible) + { + mWidget->setVisible(visible); + } + +} diff --git a/apps/openmw/mwgui/cursor.hpp b/apps/openmw/mwgui/cursor.hpp new file mode 100644 index 000000000..badf82262 --- /dev/null +++ b/apps/openmw/mwgui/cursor.hpp @@ -0,0 +1,61 @@ +#ifndef MWGUI_CURSOR_H +#define MWGUI_CURSOR_H + +#include +#include + +namespace MWGui +{ + + /// \brief Allows us to get the members of + /// ResourceImageSetPointer that we need. + /// \example MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); + /// MyGUI::ResourceManager::getInstance().load("core.xml"); + class ResourceImageSetPointerFix : + public MyGUI::IPointer + { + MYGUI_RTTI_DERIVED( ResourceImageSetPointerFix ) + + public: + ResourceImageSetPointerFix(); + virtual ~ResourceImageSetPointerFix(); + + virtual void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version); + + virtual void setImage(MyGUI::ImageBox* _image); + virtual void setPosition(MyGUI::ImageBox* _image, const MyGUI::IntPoint& _point); + + //and now for the whole point of this class, allow us to get + //the hot spot, the image and the size of the cursor. + virtual MyGUI::ResourceImageSetPtr getImageSet(); + virtual MyGUI::IntPoint getHotSpot(); + virtual MyGUI::IntSize getSize(); + virtual int getRotation(); + + private: + MyGUI::IntPoint mPoint; + MyGUI::IntSize mSize; + MyGUI::ResourceImageSetPtr mImageSet; + int mRotation; // rotation in degrees + }; + + class Cursor + { + public: + Cursor(); + ~Cursor(); + void update (); + + void setVisible (bool visible); + + void onCursorChange (const std::string& name); + + private: + MyGUI::ImageBox* mWidget; + + MyGUI::IntSize mSize; + MyGUI::IntPoint mHotSpot; + }; +} + +#endif diff --git a/apps/openmw/mwgui/cursorreplace.cpp b/apps/openmw/mwgui/cursorreplace.cpp deleted file mode 100644 index f883a4ed7..000000000 --- a/apps/openmw/mwgui/cursorreplace.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "cursorreplace.hpp" - -#include -#include - -#include -#include -#include - -using namespace MWGui; - - -#include "MyGUI_Precompiled.h" -#include "MyGUI_ResourceImageSetPointer.h" -#include "MyGUI_ImageBox.h" -#include "MyGUI_ResourceManager.h" - - - - ResourceImageSetPointerFix::ResourceImageSetPointerFix() : - mImageSet(nullptr) - { - } - - ResourceImageSetPointerFix::~ResourceImageSetPointerFix() - { - } - - void ResourceImageSetPointerFix::deserialization(xml::ElementPtr _node, Version _version) - { - Base::deserialization(_node, _version); - - // берем детей и крутимся, основной цикл - xml::ElementEnumerator info = _node->getElementEnumerator(); - while (info.next("Property")) - { - const std::string& key = info->findAttribute("key"); - const std::string& value = info->findAttribute("value"); - - if (key == "Point") - mPoint = IntPoint::parse(value); - else if (key == "Size") - mSize = IntSize::parse(value); - else if (key == "Resource") - mImageSet = ResourceManager::getInstance().getByName(value)->castType(); - } - } - - void ResourceImageSetPointerFix::setImage(ImageBox* _image) - { - if (mImageSet != nullptr) - _image->setItemResourceInfo(mImageSet->getIndexInfo(0, 0)); - } - - void ResourceImageSetPointerFix::setPosition(ImageBox* _image, const IntPoint& _point) - { - _image->setCoord(_point.left - mPoint.left, _point.top - mPoint.top, mSize.width, mSize.height); - } - - ResourceImageSetPtr ResourceImageSetPointerFix:: getImageSet() - { - return mImageSet; - } - - IntPoint ResourceImageSetPointerFix::getHotSpot() - { - return mPoint; - } - - IntSize ResourceImageSetPointerFix::getSize() - { - return mSize; - } - -CursorReplace::CursorReplace() -{ - OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_vresize.png", 90); - OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize1.png", -45); - OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize2.png", 45); -} diff --git a/apps/openmw/mwgui/cursorreplace.hpp b/apps/openmw/mwgui/cursorreplace.hpp deleted file mode 100644 index 402eda1ab..000000000 --- a/apps/openmw/mwgui/cursorreplace.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef GAME_CURSORREPLACE_H -#define GAME_CURSORREPLACE_H - -#include -#include -#include - -using namespace MyGUI; - -namespace MWGui -{ - /// \brief A simple class that allows us to get the members of - /// ResourceImageSetPointer that we need. Use with - /// MyGUI - /// \example MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); - /// MyGUI::ResourceManager::getInstance().load("core.xml"); - class ResourceImageSetPointerFix : - public IPointer - { - MYGUI_RTTI_DERIVED( ResourceImageSetPointerFix ) - - public: - ResourceImageSetPointerFix(); - virtual ~ResourceImageSetPointerFix(); - - virtual void deserialization(xml::ElementPtr _node, Version _version); - - virtual void setImage(ImageBox* _image); - virtual void setPosition(ImageBox* _image, const IntPoint& _point); - - //and now for the whole point of this class, allow us to get - //the hot spot, the image and the size of the cursor. - virtual ResourceImageSetPtr getImageSet(); - virtual IntPoint getHotSpot(); - virtual IntSize getSize(); - - private: - IntPoint mPoint; - IntSize mSize; - ResourceImageSetPtr mImageSet; - }; - - /// \brief MyGUI does not support rotating cursors, so we have to do it manually - class CursorReplace - { - public: - CursorReplace(); - }; -} - -#endif diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index f62bf2a05..64c61abc0 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -1,503 +1,607 @@ #include "dialogue.hpp" -#include -#include - -#include +#include #include -#include "../mwworld/esmstore.hpp" - #include "../mwbase/environment.hpp" -#include "../mwbase/dialoguemanager.hpp" -#include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/world.hpp" #include "../mwmechanics/npcstats.hpp" +#include "../mwworld/class.hpp" + #include "../mwdialogue/dialoguemanagerimp.hpp" -#include "dialogue_history.hpp" #include "widgets.hpp" #include "list.hpp" #include "tradewindow.hpp" #include "spellbuyingwindow.hpp" #include "inventorywindow.hpp" #include "travelwindow.hpp" +#include "bookpage.hpp" -using namespace MWGui; -using namespace Widgets; - -/** -*Copied from the internet. -*/ - -namespace { - -std::string lower_string(const std::string& str) -{ - std::string lowerCase = Misc::StringUtils::lowerCase (str); - - return lowerCase; -} - -std::string::size_type find_str_ci(const std::string& str, const std::string& substr,size_t pos) -{ - return lower_string(str).find(lower_string(substr),pos); -} -bool sortByLength (const std::string& left, const std::string& right) +namespace { - return left.size() > right.size(); -} -} - + MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text) + { + typedef MWGui::BookTypesetter::Utf8Point point; + point begin = reinterpret_cast (text); -PersuasionDialog::PersuasionDialog(MWBase::WindowManager &parWindowManager) - : WindowModal("openmw_persuasion_dialog.layout", parWindowManager) -{ - getWidget(mCancelButton, "CancelButton"); - getWidget(mAdmireButton, "AdmireButton"); - getWidget(mIntimidateButton, "IntimidateButton"); - getWidget(mTauntButton, "TauntButton"); - getWidget(mBribe10Button, "Bribe10Button"); - getWidget(mBribe100Button, "Bribe100Button"); - getWidget(mBribe1000Button, "Bribe1000Button"); - getWidget(mGoldLabel, "GoldLabel"); - - mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onCancel); - mAdmireButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); - mIntimidateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); - mTauntButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); - mBribe10Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); - mBribe100Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); - mBribe1000Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); + return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text)); + } } -void PersuasionDialog::onCancel(MyGUI::Widget *sender) +namespace MWGui { - setVisible(false); -} -void PersuasionDialog::onPersuade(MyGUI::Widget *sender) -{ - MWBase::MechanicsManager::PersuasionType type; - if (sender == mAdmireButton) type = MWBase::MechanicsManager::PT_Admire; - else if (sender == mIntimidateButton) type = MWBase::MechanicsManager::PT_Intimidate; - else if (sender == mTauntButton) type = MWBase::MechanicsManager::PT_Taunt; - else if (sender == mBribe10Button) - { - mWindowManager.getTradeWindow()->addOrRemoveGold(-10); - type = MWBase::MechanicsManager::PT_Bribe10; - } - else if (sender == mBribe100Button) + PersuasionDialog::PersuasionDialog() + : WindowModal("openmw_persuasion_dialog.layout") { - mWindowManager.getTradeWindow()->addOrRemoveGold(-100); - type = MWBase::MechanicsManager::PT_Bribe100; + getWidget(mCancelButton, "CancelButton"); + getWidget(mAdmireButton, "AdmireButton"); + getWidget(mIntimidateButton, "IntimidateButton"); + getWidget(mTauntButton, "TauntButton"); + getWidget(mBribe10Button, "Bribe10Button"); + getWidget(mBribe100Button, "Bribe100Button"); + getWidget(mBribe1000Button, "Bribe1000Button"); + getWidget(mGoldLabel, "GoldLabel"); + + mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onCancel); + mAdmireButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); + mIntimidateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); + mTauntButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); + mBribe10Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); + mBribe100Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); + mBribe1000Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); } - else /*if (sender == mBribe1000Button)*/ + + void PersuasionDialog::onCancel(MyGUI::Widget *sender) { - mWindowManager.getTradeWindow()->addOrRemoveGold(-1000); - type = MWBase::MechanicsManager::PT_Bribe1000; + setVisible(false); } - MWBase::Environment::get().getDialogueManager()->persuade(type); - - setVisible(false); -} - -void PersuasionDialog::open() -{ - WindowModal::open(); - center(); + void PersuasionDialog::onPersuade(MyGUI::Widget *sender) + { + MWBase::MechanicsManager::PersuasionType type; + if (sender == mAdmireButton) type = MWBase::MechanicsManager::PT_Admire; + else if (sender == mIntimidateButton) type = MWBase::MechanicsManager::PT_Intimidate; + else if (sender == mTauntButton) type = MWBase::MechanicsManager::PT_Taunt; + else if (sender == mBribe10Button) + { + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-10); + type = MWBase::MechanicsManager::PT_Bribe10; + } + else if (sender == mBribe100Button) + { + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-100); + type = MWBase::MechanicsManager::PT_Bribe100; + } + else /*if (sender == mBribe1000Button)*/ + { + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-1000); + type = MWBase::MechanicsManager::PT_Bribe1000; + } - int playerGold = mWindowManager.getInventoryWindow()->getPlayerGold(); + MWBase::Environment::get().getDialogueManager()->persuade(type); - mBribe10Button->setEnabled (playerGold >= 10); - mBribe100Button->setEnabled (playerGold >= 100); - mBribe1000Button->setEnabled (playerGold >= 1000); + setVisible(false); + } - mGoldLabel->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(playerGold)); -} + void PersuasionDialog::open() + { + WindowModal::open(); + center(); -// -------------------------------------------------------------------------------------------------- + int playerGold = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold(); -DialogueWindow::DialogueWindow(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_dialogue_window.layout", parWindowManager) - , mPersuasionDialog(parWindowManager) - , mEnabled(false) - , mServices(0) -{ - // Centre dialog - center(); + mBribe10Button->setEnabled (playerGold >= 10); + mBribe100Button->setEnabled (playerGold >= 100); + mBribe1000Button->setEnabled (playerGold >= 1000); - mPersuasionDialog.setVisible(false); + mGoldLabel->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(playerGold)); + } - //History view - getWidget(mHistory, "History"); - mHistory->setOverflowToTheLeft(true); - mHistory->setMaxTextLength(1000000); - MyGUI::Widget* eventbox; + // -------------------------------------------------------------------------------------------------- - //An EditBox cannot receive mouse click events, so we use an - //invisible widget on top of the editbox to receive them - getWidget(eventbox, "EventBox"); - eventbox->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onHistoryClicked); - eventbox->eventMouseWheel += MyGUI::newDelegate(this, &DialogueWindow::onMouseWheel); + Response::Response(const std::string &text, const std::string &title) + : mTitle(title) + { + mText = text; + } - //Topics list - getWidget(mTopicsList, "TopicsList"); - mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic); + void Response::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const + { + BookTypesetter::Style* title = typesetter->createStyle("", MyGUI::Colour(223/255.f, 201/255.f, 159/255.f)); + typesetter->sectionBreak(9); + if (mTitle != "") + typesetter->write(title, to_utf8_span(mTitle.c_str())); + typesetter->sectionBreak(9); - MyGUI::ButtonPtr byeButton; - getWidget(byeButton, "ByeButton"); - byeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked); + typedef std::pair Range; + std::map hyperLinks; - getWidget(mDispositionBar, "Disposition"); - getWidget(mDispositionText,"DispositionText"); + // We need this copy for when @# hyperlinks are replaced + std::string text = mText; - static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize); -} + size_t pos_begin, pos_end; + for(;;) + { + pos_begin = text.find('@'); + if (pos_begin != std::string::npos) + pos_end = text.find('#', pos_begin); -void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender) -{ - MyGUI::ISubWidgetText* t = mHistory->getClient()->getSubWidgetText(); - if(t == nullptr) - return; + if (pos_begin != std::string::npos && pos_end != std::string::npos) + { + std::string link = text.substr(pos_begin + 1, pos_end - pos_begin - 1); + const char specialPseudoAsteriskCharacter = 127; + std::replace(link.begin(), link.end(), specialPseudoAsteriskCharacter, '*'); + std::string topicName = MWBase::Environment::get().getWindowManager()-> + getTranslationDataStorage().topicStandardForm(link); - const MyGUI::IntPoint& lastPressed = MyGUI::InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left); + std::string displayName = link; + while (displayName[displayName.size()-1] == '*') + displayName.erase(displayName.size()-1, 1); - size_t cursorPosition = t->getCursorPosition(lastPressed); - MyGUI::UString color = mHistory->getColorAtPos(cursorPosition); + text.replace(pos_begin, pos_end+1-pos_begin, displayName); - if (!mEnabled && color == "#572D21") - MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + if (topicLinks.find(Misc::StringUtils::lowerCase(topicName)) != topicLinks.end()) + hyperLinks[std::make_pair(pos_begin, pos_begin+displayName.size())] = intptr_t(topicLinks[Misc::StringUtils::lowerCase(topicName)]); + } + else + break; + } - if(color != "#B29154") - { - MyGUI::UString key = mHistory->getColorTextAt(cursorPosition); + typesetter->addContent(to_utf8_span(text.c_str())); - if(color == "#686EBA") + if (hyperLinks.size() && MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation()) { - std::map::iterator i = mHyperLinks.upper_bound(cursorPosition); - if( !mHyperLinks.empty() ) + BookTypesetter::Style* style = typesetter->createStyle("", MyGUI::Colour(202/255.f, 165/255.f, 96/255.f)); + size_t formatted = 0; // points to the first character that is not laid out yet + for (std::map::iterator it = hyperLinks.begin(); it != hyperLinks.end(); ++it) { - --i; - - if( i->first + i->second.mLength > cursorPosition) - { - MWBase::Environment::get().getDialogueManager()->keywordSelected(i->second.mTrueValue); - } - } - else - { - // the link was colored, but it is not in mHyperLinks. - // It means that those liunks are not marked with @# and found - // by topic name search - MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(key)); + intptr_t topicId = it->second; + const MyGUI::Colour linkHot (143/255.f, 155/255.f, 218/255.f); + const MyGUI::Colour linkNormal (112/255.f, 126/255.f, 207/255.f); + const MyGUI::Colour linkActive (175/255.f, 184/255.f, 228/255.f); + BookTypesetter::Style* hotStyle = typesetter->createHotStyle (style, linkNormal, linkHot, linkActive, topicId); + if (formatted < it->first.first) + typesetter->write(style, formatted, it->first.first); + typesetter->write(hotStyle, it->first.first, it->first.second); + formatted = it->first.second; } + if (formatted < text.size()) + typesetter->write(style, formatted, text.size()); } + else + { + std::string::const_iterator i = text.begin (); + KeywordSearchT::Match match; + while (i != text.end () && keywordSearch->search (i, text.end (), match)) + { + if (i != match.mBeg) + addTopicLink (typesetter, 0, i - text.begin (), match.mBeg - text.begin ()); - if(color == "#572D21") - MWBase::Environment::get().getDialogueManager()->questionAnswered(lower_string(key)); - } -} - -void DialogueWindow::onWindowResize(MyGUI::Window* _sender) -{ - mTopicsList->adjustSize(); -} + addTopicLink (typesetter, match.mValue, match.mBeg - text.begin (), match.mEnd - text.begin ()); -void DialogueWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) -{ - if (mHistory->getVScrollPosition() - _rel*0.3 < 0) - mHistory->setVScrollPosition(0); - else - mHistory->setVScrollPosition(mHistory->getVScrollPosition() - _rel*0.3); -} + i = match.mEnd; + } -void DialogueWindow::onByeClicked(MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); -} + if (i != text.end ()) + addTopicLink (typesetter, 0, i - text.begin (), text.size ()); + } + } -void DialogueWindow::onSelectTopic(std::string topic) -{ - if (!mEnabled) return; + void Response::addTopicLink(BookTypesetter::Ptr typesetter, intptr_t topicId, size_t begin, size_t end) const + { + BookTypesetter::Style* style = typesetter->createStyle("", MyGUI::Colour(202/255.f, 165/255.f, 96/255.f)); - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); + const MyGUI::Colour linkHot (143/255.f, 155/255.f, 218/255.f); + const MyGUI::Colour linkNormal (112/255.f, 126/255.f, 207/255.f); + const MyGUI::Colour linkActive (175/255.f, 184/255.f, 228/255.f); - if (topic == gmst.find("sBarter")->getString()) - { - /// \todo check if the player is allowed to trade with this actor (e.g. faction rank high enough)? - mWindowManager.pushGuiMode(GM_Barter); - mWindowManager.getTradeWindow()->startTrade(mPtr); - } - if (topic == gmst.find("sPersuasion")->getString()) - { - mPersuasionDialog.setVisible(true); + if (topicId) + style = typesetter->createHotStyle (style, linkNormal, linkHot, linkActive, topicId); + typesetter->write (style, begin, end); } - else if (topic == gmst.find("sSpells")->getString()) + + Message::Message(const std::string& text) { - mWindowManager.pushGuiMode(GM_SpellBuying); - mWindowManager.getSpellBuyingWindow()->startSpellBuying(mPtr); + mText = text; } - else if (topic == gmst.find("sTravel")->getString()) + + void Message::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const { - mWindowManager.pushGuiMode(GM_Travel); - mWindowManager.getTravelWindow()->startTravel(mPtr); + BookTypesetter::Style* title = typesetter->createStyle("", MyGUI::Colour(223/255.f, 201/255.f, 159/255.f)); + typesetter->sectionBreak(9); + typesetter->write(title, to_utf8_span(mText.c_str())); } - else if (topic == gmst.find("sSpellMakingMenuTitle")->getString()) + + // -------------------------------------------------------------------------------------------------- + + void Choice::activated() { - mWindowManager.pushGuiMode(GM_SpellCreation); - mWindowManager.startSpellMaking (mPtr); + MWBase::Environment::get().getDialogueManager()->questionAnswered(mChoiceId); } - else if (topic == gmst.find("sEnchanting")->getString()) + + void Topic::activated() { - mWindowManager.pushGuiMode(GM_Enchanting); - mWindowManager.startEnchanting (mPtr); + MWBase::Environment::get().getDialogueManager()->keywordSelected(Misc::StringUtils::lowerCase(mTopicId)); } - else if (topic == gmst.find("sServiceTrainingTitle")->getString()) + + void Goodbye::activated() { - mWindowManager.pushGuiMode(GM_Training); - mWindowManager.startTraining (mPtr); + MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); } - else - MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(topic)); -} -void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName) -{ - mEnabled = true; - mPtr = actor; - mTopicsList->setEnabled(true); - setTitle(npcName); - - mTopicsList->clear(); - mHyperLinks.clear(); - mHistory->setCaption(""); - updateOptions(); -} - -void DialogueWindow::setKeywords(std::list keyWords) -{ - mTopicsList->clear(); + // -------------------------------------------------------------------------------------------------- - bool anyService = mServices > 0; + DialogueWindow::DialogueWindow() + : WindowBase("openmw_dialogue_window.layout") + , mPersuasionDialog() + , mEnabled(false) + , mServices(0) + , mGoodbye(false) + { + // Centre dialog + center(); - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); + mPersuasionDialog.setVisible(false); - if (mPtr.getTypeName() == typeid(ESM::NPC).name()) - mTopicsList->addItem(gmst.find("sPersuasion")->getString()); + //History view + getWidget(mHistory, "History"); - if (mServices & Service_Trade) - mTopicsList->addItem(gmst.find("sBarter")->getString()); + //Topics list + getWidget(mTopicsList, "TopicsList"); + mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic); - if (mServices & Service_BuySpells) - mTopicsList->addItem(gmst.find("sSpells")->getString()); + MyGUI::Button* byeButton; + getWidget(byeButton, "ByeButton"); + byeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked); - if (mServices & Service_Travel) - mTopicsList->addItem(gmst.find("sTravel")->getString()); + getWidget(mDispositionBar, "Disposition"); + getWidget(mDispositionText,"DispositionText"); + getWidget(mScrollBar, "VScroll"); - if (mServices & Service_CreateSpells) - mTopicsList->addItem(gmst.find("sSpellmakingMenuTitle")->getString()); + mScrollBar->eventScrollChangePosition += MyGUI::newDelegate(this, &DialogueWindow::onScrollbarMoved); + mHistory->eventMouseWheel += MyGUI::newDelegate(this, &DialogueWindow::onMouseWheel); -// if (mServices & Service_Enchant) -// mTopicsList->addItem(gmst.find("sEnchanting")->getString()); + BookPage::ClickCallback callback = boost::bind (&DialogueWindow::notifyLinkClicked, this, _1); + mHistory->adviseLinkClicked(callback); - if (mServices & Service_Training) - mTopicsList->addItem(gmst.find("sServiceTrainingTitle")->getString()); + static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize); + } - if (anyService || mPtr.getTypeName() == typeid(ESM::NPC).name()) - mTopicsList->addSeparator(); + void DialogueWindow::onWindowResize(MyGUI::Window* _sender) + { + mTopicsList->adjustSize(); + updateHistory(); + } - for(std::list::iterator it = keyWords.begin(); it != keyWords.end(); ++it) + void DialogueWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) { - mTopicsList->addItem(*it); + if (!mScrollBar->getVisible()) + return; + mScrollBar->setScrollPosition(std::min(static_cast(mScrollBar->getScrollRange()-1), + std::max(0, static_cast(mScrollBar->getScrollPosition() - _rel*0.3)))); + onScrollbarMoved(mScrollBar, mScrollBar->getScrollPosition()); } - mTopicsList->adjustSize(); -} -void DialogueWindow::removeKeyword(std::string keyWord) -{ - if(mTopicsList->hasItem(keyWord)) + void DialogueWindow::onByeClicked(MyGUI::Widget* _sender) { - mTopicsList->removeItem(keyWord); + MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); } - mTopicsList->adjustSize(); -} -void addColorInString(std::string& str, const std::string& keyword,std::string color1, std::string color2) -{ - size_t pos = 0; - while((pos = find_str_ci(str,keyword, pos)) != std::string::npos) + void DialogueWindow::onSelectTopic(const std::string& topic, int id) { - // do not add color if this portion of text is already colored. + if (!mEnabled || MWBase::Environment::get().getDialogueManager()->isInChoice()) + return; + + int separatorPos = 0; + for (unsigned int i=0; igetItemCount(); ++i) { - MyGUI::TextIterator iterator (str); - MyGUI::UString colour; - while(iterator.moveNext()) + if (mTopicsList->getItemNameAt(i) == "") + separatorPos = i; + } + + if (id >= separatorPos) + MWBase::Environment::get().getDialogueManager()->keywordSelected(Misc::StringUtils::lowerCase(topic)); + else + { + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + + if (topic == gmst.find("sPersuasion")->getString()) { - size_t iteratorPos = iterator.getPosition(); - iterator.getTagColour(colour); - if (iteratorPos == pos) - break; + mPersuasionDialog.setVisible(true); + } + else if (topic == gmst.find("sCompanionShare")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Companion); + MWBase::Environment::get().getWindowManager()->showCompanionWindow(mPtr); + } + else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused()) + { + if (topic == gmst.find("sBarter")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->startTrade(mPtr); + } + else if (topic == gmst.find("sSpells")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellBuying); + MWBase::Environment::get().getWindowManager()->getSpellBuyingWindow()->startSpellBuying(mPtr); + } + else if (topic == gmst.find("sTravel")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Travel); + MWBase::Environment::get().getWindowManager()->getTravelWindow()->startTravel(mPtr); + } + else if (topic == gmst.find("sSpellMakingMenuTitle")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellCreation); + MWBase::Environment::get().getWindowManager()->startSpellMaking (mPtr); + } + else if (topic == gmst.find("sEnchanting")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting); + MWBase::Environment::get().getWindowManager()->startEnchanting (mPtr); + } + else if (topic == gmst.find("sServiceTrainingTitle")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Training); + MWBase::Environment::get().getWindowManager()->startTraining (mPtr); + } + else if (topic == gmst.find("sRepair")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_MerchantRepair); + MWBase::Environment::get().getWindowManager()->startRepair (mPtr); + } } - - if (colour == color1) - return; } - - str.insert(pos,color1); - pos += color1.length(); - pos += keyword.length(); - str.insert(pos,color2); - pos+= color2.length(); } -} -std::string DialogueWindow::parseText(const std::string& text) -{ - bool separatorReached = false; // only parse topics that are below the separator (this prevents actions like "Barter" that are not topics from getting blue-colored) + void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName) + { + mGoodbye = false; + mEnabled = true; + mPtr = actor; + mTopicsList->setEnabled(true); + setTitle(npcName); - std::vector topics; + mTopicsList->clear(); - for(unsigned int i = 0;igetItemCount();i++) - { - std::string keyWord = mTopicsList->getItemNameAt(i); - if (separatorReached) - topics.push_back(keyWord); - else if (keyWord == "") - separatorReached = true; - } + for (std::vector::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it) + delete (*it); + mHistoryContents.clear(); - // sort by length to make sure longer topics are replaced first - std::sort(topics.begin(), topics.end(), sortByLength); + for (std::vector::iterator it = mLinks.begin(); it != mLinks.end(); ++it) + delete (*it); + mLinks.clear(); - std::vector hypertext = MWDialogue::ParseHyperText(text); + updateOptions(); + } - size_t historySize = 0; - if(mHistory->getClient()->getSubWidgetText() != nullptr) + void DialogueWindow::setKeywords(std::list keyWords) { - historySize = mHistory->getOnlyText().size(); + mTopicsList->clear(); + for (std::map::iterator it = mTopicLinks.begin(); it != mTopicLinks.end(); ++it) + delete it->second; + mTopicLinks.clear(); + mKeywordSearch.clear(); + + bool isCompanion = !MWWorld::Class::get(mPtr).getScript(mPtr).empty() + && mPtr.getRefData().getLocals().getIntVar(MWWorld::Class::get(mPtr).getScript(mPtr), "companion"); + + bool anyService = mServices > 0 || isCompanion || mPtr.getTypeName() == typeid(ESM::NPC).name(); + + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + + if (mPtr.getTypeName() == typeid(ESM::NPC).name()) + mTopicsList->addItem(gmst.find("sPersuasion")->getString()); + + if (mServices & Service_Trade) + mTopicsList->addItem(gmst.find("sBarter")->getString()); + + if (mServices & Service_BuySpells) + mTopicsList->addItem(gmst.find("sSpells")->getString()); + + if (mServices & Service_Travel) + mTopicsList->addItem(gmst.find("sTravel")->getString()); + + if (mServices & Service_CreateSpells) + mTopicsList->addItem(gmst.find("sSpellmakingMenuTitle")->getString()); + + if (mServices & Service_Enchant) + mTopicsList->addItem(gmst.find("sEnchanting")->getString()); + + if (mServices & Service_Training) + mTopicsList->addItem(gmst.find("sServiceTrainingTitle")->getString()); + + if (mServices & Service_Repair) + mTopicsList->addItem(gmst.find("sRepair")->getString()); + + if (isCompanion) + mTopicsList->addItem(gmst.find("sCompanionShare")->getString()); + + if (anyService) + mTopicsList->addSeparator(); + + + for(std::list::iterator it = keyWords.begin(); it != keyWords.end(); ++it) + { + mTopicsList->addItem(*it); + + Topic* t = new Topic(*it); + mTopicLinks[Misc::StringUtils::lowerCase(*it)] = t; + + mKeywordSearch.seed(Misc::StringUtils::lowerCase(*it), intptr_t(t)); + } + mTopicsList->adjustSize(); + + updateHistory(); } - std::string result; - size_t hypertextPos = 0; - for (size_t i = 0; i < hypertext.size(); ++i) + void DialogueWindow::updateHistory(bool scrollbar) { - if (hypertext[i].mLink) + if (!scrollbar && mScrollBar->getVisible()) { - size_t asterisk_count = MWDialogue::RemovePseudoAsterisks(hypertext[i].mText); - std::string standardForm = hypertext[i].mText; - for(; asterisk_count > 0; --asterisk_count) - standardForm.append("*"); + mHistory->setSize(mHistory->getSize()+MyGUI::IntSize(mScrollBar->getWidth(),0)); + mScrollBar->setVisible(false); + } + if (scrollbar && !mScrollBar->getVisible()) + { + mHistory->setSize(mHistory->getSize()-MyGUI::IntSize(mScrollBar->getWidth(),0)); + mScrollBar->setVisible(true); + } - standardForm = - MWBase::Environment::get().getWindowManager()-> - getTranslationDataStorage().topicStandardForm(standardForm); + BookTypesetter::Ptr typesetter = BookTypesetter::create (mHistory->getWidth(), std::numeric_limits().max()); - if( std::find(topics.begin(), topics.end(), std::string(standardForm) ) != topics.end() ) - { - result.append("#686EBA").append(hypertext[i].mText).append("#B29154"); + for (std::vector::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it) + (*it)->write(typesetter, &mKeywordSearch, mTopicLinks); - mHyperLinks[historySize+hypertextPos].mLength = MyGUI::UString(hypertext[i].mText).length(); - mHyperLinks[historySize+hypertextPos].mTrueValue = lower_string(standardForm); - } - else - result += hypertext[i].mText; + + BookTypesetter::Style* body = typesetter->createStyle("", MyGUI::Colour::White); + + // choices + const MyGUI::Colour linkHot (223/255.f, 201/255.f, 159/255.f); + const MyGUI::Colour linkNormal (150/255.f, 50/255.f, 30/255.f); + const MyGUI::Colour linkActive (243/255.f, 237/255.f, 221/255.f); + for (std::map::iterator it = mChoices.begin(); it != mChoices.end(); ++it) + { + Choice* link = new Choice(it->second); + mLinks.push_back(link); + + typesetter->lineBreak(); + BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, linkNormal, linkHot, linkActive, + TypesetBook::InteractiveId(link)); + typesetter->write(questionStyle, to_utf8_span(it->first.c_str())); + } + + if (mGoodbye) + { + std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get().find("sGoodbye")->getString(); + BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, linkNormal, linkHot, linkActive, + TypesetBook::InteractiveId(mLinks.back())); + typesetter->lineBreak(); + typesetter->write(questionStyle, to_utf8_span(goodbye.c_str())); + } + + TypesetBook::Ptr book = typesetter->complete(); + mHistory->showPage(book, 0); + size_t viewHeight = mHistory->getParent()->getHeight(); + if (!scrollbar && book->getSize().second > viewHeight) + updateHistory(true); + else if (scrollbar) + { + mHistory->setSize(MyGUI::IntSize(mHistory->getWidth(), book->getSize().second)); + size_t range = book->getSize().second - viewHeight; + mScrollBar->setScrollRange(range); + mScrollBar->setScrollPosition(range-1); + mScrollBar->setTrackSize(viewHeight / static_cast(book->getSize().second) * mScrollBar->getLineSize()); + onScrollbarMoved(mScrollBar, range-1); } else { - if( !mWindowManager.getTranslationDataStorage().hasTranslation() ) + // no scrollbar + onScrollbarMoved(mScrollBar, 0); + } + } + + void DialogueWindow::notifyLinkClicked (TypesetBook::InteractiveId link) + { + reinterpret_cast(link)->activated(); + } + + void DialogueWindow::onScrollbarMoved(MyGUI::ScrollBar *sender, size_t pos) + { + mHistory->setPosition(0,-pos); + } + + void DialogueWindow::addResponse(const std::string &text, const std::string &title) + { + // This is called from the dialogue manager, so text is + // case-smashed - thus we have to retrieve the correct case + // of the title through the topic list. + std::string realTitle = title; + if (realTitle != "") + { + for (size_t i=0; igetItemCount(); ++i) { - for(std::vector::const_iterator it = topics.begin(); it != topics.end(); ++it) + std::string item = mTopicsList->getItemNameAt(i); + if (Misc::StringUtils::lowerCase(item) == title) { - addColorInString(hypertext[i].mText, *it, "#686EBA", "#B29154"); + realTitle = item; + break; } } - - result += hypertext[i].mText; } - hypertextPos += MyGUI::UString(hypertext[i].mText).length(); + mHistoryContents.push_back(new Response(text, realTitle)); + updateHistory(); } - return result; -} - -void DialogueWindow::addText(std::string text) -{ - mHistory->addDialogText("#B29154"+parseText(text)+"#B29154"); -} - -void DialogueWindow::addMessageBox(const std::string& text) -{ - mHistory->addDialogText("\n#FFFFFF"+text+"#B29154"); -} + void DialogueWindow::addMessageBox(const std::string& text) + { + mHistoryContents.push_back(new Message(text)); + updateHistory(); + } -void DialogueWindow::addTitle(std::string text) -{ - // This is called from the dialogue manager, so text is - // case-smashed - thus we have to retrieve the correct case - // of the text through the topic list. - for (size_t i=0; igetItemCount(); ++i) + void DialogueWindow::addChoice(const std::string& choice, int id) { - std::string item = mTopicsList->getItemNameAt(i); - if (lower_string(item) == text) - text = item; + mChoices[choice] = id; + updateHistory(); } - mHistory->addDialogHeading(text); -} + void DialogueWindow::clearChoices() + { + mChoices.clear(); + updateHistory(); + } -void DialogueWindow::askQuestion(std::string question) -{ - mHistory->addDialogText("#572D21"+question+"#B29154"+" "); -} + void DialogueWindow::updateOptions() + { + //Clear the list of topics + mTopicsList->clear(); -void DialogueWindow::updateOptions() -{ - //Clear the list of topics - mTopicsList->clear(); - mHyperLinks.clear(); - mHistory->eraseText(0, mHistory->getTextLength()); + if (mPtr.getTypeName() == typeid(ESM::NPC).name()) + { + mDispositionBar->setProgressRange(100); + mDispositionBar->setProgressPosition(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr)); + mDispositionText->eraseText(0, mDispositionText->getTextLength()); + mDispositionText->addText("#B29154"+boost::lexical_cast(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr))+std::string("/100")+"#B29154"); + } + } - if (mPtr.getTypeName() == typeid(ESM::NPC).name()) + void DialogueWindow::goodbye() { - mDispositionBar->setProgressRange(100); - mDispositionBar->setProgressPosition(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr)); - mDispositionText->eraseText(0, mDispositionText->getTextLength()); - mDispositionText->addText("#B29154"+boost::lexical_cast(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr))+std::string("/100")+"#B29154"); + mLinks.push_back(new Goodbye()); + mGoodbye = true; + mTopicsList->setEnabled(false); + mEnabled = false; + updateHistory(); } -} -void DialogueWindow::goodbye() -{ - mHistory->addDialogText("\n#572D21" + MWBase::Environment::get().getWorld()->getStore().get().find("sGoodbye")->getString()); - mTopicsList->setEnabled(false); - mEnabled = false; -} - -void DialogueWindow::onReferenceUnavailable() -{ - mWindowManager.removeGuiMode(GM_Dialogue); -} + void DialogueWindow::onReferenceUnavailable() + { + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); + } -void DialogueWindow::onFrame() -{ - if(mEnabled && mPtr.getTypeName() == typeid(ESM::NPC).name()) + void DialogueWindow::onFrame() { - int disp = std::max(0, std::min(100, - MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr) - + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange())); - mDispositionBar->setProgressRange(100); - mDispositionBar->setProgressPosition(disp); - mDispositionText->eraseText(0, mDispositionText->getTextLength()); - mDispositionText->addText("#B29154"+boost::lexical_cast(disp)+std::string("/100")+"#B29154"); + if(mMainWidget->getVisible() && mEnabled && mPtr.getTypeName() == typeid(ESM::NPC).name()) + { + int disp = std::max(0, std::min(100, + MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr) + + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange())); + mDispositionBar->setProgressRange(100); + mDispositionBar->setProgressPosition(disp); + mDispositionText->eraseText(0, mDispositionText->getTextLength()); + mDispositionText->addText("#B29154"+boost::lexical_cast(disp)+std::string("/100")+"#B29154"); + } } } diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 5c11c311a..befbd6eee 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -1,11 +1,12 @@ #ifndef MWGUI_DIALOGE_H #define MWGUI_DIALOGE_H -#include "window_base.hpp" +#include "windowbase.hpp" #include "referenceinterface.hpp" -#include -#include "../mwworld/ptr.hpp" +#include "bookpage.hpp" + +#include "keywordsearch.hpp" namespace MWGui { @@ -24,12 +25,13 @@ namespace MWGui namespace MWGui { - class DialogueHistory; + class DialogueHistoryViewModel; + class BookPage; class PersuasionDialog : public WindowModal { public: - PersuasionDialog(MWBase::WindowManager& parWindowManager); + PersuasionDialog(); virtual void open(); @@ -47,27 +49,75 @@ namespace MWGui void onPersuade (MyGUI::Widget* sender); }; + + struct Link + { + virtual ~Link() {} + virtual void activated () = 0; + }; + + struct Topic : Link + { + Topic(const std::string& id) : mTopicId(id) {} + std::string mTopicId; + virtual void activated (); + }; + + struct Choice : Link + { + Choice(int id) : mChoiceId(id) {} + int mChoiceId; + virtual void activated (); + }; + + struct Goodbye : Link + { + virtual void activated (); + }; + + typedef KeywordSearch KeywordSearchT; + + struct DialogueText + { + virtual ~DialogueText() {} + virtual void write (BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const = 0; + std::string mText; + }; + + struct Response : DialogueText + { + Response(const std::string& text, const std::string& title = ""); + virtual void write (BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const; + void addTopicLink (BookTypesetter::Ptr typesetter, intptr_t topicId, size_t begin, size_t end) const; + std::string mTitle; + }; + + struct Message : DialogueText + { + Message(const std::string& text); + virtual void write (BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const; + }; + class DialogueWindow: public WindowBase, public ReferenceInterface { public: - DialogueWindow(MWBase::WindowManager& parWindowManager); + DialogueWindow(); // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; - /** Event : Dialog finished, OK button clicked.\n - signature : void method()\n - */ - EventHandle_Void eventBye; + void notifyLinkClicked (TypesetBook::InteractiveId link); void startDialogue(MWWorld::Ptr actor, std::string npcName); - void stopDialogue(); void setKeywords(std::list keyWord); - void removeKeyword(std::string keyWord); - void addText(std::string text); + + void addResponse (const std::string& text, const std::string& title=""); + void addMessageBox(const std::string& text); - void addTitle(std::string text); - void askQuestion(std::string question); + + void addChoice(const std::string& choice, int id); + void clearChoices(); + void goodbye(); void onFrame(); @@ -81,43 +131,46 @@ namespace MWGui Service_CreateSpells = 0x04, Service_Enchant = 0x08, Service_Training = 0x10, - Service_Travel = 0x20 + Service_Travel = 0x20, + Service_Repair = 0x40 }; protected: - void onSelectTopic(std::string topic); + void onSelectTopic(const std::string& topic, int id); void onByeClicked(MyGUI::Widget* _sender); - void onHistoryClicked(MyGUI::Widget* _sender); void onMouseWheel(MyGUI::Widget* _sender, int _rel); void onWindowResize(MyGUI::Window* _sender); - virtual void onReferenceUnavailable(); + void onScrollbarMoved (MyGUI::ScrollBar* sender, size_t pos); - struct HyperLink - { - size_t mLength; - std::string mTrueValue; - }; + void updateHistory(bool scrollbar=false); + + virtual void onReferenceUnavailable(); private: void updateOptions(); - /** - *Helper function that add topic keyword in blue in a text. - */ - std::string parseText(const std::string& text); int mServices; bool mEnabled; - DialogueHistory* mHistory; + bool mGoodbye; + + std::vector mHistoryContents; + std::map mChoices; + + std::vector mLinks; + std::map mTopicLinks; + + KeywordSearchT mKeywordSearch; + + BookPage* mHistory; Widgets::MWList* mTopicsList; + MyGUI::ScrollBar* mScrollBar; MyGUI::ProgressPtr mDispositionBar; - MyGUI::EditPtr mDispositionText; + MyGUI::EditBox* mDispositionText; PersuasionDialog mPersuasionDialog; - - std::map mHyperLinks; }; } #endif diff --git a/apps/openmw/mwgui/dialogue_history.cpp b/apps/openmw/mwgui/dialogue_history.cpp deleted file mode 100644 index 13f72545e..000000000 --- a/apps/openmw/mwgui/dialogue_history.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "dialogue_history.hpp" - -#include "../mwbase/windowmanager.hpp" - -#include "widgets.hpp" - -#include "../mwworld/esmstore.hpp" - -#include -#include - -#include -#include - -using namespace MWGui; -using namespace Widgets; - -MyGUI::UString DialogueHistory::getColorAtPos(size_t _pos) -{ - MyGUI::UString colour = MyGUI::TextIterator::convertTagColour(getTextColour()); - MyGUI::TextIterator iterator(getCaption()); - while(iterator.moveNext()) - { - size_t pos = iterator.getPosition(); - iterator.getTagColour(colour); - if (pos < _pos) - continue; - else if (pos == _pos) - break; - } - return colour; -} - -MyGUI::UString DialogueHistory::getColorTextAt(size_t _pos) -{ - bool breakOnNext = false; - MyGUI::UString colour = MyGUI::TextIterator::convertTagColour(getTextColour()); - MyGUI::UString colour2 = colour; - MyGUI::TextIterator iterator(getCaption()); - MyGUI::TextIterator col_start = iterator; - while(iterator.moveNext()) - { - size_t pos = iterator.getPosition(); - iterator.getTagColour(colour); - if(colour != colour2) - { - if(breakOnNext) - { - return getOnlyText().substr(col_start.getPosition(), iterator.getPosition()-col_start.getPosition()); - } - col_start = iterator; - colour2 = colour; - } - if (pos < _pos) - continue; - else if (pos == _pos) - { - breakOnNext = true; - } - } - return ""; -} - -void DialogueHistory::addDialogHeading(const MyGUI::UString& parText) -{ - MyGUI::UString head("\n#D8C09A"); - head.append(parText); - head.append("#B29154\n"); - addText(head); -} - -void DialogueHistory::addDialogText(const MyGUI::UString& parText) -{ - addText(parText); - addText("\n"); -} diff --git a/apps/openmw/mwgui/dialogue_history.hpp b/apps/openmw/mwgui/dialogue_history.hpp deleted file mode 100644 index c37504af7..000000000 --- a/apps/openmw/mwgui/dialogue_history.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef MWGUI_DIALOGE_HISTORY_H -#define MWGUI_DIALOGE_HISTORY_H -#include - -namespace MWGui -{ - class DialogueHistory : public MyGUI::EditBox - { - MYGUI_RTTI_DERIVED( DialogueHistory ) - public: - Widget* getClient() { return mClient; } - MyGUI::UString getColorAtPos(size_t _pos); - MyGUI::UString getColorTextAt(size_t _pos); - void addDialogHeading(const MyGUI::UString& parText); - void addDialogText(const MyGUI::UString& parText); - }; -} -#endif - diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 3bd67ade6..98ba8ec2f 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -1,43 +1,307 @@ #include "enchantingdialog.hpp" +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwworld/player.hpp" +#include "../mwworld/manualref.hpp" +#include "../mwworld/class.hpp" + +#include "itemselection.hpp" +#include "container.hpp" +#include "inventorywindow.hpp" + +#include "sortfilteritemmodel.hpp" namespace MWGui { - EnchantingDialog::EnchantingDialog(MWBase::WindowManager &parWindowManager) - : WindowBase("openmw_enchanting_dialog.layout", parWindowManager) - , EffectEditorBase(parWindowManager) + EnchantingDialog::EnchantingDialog() + : WindowBase("openmw_enchanting_dialog.layout") + , EffectEditorBase() + , mItemSelectionDialog(NULL) { + getWidget(mName, "NameEdit"); getWidget(mCancelButton, "CancelButton"); getWidget(mAvailableEffectsList, "AvailableEffects"); getWidget(mUsedEffectsView, "UsedEffects"); + getWidget(mItemBox, "ItemBox"); + getWidget(mSoulBox, "SoulBox"); + getWidget(mEnchantmentPoints, "Enchantment"); + getWidget(mCastCost, "CastCost"); + getWidget(mCharge, "Charge"); + getWidget(mTypeButton, "TypeButton"); + getWidget(mBuyButton, "BuyButton"); + getWidget(mPrice, "PriceLabel"); + getWidget(mPriceText, "PriceTextLabel"); setWidgets(mAvailableEffectsList, mUsedEffectsView); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onCancelButtonClicked); + mItemBox->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onSelectItem); + mSoulBox->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onSelectSoul); + mBuyButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onBuyButtonClicked); + mTypeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onTypeButtonClicked); + } + + EnchantingDialog::~EnchantingDialog() + { + delete mItemSelectionDialog; } void EnchantingDialog::open() { center(); + onRemoveItem(NULL); + onRemoveSoul(NULL); + } + + void EnchantingDialog::updateLabels() + { + std::stringstream enchantCost; + enchantCost << std::setprecision(1) << std::fixed << mEnchanting.getEnchantPoints(); + mEnchantmentPoints->setCaption(enchantCost.str() + " / " + boost::lexical_cast(mEnchanting.getMaxEnchantValue())); + + mCharge->setCaption(boost::lexical_cast(mEnchanting.getGemCharge())); + + std::stringstream castCost; + castCost << std::setprecision(1) << std::fixed << mEnchanting.getCastCost(); + mCastCost->setCaption(boost::lexical_cast(castCost.str())); + + mPrice->setCaption(boost::lexical_cast(mEnchanting.getEnchantPrice())); + + switch(mEnchanting.getCastStyle()) + { + case ESM::Enchantment::CastOnce: + mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastOnce","Cast Once")); + mAddEffectDialog.constantEffect=false; + break; + case ESM::Enchantment::WhenStrikes: + mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenStrikes", "When Strikes")); + mAddEffectDialog.constantEffect=false; + break; + case ESM::Enchantment::WhenUsed: + mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenUsed", "When Used")); + mAddEffectDialog.constantEffect=false; + break; + case ESM::Enchantment::ConstantEffect: + mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastConstant", "Cast Constant")); + mAddEffectDialog.constantEffect=true; + break; + } } void EnchantingDialog::startEnchanting (MWWorld::Ptr actor) { + mEnchanting.setSelfEnchanting(false); + mEnchanting.setEnchanter(actor); + mPtr = actor; startEditing (); } + void EnchantingDialog::startSelfEnchanting(MWWorld::Ptr soulgem) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + + mEnchanting.setSelfEnchanting(true); + mEnchanting.setEnchanter(player); + + mPtr = player; + startEditing(); + mEnchanting.setSoulGem(soulgem); + + MyGUI::ImageBox* image = mSoulBox->createWidget("ImageBox", MyGUI::IntCoord(0, 0, 32, 32), MyGUI::Align::Default); + std::string path = std::string("icons\\"); + path += MWWorld::Class::get(soulgem).getInventoryIcon(soulgem); + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + image->setImageTexture (path); + image->setUserString ("ToolTipType", "ItemPtr"); + image->setUserData(soulgem); + image->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onRemoveSoul); + + mPrice->setVisible(false); + mPriceText->setVisible(false); + updateLabels(); + } + void EnchantingDialog::onReferenceUnavailable () { - mWindowManager.removeGuiMode (GM_Dialogue); - mWindowManager.removeGuiMode (GM_Enchanting); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); } void EnchantingDialog::onCancelButtonClicked(MyGUI::Widget* sender) { - mWindowManager.removeGuiMode (GM_Enchanting); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); + } + + void EnchantingDialog::onSelectItem(MyGUI::Widget *sender) + { + delete mItemSelectionDialog; + mItemSelectionDialog = new ItemSelectionDialog("#{sEnchantItems}"); + 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->setFilter(SortFilterItemModel::Filter_OnlyEnchantable); + } + + void EnchantingDialog::onItemSelected(MWWorld::Ptr item) + { + mItemSelectionDialog->setVisible(false); + + while (mItemBox->getChildCount ()) + MyGUI::Gui::getInstance ().destroyWidget (mItemBox->getChildAt(0)); + + MyGUI::ImageBox* image = mItemBox->createWidget("ImageBox", MyGUI::IntCoord(0, 0, 32, 32), MyGUI::Align::Default); + std::string path = std::string("icons\\"); + path += MWWorld::Class::get(item).getInventoryIcon(item); + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + image->setImageTexture (path); + image->setUserString ("ToolTipType", "ItemPtr"); + image->setUserData(item); + image->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onRemoveItem); + + mEnchanting.setOldItem(item); + mEnchanting.nextCastStyle(); + updateLabels(); + } + + void EnchantingDialog::onRemoveItem(MyGUI::Widget *sender) + { + while (mItemBox->getChildCount ()) + MyGUI::Gui::getInstance ().destroyWidget (mItemBox->getChildAt(0)); + mEnchanting.setOldItem(MWWorld::Ptr()); + updateLabels(); + } + + void EnchantingDialog::onItemCancel() + { + mItemSelectionDialog->setVisible(false); + } + + void EnchantingDialog::onSoulSelected(MWWorld::Ptr item) + { + mItemSelectionDialog->setVisible(false); + mEnchanting.setSoulGem(item); + + if(mEnchanting.getGemCharge()==0) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage32}"); + return; + } + + while (mSoulBox->getChildCount ()) + MyGUI::Gui::getInstance ().destroyWidget (mSoulBox->getChildAt(0)); + + MyGUI::ImageBox* image = mSoulBox->createWidget("ImageBox", MyGUI::IntCoord(0, 0, 32, 32), MyGUI::Align::Default); + std::string path = std::string("icons\\"); + path += MWWorld::Class::get(item).getInventoryIcon(item); + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + image->setImageTexture (path); + image->setUserString ("ToolTipType", "ItemPtr"); + image->setUserData(item); + image->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onRemoveSoul); + updateLabels(); + } + + void EnchantingDialog::onRemoveSoul(MyGUI::Widget *sender) + { + while (mSoulBox->getChildCount ()) + MyGUI::Gui::getInstance ().destroyWidget (mSoulBox->getChildAt(0)); + mEnchanting.setSoulGem(MWWorld::Ptr()); + updateLabels(); + } + + void EnchantingDialog::onSoulCancel() + { + mItemSelectionDialog->setVisible(false); + } + + void EnchantingDialog::onSelectSoul(MyGUI::Widget *sender) + { + delete mItemSelectionDialog; + mItemSelectionDialog = new ItemSelectionDialog("#{sSoulGemsWithSouls}"); + 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->setFilter(SortFilterItemModel::Filter_OnlyChargedSoulstones); + + //MWBase::Environment::get().getWindowManager()->messageBox("#{sInventorySelectNoSoul}"); + } + + void EnchantingDialog::notifyEffectsChanged () + { + mEffectList.mList = mEffects; + mEnchanting.setEffect(mEffectList); + updateLabels(); + } + + void EnchantingDialog::onTypeButtonClicked(MyGUI::Widget* sender) + { + mEnchanting.nextCastStyle(); + updateLabels(); + } + + void EnchantingDialog::onBuyButtonClicked(MyGUI::Widget* sender) + { + if (mEffects.size() <= 0) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage30}"); + return; + } + + if (mName->getCaption ().empty()) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage10}"); + return; + } + + if (mEnchanting.soulEmpty()) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage52}"); + return; + } + + if (mEnchanting.itemEmpty()) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage11}"); + return; + } + + if (mEnchanting.getEnchantPoints() > mEnchanting.getMaxEnchantValue()) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage29}"); + return; + } + + mEnchanting.setNewItemName(mName->getCaption()); + mEnchanting.setEffect(mEffectList); + + if (mEnchanting.getEnchantPrice() > MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage18}"); + return; + } + + int result = mEnchanting.create(); + + if(result==1) + MWBase::Environment::get().getWindowManager()->messageBox ("#{sEnchantmentMenu12}"); + else + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage34}"); + + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); } } diff --git a/apps/openmw/mwgui/enchantingdialog.hpp b/apps/openmw/mwgui/enchantingdialog.hpp index 0415c9d8d..8bad60c8e 100644 --- a/apps/openmw/mwgui/enchantingdialog.hpp +++ b/apps/openmw/mwgui/enchantingdialog.hpp @@ -1,29 +1,63 @@ #ifndef MWGUI_ENCHANTINGDIALOG_H #define MWGUI_ENCHANTINGDIALOG_H -#include "window_base.hpp" -#include "referenceinterface.hpp" #include "spellcreationdialog.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwmechanics/enchanting.hpp" + namespace MWGui { + class ItemSelectionDialog; + class EnchantingDialog : public WindowBase, public ReferenceInterface, public EffectEditorBase { public: - EnchantingDialog(MWBase::WindowManager& parWindowManager); + EnchantingDialog(); + virtual ~EnchantingDialog(); virtual void open(); void startEnchanting(MWWorld::Ptr actor); + void startSelfEnchanting(MWWorld::Ptr soulgem); protected: virtual void onReferenceUnavailable(); + virtual void notifyEffectsChanged (); void onCancelButtonClicked(MyGUI::Widget* sender); + void onSelectItem (MyGUI::Widget* sender); + void onSelectSoul (MyGUI::Widget* sender); + void onRemoveItem (MyGUI::Widget* sender); + void onRemoveSoul (MyGUI::Widget* sender); + + void onItemSelected(MWWorld::Ptr item); + void onItemCancel(); + void onSoulSelected(MWWorld::Ptr item); + void onSoulCancel(); + void onBuyButtonClicked(MyGUI::Widget* sender); + void updateLabels(); + void onTypeButtonClicked(MyGUI::Widget* sender); + + ItemSelectionDialog* mItemSelectionDialog; MyGUI::Button* mCancelButton; + MyGUI::ImageBox* mItemBox; + MyGUI::ImageBox* mSoulBox; + + MyGUI::Button* mTypeButton; + MyGUI::Button* mBuyButton; + + MyGUI::TextBox* mName; + MyGUI::TextBox* mEnchantmentPoints; + MyGUI::TextBox* mCastCost; + MyGUI::TextBox* mCharge; + MyGUI::TextBox* mPrice; + MyGUI::TextBox* mPriceText; + + MWMechanics::Enchanting mEnchanting; + ESM::EffectList mEffectList; }; } diff --git a/apps/openmw/mwgui/exposedwindow.cpp b/apps/openmw/mwgui/exposedwindow.cpp new file mode 100644 index 000000000..150a8c893 --- /dev/null +++ b/apps/openmw/mwgui/exposedwindow.cpp @@ -0,0 +1,24 @@ +#include "exposedwindow.hpp" + +namespace MWGui +{ + MyGUI::VectorWidgetPtr ExposedWindow::getSkinWidgetsByName (const std::string &name) + { + return MyGUI::Widget::getSkinWidgetsByName (name); + } + + MyGUI::Widget* ExposedWindow::getSkinWidget(const std::string & _name, bool _throw) + { + MyGUI::VectorWidgetPtr widgets = getSkinWidgetsByName (_name); + + if (widgets.empty()) + { + MYGUI_ASSERT( ! _throw, "widget name '" << _name << "' not found in skin of layout '" << getName() << "'"); + return NULL; + } + else + { + return widgets[0]; + } + } +} diff --git a/apps/openmw/mwgui/exposedwindow.hpp b/apps/openmw/mwgui/exposedwindow.hpp new file mode 100644 index 000000000..7df2fcb35 --- /dev/null +++ b/apps/openmw/mwgui/exposedwindow.hpp @@ -0,0 +1,26 @@ +#ifndef MWGUI_EXPOSEDWINDOW_H +#define MWGUI_EXPOSEDWINDOW_H + +#include + +namespace MWGui +{ + + /** + * @brief subclass to provide access to some Widget internals. + */ + class ExposedWindow : public MyGUI::Window + { + MYGUI_RTTI_DERIVED(ExposedWindow) + + public: + MyGUI::VectorWidgetPtr getSkinWidgetsByName (const std::string &name); + + MyGUI::Widget* getSkinWidget(const std::string & _name, bool _throw = true); + ///< Get a widget defined in the inner skin of this window. + }; + +} + +#endif + diff --git a/apps/openmw/mwgui/fontloader.cpp b/apps/openmw/mwgui/fontloader.cpp new file mode 100644 index 000000000..ff160105a --- /dev/null +++ b/apps/openmw/mwgui/fontloader.cpp @@ -0,0 +1,238 @@ +#include "fontloader.hpp" + +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace +{ + unsigned long utf8ToUnicode(const std::string& utf8) + { + size_t i = 0; + unsigned long unicode; + size_t todo; + unsigned char ch = utf8[i++]; + if (ch <= 0x7F) + { + unicode = ch; + todo = 0; + } + else if (ch <= 0xBF) + { + throw std::logic_error("not a UTF-8 string"); + } + else if (ch <= 0xDF) + { + unicode = ch&0x1F; + todo = 1; + } + else if (ch <= 0xEF) + { + unicode = ch&0x0F; + todo = 2; + } + else if (ch <= 0xF7) + { + unicode = ch&0x07; + todo = 3; + } + else + { + throw std::logic_error("not a UTF-8 string"); + } + for (size_t j = 0; j < todo; ++j) + { + unsigned char ch = utf8[i++]; + if (ch < 0x80 || ch > 0xBF) + throw std::logic_error("not a UTF-8 string"); + unicode <<= 6; + unicode += ch & 0x3F; + } + if (unicode >= 0xD800 && unicode <= 0xDFFF) + throw std::logic_error("not a UTF-8 string"); + if (unicode > 0x10FFFF) + throw std::logic_error("not a UTF-8 string"); + + return unicode; + } +} + +namespace MWGui +{ + + FontLoader::FontLoader(ToUTF8::FromType encoding) + { + if (encoding == ToUTF8::WINDOWS_1252) + mEncoding = ToUTF8::CP437; + else + mEncoding = encoding; + } + + void FontLoader::loadAllFonts() + { + Ogre::StringVector groups = Ogre::ResourceGroupManager::getSingleton().getResourceGroups (); + for (Ogre::StringVector::iterator it = groups.begin(); it != groups.end(); ++it) + { + Ogre::StringVectorPtr resourcesInThisGroup = Ogre::ResourceGroupManager::getSingleton ().findResourceNames (*it, "*.fnt"); + for (Ogre::StringVector::iterator resource = resourcesInThisGroup->begin(); resource != resourcesInThisGroup->end(); ++resource) + { + loadFont(*resource); + } + } + } + + + typedef struct + { + float x; + float y; + } Point; + + typedef struct + { + float u1; // appears unused, always 0 + Point top_left; + Point top_right; + Point bottom_left; + Point bottom_right; + float width; + float height; + float u2; // appears unused, always 0 + float kerning; + float ascent; + } GlyphInfo; + + void FontLoader::loadFont(const std::string &fileName) + { + Ogre::DataStreamPtr file = Ogre::ResourceGroupManager::getSingleton().openResource(fileName); + + float fontSize; + int one; + file->read(&fontSize, sizeof(fontSize)); + + file->read(&one, sizeof(int)); + assert(one == 1); + file->read(&one, sizeof(int)); + assert(one == 1); + + char name_[284]; + file->read(name_, sizeof(name_)); + std::string name(name_); + + GlyphInfo data[256]; + file->read(data, sizeof(data)); + file->close(); + + // Create the font texture + std::string bitmapFilename = "Fonts/" + std::string(name) + ".tex"; + Ogre::DataStreamPtr bitmapFile = Ogre::ResourceGroupManager::getSingleton().openResource(bitmapFilename); + + int width, height; + bitmapFile->read(&width, sizeof(int)); + bitmapFile->read(&height, sizeof(int)); + + std::vector textureData; + textureData.resize(width*height*4); + bitmapFile->read(&textureData[0], width*height*4); + bitmapFile->close(); + + std::string textureName = name; + Ogre::Image image; + image.loadDynamicImage(&textureData[0], width, height, Ogre::PF_BYTE_RGBA); + Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().createManual(textureName, + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, + width, height, 0, Ogre::PF_BYTE_RGBA); + texture->loadImage(image); + + // Register the font with MyGUI + MyGUI::ResourceManualFont* font = static_cast( + MyGUI::FactoryManager::getInstance().createObject("Resource", "ResourceManualFont")); + // We need to emulate loading from XML because the data members are private as of mygui 3.2.0 + MyGUI::xml::Document xmlDocument; + MyGUI::xml::ElementPtr root = xmlDocument.createRoot("ResourceManualFont"); + + if (name.size() >= 5 && Misc::StringUtils::ciEqual(name.substr(0, 5), "magic")) + root->addAttribute("name", "Magic Cards"); + else if (name.size() >= 7 && Misc::StringUtils::ciEqual(name.substr(0, 7), "century")) + root->addAttribute("name", "Century Gothic"); + else if (name.size() >= 7 && Misc::StringUtils::ciEqual(name.substr(0, 7), "daedric")) + root->addAttribute("name", "Daedric"); + else + return; // no point in loading it, since there is no way of using additional fonts + + MyGUI::xml::ElementPtr defaultHeight = root->createChild("Property"); + defaultHeight->addAttribute("key", "DefaultHeight"); + defaultHeight->addAttribute("value", fontSize); + MyGUI::xml::ElementPtr source = root->createChild("Property"); + source->addAttribute("key", "Source"); + source->addAttribute("value", std::string(textureName)); + MyGUI::xml::ElementPtr codes = root->createChild("Codes"); + + for(int i = 0; i < 256; i++) + { + int x1 = data[i].top_left.x*width; + int y1 = data[i].top_left.y*height; + int w = data[i].top_right.x*width - x1; + int h = data[i].bottom_left.y*height - y1; + + ToUTF8::Utf8Encoder encoder(mEncoding); + unsigned long unicodeVal = utf8ToUnicode(encoder.getUtf8(std::string(1, (unsigned char)(i)))); + + MyGUI::xml::ElementPtr code = codes->createChild("Code"); + code->addAttribute("index", unicodeVal); + code->addAttribute("coord", MyGUI::utility::toString(x1) + " " + + MyGUI::utility::toString(y1) + " " + + MyGUI::utility::toString(w) + " " + + MyGUI::utility::toString(h)); + code->addAttribute("advance", data[i].width); + code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " + + MyGUI::utility::toString((fontSize-data[i].ascent))); + + // ASCII vertical bar, use this as text input cursor + if (i == 124) + { + MyGUI::xml::ElementPtr cursorCode = codes->createChild("Code"); + cursorCode->addAttribute("index", MyGUI::FontCodeType::Cursor); + cursorCode->addAttribute("coord", MyGUI::utility::toString(x1) + " " + + MyGUI::utility::toString(y1) + " " + + MyGUI::utility::toString(w) + " " + + MyGUI::utility::toString(h)); + cursorCode->addAttribute("advance", data[i].width); + cursorCode->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " + + MyGUI::utility::toString((fontSize-data[i].ascent))); + } + } + + // These are required as well, but the fonts don't provide them + for (int i=0; i<3; ++i) + { + MyGUI::FontCodeType::Enum type; + if(i == 0) + type = MyGUI::FontCodeType::Selected; + else if (i == 1) + type = MyGUI::FontCodeType::SelectedBack; + else if (i == 2) + type = MyGUI::FontCodeType::NotDefined; + + MyGUI::xml::ElementPtr cursorCode = codes->createChild("Code"); + cursorCode->addAttribute("index", type); + cursorCode->addAttribute("coord", "0 0 0 0"); + cursorCode->addAttribute("advance", "0"); + cursorCode->addAttribute("bearing", "0 0"); + + } + + font->deserialization(root, MyGUI::Version(3,2,0)); + + MyGUI::ResourceManager::getInstance().addResource(font); + } + +} diff --git a/apps/openmw/mwgui/fontloader.hpp b/apps/openmw/mwgui/fontloader.hpp new file mode 100644 index 000000000..7954b0875 --- /dev/null +++ b/apps/openmw/mwgui/fontloader.hpp @@ -0,0 +1,25 @@ +#ifndef MWGUI_FONTLOADER_H +#define MWGUI_FONTLOADER_H + +#include + +namespace MWGui +{ + + + /// @brief loads Morrowind's .fnt/.tex fonts for use with MyGUI and Ogre + class FontLoader + { + public: + FontLoader (ToUTF8::FromType encoding); + void loadAllFonts (); + + private: + ToUTF8::FromType mEncoding; + + void loadFont (const std::string& fileName); + }; + +} + +#endif diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 4090b592d..aeff573ae 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -3,12 +3,14 @@ #include #include "../mwscript/interpretercontext.hpp" -#include "../mwworld/ptr.hpp" #include +#include +#include #include - -using namespace MWGui; +#include +#include +#include namespace { @@ -67,277 +69,341 @@ namespace return value; } + + Ogre::UTFString::unicode_char unicodeCharFromChar(char ch) + { + std::string s; + s += ch; + Ogre::UTFString string(s); + return string.getChar(0); + } + + bool is_not_empty(const std::string s) { + std::string temp = s; + boost::algorithm::trim(temp); + return !temp.empty(); + } } -std::vector BookTextParser::split(std::string text, const int width, const int height) +namespace MWGui { - std::vector result; - MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor - text = Interpreter::fixDefinesBook(text, interpreterContext); + std::vector BookTextParser::split(std::string utf8Text, const int width, const int height) + { + using Ogre::UTFString; + std::vector result; + + MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor + utf8Text = Interpreter::fixDefinesBook(utf8Text, interpreterContext); - boost::algorithm::replace_all(text, "
", "\n"); - boost::algorithm::replace_all(text, "

", "\n\n"); + boost::algorithm::replace_all(utf8Text, "\n", ""); + boost::algorithm::replace_all(utf8Text, "\r", ""); + boost::algorithm::replace_all(utf8Text, "
", "\n"); + boost::algorithm::replace_all(utf8Text, "

", "\n\n"); - const int spacing = 48; + UTFString text(utf8Text); + const int spacing = 48; - while (text.size() > 0) - { - // read in characters until we have exceeded the size, or run out of text - int currentWidth = 0; - int currentHeight = 0; - std::string currentText; - std::string currentWord; - - unsigned int i=0; - while (currentHeight <= height-spacing && i', i) == std::string::npos) - throw std::runtime_error("BookTextParser Error: Tag is not terminated"); + // read in characters until we have exceeded the size, or run out of text + int currentWidth = 0; + int currentHeight = 0; - if (text.size() > i+4 && text.substr(i, 4) == "', i)-i), false); - currentHeight += (mHeight-h); - currentWidth = 0; + const size_t tagStart = index + 1; + const size_t tagEnd = text.find('>', tagStart); + if (tagEnd == UTFString::npos) + throw std::runtime_error("BookTextParser Error: Tag is not terminated"); + const std::string tag = text.substr(tagStart, tagEnd - tagStart).asUTF8(); + + if (boost::algorithm::starts_with(tag, "IMG")) + { + const int h = mHeight; + parseImage(tag, false); + currentHeight += (mHeight - h); + currentWidth = 0; + } + else if (boost::algorithm::starts_with(tag, "FONT")) + { + parseFont(tag); + if (currentWidth != 0) { + currentHeight += currentFontHeight(); + currentWidth = 0; + } + currentWidth = 0; + } + else if (boost::algorithm::starts_with(tag, "DIV")) + { + parseDiv(tag); + if (currentWidth != 0) { + currentHeight += currentFontHeight(); + currentWidth = 0; + } + } + index = tagEnd; } - else if (text.size() > i+5 && text.substr(i, 5) == "', i)-i)); - currentHeight += 18; // keep this in sync with the font size + currentHeight += currentFontHeight(); currentWidth = 0; + currentWordStart = index; } - else if (text.size() > i+4 && text.substr(i, 4) == "', i)-i)); - currentHeight += 18; // keep this in sync with the font size - currentWidth = 0; + currentWidth += 3; // keep this in sync with the font's SpaceWidth property + currentWordStart = index; + } + else + { + currentWidth += widthForCharGlyph(ch); } - currentText += text.substr(i, text.find('>', i)-i+1); - i = text.find('>', i); - } - else if (text[i] == '\n') - { - currentHeight += 18; // keep this in sync with the font size - currentWidth = 0; - currentWord = ""; - currentText += text[i]; - } - else if (text[i] == ' ') - { - currentWidth += 3; // keep this in sync with the font's SpaceWidth property - currentWord = ""; - currentText += text[i]; - } - else - { - currentWidth += - MyGUI::FontManager::getInstance().getByName (mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont) - ->getGlyphInfo(static_cast(text[i]))->width; - currentWord += text[i]; - currentText += text[i]; - } - - if (currentWidth > width) - { - currentHeight += 18; // keep this in sync with the font size - currentWidth = 0; - - // add size of the current word - unsigned int j=0; - while (j width) { - currentWidth += - MyGUI::FontManager::getInstance().getByName (mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont) - ->getGlyphInfo(static_cast(currentWord[j]))->width; - ++j; + currentHeight += currentFontHeight(); + currentWidth = 0; + // add size of the current word + UTFString word = text.substr(currentWordStart, index - currentWordStart); + for (UTFString::const_iterator it = word.begin(), end = word.end(); it != end; ++it) + currentWidth += widthForCharGlyph(it.getCharacter()); } + index += UTFString::_utf16_char_length(ch); } + const size_t pageEnd = (currentHeight > height - spacing && currentWordStart != 0) + ? currentWordStart : index; - ++i; - } - if (currentHeight > height-spacing) - { - // remove the last word - currentText.erase(currentText.size()-currentWord.size(), currentText.size()); + result.push_back(text.substr(0, pageEnd).asUTF8()); + text.erase(0, pageEnd); } + + std::vector nonEmptyPages; + boost::copy(result | boost::adaptors::filtered(is_not_empty), std::back_inserter(nonEmptyPages)); + return nonEmptyPages; + } + + float BookTextParser::widthForCharGlyph(unsigned unicodeChar) const + { + std::string fontName(mTextStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mTextStyle.mFont); + return MyGUI::FontManager::getInstance().getByName(fontName) + ->getGlyphInfo(unicodeChar)->width; + } - result.push_back(currentText); - text.erase(0, currentText.size()); + float BookTextParser::currentFontHeight() const + { + std::string fontName(mTextStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mTextStyle.mFont); + return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight(); } - return result; -} + MyGUI::IntSize BookTextParser::parsePage(std::string text, MyGUI::Widget* parent, const int width) + { + MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor + text = Interpreter::fixDefinesBook(text, interpreterContext); -MyGUI::IntSize BookTextParser::parse(std::string text, MyGUI::Widget* parent, const int width) -{ - MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor - text = Interpreter::fixDefinesBook(text, interpreterContext); + mParent = parent; + mWidth = width; + mHeight = 0; + assert(mParent); + while (mParent->getChildCount()) + { + MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0)); + } - mParent = parent; - mWidth = width; - mHeight = 0; + // remove trailing " + if (text[text.size()-1] == '\"') + text.erase(text.size()-1); - assert(mParent); - while (mParent->getChildCount()) - { - MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0)); + parseSubText(text); + return MyGUI::IntSize(mWidth, mHeight); } + + MyGUI::IntSize BookTextParser::parseScroll(std::string text, MyGUI::Widget* parent, const int width) + { + MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor + text = Interpreter::fixDefinesBook(text, interpreterContext); - boost::algorithm::replace_all(text, "
", "\n"); - boost::algorithm::replace_all(text, "

", "\n\n"); - - // remove leading newlines - //while (text[0] == '\n') - // text.erase(0); + mParent = parent; + mWidth = width; + mHeight = 0; - // remove trailing " - if (text[text.size()-1] == '\"') - text.erase(text.size()-1); + assert(mParent); + while (mParent->getChildCount()) + { + MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0)); + } - parseSubText(text); - return MyGUI::IntSize(mWidth, mHeight); -} + boost::algorithm::replace_all(text, "\n", "\n"); + boost::algorithm::replace_all(text, "\r", "\r"); + boost::algorithm::replace_all(text, "
", "\n\n"); + boost::algorithm::replace_all(text, "

", "\n\n"); // tweaking by adding another newline to see if that spaces out better + boost::algorithm::trim_left(text); -void BookTextParser::parseImage(std::string tag, bool createWidget) -{ - int src_start = tag.find("SRC=")+5; - std::string image = tag.substr(src_start, tag.find('"', src_start)-src_start); + // remove trailing " + if (text[text.size()-1] == '\"') + text.erase(text.size()-1); - // fix texture extension to .dds - if (image.size() > 4) - { - image[image.size()-3] = 'd'; - image[image.size()-2] = 'd'; - image[image.size()-1] = 's'; + parseSubText(text); + return MyGUI::IntSize(mWidth, mHeight); } + - int width_start = tag.find("WIDTH=")+7; - int width = boost::lexical_cast(tag.substr(width_start, tag.find('"', width_start)-width_start)); - - int height_start = tag.find("HEIGHT=")+8; - int height = boost::lexical_cast(tag.substr(height_start, tag.find('"', height_start)-height_start)); - - if (createWidget) + void BookTextParser::parseImage(std::string tag, bool createWidget) { - 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())); - box->setImageTexture("bookart\\" + image); - box->setProperty("NeedMouse", "false"); - } + int src_start = tag.find("SRC=")+5; + std::string image = tag.substr(src_start, tag.find('"', src_start)-src_start); - mWidth = std::max(mWidth, width); - mHeight += height; -} + // fix texture extension to .dds + if (image.size() > 4) + { + image[image.size()-3] = 'd'; + image[image.size()-2] = 'd'; + image[image.size()-1] = 's'; + } -void BookTextParser::parseDiv(std::string tag) -{ - if (tag.find("ALIGN=") == std::string::npos) - return; - - int align_start = tag.find("ALIGN=")+7; - std::string align = tag.substr(align_start, tag.find('"', align_start)-align_start); - if (align == "CENTER") - mTextStyle.mTextAlign = MyGUI::Align::HCenter; - else if (align == "LEFT") - mTextStyle.mTextAlign = MyGUI::Align::Left; -} + int width_start = tag.find("WIDTH=")+7; + int width = boost::lexical_cast(tag.substr(width_start, tag.find('"', width_start)-width_start)); -void BookTextParser::parseFont(std::string tag) -{ - if (tag.find("COLOR=") != std::string::npos) - { - int color_start = tag.find("COLOR=")+7; - std::string color = tag.substr(color_start, tag.find('"', color_start)-color_start); + int height_start = tag.find("HEIGHT=")+8; + int height = boost::lexical_cast(tag.substr(height_start, tag.find('"', height_start)-height_start)); - mTextStyle.mColour = MyGUI::Colour( - convertFromHex(color.substr(0, 2))/255.0, - convertFromHex(color.substr(2, 2))/255.0, - convertFromHex(color.substr(4, 2))/255.0); - } - if (tag.find("FACE=") != std::string::npos) - { - int face_start = tag.find("FACE=")+6; - std::string face = tag.substr(face_start, tag.find('"', face_start)-face_start); + if (createWidget) + { + 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())); + box->setImageTexture("bookart\\" + image); + box->setProperty("NeedMouse", "false"); + } - if (face != "Magic Cards") - mTextStyle.mFont = face; + mWidth = std::max(mWidth, width); + mHeight += height; } - if (tag.find("SIZE=") != std::string::npos) + + void BookTextParser::parseDiv(std::string tag) { - /// \todo + if (tag.find("ALIGN=") == std::string::npos) + return; + + int align_start = tag.find("ALIGN=")+7; + std::string align = tag.substr(align_start, tag.find('"', align_start)-align_start); + if (align == "CENTER") + mTextStyle.mTextAlign = MyGUI::Align::HCenter; + else if (align == "LEFT") + mTextStyle.mTextAlign = MyGUI::Align::Left; } -} -void BookTextParser::parseSubText(std::string text) -{ - if (text[0] == '<') + void BookTextParser::parseFont(std::string tag) { - if (text.find('>') == std::string::npos) - throw std::runtime_error("BookTextParser Error: Tag is not terminated"); + if (tag.find("COLOR=") != std::string::npos) + { + int color_start = tag.find("COLOR=")+7; + std::string color = tag.substr(color_start, tag.find('"', color_start)-color_start); - if (text.size() > 4 && text.substr(0, 4) == "'))); - else if (text.size() > 5 && text.substr(0, 5) == "'))); - else if (text.size() > 4 && text.substr(0, 4) == "'))); + mTextStyle.mColour = MyGUI::Colour( + convertFromHex(color.substr(0, 2))/255.0, + convertFromHex(color.substr(2, 2))/255.0, + convertFromHex(color.substr(4, 2))/255.0); + } + if (tag.find("FACE=") != std::string::npos) + { + int face_start = tag.find("FACE=")+6; + std::string face = tag.substr(face_start, tag.find('"', face_start)-face_start); - text.erase(0, text.find('>')+1); + if (face != "Magic Cards") + mTextStyle.mFont = face; + } + if (tag.find("SIZE=") != std::string::npos) + { + /// \todo + } } - bool tagFound = false; - std::string realText; // real text, without tags - unsigned int i=0; - for (; i', tagStart); + if (tagEnd == std::string::npos) + throw std::runtime_error("BookTextParser Error: Tag is not terminated"); + const std::string tag = text.substr(tagStart, tagEnd - tagStart); + + if (boost::algorithm::starts_with(tag, "IMG")) + parseImage(tag); + if (boost::algorithm::starts_with(tag, "FONT")) + parseFont(tag); + if (boost::algorithm::starts_with(tag, "DIV")) + parseDiv(tag); + + text.erase(0, tagEnd + 1); + } + + size_t tagStart = std::string::npos; + std::string realText; // real text, without tags + for (size_t i = 0; i= text.size()) - throw std::runtime_error("BookTextParser Error: Tag is not terminated"); - ++i; - c = text[i]; + while (c != '>') + { + if (i >= text.size()) + throw std::runtime_error("BookTextParser Error: Tag is not terminated"); + ++i; + c = text[i]; + } + continue; + } + else + { + tagStart = i; + break; } - continue; } else - { - tagFound = true; - break; - } + realText += c; } - else - realText += c; - } - MyGUI::EditBox* box = mParent->createWidget("NormalText", - MyGUI::IntCoord(0, mHeight, mWidth, 24), MyGUI::Align::Left | MyGUI::Align::Top, - mParent->getName() + boost::lexical_cast(mParent->getChildCount())); - box->setProperty("Static", "true"); - box->setProperty("MultiLine", "true"); - box->setProperty("WordWrap", "true"); - box->setProperty("NeedMouse", "false"); - box->setMaxTextLength(realText.size()); - box->setTextAlign(mTextStyle.mTextAlign); - box->setTextColour(mTextStyle.mColour); - box->setFontName(mTextStyle.mFont); - box->setCaption(realText); - box->setSize(box->getSize().width, box->getTextSize().height); - mHeight += box->getTextSize().height; - - if (tagFound) - { - parseSubText(text.substr(i, text.size())); + MyGUI::EditBox* box = mParent->createWidget("NormalText", + MyGUI::IntCoord(0, mHeight, mWidth, 24), MyGUI::Align::Left | MyGUI::Align::Top, + mParent->getName() + boost::lexical_cast(mParent->getChildCount())); + box->setProperty("Static", "true"); + box->setProperty("MultiLine", "true"); + box->setProperty("WordWrap", "true"); + box->setProperty("NeedMouse", "false"); + box->setMaxTextLength(realText.size()); + box->setTextAlign(mTextStyle.mTextAlign); + box->setTextColour(mTextStyle.mColour); + box->setFontName(mTextStyle.mFont); + box->setCaption(realText); + box->setSize(box->getSize().width, box->getTextSize().height); + mHeight += box->getTextSize().height; + + if (tagStart != std::string::npos) + { + parseSubText(text.substr(tagStart, text.size())); + } } + } diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp index a1e115491..a32d98fe5 100644 --- a/apps/openmw/mwgui/formatting.hpp +++ b/apps/openmw/mwgui/formatting.hpp @@ -32,7 +32,16 @@ namespace MWGui * @param maximum width * @return size of the created widgets */ - MyGUI::IntSize parse(std::string text, MyGUI::Widget* parent, const int width); + MyGUI::IntSize parsePage(std::string text, MyGUI::Widget* parent, const int width); + + /** + * Parse markup as MyGUI widgets + * @param markup to parse + * @param parent for the created widgets + * @param maximum width + * @return size of the created widgets + */ + MyGUI::IntSize parseScroll(std::string text, MyGUI::Widget* parent, const int width); /** * Split the specified text into pieces that fit in the area specified by width and height parameters @@ -40,6 +49,8 @@ namespace MWGui std::vector split(std::string text, const int width, const int height); protected: + float widthForCharGlyph(unsigned unicodeChar) const; + float currentFontHeight() const; void parseSubText(std::string text); void parseImage(std::string tag, bool createWidget=true); diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index a2c3a318b..469c45936 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -1,544 +1,540 @@ #include "hud.hpp" -#include - -#include - #include #include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/class.hpp" #include "../mwworld/player.hpp" - -#include "../mwgui/widgets.hpp" +#include "../mwworld/class.hpp" #include "inventorywindow.hpp" -#include "container.hpp" #include "console.hpp" +#include "spellicons.hpp" +#include "itemmodel.hpp" +#include "container.hpp" -using namespace MWGui; - -HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) - : Layout("openmw_hud.layout") - , mHealth(NULL) - , mMagicka(NULL) - , mStamina(NULL) - , mWeapImage(NULL) - , mSpellImage(NULL) - , mWeapStatus(NULL) - , mSpellStatus(NULL) - , mEffectBox(NULL) - , mEffect1(NULL) - , mMinimap(NULL) - , mCompass(NULL) - , mCrosshair(NULL) - , mFpsBox(NULL) - , mFpsCounter(NULL) - , mTriangleCounter(NULL) - , mBatchCounter(NULL) - , mHealthManaStaminaBaseLeft(0) - , mWeapBoxBaseLeft(0) - , mSpellBoxBaseLeft(0) - , mEffectBoxBaseRight(0) - , mMinimapBoxBaseRight(0) - , mDragAndDrop(dragAndDrop) - , mCellNameTimer(0.0f) - , mCellNameBox(NULL) - , mMapVisible(true) - , mWeaponVisible(true) - , mSpellVisible(true) - , mWorldMouseOver(false) +namespace MWGui { - setCoord(0,0, width, height); - - // Energy bars - getWidget(mHealthFrame, "HealthFrame"); - getWidget(mHealth, "Health"); - getWidget(mMagicka, "Magicka"); - getWidget(mStamina, "Stamina"); - mHealthManaStaminaBaseLeft = mHealthFrame->getLeft(); - - MyGUI::Widget *healthFrame, *magickaFrame, *fatigueFrame; - getWidget(healthFrame, "HealthFrame"); - getWidget(magickaFrame, "MagickaFrame"); - getWidget(fatigueFrame, "FatigueFrame"); - healthFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); - magickaFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); - fatigueFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); - - const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - - // Item and spell images and status bars - getWidget(mWeapBox, "WeapBox"); - getWidget(mWeapImage, "WeapImage"); - getWidget(mWeapStatus, "WeapStatus"); - mWeapBoxBaseLeft = mWeapBox->getLeft(); - mWeapBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWeaponClicked); - - getWidget(mSpellBox, "SpellBox"); - getWidget(mSpellImage, "SpellImage"); - getWidget(mSpellStatus, "SpellStatus"); - mSpellBoxBaseLeft = mSpellBox->getLeft(); - mSpellBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMagicClicked); - - getWidget(mEffectBox, "EffectBox"); - getWidget(mEffect1, "Effect1"); - mEffectBoxBaseRight = viewSize.width - mEffectBox->getRight(); - mEffectBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMagicClicked); - - getWidget(mMinimapBox, "MiniMapBox"); - mMinimapBoxBaseRight = viewSize.width - mMinimapBox->getRight(); - getWidget(mMinimap, "MiniMap"); - getWidget(mCompass, "Compass"); - getWidget(mMinimapButton, "MiniMapButton"); - mMinimapButton->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked); - - getWidget(mCellNameBox, "CellName"); - getWidget(mWeaponSpellBox, "WeaponSpellName"); - - getWidget(mCrosshair, "Crosshair"); - - setFpsLevel(fpsLevel); - - getWidget(mTriangleCounter, "TriangleCounter"); - getWidget(mBatchCounter, "BatchCounter"); - - setEffect("icons\\s\\tx_s_chameleon.dds"); - - LocalMapBase::init(mMinimap, mCompass, this); - - mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked); - mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver); - mMainWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &HUD::onWorldMouseLostFocus); -} -void HUD::setFpsLevel(int level) -{ - mFpsCounter = 0; + HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) + : Layout("openmw_hud.layout") + , mHealth(NULL) + , mMagicka(NULL) + , mStamina(NULL) + , mWeapImage(NULL) + , mSpellImage(NULL) + , mWeapStatus(NULL) + , mSpellStatus(NULL) + , mEffectBox(NULL) + , mMinimap(NULL) + , mCompass(NULL) + , mCrosshair(NULL) + , mFpsBox(NULL) + , mFpsCounter(NULL) + , mTriangleCounter(NULL) + , mBatchCounter(NULL) + , mHealthManaStaminaBaseLeft(0) + , mWeapBoxBaseLeft(0) + , mSpellBoxBaseLeft(0) + , mEffectBoxBaseRight(0) + , mMinimapBoxBaseRight(0) + , mDragAndDrop(dragAndDrop) + , mCellNameTimer(0.0f) + , mCellNameBox(NULL) + , mMapVisible(true) + , mWeaponVisible(true) + , mSpellVisible(true) + , mWorldMouseOver(false) + { + setCoord(0,0, width, height); - MyGUI::Widget* fps; - getWidget(fps, "FPSBoxAdv"); - fps->setVisible(false); - getWidget(fps, "FPSBox"); - fps->setVisible(false); + // Energy bars + getWidget(mHealthFrame, "HealthFrame"); + getWidget(mHealth, "Health"); + getWidget(mMagicka, "Magicka"); + getWidget(mStamina, "Stamina"); + mHealthManaStaminaBaseLeft = mHealthFrame->getLeft(); - if (level == 2) - { - getWidget(mFpsBox, "FPSBoxAdv"); - mFpsBox->setVisible(true); - getWidget(mFpsCounter, "FPSCounterAdv"); + MyGUI::Widget *healthFrame, *magickaFrame, *fatigueFrame; + getWidget(healthFrame, "HealthFrame"); + getWidget(magickaFrame, "MagickaFrame"); + getWidget(fatigueFrame, "FatigueFrame"); + healthFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); + magickaFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); + fatigueFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); + + const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + + // Item and spell images and status bars + getWidget(mWeapBox, "WeapBox"); + getWidget(mWeapImage, "WeapImage"); + getWidget(mWeapStatus, "WeapStatus"); + mWeapBoxBaseLeft = mWeapBox->getLeft(); + mWeapBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWeaponClicked); + + getWidget(mSpellBox, "SpellBox"); + getWidget(mSpellImage, "SpellImage"); + getWidget(mSpellStatus, "SpellStatus"); + mSpellBoxBaseLeft = mSpellBox->getLeft(); + mSpellBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMagicClicked); + + getWidget(mEffectBox, "EffectBox"); + mEffectBoxBaseRight = viewSize.width - mEffectBox->getRight(); + + getWidget(mMinimapBox, "MiniMapBox"); + mMinimapBoxBaseRight = viewSize.width - mMinimapBox->getRight(); + getWidget(mMinimap, "MiniMap"); + getWidget(mCompass, "Compass"); + getWidget(mMinimapButton, "MiniMapButton"); + mMinimapButton->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked); + + getWidget(mCellNameBox, "CellName"); + getWidget(mWeaponSpellBox, "WeaponSpellName"); + + getWidget(mCrosshair, "Crosshair"); + + setFpsLevel(fpsLevel); + + getWidget(mTriangleCounter, "TriangleCounter"); + getWidget(mBatchCounter, "BatchCounter"); + + LocalMapBase::init(mMinimap, mCompass, this); + + mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked); + mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver); + mMainWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &HUD::onWorldMouseLostFocus); + + mSpellIcons = new SpellIcons(); } - else if (level == 1) + + HUD::~HUD() { - getWidget(mFpsBox, "FPSBox"); - mFpsBox->setVisible(true); - getWidget(mFpsCounter, "FPSCounter"); + delete mSpellIcons; } -} -void HUD::setFPS(float fps) -{ - if (mFpsCounter) - mFpsCounter->setCaption(boost::lexical_cast((int)fps)); -} + void HUD::setFpsLevel(int level) + { + mFpsCounter = 0; -void HUD::setTriangleCount(unsigned int count) -{ - mTriangleCounter->setCaption(boost::lexical_cast(count)); -} + MyGUI::Widget* fps; + getWidget(fps, "FPSBoxAdv"); + fps->setVisible(false); + getWidget(fps, "FPSBox"); + fps->setVisible(false); -void HUD::setBatchCount(unsigned int count) -{ - mBatchCounter->setCaption(boost::lexical_cast(count)); -} + if (level == 2) + { + getWidget(mFpsBox, "FPSBoxAdv"); + mFpsBox->setVisible(true); + getWidget(mFpsCounter, "FPSCounterAdv"); + } + else if (level == 1) + { + getWidget(mFpsBox, "FPSBox"); + mFpsBox->setVisible(true); + getWidget(mFpsCounter, "FPSCounter"); + } + } -void HUD::setEffect(const char *img) -{ - mEffect1->setImageTexture(img); -} + void HUD::setFPS(float fps) + { + if (mFpsCounter) + mFpsCounter->setCaption(boost::lexical_cast((int)fps)); + } -void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat& value) -{ - static const char *ids[] = + void HUD::setTriangleCount(unsigned int count) { - "HBar", "MBar", "FBar", 0 - }; + mTriangleCounter->setCaption(boost::lexical_cast(count)); + } - for (int i=0; ids[i]; ++i) - if (ids[i]==id) + void HUD::setBatchCount(unsigned int count) + { + mBatchCounter->setCaption(boost::lexical_cast(count)); + } + + void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat& value) + { + static const char *ids[] = { - MyGUI::Widget* w; - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); - switch (i) + "HBar", "MBar", "FBar", 0 + }; + + for (int i=0; ids[i]; ++i) + if (ids[i]==id) { - 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(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; + } } - } -} - -void HUD::onWorldClicked(MyGUI::Widget* _sender) -{ - if (!MWBase::Environment::get().getWindowManager ()->isGuiMode ()) - return; + } - if (mDragAndDrop->mIsOnDragAndDrop) + void HUD::onWorldClicked(MyGUI::Widget* _sender) { - // drop item into the gameworld - MWWorld::Ptr object = *mDragAndDrop->mDraggedWidget->getUserData(); + if (!MWBase::Environment::get().getWindowManager ()->isGuiMode ()) + return; - MWBase::World* world = MWBase::Environment::get().getWorld(); + if (mDragAndDrop->mIsOnDragAndDrop) + { + // drop item into the gameworld + MWWorld::Ptr object = mDragAndDrop->mItem.mBase; - MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition(); - float mouseX = cursorPosition.left / float(viewSize.width); - float mouseY = cursorPosition.top / float(viewSize.height); + MWBase::World* world = MWBase::Environment::get().getWorld(); - int origCount = object.getRefData().getCount(); - object.getRefData().setCount(mDragAndDrop->mDraggedCount); + MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition(); + float mouseX = cursorPosition.left / float(viewSize.width); + float mouseY = cursorPosition.top / float(viewSize.height); - if (world->canPlaceObject(mouseX, mouseY)) - world->placeObject(object, mouseX, mouseY); - else - world->dropObjectOnGround(world->getPlayer().getPlayer(), object); + int origCount = object.getRefData().getCount(); + object.getRefData().setCount(mDragAndDrop->mDraggedCount); - MyGUI::PointerManager::getInstance().setPointer("arrow"); + if (world->canPlaceObject(mouseX, mouseY)) + world->placeObject(object, mouseX, mouseY); + else + world->dropObjectOnGround(world->getPlayer().getPlayer(), object); - std::string sound = MWWorld::Class::get(object).getDownSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); + MWBase::Environment::get().getWindowManager()->changePointer("arrow"); - // remove object from the container it was coming from - object.getRefData().setCount(origCount - mDragAndDrop->mDraggedCount); + std::string sound = MWWorld::Class::get(object).getDownSoundId(object); + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - mDragAndDrop->mIsOnDragAndDrop = false; - MyGUI::Gui::getInstance().destroyWidget(mDragAndDrop->mDraggedWidget); - mDragAndDrop->mDraggedWidget = 0; + object.getRefData().setCount(origCount); - MWBase::Environment::get().getWindowManager()->setDragDrop(false); - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems(); - } - else - { - GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); + // remove object from the container it was coming from + mDragAndDrop->mSourceModel->removeItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount); + mDragAndDrop->finish(); + } + else + { + GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); - if ( (mode != GM_Console) && (mode != GM_Container) && (mode != GM_Inventory) ) - return; + if ( (mode != GM_Console) && (mode != GM_Container) && (mode != GM_Inventory) ) + return; - MWWorld::Ptr object = MWBase::Environment::get().getWorld()->getFacedObject(); + MWWorld::Ptr object = MWBase::Environment::get().getWorld()->getFacedObject(); - if (mode == GM_Console) - MWBase::Environment::get().getWindowManager()->getConsole()->setSelectedObject(object); - else if ((mode == GM_Container) || (mode == GM_Inventory)) - { - // pick up object - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object); + if (mode == GM_Console) + MWBase::Environment::get().getWindowManager()->getConsole()->setSelectedObject(object); + else if ((mode == GM_Container) || (mode == GM_Inventory)) + { + // pick up object + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object); + } } } -} -void HUD::onWorldMouseOver(MyGUI::Widget* _sender, int x, int y) -{ - if (mDragAndDrop->mIsOnDragAndDrop) + void HUD::onWorldMouseOver(MyGUI::Widget* _sender, int x, int y) { - mWorldMouseOver = false; + if (mDragAndDrop->mIsOnDragAndDrop) + { + mWorldMouseOver = false; - MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition(); - float mouseX = cursorPosition.left / float(viewSize.width); - float mouseY = cursorPosition.top / float(viewSize.height); + MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition(); + float mouseX = cursorPosition.left / float(viewSize.width); + float mouseY = cursorPosition.top / float(viewSize.height); - MWBase::World* world = MWBase::Environment::get().getWorld(); + MWBase::World* world = MWBase::Environment::get().getWorld(); - // if we can't drop the object at the wanted position, show the "drop on ground" cursor. - bool canDrop = world->canPlaceObject(mouseX, mouseY); + // if we can't drop the object at the wanted position, show the "drop on ground" cursor. + bool canDrop = world->canPlaceObject(mouseX, mouseY); - if (!canDrop) - MyGUI::PointerManager::getInstance().setPointer("drop_ground"); + if (!canDrop) + MWBase::Environment::get().getWindowManager()->changePointer("drop_ground"); + else + MWBase::Environment::get().getWindowManager()->changePointer("arrow"); + + } else - MyGUI::PointerManager::getInstance().setPointer("arrow"); + { + MWBase::Environment::get().getWindowManager()->changePointer("arrow"); + mWorldMouseOver = true; + } + } + void HUD::onWorldMouseLostFocus(MyGUI::Widget* _sender, MyGUI::Widget* _new) + { + MWBase::Environment::get().getWindowManager()->changePointer("arrow"); + mWorldMouseOver = false; } - else + + void HUD::onHMSClicked(MyGUI::Widget* _sender) { - MyGUI::PointerManager::getInstance().setPointer("arrow"); - mWorldMouseOver = true; + MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Stats); } -} -void HUD::onWorldMouseLostFocus(MyGUI::Widget* _sender, MyGUI::Widget* _new) -{ - MyGUI::PointerManager::getInstance().setPointer("arrow"); - mWorldMouseOver = false; -} + void HUD::onMapClicked(MyGUI::Widget* _sender) + { + MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Map); + } -void HUD::onHMSClicked(MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Stats); -} + void HUD::onWeaponClicked(MyGUI::Widget* _sender) + { + MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory); + } -void HUD::onMapClicked(MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Map); -} + void HUD::onMagicClicked(MyGUI::Widget* _sender) + { + MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Magic); + } -void HUD::onWeaponClicked(MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory); -} + void HUD::setCellName(const std::string& cellName) + { + if (mCellName != cellName) + { + mCellNameTimer = 5.0f; + mCellName = cellName; -void HUD::onMagicClicked(MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Magic); -} + mCellNameBox->setCaptionWithReplacing("#{sCell=" + mCellName + "}"); + mCellNameBox->setVisible(mMapVisible); + } + } -void HUD::setCellName(const std::string& cellName) -{ - if (mCellName != cellName) + void HUD::onFrame(float dt) { - mCellNameTimer = 5.0f; - mCellName = cellName; + mCellNameTimer -= dt; + mWeaponSpellTimer -= dt; + if (mCellNameTimer < 0) + mCellNameBox->setVisible(false); + if (mWeaponSpellTimer < 0) + mWeaponSpellBox->setVisible(false); + } - mCellNameBox->setCaptionWithReplacing("#{sCell=" + mCellName + "}"); - mCellNameBox->setVisible(mMapVisible); + void HUD::onResChange(int width, int height) + { + setCoord(0, 0, width, height); } -} -void HUD::onFrame(float dt) -{ - mCellNameTimer -= dt; - mWeaponSpellTimer -= dt; - if (mCellNameTimer < 0) - mCellNameBox->setVisible(false); - if (mWeaponSpellTimer < 0) - mWeaponSpellBox->setVisible(false); -} + void HUD::setSelectedSpell(const std::string& spellId, int successChancePercent) + { + const ESM::Spell* spell = + MWBase::Environment::get().getWorld()->getStore().get().find(spellId); -void HUD::onResChange(int width, int height) -{ - setCoord(0, 0, width, height); -} + std::string spellName = spell->mName; + if (spellName != mSpellName && mSpellVisible) + { + mWeaponSpellTimer = 5.0f; + mSpellName = spellName; + mWeaponSpellBox->setCaption(mSpellName); + mWeaponSpellBox->setVisible(true); + } -void HUD::setSelectedSpell(const std::string& spellId, int successChancePercent) -{ - const ESM::Spell* spell = - MWBase::Environment::get().getWorld()->getStore().get().find(spellId); + mSpellStatus->setProgressRange(100); + mSpellStatus->setProgressPosition(successChancePercent); - std::string spellName = spell->mName; - if (spellName != mSpellName && mSpellVisible) - { - mWeaponSpellTimer = 5.0f; - mSpellName = spellName; - mWeaponSpellBox->setCaption(mSpellName); - mWeaponSpellBox->setVisible(true); + if (mSpellImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); + + mSpellBox->setUserString("ToolTipType", "Spell"); + mSpellBox->setUserString("Spell", spellId); + + // use the icon of the first effect + const ESM::MagicEffect* effect = + MWBase::Environment::get().getWorld()->getStore().get().find(spell->mEffects.mList.front().mEffectID); + + std::string icon = effect->mIcon; + int slashPos = icon.find("\\"); + icon.insert(slashPos+1, "b_"); + icon = std::string("icons\\") + icon; + Widgets::fixTexturePath(icon); + mSpellImage->setImageTexture(icon); } - mSpellStatus->setProgressRange(100); - mSpellStatus->setProgressPosition(successChancePercent); + void HUD::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) + { + std::string itemName = MWWorld::Class::get(item).getName(item); + if (itemName != mSpellName && mSpellVisible) + { + mWeaponSpellTimer = 5.0f; + mSpellName = itemName; + mWeaponSpellBox->setCaption(mSpellName); + mWeaponSpellBox->setVisible(true); + } - if (mSpellImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); + mSpellStatus->setProgressRange(100); + mSpellStatus->setProgressPosition(chargePercent); - mSpellBox->setUserString("ToolTipType", "Spell"); - mSpellBox->setUserString("Spell", spellId); + if (mSpellImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); - // use the icon of the first effect - const ESM::MagicEffect* effect = - MWBase::Environment::get().getWorld()->getStore().get().find(spell->mEffects.mList.front().mEffectID); + mSpellBox->setUserString("ToolTipType", "ItemPtr"); + mSpellBox->setUserData(item); - std::string icon = effect->mIcon; - int slashPos = icon.find("\\"); - icon.insert(slashPos+1, "b_"); - icon = std::string("icons\\") + icon; - Widgets::fixTexturePath(icon); - mSpellImage->setImageTexture(icon); -} + mSpellImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); + MyGUI::ImageBox* itemBox = mSpellImage->createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1) + , MyGUI::Align::Stretch); -void HUD::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) -{ - std::string itemName = MWWorld::Class::get(item).getName(item); - if (itemName != mSpellName && mSpellVisible) - { - mWeaponSpellTimer = 5.0f; - mSpellName = itemName; - mWeaponSpellBox->setCaption(mSpellName); - mWeaponSpellBox->setVisible(true); + std::string path = std::string("icons\\"); + path+=MWWorld::Class::get(item).getInventoryIcon(item); + Widgets::fixTexturePath(path); + itemBox->setImageTexture(path); + itemBox->setNeedMouseFocus(false); } - mSpellStatus->setProgressRange(100); - mSpellStatus->setProgressPosition(chargePercent); + void HUD::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) + { + std::string itemName = MWWorld::Class::get(item).getName(item); + if (itemName != mWeaponName && mWeaponVisible) + { + mWeaponSpellTimer = 5.0f; + mWeaponName = itemName; + mWeaponSpellBox->setCaption(mWeaponName); + mWeaponSpellBox->setVisible(true); + } - if (mSpellImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); + mWeapBox->setUserString("ToolTipType", "ItemPtr"); + mWeapBox->setUserData(item); - mSpellBox->setUserString("ToolTipType", "ItemPtr"); - mSpellBox->setUserData(item); + mWeapStatus->setProgressRange(100); + mWeapStatus->setProgressPosition(durabilityPercent); - mSpellImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); - MyGUI::ImageBox* itemBox = mSpellImage->createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1) - , MyGUI::Align::Stretch); + if (mWeapImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mWeapImage->getChildAt(0)); - std::string path = std::string("icons\\"); - path+=MWWorld::Class::get(item).getInventoryIcon(item); - Widgets::fixTexturePath(path); - itemBox->setImageTexture(path); - itemBox->setNeedMouseFocus(false); -} + std::string path = std::string("icons\\"); + path+=MWWorld::Class::get(item).getInventoryIcon(item); + Widgets::fixTexturePath(path); -void HUD::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) -{ - std::string itemName = MWWorld::Class::get(item).getName(item); - if (itemName != mWeaponName && mWeaponVisible) - { - mWeaponSpellTimer = 5.0f; - mWeaponName = itemName; - mWeaponSpellBox->setCaption(mWeaponName); - mWeaponSpellBox->setVisible(true); + if (MWWorld::Class::get(item).getEnchantment(item) != "") + { + mWeapImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); + MyGUI::ImageBox* itemBox = mWeapImage->createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1) + , MyGUI::Align::Stretch); + itemBox->setImageTexture(path); + itemBox->setNeedMouseFocus(false); + } + else + mWeapImage->setImageTexture(path); } - mWeapBox->setUserString("ToolTipType", "ItemPtr"); - mWeapBox->setUserData(item); + void HUD::unsetSelectedSpell() + { + std::string spellName = "#{sNone}"; + if (spellName != mSpellName && mSpellVisible) + { + mWeaponSpellTimer = 5.0f; + mSpellName = spellName; + mWeaponSpellBox->setCaptionWithReplacing(mSpellName); + mWeaponSpellBox->setVisible(true); + } - mWeapStatus->setProgressRange(100); - mWeapStatus->setProgressPosition(durabilityPercent); + if (mSpellImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); + mSpellStatus->setProgressRange(100); + mSpellStatus->setProgressPosition(0); + mSpellImage->setImageTexture(""); + mSpellBox->clearUserStrings(); + } - if (mWeapImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(mWeapImage->getChildAt(0)); + void HUD::unsetSelectedWeapon() + { + std::string itemName = "#{sSkillHandtohand}"; + if (itemName != mWeaponName && mWeaponVisible) + { + mWeaponSpellTimer = 5.0f; + mWeaponName = itemName; + mWeaponSpellBox->setCaptionWithReplacing(mWeaponName); + mWeaponSpellBox->setVisible(true); + } - std::string path = std::string("icons\\"); - path+=MWWorld::Class::get(item).getInventoryIcon(item); - Widgets::fixTexturePath(path); + if (mWeapImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mWeapImage->getChildAt(0)); + mWeapStatus->setProgressRange(100); + mWeapStatus->setProgressPosition(0); + mWeapImage->setImageTexture("icons\\k\\stealth_handtohand.dds"); + mWeapBox->clearUserStrings(); + } - if (MWWorld::Class::get(item).getEnchantment(item) != "") + void HUD::setCrosshairVisible(bool visible) { - mWeapImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); - MyGUI::ImageBox* itemBox = mWeapImage->createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1) - , MyGUI::Align::Stretch); - itemBox->setImageTexture(path); - itemBox->setNeedMouseFocus(false); + mCrosshair->setVisible (visible); } - else - mWeapImage->setImageTexture(path); -} -void HUD::unsetSelectedSpell() -{ - std::string spellName = "#{sNone}"; - if (spellName != mSpellName && mSpellVisible) + void HUD::setHmsVisible(bool visible) { - mWeaponSpellTimer = 5.0f; - mSpellName = spellName; - mWeaponSpellBox->setCaptionWithReplacing(mSpellName); - mWeaponSpellBox->setVisible(true); + mHealth->setVisible(visible); + mMagicka->setVisible(visible); + mStamina->setVisible(visible); + updatePositions(); } - if (mSpellImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); - mSpellStatus->setProgressRange(100); - mSpellStatus->setProgressPosition(0); - mSpellImage->setImageTexture(""); - mSpellBox->clearUserStrings(); -} - -void HUD::unsetSelectedWeapon() -{ - std::string itemName = "#{sSkillHandtohand}"; - if (itemName != mWeaponName && mWeaponVisible) + void HUD::setWeapVisible(bool visible) { - mWeaponSpellTimer = 5.0f; - mWeaponName = itemName; - mWeaponSpellBox->setCaptionWithReplacing(mWeaponName); - mWeaponSpellBox->setVisible(true); + mWeapBox->setVisible(visible); + updatePositions(); } - if (mWeapImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(mWeapImage->getChildAt(0)); - mWeapStatus->setProgressRange(100); - mWeapStatus->setProgressPosition(0); - mWeapImage->setImageTexture("icons\\k\\stealth_handtohand.dds"); - mWeapBox->clearUserStrings(); -} - -void HUD::setCrosshairVisible(bool visible) -{ - mCrosshair->setVisible (visible); -} - -void HUD::setHmsVisible(bool visible) -{ - mHealth->setVisible(visible); - mMagicka->setVisible(visible); - mStamina->setVisible(visible); - updatePositions(); -} + void HUD::setSpellVisible(bool visible) + { + mSpellBox->setVisible(visible); + updatePositions(); + } -void HUD::setWeapVisible(bool visible) -{ - mWeapBox->setVisible(visible); - updatePositions(); -} + void HUD::setEffectVisible(bool visible) + { + mEffectBox->setVisible (visible); + updatePositions(); + } -void HUD::setSpellVisible(bool visible) -{ - mSpellBox->setVisible(visible); - updatePositions(); -} + void HUD::setMinimapVisible(bool visible) + { + mMinimapBox->setVisible (visible); + updatePositions(); + } -void HUD::setEffectVisible(bool visible) -{ - mEffectBox->setVisible (visible); - updatePositions(); -} + void HUD::updatePositions() + { + int weapDx = 0, spellDx = 0; + if (!mHealth->getVisible()) + spellDx = weapDx = mWeapBoxBaseLeft - mHealthManaStaminaBaseLeft; -void HUD::setMinimapVisible(bool visible) -{ - mMinimapBox->setVisible (visible); - updatePositions(); -} + if (!mWeapBox->getVisible()) + spellDx += mSpellBoxBaseLeft - mWeapBoxBaseLeft; -void HUD::updatePositions() -{ - int weapDx = 0, spellDx = 0; - if (!mHealth->getVisible()) - spellDx = weapDx = mWeapBoxBaseLeft - mHealthManaStaminaBaseLeft; + mWeaponVisible = mWeapBox->getVisible(); + mSpellVisible = mSpellBox->getVisible(); + if (!mWeaponVisible && !mSpellVisible) + mWeaponSpellBox->setVisible(false); - if (!mWeapBox->getVisible()) - spellDx += mSpellBoxBaseLeft - mWeapBoxBaseLeft; + mWeapBox->setPosition(mWeapBoxBaseLeft - weapDx, mWeapBox->getTop()); + mSpellBox->setPosition(mSpellBoxBaseLeft - spellDx, mSpellBox->getTop()); - mWeaponVisible = mWeapBox->getVisible(); - mSpellVisible = mSpellBox->getVisible(); - if (!mWeaponVisible && !mSpellVisible) - mWeaponSpellBox->setVisible(false); + const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - mWeapBox->setPosition(mWeapBoxBaseLeft - weapDx, mWeapBox->getTop()); - mSpellBox->setPosition(mSpellBoxBaseLeft - spellDx, mSpellBox->getTop()); + // effect box can have variable width -> variable left coordinate + int effectsDx = 0; + if (!mMinimapBox->getVisible ()) + effectsDx = (viewSize.width - mMinimapBoxBaseRight) - (viewSize.width - mEffectBoxBaseRight); - const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + mMapVisible = mMinimapBox->getVisible (); + mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop()); + } - // effect box can have variable width -> variable left coordinate - int effectsDx = 0; - if (!mMinimapBox->getVisible ()) - effectsDx = (viewSize.width - mMinimapBoxBaseRight) - (viewSize.width - mEffectBoxBaseRight); + void HUD::update() + { + mSpellIcons->updateWidgets(mEffectBox, true); + } - mMapVisible = mMinimapBox->getVisible (); - mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop()); } diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index 013ad59f0..1dd53683b 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -1,6 +1,4 @@ -#include "map_window.hpp" - -#include +#include "mapwindow.hpp" #include "../mwmechanics/stat.hpp" #include "../mwworld/ptr.hpp" @@ -8,12 +6,13 @@ namespace MWGui { class DragAndDrop; + class SpellIcons; class HUD : public OEngine::GUI::Layout, public LocalMapBase { public: HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop); - void setEffect(const char *img); + virtual ~HUD(); void setValue (const std::string& id, const MWMechanics::DynamicStat& value); void setFPS(float fps); void setTriangleCount(unsigned int count); @@ -43,6 +42,10 @@ namespace MWGui bool getWorldMouseOver() { return mWorldMouseOver; } + MyGUI::Widget* getEffectBox() { return mEffectBox; } + + void update(); + private: MyGUI::ProgressPtr mHealth, mMagicka, mStamina; MyGUI::Widget* mHealthFrame; @@ -51,7 +54,6 @@ namespace MWGui MyGUI::ProgressPtr mWeapStatus, mSpellStatus; MyGUI::Widget *mEffectBox, *mMinimapBox; MyGUI::Button* mMinimapButton; - MyGUI::ImageBox* mEffect1; MyGUI::ScrollView* mMinimap; MyGUI::ImageBox* mCompass; MyGUI::ImageBox* mCrosshair; @@ -60,7 +62,7 @@ namespace MWGui MyGUI::Widget* mDummy; - MyGUI::WidgetPtr mFpsBox; + MyGUI::Widget* mFpsBox; MyGUI::TextBox* mFpsCounter; MyGUI::TextBox* mTriangleCounter; MyGUI::TextBox* mBatchCounter; @@ -85,6 +87,8 @@ namespace MWGui bool mWorldMouseOver; + SpellIcons* mSpellIcons; + void onWorldClicked(MyGUI::Widget* _sender); void onWorldMouseOver(MyGUI::Widget* _sender, int x, int y); void onWorldMouseLostFocus(MyGUI::Widget* _sender, MyGUI::Widget* _new); diff --git a/apps/openmw/mwgui/imagebutton.hpp b/apps/openmw/mwgui/imagebutton.hpp index 9fce12da1..f531e2246 100644 --- a/apps/openmw/mwgui/imagebutton.hpp +++ b/apps/openmw/mwgui/imagebutton.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_IMAGEBUTTON_H #define MWGUI_IMAGEBUTTON_H -#include "MyGUI_ImageBox.h" +#include namespace MWGui { diff --git a/apps/openmw/mwgui/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp new file mode 100644 index 000000000..c78bc2c00 --- /dev/null +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -0,0 +1,97 @@ +#include "inventoryitemmodel.hpp" + +#include "../mwworld/containerstore.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/inventorystore.hpp" + +namespace MWGui +{ + +InventoryItemModel::InventoryItemModel(const MWWorld::Ptr &actor) + : mActor(actor) +{ +} + +ItemStack InventoryItemModel::getItem (ModelIndex index) +{ + if (index < 0) + throw std::runtime_error("Invalid index supplied"); + if (mItems.size() <= static_cast(index)) + throw std::runtime_error("Item index out of range"); + return mItems[index]; +} + +size_t InventoryItemModel::getItemCount() +{ + return mItems.size(); +} + +ItemModel::ModelIndex InventoryItemModel::getIndex (ItemStack item) +{ + size_t i = 0; + for (std::vector::iterator it = mItems.begin(); it != mItems.end(); ++it) + { + if (*it == item) + return i; + ++i; + } + return -1; +} + +void InventoryItemModel::copyItem (const ItemStack& item, size_t count) +{ + int origCount = item.mBase.getRefData().getCount(); + item.mBase.getRefData().setCount(count); + MWWorld::ContainerStoreIterator it = MWWorld::Class::get(mActor).getContainerStore(mActor).add(item.mBase); + if (*it != item.mBase) + item.mBase.getRefData().setCount(origCount); + else + item.mBase.getRefData().setCount(origCount + count); // item copied onto itself +} + + +void InventoryItemModel::removeItem (const ItemStack& item, size_t count) +{ + MWWorld::ContainerStore& store = MWWorld::Class::get(mActor).getContainerStore(mActor); + + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + if (*it == item.mBase) + { + if (it->getRefData().getCount() < static_cast(count)) + throw std::runtime_error("Not enough items in the stack to remove"); + it->getRefData().setCount(it->getRefData().getCount() - count); + return; + } + } + throw std::runtime_error("Item to remove not found in container store"); +} + +void InventoryItemModel::update() +{ + MWWorld::ContainerStore& store = MWWorld::Class::get(mActor).getContainerStore(mActor); + + mItems.clear(); + + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + ItemStack newItem (*it, this, it->getRefData().getCount()); + + if (mActor.getTypeName() == typeid(ESM::NPC).name()) + { + MWWorld::InventoryStore& store = MWWorld::Class::get(mActor).getInventoryStore(mActor); + for (int slot=0; slot mItems; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 5390f1602..9fa87c4b8 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -1,9 +1,6 @@ #include "inventorywindow.hpp" -#include -#include -#include -#include +#include #include @@ -12,35 +9,39 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/containerstore.hpp" -#include "../mwworld/class.hpp" #include "../mwworld/player.hpp" -#include "../mwworld/manualref.hpp" -#include "../mwworld/actiontake.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/action.hpp" -#include "widgets.hpp" #include "bookwindow.hpp" #include "scrollwindow.hpp" #include "spellwindow.hpp" +#include "itemview.hpp" +#include "inventoryitemmodel.hpp" +#include "sortfilteritemmodel.hpp" +#include "tradeitemmodel.hpp" +#include "countdialog.hpp" +#include "tradewindow.hpp" +#include "container.hpp" namespace MWGui { - InventoryWindow::InventoryWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop) - : ContainerBase(dragAndDrop) - , WindowPinnableBase("openmw_inventory_window.layout", parWindowManager) + InventoryWindow::InventoryWindow(DragAndDrop* dragAndDrop) + : WindowPinnableBase("openmw_inventory_window.layout") , mTrading(false) , mLastXSize(0) , mLastYSize(0) , mPreview(MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()) + , mPreviewDirty(true) + , mDragAndDrop(dragAndDrop) { static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &InventoryWindow::onWindowResize); getWidget(mAvatar, "Avatar"); getWidget(mAvatarImage, "AvatarImage"); getWidget(mEncumbranceBar, "EncumbranceBar"); - getWidget(mEncumbranceText, "EncumbranceBarT"); getWidget(mFilterAll, "AllButton"); getWidget(mFilterWeapon, "WeaponButton"); getWidget(mFilterApparel, "ApparelButton"); @@ -48,14 +49,15 @@ namespace MWGui getWidget(mFilterMisc, "MiscButton"); getWidget(mLeftPane, "LeftPane"); getWidget(mRightPane, "RightPane"); + getWidget(mArmorRating, "ArmorRating"); mAvatar->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onAvatarClicked); - MyGUI::ScrollView* itemView; - MyGUI::Widget* containerWidget; - getWidget(containerWidget, "Items"); - getWidget(itemView, "ItemView"); - setWidgets(containerWidget, itemView); + getWidget(mItemView, "ItemView"); + mItemView->eventItemClicked += MyGUI::newDelegate(this, &InventoryWindow::onItemSelected); + mItemView->eventBackgroundClicked += MyGUI::newDelegate(this, &InventoryWindow::onBackgroundSelected); + + updatePlayer(); mFilterAll->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged); mFilterWeapon->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged); @@ -66,23 +68,137 @@ namespace MWGui mFilterAll->setStateSelected(true); setCoord(0, 342, 498, 258); + onWindowResize(static_cast(mMainWidget)); mPreview.setup(); + } - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - openContainer(player); + void InventoryWindow::updatePlayer() + { + mPtr = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer (); + mTradeModel = new TradeItemModel(new InventoryItemModel(mPtr), MWWorld::Ptr()); + mSortModel = new SortFilterItemModel(mTradeModel); + mItemView->setModel(mSortModel); + mPreview = MWRender::InventoryPreview(mPtr); + mPreview.setup(); + } + + TradeItemModel* InventoryWindow::getTradeModel() + { + return mTradeModel; + } + + ItemModel* InventoryWindow::getModel() + { + return mTradeModel; + } + + void InventoryWindow::onBackgroundSelected() + { + if (mDragAndDrop->mIsOnDragAndDrop) + mDragAndDrop->drop(mTradeModel, mItemView); + } + + void InventoryWindow::onItemSelected (int index) + { + onItemSelectedFromSourceModel (mSortModel->mapToSource(index)); + } + + void InventoryWindow::onItemSelectedFromSourceModel (int index) + { + if (mDragAndDrop->mIsOnDragAndDrop) + { + mDragAndDrop->drop(mTradeModel, mItemView); + return; + } + + const ItemStack& item = mTradeModel->getItem(index); + + unequipItem(item.mBase); + + MWWorld::Ptr object = item.mBase; + int count = item.mCount; + bool shift = MyGUI::InputManager::getInstance().isShiftPressed(); + if (MyGUI::InputManager::getInstance().isControlPressed()) + count = 1; + + if (mTrading) + { + // check if merchant accepts item + int services = MWBase::Environment::get().getWindowManager()->getTradeWindow()->getMerchantServices(); + if (!MWWorld::Class::get(object).canSell(object, services)) + { + MWBase::Environment::get().getWindowManager()-> + messageBox("#{sBarterDialog4}"); + return; + } + } + + if (count > 1 && !shift) + { + CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); + std::string message = mTrading ? "#{sQuanityMenuMessage01}" : "#{sTake}"; + dialog->open(MWWorld::Class::get(object).getName(object), message, count); + dialog->eventOkClicked.clear(); + if (mTrading) + dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::sellItem); + else + dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::dragItem); + mSelectedItem = index; + } + else + { + mSelectedItem = index; + if (mTrading) + sellItem (NULL, count); + else + dragItem (NULL, count); + } + + // item might have been unequipped + notifyContentChanged(); + } + + void InventoryWindow::dragItem(MyGUI::Widget* sender, int count) + { + mDragAndDrop->startDrag(mSelectedItem, mSortModel, mTradeModel, mItemView, count); + } + + void InventoryWindow::sellItem(MyGUI::Widget* sender, int count) + { + const ItemStack& item = mTradeModel->getItem(mSelectedItem); + std::string sound = MWWorld::Class::get(item.mBase).getDownSoundId(item.mBase); + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); + + if (item.mType == ItemStack::Type_Barter) + { + // this was an item borrowed to us by the merchant + MWBase::Environment::get().getWindowManager()->getTradeWindow()->returnItem(mSelectedItem, count); + mTradeModel->returnItemBorrowedToUs(mSelectedItem, count); + } + else + { + // borrow item to the merchant + MWBase::Environment::get().getWindowManager()->getTradeWindow()->borrowItem(mSelectedItem, count); + mTradeModel->borrowItemFromUs(mSelectedItem, count); + } + + mItemView->update(); + } + + void InventoryWindow::updateItemView() + { + mItemView->update(); + mPreviewDirty = true; } void InventoryWindow::open() { updateEncumbranceBar(); - mTrading = false; + mItemView->update(); - mBoughtItems.clear(); - - onWindowResize(static_cast(mMainWidget)); - drawItems(); + notifyContentChanged(); } void InventoryWindow::onWindowResize(MyGUI::Window* _sender) @@ -96,24 +212,24 @@ namespace MWGui if (mMainWidget->getSize().width != mLastXSize || mMainWidget->getSize().height != mLastYSize) { - drawItems(); mLastXSize = mMainWidget->getSize().width; mLastYSize = mMainWidget->getSize().height; + mPreviewDirty = true; } } void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender) { if (_sender == mFilterAll) - setFilter(ContainerBase::Filter_All); + mSortModel->setCategory(SortFilterItemModel::Category_All); else if (_sender == mFilterWeapon) - setFilter(ContainerBase::Filter_Weapon); + mSortModel->setCategory(SortFilterItemModel::Category_Weapon); else if (_sender == mFilterApparel) - setFilter(ContainerBase::Filter_Apparel); + mSortModel->setCategory(SortFilterItemModel::Category_Apparel); else if (_sender == mFilterMagic) - setFilter(ContainerBase::Filter_Magic); + mSortModel->setCategory(SortFilterItemModel::Category_Magic); else if (_sender == mFilterMisc) - setFilter(ContainerBase::Filter_Misc); + mSortModel->setCategory(SortFilterItemModel::Category_Misc); mFilterAll->setStateSelected(false); mFilterWeapon->setStateSelected(false); @@ -121,35 +237,38 @@ namespace MWGui mFilterMagic->setStateSelected(false); mFilterMisc->setStateSelected(false); + mItemView->update(); + static_cast(_sender)->setStateSelected(true); } void InventoryWindow::onPinToggled() { - mWindowManager.setWeaponVisibility(!mPinned); + MWBase::Environment::get().getWindowManager()->setWeaponVisibility(!mPinned); } void InventoryWindow::onAvatarClicked(MyGUI::Widget* _sender) { if (mDragAndDrop->mIsOnDragAndDrop) { - MWWorld::Ptr ptr = *mDragAndDrop->mDraggedWidget->getUserData(); + MWWorld::Ptr ptr = mDragAndDrop->mItem.mBase; + mDragAndDrop->finish(); - if (mDragAndDrop->mDraggedFrom != this) + if (mDragAndDrop->mSourceModel != mTradeModel) { // add item to the player's inventory MWWorld::ContainerStore& invStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); MWWorld::ContainerStoreIterator it = invStore.begin(); int origCount = ptr.getRefData().getCount(); - ptr.getRefData().setCount(origCount - mDragAndDrop->mDraggedCount); + ptr.getRefData().setCount(mDragAndDrop->mDraggedCount); it = invStore.add(ptr); - (*it).getRefData().setCount(mDragAndDrop->mDraggedCount); + ptr.getRefData().setCount(origCount); + + mDragAndDrop->mSourceModel->removeItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount); ptr = *it; } - /// \todo scripts - boost::shared_ptr action = MWWorld::Class::get(ptr).use(ptr); action->execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); @@ -158,18 +277,10 @@ namespace MWGui // 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 - if (mDragAndDrop->mDraggedFrom == this) - { - mWindowManager.getBookWindow()->setTakeButtonShow(false); - mWindowManager.getScrollWindow()->setTakeButtonShow(false); - } - - mDragAndDrop->mIsOnDragAndDrop = false; - MyGUI::Gui::getInstance().destroyWidget(mDragAndDrop->mDraggedWidget); - - mWindowManager.setDragDrop(false); + MWBase::Environment::get().getWindowManager()->getBookWindow()->setTakeButtonShow(false); + MWBase::Environment::get().getWindowManager()->getScrollWindow()->setTakeButtonShow(false); - drawItems(); + mItemView->update(); notifyContentChanged(); } @@ -184,16 +295,15 @@ namespace MWGui if (itemSelected.isEmpty ()) return; - for (unsigned int i=0; i < mContainerWidget->getChildCount (); ++i) + for (size_t i=0; i < mTradeModel->getItemCount (); ++i) { - MyGUI::Widget* w = mContainerWidget->getChildAt (i); - - if (*w->getUserData() == itemSelected) + if (mTradeModel->getItem(i).mBase == itemSelected) { - onSelectedItem(w); + onItemSelectedFromSourceModel(i); return; } } + throw std::runtime_error("Can't find clicked item"); } } @@ -212,25 +322,7 @@ namespace MWGui return MWWorld::Ptr(); } - std::vector InventoryWindow::getEquippedItems() - { - MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); - - std::vector items; - - for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) - { - MWWorld::ContainerStoreIterator it = invStore.getSlot(slot); - if (it != invStore.end()) - { - items.push_back(*it); - } - } - - return items; - } - - void InventoryWindow::_unequipItem(MWWorld::Ptr item) + void InventoryWindow::unequipItem(const MWWorld::Ptr& item) { MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); @@ -240,6 +332,12 @@ namespace MWGui if (it != invStore.end() && *it == item) { invStore.equip(slot, invStore.end()); + std::string script = MWWorld::Class::get(*it).getScript(*it); + + // Unset OnPCEquip Variable on item's script, if it has a script with that variable declared + if(script != "") + (*it).mRefData->getLocals().setVarByInt(script, "onpcequip", 0); + return; } } @@ -251,9 +349,7 @@ namespace MWGui float capacity = MWWorld::Class::get(player).getCapacity(player); float encumbrance = MWWorld::Class::get(player).getEncumbrance(player); - mEncumbranceBar->setProgressRange(capacity); - mEncumbranceBar->setProgressPosition(encumbrance); - mEncumbranceText->setCaption( boost::lexical_cast(int(encumbrance)) + "/" + boost::lexical_cast(int(capacity)) ); + mEncumbranceBar->setValue(encumbrance, capacity); } void InventoryWindow::onFrame() @@ -277,36 +373,46 @@ namespace MWGui return 0; } - void InventoryWindow::startTrade() + void InventoryWindow::setTrading(bool trading) { - mTrading = true; + mTrading = trading; + } + + void InventoryWindow::doRenderUpdate () + { + if (mPreviewDirty) + { + mPreviewDirty = false; + MyGUI::IntSize size = mAvatar->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"); + } } void InventoryWindow::notifyContentChanged() { // update the spell window just in case new enchanted items were added to inventory - if (mWindowManager.getSpellWindow()) - mWindowManager.getSpellWindow()->updateSpells(); + if (MWBase::Environment::get().getWindowManager()->getSpellWindow()) + MWBase::Environment::get().getWindowManager()->getSpellWindow()->updateSpells(); // update selected weapon icon MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); MWWorld::ContainerStoreIterator weaponSlot = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if (weaponSlot == invStore.end()) - mWindowManager.unsetSelectedWeapon(); + MWBase::Environment::get().getWindowManager()->unsetSelectedWeapon(); else - mWindowManager.setSelectedWeapon(*weaponSlot, 100); /// \todo track weapon durability + MWBase::Environment::get().getWindowManager()->setSelectedWeapon(*weaponSlot); - MyGUI::IntSize size = mAvatar->getSize(); + mPreviewDirty = true; - 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"); + mArmorRating->setCaptionWithReplacing ("#{sArmor}: " + + boost::lexical_cast(static_cast(MWWorld::Class::get(mPtr).getArmorRating(mPtr)))); } void InventoryWindow::pickUpObject (MWWorld::Ptr object) { - /// \todo scripts - // make sure the object is of a type that can be picked up std::string type = object.getTypeName(); if ( (type != typeid(ESM::Apparatus).name()) @@ -316,7 +422,7 @@ namespace MWGui && (type != typeid(ESM::Ingredient).name()) && (type != typeid(ESM::Light).name()) && (type != typeid(ESM::Miscellaneous).name()) - && (type != typeid(ESM::Tool).name()) + && (type != typeid(ESM::Lockpick).name()) && (type != typeid(ESM::Probe).name()) && (type != typeid(ESM::Repair).name()) && (type != typeid(ESM::Weapon).name()) @@ -326,11 +432,9 @@ namespace MWGui if (MWWorld::Class::get(object).getName(object) == "") // objects without name presented to user can never be picked up return; - // sound - std::string sound = MWWorld::Class::get(object).getUpSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound(sound, 1, 1); - 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 @@ -339,31 +443,17 @@ namespace MWGui // remove from world MWBase::Environment::get().getWorld()->deleteObject (object); - mDragAndDrop->mIsOnDragAndDrop = true; - mDragAndDrop->mDraggedCount = count; - - std::string path = std::string("icons\\"); - path += MWWorld::Class::get(newObject).getInventoryIcon(newObject); - MyGUI::ImageBox* baseWidget = mContainerWidget->createWidget("ImageBox", MyGUI::IntCoord(0, 0, 42, 42), MyGUI::Align::Default); - baseWidget->detachFromWidget(); - baseWidget->attachToWidget(mDragAndDrop->mDragAndDropWidget); - baseWidget->setUserData(newObject); - mDragAndDrop->mDraggedWidget = baseWidget; - MyGUI::ImageBox* image = baseWidget->createWidget("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); - int pos = path.rfind("."); - path.erase(pos); - path.append(".dds"); - image->setImageTexture(path); - image->setNeedMouseFocus(false); - - // text widget that shows item count - MyGUI::TextBox* text = image->createWidget("SandBrightText", MyGUI::IntCoord(0, 14, 32, 18), MyGUI::Align::Default, std::string("Label")); - text->setTextAlign(MyGUI::Align::Right); - text->setNeedMouseFocus(false); - text->setTextShadow(true); - text->setTextShadowColour(MyGUI::Colour(0,0,0)); - text->setCaption(getCountString(count)); - mDragAndDrop->mDraggedFrom = this; + // get ModelIndex to the item + mTradeModel->update(); + size_t i=0; + for (; igetItemCount(); ++i) + { + if (mTradeModel->getItem(i).mBase == newObject) + break; + } + if (i == mTradeModel->getItemCount()) + throw std::runtime_error("Added item not found"); + mDragAndDrop->startDrag(i, mSortModel, mTradeModel, mItemView, count); } MyGUI::IntCoord InventoryWindow::getAvatarScreenCoord () diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 6b45a9980..13c118913 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -3,20 +3,28 @@ #include "../mwrender/characterpreview.hpp" -#include "container.hpp" -#include "window_pinnable_base.hpp" +#include "windowpinnablebase.hpp" +#include "widgets.hpp" namespace MWGui { - class InventoryWindow : public ContainerBase, public WindowPinnableBase + class ItemView; + class SortFilterItemModel; + class TradeItemModel; + class DragAndDrop; + class ItemModel; + + class InventoryWindow : public WindowPinnableBase { public: - InventoryWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop); + InventoryWindow(DragAndDrop* dragAndDrop); virtual void open(); + void doRenderUpdate(); + /// start trading, disables item drag&drop - void startTrade(); + void setTrading(bool trading); void onFrame(); @@ -32,12 +40,29 @@ namespace MWGui mPreview.rebuild(); } - protected: + TradeItemModel* getTradeModel(); + ItemModel* getModel(); + + void updateItemView(); + + void updatePlayer(); + + private: + DragAndDrop* mDragAndDrop; + + bool mPreviewDirty; + size_t mSelectedItem; + + MWWorld::Ptr mPtr; + + MWGui::ItemView* mItemView; + SortFilterItemModel* mSortModel; + TradeItemModel* mTradeModel; + MyGUI::Widget* mAvatar; MyGUI::ImageBox* mAvatarImage; MyGUI::TextBox* mArmorRating; - MyGUI::ProgressBar* mEncumbranceBar; - MyGUI::TextBox* mEncumbranceText; + Widgets::MWDynamicStat* mEncumbranceBar; MyGUI::Widget* mLeftPane; MyGUI::Widget* mRightPane; @@ -55,21 +80,22 @@ namespace MWGui bool mTrading; + void onItemSelected(int index); + void onItemSelectedFromSourceModel(int index); + + void onBackgroundSelected(); + + void sellItem(MyGUI::Widget* sender, int count); + void dragItem(MyGUI::Widget* sender, int count); + void onWindowResize(MyGUI::Window* _sender); void onFilterChanged(MyGUI::Widget* _sender); void onAvatarClicked(MyGUI::Widget* _sender); void onPinToggled(); + void unequipItem(const MWWorld::Ptr& item); void updateEncumbranceBar(); - - virtual bool isTrading() { return mTrading; } - virtual bool isInventory() { return true; } - virtual std::vector getEquippedItems(); - virtual void _unequipItem(MWWorld::Ptr item); - - virtual void onReferenceUnavailable() { ; } - - virtual void notifyContentChanged(); + void notifyContentChanged(); }; } diff --git a/apps/openmw/mwgui/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp new file mode 100644 index 000000000..58e89c4fc --- /dev/null +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -0,0 +1,120 @@ +#include "itemmodel.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/containerstore.hpp" + +namespace MWGui +{ + + ItemStack::ItemStack(const MWWorld::Ptr &base, ItemModel *creator, size_t count) + : mCreator(creator) + , mCount(count) + , mFlags(0) + , mType(Type_Normal) + , mBase(base) + { + if (MWWorld::Class::get(base).getEnchantment(base) != "") + mFlags |= Flag_Enchanted; + } + + ItemStack::ItemStack() + : mCreator(NULL) + , mCount(0) + , mFlags(0) + , mType(Type_Normal) + { + } + + bool ItemStack::stacks(const ItemStack &other) + { + if(mBase == other.mBase) + return true; + + // If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure + if (mBase.getContainerStore() && other.mBase.getContainerStore()) + return mBase.getContainerStore()->stacks(mBase, other.mBase) + && other.mBase.getContainerStore()->stacks(mBase, other.mBase); + + if (mBase.getContainerStore()) + return mBase.getContainerStore()->stacks(mBase, other.mBase); + if (other.mBase.getContainerStore()) + return other.mBase.getContainerStore()->stacks(mBase, other.mBase); + + MWWorld::ContainerStore store; + return store.stacks(mBase, other.mBase); + + } + + bool operator == (const ItemStack& left, const ItemStack& right) + { + if (left.mType != right.mType) + return false; + + if(left.mBase == right.mBase) + return true; + + // If one of the items is in an inventory and currently equipped, we need to check stacking both ways to be sure + if (left.mBase.getContainerStore() && right.mBase.getContainerStore()) + return left.mBase.getContainerStore()->stacks(left.mBase, right.mBase) + && right.mBase.getContainerStore()->stacks(left.mBase, right.mBase); + + if (left.mBase.getContainerStore()) + return left.mBase.getContainerStore()->stacks(left.mBase, right.mBase); + if (right.mBase.getContainerStore()) + return right.mBase.getContainerStore()->stacks(left.mBase, right.mBase); + + MWWorld::ContainerStore store; + return store.stacks(left.mBase, right.mBase); + } + + ItemModel::ItemModel() + { + } + + + ProxyItemModel::~ProxyItemModel() + { + delete mSourceModel; + } + + void ProxyItemModel::copyItem (const ItemStack& item, size_t count) + { + // no need to use mapToSource since itemIndex refers to an index in the sourceModel + mSourceModel->copyItem (item, count); + } + + void ProxyItemModel::removeItem (const ItemStack& item, size_t count) + { + mSourceModel->removeItem (item, count); + } + + ItemModel::ModelIndex ProxyItemModel::mapToSource (ModelIndex index) + { + const ItemStack& itemToSearch = getItem(index); + for (size_t i=0; igetItemCount(); ++i) + { + const ItemStack& item = mSourceModel->getItem(i); + if (item == itemToSearch) + return i; + } + return -1; + } + + ItemModel::ModelIndex ProxyItemModel::mapFromSource (ModelIndex index) + { + const ItemStack& itemToSearch = mSourceModel->getItem(index); + for (size_t i=0; igetIndex(item); + } + +} diff --git a/apps/openmw/mwgui/itemmodel.hpp b/apps/openmw/mwgui/itemmodel.hpp new file mode 100644 index 000000000..47aaf79e8 --- /dev/null +++ b/apps/openmw/mwgui/itemmodel.hpp @@ -0,0 +1,84 @@ +#ifndef MWGUI_ITEM_MODEL_H +#define MWGUI_ITEM_MODEL_H + +#include "../mwworld/ptr.hpp" + +namespace MWGui +{ + + class ItemModel; + + /// @brief A single item stack managed by an item model + struct ItemStack + { + ItemStack (const MWWorld::Ptr& base, ItemModel* creator, size_t count); + ItemStack(); + bool stacks (const ItemStack& other); + ///< like operator==, only without checking mType + + enum Type + { + Type_Barter, + Type_Equipped, + Type_Normal + }; + Type mType; + + enum Flags + { + Flag_Enchanted = (1<<0) + }; + int mFlags; + + ItemModel* mCreator; + size_t mCount; + MWWorld::Ptr mBase; + }; + + bool operator == (const ItemStack& left, const ItemStack& right); + + + /// @brief The base class that all item models should derive from. + class ItemModel + { + public: + ItemModel(); + virtual ~ItemModel() {} + + typedef int ModelIndex; + + virtual ItemStack getItem (ModelIndex index) = 0; + ///< throws for invalid index + virtual size_t getItemCount() = 0; + + virtual ModelIndex getIndex (ItemStack item) = 0; + + virtual void update() = 0; + + virtual void copyItem (const ItemStack& item, size_t count) = 0; + virtual void removeItem (const ItemStack& item, size_t count) = 0; + + private: + ItemModel(const ItemModel&); + ItemModel& operator=(const ItemModel&); + }; + + /// @brief A proxy item model can be used to filter or rearrange items from a source model (or even add new items to it). + /// The neat thing is that this does not actually alter the source model. + class ProxyItemModel : public ItemModel + { + public: + virtual ~ProxyItemModel(); + virtual void copyItem (const ItemStack& item, size_t count); + virtual void removeItem (const ItemStack& item, size_t count); + virtual ModelIndex getIndex (ItemStack item); + + ModelIndex mapToSource (ModelIndex index); + ModelIndex mapFromSource (ModelIndex index); + protected: + ItemModel* mSourceModel; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/itemselection.cpp b/apps/openmw/mwgui/itemselection.cpp index 14b1cf8ee..a57c6d81f 100644 --- a/apps/openmw/mwgui/itemselection.cpp +++ b/apps/openmw/mwgui/itemselection.cpp @@ -1,19 +1,17 @@ #include "itemselection.hpp" +#include "itemview.hpp" +#include "inventoryitemmodel.hpp" +#include "sortfilteritemmodel.hpp" + namespace MWGui { - ItemSelectionDialog::ItemSelectionDialog(const std::string &label, ContainerBase::Filter filter, MWBase::WindowManager& parWindowManager) - : ContainerBase(NULL) - , WindowModal("openmw_itemselection_dialog.layout", parWindowManager) + ItemSelectionDialog::ItemSelectionDialog(const std::string &label) + : WindowModal("openmw_itemselection_dialog.layout") { - mFilter = filter; - - MyGUI::ScrollView* itemView; - MyGUI::Widget* containerWidget; - getWidget(containerWidget, "Items"); - getWidget(itemView, "ItemView"); - setWidgets(containerWidget, itemView); + getWidget(mItemView, "ItemView"); + mItemView->eventItemClicked += MyGUI::newDelegate(this, &ItemSelectionDialog::onSelectedItem); MyGUI::TextBox* l; getWidget(l, "Label"); @@ -26,9 +24,29 @@ namespace MWGui center(); } - void ItemSelectionDialog::onSelectedItemImpl(MWWorld::Ptr item) + void ItemSelectionDialog::openContainer(const MWWorld::Ptr& container) + { + mModel = new InventoryItemModel(container); + mSortModel = new SortFilterItemModel(mModel); + mItemView->setModel(mSortModel); + } + + void ItemSelectionDialog::setCategory(int category) + { + mSortModel->setCategory(category); + mItemView->update(); + } + + void ItemSelectionDialog::setFilter(int filter) + { + mSortModel->setFilter(filter); + mItemView->update(); + } + + void ItemSelectionDialog::onSelectedItem(int index) { - eventItemSelected(item); + ItemStack item = mSortModel->getItem(index); + eventItemSelected(item.mBase); } void ItemSelectionDialog::onCancelButtonClicked(MyGUI::Widget* sender) diff --git a/apps/openmw/mwgui/itemselection.hpp b/apps/openmw/mwgui/itemselection.hpp index cab125f1f..d6d19d9e1 100644 --- a/apps/openmw/mwgui/itemselection.hpp +++ b/apps/openmw/mwgui/itemselection.hpp @@ -1,14 +1,15 @@ #include "container.hpp" -#include "../mwworld/ptr.hpp" - namespace MWGui { + class ItemView; + class SortFilterItemModel; + class InventoryItemModel; - class ItemSelectionDialog : public ContainerBase, public WindowModal + class ItemSelectionDialog : public WindowModal { public: - ItemSelectionDialog(const std::string& label, ContainerBase::Filter filter, MWBase::WindowManager& parWindowManager); + ItemSelectionDialog(const std::string& label); typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Item; @@ -16,11 +17,16 @@ namespace MWGui EventHandle_Item eventItemSelected; EventHandle_Void eventDialogCanceled; + void openContainer (const MWWorld::Ptr& container); + void setCategory(int category); + void setFilter(int filter); private: - virtual void onReferenceUnavailable() { ; } + ItemView* mItemView; + SortFilterItemModel* mSortModel; + InventoryItemModel* mModel; - virtual void onSelectedItemImpl(MWWorld::Ptr item); + void onSelectedItem(int index); void onCancelButtonClicked(MyGUI::Widget* sender); }; diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp new file mode 100644 index 000000000..5155d2712 --- /dev/null +++ b/apps/openmw/mwgui/itemview.cpp @@ -0,0 +1,200 @@ +#include "itemview.hpp" + +#include + +#include +#include +#include +#include +#include +#include + +#include "../mwworld/class.hpp" + +#include "itemmodel.hpp" + +namespace +{ + std::string getCountString(const int count) + { + if (count == 1) + return ""; + if (count > 9999) + return boost::lexical_cast(int(count/1000.f)) + "k"; + else + return boost::lexical_cast(count); + } +} + + +namespace MWGui +{ + +ItemView::ItemView() + : mModel(NULL) +{ +} + +ItemView::~ItemView() +{ + delete mModel; +} + +void ItemView::setModel(ItemModel *model) +{ + delete mModel; + mModel = model; + update(); +} + +void ItemView::initialiseOverride() +{ + Base::initialiseOverride(); + + assignWidget(mScrollView, "ScrollView"); + if (mScrollView == NULL) + throw std::runtime_error("Item view needs a scroll view"); + + mScrollView->setCanvasAlign(MyGUI::Align::Left | MyGUI::Align::Top); +} + +void ItemView::update() +{ + while (mScrollView->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0)); + + if (!mModel) + return; + + int x = 0; + int y = 0; + int maxHeight = mScrollView->getSize().height - 58; + + mModel->update(); + + MyGUI::Widget* dragArea = mScrollView->createWidget("",0,0,mScrollView->getWidth(),mScrollView->getHeight(), + MyGUI::Align::Stretch); + dragArea->setNeedMouseFocus(true); + dragArea->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemView::onSelectedBackground); + dragArea->eventMouseWheel += MyGUI::newDelegate(this, &ItemView::onMouseWheel); + + for (ItemModel::ModelIndex i=0; i(mModel->getItemCount()); ++i) + { + const ItemStack& item = mModel->getItem(i); + + /// \todo performance improvement: don't create/destroy all the widgets everytime the container window changes size, only reposition them + std::string path = std::string("icons\\"); + path += MWWorld::Class::get(item.mBase).getInventoryIcon(item.mBase); + + // background widget (for the "equipped" frame and magic item background image) + bool isMagic = (item.mFlags & ItemStack::Flag_Enchanted); + MyGUI::ImageBox* backgroundWidget = dragArea->createWidget("ImageBox", + MyGUI::IntCoord(x, y, 42, 42), MyGUI::Align::Default); + backgroundWidget->setUserString("ToolTipType", "ItemModelIndex"); + backgroundWidget->setUserData(std::make_pair(i, mModel)); + + std::string backgroundTex = "textures\\menu_icon"; + if (isMagic) + backgroundTex += "_magic"; + if (item.mType == ItemStack::Type_Normal) + { + if (!isMagic) + backgroundTex = ""; + } + else if (item.mType == ItemStack::Type_Equipped) + backgroundTex += "_equip"; + else if (item.mType == ItemStack::Type_Barter) + backgroundTex += "_barter"; + + if (backgroundTex != "") + backgroundTex += ".dds"; + + backgroundWidget->setImageTexture(backgroundTex); + if ((item.mType == ItemStack::Type_Barter) && !isMagic) + backgroundWidget->setProperty("ImageCoord", "2 2 42 42"); + else + backgroundWidget->setProperty("ImageCoord", "0 0 42 42"); + backgroundWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemView::onSelectedItem); + backgroundWidget->eventMouseWheel += MyGUI::newDelegate(this, &ItemView::onMouseWheel); + + // image + MyGUI::ImageBox* image = backgroundWidget->createWidget("ImageBox", + MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + image->setImageTexture(path); + image->setNeedMouseFocus(false); + + // text widget that shows item count + MyGUI::TextBox* text = image->createWidget("SandBrightText", + MyGUI::IntCoord(0, 14, 32, 18), MyGUI::Align::Default, std::string("Label")); + text->setTextAlign(MyGUI::Align::Right); + text->setNeedMouseFocus(false); + text->setTextShadow(true); + text->setTextShadowColour(MyGUI::Colour(0,0,0)); + text->setCaption(getCountString(item.mCount)); + + y += 42; + if (y > maxHeight) + { + x += 42; + y = 0; + } + + } + x += 42; + MyGUI::IntSize size = MyGUI::IntSize(std::max(mScrollView->getSize().width, x), mScrollView->getSize().height); + mScrollView->setCanvasSize(size); + dragArea->setSize(size); +} + +void ItemView::onSelectedItem(MyGUI::Widget *sender) +{ + ItemModel::ModelIndex index = (*sender->getUserData >()).first; + eventItemClicked(index); +} + +void ItemView::onSelectedBackground(MyGUI::Widget *sender) +{ + eventBackgroundClicked(); +} + +void ItemView::onMouseWheel(MyGUI::Widget *_sender, int _rel) +{ + if (mScrollView->getViewOffset().left + _rel*0.3 > 0) + mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mScrollView->setViewOffset(MyGUI::IntPoint(mScrollView->getViewOffset().left + _rel*0.3, 0)); +} + +void ItemView::setSize(const MyGUI::IntSize &_value) +{ + Base::setSize(_value); + update(); +} + +void ItemView::setSize(int _width, int _height) +{ + Base::setSize(_width, _height); + update(); +} + +void ItemView::setCoord(const MyGUI::IntCoord &_value) +{ + Base::setCoord(_value); + update(); +} + +void ItemView::setCoord(int _left, int _top, int _width, int _height) +{ + Base::setCoord(_left, _top, _width, _height); + update(); +} + +void ItemView::registerComponents() +{ + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); +} + +} diff --git a/apps/openmw/mwgui/itemview.hpp b/apps/openmw/mwgui/itemview.hpp new file mode 100644 index 000000000..17f609f2b --- /dev/null +++ b/apps/openmw/mwgui/itemview.hpp @@ -0,0 +1,52 @@ +#ifndef MWGUI_ITEMVIEW_H +#define MWGUI_ITEMVIEW_H + +#include + +#include "itemmodel.hpp" + +namespace MWGui +{ + + class ItemView : public MyGUI::Widget + { + MYGUI_RTTI_DERIVED(ItemView) + public: + ItemView(); + virtual ~ItemView(); + + /// Register needed components with MyGUI's factory manager + static void registerComponents (); + + /// Takes ownership of \a model + void setModel (ItemModel* model); + + typedef MyGUI::delegates::CMultiDelegate1 EventHandle_ModelIndex; + typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; + /// Fired when an item was clicked + EventHandle_ModelIndex eventItemClicked; + /// Fired when the background was clicked (useful for drag and drop) + EventHandle_Void eventBackgroundClicked; + + void update(); + + private: + virtual void initialiseOverride(); + + virtual void setSize(const MyGUI::IntSize& _value); + virtual void setCoord(const MyGUI::IntCoord& _value); + void setSize(int _width, int _height); + void setCoord(int _left, int _top, int _width, int _height); + + void onSelectedItem (MyGUI::Widget* sender); + void onSelectedBackground (MyGUI::Widget* sender); + void onMouseWheel(MyGUI::Widget* _sender, int _rel); + + ItemModel* mModel; + MyGUI::ScrollView* mScrollView; + + }; + +} + +#endif diff --git a/apps/openmw/mwgui/journalbooks.cpp b/apps/openmw/mwgui/journalbooks.cpp new file mode 100644 index 000000000..dbea10e77 --- /dev/null +++ b/apps/openmw/mwgui/journalbooks.cpp @@ -0,0 +1,326 @@ +#include "journalbooks.hpp" + +namespace +{ + MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text) + { + typedef MWGui::BookTypesetter::Utf8Point point; + + point begin = reinterpret_cast (text); + + return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text)); + } + + const MyGUI::Colour linkHot (0.40f, 0.40f, 0.80f); + const MyGUI::Colour linkNormal (0.20f, 0.20f, 0.60f); + const MyGUI::Colour linkActive (0.50f, 0.50f, 1.00f); + + struct AddContent + { + MWGui::BookTypesetter::Ptr mTypesetter; + MWGui::BookTypesetter::Style* mBodyStyle; + + AddContent (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style) : + mTypesetter (typesetter), mBodyStyle (body_style) + { + } + }; + + struct AddSpan : AddContent + { + AddSpan (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style) : + AddContent (typesetter, body_style) + { + } + + void operator () (intptr_t topicId, size_t begin, size_t end) + { + MWGui::BookTypesetter::Style* style = mBodyStyle; + + if (topicId) + style = mTypesetter->createHotStyle (mBodyStyle, linkNormal, linkHot, linkActive, topicId); + + mTypesetter->write (style, begin, end); + } + }; + + struct AddEntry + { + MWGui::BookTypesetter::Ptr mTypesetter; + MWGui::BookTypesetter::Style* mBodyStyle; + + AddEntry (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style) : + mTypesetter (typesetter), mBodyStyle (body_style) + { + } + + void operator () (MWGui::JournalViewModel::Entry const & entry) + { + mTypesetter->addContent (entry.body ()); + + entry.visitSpans (AddSpan (mTypesetter, mBodyStyle)); + } + }; + + struct AddJournalEntry : AddEntry + { + bool mAddHeader; + MWGui::BookTypesetter::Style* mHeaderStyle; + + AddJournalEntry (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style, + MWGui::BookTypesetter::Style* header_style, bool add_header) : + AddEntry (typesetter, body_style), + mHeaderStyle (header_style), + mAddHeader (add_header) + { + } + + void operator () (MWGui::JournalViewModel::JournalEntry const & entry) + { + if (mAddHeader) + { + mTypesetter->write (mHeaderStyle, entry.timestamp ()); + mTypesetter->lineBreak (); + } + + AddEntry::operator () (entry); + + mTypesetter->sectionBreak (10); + } + }; + + struct AddTopicEntry : AddEntry + { + intptr_t mContentId; + MWGui::BookTypesetter::Style* mHeaderStyle; + + AddTopicEntry (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* body_style, + MWGui::BookTypesetter::Style* header_style, intptr_t contentId) : + AddEntry (typesetter, body_style), mHeaderStyle (header_style), mContentId (contentId) + { + } + + void operator () (MWGui::JournalViewModel::TopicEntry const & entry) + { + mTypesetter->write (mBodyStyle, entry.source ()); + mTypesetter->write (mBodyStyle, 0, 3);// begin + + AddEntry::operator() (entry); + + mTypesetter->selectContent (mContentId); + mTypesetter->write (mBodyStyle, 2, 3);// end quote + + mTypesetter->sectionBreak (10); + } + }; + + struct AddTopicName : AddContent + { + AddTopicName (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* style) : + AddContent (typesetter, style) + { + } + + void operator () (MWGui::JournalViewModel::Utf8Span topicName) + { + mTypesetter->write (mBodyStyle, topicName); + mTypesetter->sectionBreak (10); + } + }; + + struct AddQuestName : AddContent + { + AddQuestName (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* style) : + AddContent (typesetter, style) + { + } + + void operator () (MWGui::JournalViewModel::Utf8Span topicName) + { + mTypesetter->write (mBodyStyle, topicName); + mTypesetter->sectionBreak (10); + } + }; + + struct AddTopicLink : AddContent + { + AddTopicLink (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* style) : + AddContent (typesetter, style) + { + } + + void operator () (MWGui::JournalViewModel::TopicId topicId, MWGui::JournalViewModel::Utf8Span name) + { + MWGui::BookTypesetter::Style* link = mTypesetter->createHotStyle (mBodyStyle, MyGUI::Colour::Black, linkHot, linkActive, topicId); + + mTypesetter->write (link, name); + mTypesetter->lineBreak (); + } + }; + + struct AddQuestLink : AddContent + { + AddQuestLink (MWGui::BookTypesetter::Ptr typesetter, MWGui::BookTypesetter::Style* style) : + AddContent (typesetter, style) + { + } + + void operator () (MWGui::JournalViewModel::QuestId id, MWGui::JournalViewModel::Utf8Span name) + { + MWGui::BookTypesetter::Style* style = mTypesetter->createHotStyle (mBodyStyle, MyGUI::Colour::Black, linkHot, linkActive, id); + + mTypesetter->write (style, name); + mTypesetter->lineBreak (); + } + }; +} + +namespace MWGui +{ + +typedef TypesetBook::Ptr book; + +JournalBooks::JournalBooks (JournalViewModel::Ptr model) : + mModel (model) +{ +} + +book JournalBooks::createEmptyJournalBook () +{ + BookTypesetter::Ptr typesetter = createTypesetter (); + + BookTypesetter::Style* header = typesetter->createStyle ("", MyGUI::Colour (0.60f, 0.00f, 0.00f)); + BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); + + typesetter->write (header, to_utf8_span ("You have no journal entries!")); + 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 (); +} + +book JournalBooks::createJournalBook () +{ + BookTypesetter::Ptr typesetter = createTypesetter (); + + BookTypesetter::Style* header = typesetter->createStyle ("", MyGUI::Colour (0.60f, 0.00f, 0.00f)); + BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); + + mModel->visitJournalEntries (0, AddJournalEntry (typesetter, body, header, true)); + + return typesetter->complete (); +} + +book JournalBooks::createTopicBook (uintptr_t topicId) +{ + BookTypesetter::Ptr typesetter = createTypesetter (); + + BookTypesetter::Style* header = typesetter->createStyle ("", MyGUI::Colour (0.60f, 0.00f, 0.00f)); + BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); + + mModel->visitTopicName (topicId, AddTopicName (typesetter, header)); + + intptr_t contentId = typesetter->addContent (to_utf8_span (", \"")); + + mModel->visitTopicEntries (topicId, AddTopicEntry (typesetter, body, header, contentId)); + + return typesetter->complete (); +} + +book JournalBooks::createQuestBook (uintptr_t questId) +{ + BookTypesetter::Ptr typesetter = createTypesetter (); + + BookTypesetter::Style* header = typesetter->createStyle ("", MyGUI::Colour (0.60f, 0.00f, 0.00f)); + BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); + + mModel->visitQuestName (questId, AddQuestName (typesetter, header)); + + mModel->visitJournalEntries (questId, AddJournalEntry (typesetter, body, header, false)); + + return typesetter->complete (); +} + +book JournalBooks::createTopicIndexBook () +{ + BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 250); + + typesetter->setSectionAlignment (BookTypesetter::AlignCenter); + + BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); + + for (int i = 0; i < 26; ++i) + { + char ch = 'A' + i; + + char buffer [32]; + + sprintf (buffer, "( %c )", ch); + + BookTypesetter::Style* style = typesetter->createHotStyle (body, MyGUI::Colour::Black, linkHot, linkActive, ch); + + if (i == 13) + typesetter->sectionBreak (); + + typesetter->write (style, to_utf8_span (buffer)); + typesetter->lineBreak (); + } + + return typesetter->complete (); +} + +book JournalBooks::createTopicIndexBook (char character) +{ + BookTypesetter::Ptr typesetter = BookTypesetter::create (0x7FFFFFFF, 0x7FFFFFFF); + BookTypesetter::Style* style = typesetter->createStyle ("", MyGUI::Colour::Black); + + mModel->visitTopicNamesStartingWith (character, AddTopicLink (typesetter, style)); + + return typesetter->complete (); +} + +book JournalBooks::createQuestIndexBook (bool activeOnly) +{ + BookTypesetter::Ptr typesetter = BookTypesetter::create (0x7FFFFFFF, 0x7FFFFFFF); + BookTypesetter::Style* base = typesetter->createStyle ("", MyGUI::Colour::Black); + + mModel->visitQuestNames (activeOnly, AddQuestLink (typesetter, base)); + + return typesetter->complete (); +} + +BookTypesetter::Ptr JournalBooks::createTypesetter () +{ + //TODO: determine page size from layout... + return BookTypesetter::create (240, 300); +} + +} diff --git a/apps/openmw/mwgui/journalbooks.hpp b/apps/openmw/mwgui/journalbooks.hpp new file mode 100644 index 000000000..09d3cf1a8 --- /dev/null +++ b/apps/openmw/mwgui/journalbooks.hpp @@ -0,0 +1,29 @@ +#ifndef MWGUI_JOURNALBOOKS_HPP +#define MWGUI_JOURNALBOOKS_HPP + +#include "bookpage.hpp" +#include "journalviewmodel.hpp" + +namespace MWGui +{ + struct JournalBooks + { + typedef TypesetBook::Ptr Book; + JournalViewModel::Ptr mModel; + + JournalBooks (JournalViewModel::Ptr model); + + Book createEmptyJournalBook (); + Book createJournalBook (); + Book createTopicBook (uintptr_t topicId); + Book createQuestBook (uintptr_t questId); + Book createTopicIndexBook (); + Book createTopicIndexBook (char character); + Book createQuestIndexBook (bool showAll); + + private: + BookTypesetter::Ptr createTypesetter (); + }; +} + +#endif // MWGUI_JOURNALBOOKS_HPP diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp new file mode 100644 index 000000000..79a77070a --- /dev/null +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -0,0 +1,389 @@ +#include "journalviewmodel.hpp" + +#include +#include +#include + +#include + +#include + +#include "../mwbase/world.hpp" +#include "../mwbase/journal.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwdialogue/journalentry.hpp" + +#include "keywordsearch.hpp" + +namespace MWGui { + +struct JournalViewModelImpl; + +static void injectMonthName (std::ostream & os, int month); + +struct JournalViewModelImpl : JournalViewModel +{ + typedef KeywordSearch KeywordSearchT; + + mutable bool mKeywordSearchLoaded; + mutable KeywordSearchT mKeywordSearch; + + std::locale mLocale; + + JournalViewModelImpl () + { + mKeywordSearchLoaded = false; + } + + virtual ~JournalViewModelImpl () + { + } + + /// \todo replace this nasty BS + static Utf8Span toUtf8Span (std::string const & str) + { + if (str.size () == 0) + return Utf8Span (Utf8Point (NULL), Utf8Point (NULL)); + + Utf8Point point = reinterpret_cast (str.c_str ()); + + return Utf8Span (point, point + str.size ()); + } + + void load () + { + } + + void unload () + { + mKeywordSearch.clear (); + mKeywordSearchLoaded = false; + } + + void ensureKeyWordSearchLoaded () const + { + if (!mKeywordSearchLoaded) + { + MWBase::Journal * journal = MWBase::Environment::get().getJournal(); + + for(MWBase::Journal::TTopicIter i = journal->topicBegin(); i != journal->topicEnd (); ++i) + mKeywordSearch.seed (i->first, intptr_t (&i->second)); + + mKeywordSearchLoaded = true; + } + } + + wchar_t tolower (wchar_t ch) const { return std::tolower (ch, mLocale); } + + bool isEmpty () const + { + MWBase::Journal * journal = MWBase::Environment::get().getJournal(); + + return journal->begin () == journal->end (); + } + + template + struct BaseEntry : Interface + { + typedef t_iterator iterator_t; + + iterator_t itr; + JournalViewModelImpl const * mModel; + + BaseEntry (JournalViewModelImpl const * model, iterator_t itr) : + mModel (model), itr (itr), loaded (false) + {} + + virtual ~BaseEntry () {} + + mutable bool loaded; + mutable std::string utf8text; + + typedef std::pair Range; + + // hyperlinks in @link# notation + mutable std::map mHyperLinks; + + virtual std::string getText () const = 0; + + void ensureLoaded () const + { + if (!loaded) + { + mModel->ensureKeyWordSearchLoaded (); + + utf8text = getText (); + + size_t pos_begin, pos_end; + for(;;) + { + pos_begin = utf8text.find('@'); + if (pos_begin != std::string::npos) + pos_end = utf8text.find('#', pos_begin); + + if (pos_begin != std::string::npos && pos_end != std::string::npos) + { + std::string link = utf8text.substr(pos_begin + 1, pos_end - pos_begin - 1); + const char specialPseudoAsteriskCharacter = 127; + std::replace(link.begin(), link.end(), specialPseudoAsteriskCharacter, '*'); + std::string topicName = MWBase::Environment::get().getWindowManager()-> + getTranslationDataStorage().topicStandardForm(link); + + std::string displayName = link; + while (displayName[displayName.size()-1] == '*') + displayName.erase(displayName.size()-1, 1); + + utf8text.replace(pos_begin, pos_end+1-pos_begin, displayName); + + intptr_t value; + if (mModel->mKeywordSearch.containsKeyword(topicName, value)) + mHyperLinks[std::make_pair(pos_begin, pos_begin+displayName.size())] = value; + } + else + break; + } + + loaded = true; + } + } + + Utf8Span body () const + { + ensureLoaded (); + + return toUtf8Span (utf8text); + } + + void visitSpans (boost::function < void (TopicId, size_t, size_t)> visitor) const + { + ensureLoaded (); + mModel->ensureKeyWordSearchLoaded (); + + if (mHyperLinks.size() && MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation()) + { + size_t formatted = 0; // points to the first character that is not laid out yet + for (std::map::const_iterator it = mHyperLinks.begin(); it != mHyperLinks.end(); ++it) + { + intptr_t topicId = it->second; + if (formatted < it->first.first) + visitor (0, formatted, it->first.first); + visitor (topicId, it->first.first, it->first.second); + formatted = it->first.second; + } + if (formatted < utf8text.size()) + visitor (0, formatted, utf8text.size()); + } + else + { + std::string::const_iterator i = utf8text.begin (); + + KeywordSearchT::Match match; + + while (i != utf8text.end () && mModel->mKeywordSearch.search (i, utf8text.end (), match)) + { + if (i != match.mBeg) + visitor (0, i - utf8text.begin (), match.mBeg - utf8text.begin ()); + + visitor (match.mValue, match.mBeg - utf8text.begin (), match.mEnd - utf8text.begin ()); + + i = match.mEnd; + } + + if (i != utf8text.end ()) + visitor (0, i - utf8text.begin (), utf8text.size ()); + } + } + + }; + + void visitQuestNames (bool active_only, boost::function visitor) const + { + MWBase::Journal * journal = MWBase::Environment::get ().getJournal (); + + for (MWBase::Journal::TQuestIter i = journal->questBegin (); i != journal->questEnd (); ++i) + { + 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)); + } + } + + void visitQuestName (QuestId questId, boost::function visitor) const + { + MWDialogue::Quest const * quest = reinterpret_cast (questId); + + std::string name = quest->getName (); + + visitor (toUtf8Span (name)); + } + + template + struct JournalEntryImpl : BaseEntry + { + using BaseEntry ::itr; + + mutable std::string timestamp_buffer; + + JournalEntryImpl (JournalViewModelImpl const * model, iterator_t itr) : + BaseEntry (model, itr) + {} + + std::string getText () const + { + return itr->getText(MWBase::Environment::get().getWorld()->getStore()); + } + + Utf8Span timestamp () const + { + if (timestamp_buffer.empty ()) + { + std::ostringstream os; + + os << itr->mDayOfMonth << ' '; + + injectMonthName (os, itr->mMonth); + + const std::string& dayStr = MyGUI::LanguageManager::getInstance().replaceTags("#{sDay}"); + os << " (" << dayStr << " " << (itr->mDay + 1) << ')'; + + timestamp_buffer = os.str (); + } + + return toUtf8Span (timestamp_buffer); + } + }; + + void visitJournalEntries (QuestId questId, boost::function visitor) const + { + MWBase::Journal * journal = MWBase::Environment::get().getJournal(); + + if (questId != 0) + { + MWDialogue::Quest const * quest = reinterpret_cast (questId); + + for(MWBase::Journal::TEntryIter i = journal->begin(); i != journal->end (); ++i) + { + for (MWDialogue::Topic::TEntryIter j = quest->begin (); j != quest->end (); ++j) + { + if (i->mInfoId == *j) + visitor (JournalEntryImpl (this, i)); + } + } + } + else + { + for(MWBase::Journal::TEntryIter i = journal->begin(); i != journal->end (); ++i) + visitor (JournalEntryImpl (this, i)); + } + } + + void visitTopics (boost::function visitor) const + { + throw std::runtime_error ("not implemented"); + } + + 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)); + } + + void visitTopicNamesStartingWith (char character, boost::function < void (TopicId , Utf8Span) > visitor) const + { + MWBase::Journal * journal = MWBase::Environment::get().getJournal(); + + for (MWBase::Journal::TTopicIter i = journal->topicBegin (); i != journal->topicEnd (); ++i) + { + 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)); + } + + } + + struct TopicEntryImpl : BaseEntry + { + 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) + {} + + 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()); + + } + + Utf8Span source () const + { + if (source_buffer.empty ()) + source_buffer = "someone"; + return toUtf8Span (source_buffer); + } + + }; + + void visitTopicEntries (TopicId topicId, boost::function visitor) const + { + typedef MWDialogue::Topic::TEntryIter iterator_t; + + MWDialogue::Topic const & topic = * reinterpret_cast (topicId); + + for (iterator_t i = topic.begin (); i != topic.end (); ++i) + visitor (TopicEntryImpl (this, topic, i)); + } +}; + +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/journalviewmodel.hpp b/apps/openmw/mwgui/journalviewmodel.hpp new file mode 100644 index 000000000..21c3a1178 --- /dev/null +++ b/apps/openmw/mwgui/journalviewmodel.hpp @@ -0,0 +1,93 @@ +#ifndef MWGUI_JOURNALVIEWMODEL_HPP +#define MWGUI_JOURNALVIEWMODEL_HPP + +#include +#include +#include +#include +#include +#include + +namespace MWGui +{ + /// View-Model for the journal GUI + /// + /// This interface defines an abstract data model suited + /// specifically to the needs of the journal GUI. It isolates + /// the journal GUI from the implementation details of the + /// game data store. + struct JournalViewModel + { + typedef boost::shared_ptr Ptr; + + typedef intptr_t QuestId; + typedef intptr_t TopicId; + typedef uint8_t const * Utf8Point; + typedef std::pair Utf8Span; + + /// The base interface for both journal entries and topics. + struct Entry + { + /// returns the body text for the journal entry + /// + /// This function returns a borrowed reference to the body of the + /// journal entry. The returned reference becomes invalid when the + /// entry is destroyed. + virtual Utf8Span body () const = 0; + + /// Visits each subset of text in the body, delivering the beginning + /// and end of the span relative to the body, and a valid topic ID if + /// the span represents a keyword, or zero if not. + virtual void visitSpans (boost::function visitor) const = 0; + }; + + /// An interface to topic data. + struct TopicEntry : Entry + { + /// Returns a pre-formatted span of UTF8 encoded text representing + /// the name of the NPC this portion of dialog was heard from. + virtual Utf8Span source () const = 0; + }; + + /// An interface to journal data. + struct JournalEntry : Entry + { + /// Returns a pre-formatted span of UTF8 encoded text representing + /// the in-game date this entry was added to the journal. + virtual Utf8Span timestamp () const = 0; + }; + + + /// called prior to journal opening + virtual void load () = 0; + + /// called prior to journal closing + virtual void unload () = 0; + + /// returns true if their are no journal entries to display + virtual bool isEmpty () const = 0; + + /// provides access to the name of the quest with the specified identifier + virtual void visitQuestName (TopicId topicId, boost::function visitor) const = 0; + + /// walks the active and optionally completed, quests providing the quest id and name + virtual void visitQuestNames (bool active_only, boost::function visitor) const = 0; + + /// walks over the journal entries related to the specified quest identified by its id + virtual void visitJournalEntries (QuestId questId, boost::function visitor) const = 0; + + /// provides the name of the topic specified by its id + virtual void visitTopicName (TopicId topicId, boost::function visitor) const = 0; + + /// walks over the topics whose names start with the specified character providing the topics id and name + virtual void visitTopicNamesStartingWith (char character, boost::function < void (TopicId , Utf8Span) > visitor) const = 0; + + /// walks over the topic entries for the topic specified by its identifier + virtual void visitTopicEntries (TopicId topicId, boost::function visitor) const = 0; + + // create an instance of the default journal view model implementation + static Ptr create (); + }; +} + +#endif // MWGUI_JOURNALVIEWMODEL_HPP diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index ba39b0101..5b84c99a5 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -1,201 +1,475 @@ #include "journalwindow.hpp" #include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" -#include "../mwbase/journal.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "list.hpp" -#include "../mwdialogue/journalentry.hpp" +#include +#include +#include +#include +#include +#include +#include +#include "boost/lexical_cast.hpp" + +#include "bookpage.hpp" +#include "windowbase.hpp" +#include "imagebutton.hpp" +#include "journalviewmodel.hpp" +#include "journalbooks.hpp" namespace { - struct book + static char const OptionsOverlay [] = "OptionsOverlay"; + static char const OptionsBTN [] = "OptionsBTN"; + static char const PrevPageBTN [] = "PrevPageBTN"; + static char const NextPageBTN [] = "NextPageBTN"; + static char const CloseBTN [] = "CloseBTN"; + static char const JournalBTN [] = "JournalBTN"; + static char const TopicsBTN [] = "TopicsBTN"; + static char const QuestsBTN [] = "QuestsBTN"; + static char const CancelBTN [] = "CancelBTN"; + static char const ShowAllBTN [] = "ShowAllBTN"; + static char const ShowActiveBTN [] = "ShowActiveBTN"; + static char const PageOneNum [] = "PageOneNum"; + static char const PageTwoNum [] = "PageTwoNum"; + static char const TopicsList [] = "TopicsList"; + static char const TopicsPage [] = "TopicsPage"; + static char const QuestsList [] = "QuestsList"; + static char const QuestsPage [] = "QuestsPage"; + static char const LeftBookPage [] = "LeftBookPage"; + static char const RightBookPage [] = "RightBookPage"; + static char const LeftTopicIndex [] = "LeftTopicIndex"; + static char const RightTopicIndex [] = "RightTopicIndex"; + + struct JournalWindowImpl : MWGui::WindowBase, MWGui::JournalBooks, MWGui::JournalWindow { - int endLine; - std::list pages; - }; -} + struct DisplayState + { + unsigned int mPage; + Book mBook; + }; -book formatText(std::string text,book mBook,int maxLine, int lineSize) -{ - //stringList.push_back(""); + typedef std::stack DisplayStateStack; - int cLineSize = 0; - int cLine = mBook.endLine +1; - std::string cString; + DisplayStateStack mStates; + Book mTopicIndexBook; + bool mQuestMode; + bool mAllQuests; - if(mBook.pages.empty()) - { - cString = ""; - cLine = 0; - } - else - { - cString = mBook.pages.back() + std::string("\n"); - mBook.pages.pop_back(); - } + template + T * getWidget (char const * name) + { + T * widget; + WindowBase::getWidget (widget, name); + return widget; + } - //std::string::iterator wordBegin = text.begin(); - //std::string::iterator wordEnd; + template + void setText (char const * name, value_type const & value) + { + getWidget (name) -> + setCaption (boost::lexical_cast (value)); + } - std::string cText = text; + void setVisible (char const * name, bool visible) + { + getWidget (name) -> + setVisible (visible); + } - while(cText.length() != 0) - { - size_t firstSpace = cText.find_first_of(' '); - if(firstSpace == std::string::npos) + void adviseButtonClick (char const * name, void (JournalWindowImpl::*Handler) (MyGUI::Widget* _sender)) { - cString = cString + cText; - mBook.pages.push_back(cString); - //TODO:finnish this - break; + getWidget (name) -> + eventMouseButtonClick += newDelegate(this, Handler); } - if(static_cast (firstSpace) + cLineSize <= lineSize) + + MWGui::BookPage* getPage (char const * name) { - cLineSize = firstSpace + cLineSize; - cString = cString + cText.substr(0,firstSpace +1); + return getWidget (name); } - else + + JournalWindowImpl (MWGui::JournalViewModel::Ptr Model) + : WindowBase("openmw_journal.layout"), JournalBooks (Model) { - cLineSize = firstSpace; - if(cLine +1 <= maxLine) + mMainWidget->setVisible(false); + center(); + + adviseButtonClick (OptionsBTN, &JournalWindowImpl::notifyOptions ); + adviseButtonClick (PrevPageBTN, &JournalWindowImpl::notifyPrevPage ); + adviseButtonClick (NextPageBTN, &JournalWindowImpl::notifyNextPage ); + adviseButtonClick (CloseBTN, &JournalWindowImpl::notifyClose ); + adviseButtonClick (JournalBTN, &JournalWindowImpl::notifyJournal ); + + adviseButtonClick (TopicsBTN, &JournalWindowImpl::notifyTopics ); + adviseButtonClick (QuestsBTN, &JournalWindowImpl::notifyQuests ); + adviseButtonClick (CancelBTN, &JournalWindowImpl::notifyCancel ); + + adviseButtonClick (ShowAllBTN, &JournalWindowImpl::notifyShowAll ); + adviseButtonClick (ShowActiveBTN, &JournalWindowImpl::notifyShowActive); + + { + MWGui::BookPage::ClickCallback callback; + + callback = boost::bind (&JournalWindowImpl::notifyTopicClicked, this, _1); + + getPage (TopicsPage)->adviseLinkClicked (callback); + getPage (LeftBookPage)->adviseLinkClicked (callback); + getPage (RightBookPage)->adviseLinkClicked (callback); + } + { - cLine = cLine + 1; - cString = cString + std::string("\n") + cText.substr(0,firstSpace +1); + MWGui::BookPage::ClickCallback callback; + + callback = boost::bind (&JournalWindowImpl::notifyIndexLinkClicked, this, _1); + + getPage (LeftTopicIndex)->adviseLinkClicked (callback); + getPage (RightTopicIndex)->adviseLinkClicked (callback); } + + { + MWGui::BookPage::ClickCallback callback; + + callback = boost::bind (&JournalWindowImpl::notifyQuestClicked, this, _1); + + getPage (QuestsPage)->adviseLinkClicked (callback); + } + + adjustButton(OptionsBTN); + adjustButton(PrevPageBTN); + adjustButton(NextPageBTN); + adjustButton(CloseBTN); + adjustButton(CancelBTN); + adjustButton(ShowAllBTN); + adjustButton(ShowActiveBTN); + adjustButton(JournalBTN); + + MWGui::ImageButton* nextButton = getWidget(NextPageBTN); + if (nextButton->getSize().width == 64) + { + // english button has a 7 pixel wide strip of garbage on its right edge + nextButton->setSize(64-7, nextButton->getSize().height); + nextButton->setImageCoord(MyGUI::IntCoord(0,0,64-7,nextButton->getSize().height)); + } + + adjustButton(TopicsBTN); + adjustButton(QuestsBTN); + int width = getWidget(TopicsBTN)->getSize().width + getWidget(QuestsBTN)->getSize().width; + int topicsWidth = getWidget(TopicsBTN)->getSize().width; + int pageWidth = getWidget(RightBookPage)->getSize().width; + + getWidget(TopicsBTN)->setPosition((pageWidth - width)/2, getWidget(TopicsBTN)->getPosition().top); + getWidget(QuestsBTN)->setPosition((pageWidth - width)/2 + topicsWidth, getWidget(QuestsBTN)->getPosition().top); + + mQuestMode = false; + mAllQuests = false; + } + + void adjustButton (char const * name) + { + MWGui::ImageButton* button = getWidget(name); + + MyGUI::IntSize diff = button->getSize() - button->getRequestedSize(); + button->setSize(button->getRequestedSize()); + + if (button->getAlign().isRight()) + button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0)); + } + + void open() + { + if (!MWBase::Environment::get().getWindowManager ()->getJournalAllowed ()) + { + MWBase::Environment::get().getWindowManager()->popGuiMode (); + } + mModel->load (); + + setBookMode (); + + /// \todo Wiping the whole book layout each time the journal is opened is probably too costly for a large journal (eg 300+ pages). + /// There should be a way to keep the existing layout and append new entries to the end of it. + /// However, that still leaves the problem of having to add links to previously unknown, but now known topics, so + /// we maybe need to find another way to speed things up. + Book journalBook; + if (mModel->isEmpty ()) + journalBook = createEmptyJournalBook (); else + journalBook = createJournalBook (); + + pushBook (journalBook, 0); + + // fast forward to the last page + if (!mStates.empty ()) { - cLine = 0; - mBook.pages.push_back(cString); - cString = cText.substr(0,firstSpace +1); + unsigned int & page = mStates.top ().mPage; + page = mStates.top().mBook->pageCount()-1; + if (page%2) + --page; } + updateShowingPages(); } - //std::cout << cText << "\n"; - //std::cout << cText.length(); - cText = cText.substr(firstSpace +1,cText.length() - firstSpace -1); - } - mBook.endLine = cLine; - return mBook; - //std::string -} + void close() + { + mModel->unload (); -MWGui::JournalWindow::JournalWindow (MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_journal.layout", parWindowManager) - , mPageNumber(0) -{ - mMainWidget->setVisible(false); - //setCoord(0,0,498, 342); - center(); - - getWidget(mLeftTextWidget, "LeftText"); - getWidget(mRightTextWidget, "RightText"); - getWidget(mPrevBtn, "PrevPageBTN"); - mPrevBtn->eventMouseButtonClick += MyGUI::newDelegate(this,&MWGui::JournalWindow::notifyPrevPage); - getWidget(mNextBtn, "NextPageBTN"); - mNextBtn->eventMouseButtonClick += MyGUI::newDelegate(this,&MWGui::JournalWindow::notifyNextPage); - //MyGUI::ItemBox* list = new MyGUI::ItemBox(); - //list->addItem("qaq","aqzazaz"); - //mScrollerWidget->addChildItem(list); - //mScrollerWidget->addItem("dserzt",MyGUI::UString("fedgdfg")); - //mEditWidget->addText("ljblsxdvdsfvgedfvdfvdkjfghldfjgn sdv,nhsxl;vvn lklksbvlksb lbsdflkbdSLKJGBLskdhbvlshow(); - //mEditWidget->setEditStatic(true); - mLeftTextWidget->addText("left texxxt "); - mLeftTextWidget->setEditReadOnly(true); - mRightTextWidget->setEditReadOnly(true); - mRightTextWidget->setEditStatic(true); - mLeftTextWidget->setEditStatic(true); - mRightTextWidget->addText("Right texxt "); - - //std::list list = formatText("OpenMW rgh dsfg sqef srg ZT uzql n ZLIEHRF LQSJH GLOIjf qjfmj hslkdgn jlkdjhg qlr isgli shli uhs fiuh qksf cg ksjnf lkqsnbf ksbf sbfkl zbf kuyzflkj sbgdfkj zlfh ozhjfmo hzmfh lizuf rty qzt ezy tkyEZT RYYJ DG fgh is an open-source implementation of the game engine found in the game Morrowind. This is a dumb test text msodjbg smojg smoig fiiinnn"); - //std::list list = formatText(); - //displayLeftText(list.front()); -} + getPage (LeftBookPage)->showPage (Book (), 0); + getPage (RightBookPage)->showPage (Book (), 0); -void MWGui::JournalWindow::close() -{ - MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); -} + while (!mStates.empty ()) + mStates.pop (); -void MWGui::JournalWindow::open() -{ - mPageNumber = 0; - MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); - if(MWBase::Environment::get().getJournal()->begin()!=MWBase::Environment::get().getJournal()->end()) - { - book journal; - journal.endLine = 0; + mTopicIndexBook.reset (); + } + + void setVisible (bool newValue) + { + WindowBase::setVisible (newValue); + } + + void setBookMode () + { + setVisible (OptionsBTN, true); + setVisible (OptionsOverlay, false); + + updateShowingPages (); + updateCloseJournalButton (); + } + + void setOptionsMode () + { + setVisible (OptionsBTN, false); + setVisible (OptionsOverlay, true); + + setVisible (PrevPageBTN, false); + setVisible (NextPageBTN, false); + setVisible (CloseBTN, false); + setVisible (JournalBTN, false); + + setVisible (TopicsList, false); + setVisible (QuestsList, mQuestMode); + setVisible (LeftTopicIndex, !mQuestMode); + setVisible (RightTopicIndex, !mQuestMode); + setVisible (ShowAllBTN, mQuestMode && !mAllQuests); + setVisible (ShowActiveBTN, mQuestMode && mAllQuests); + + //TODO: figure out how to make "options" page overlay book page + // correctly, so that text may show underneath + getPage (RightBookPage)->showPage (Book (), 0); + } + + void pushBook (Book book, unsigned int page) + { + DisplayState bs; + bs.mPage = page; + bs.mBook = book; + mStates.push (bs); + updateShowingPages (); + updateCloseJournalButton (); + } + + void replaceBook (Book book, unsigned int page) + { + assert (!mStates.empty ()); + mStates.top ().mBook = book; + mStates.top ().mPage = page; + updateShowingPages (); + } + + void popBook () + { + mStates.pop (); + updateShowingPages (); + updateCloseJournalButton (); + } - for(std::deque::const_iterator it = MWBase::Environment::get().getJournal()->begin();it!=MWBase::Environment::get().getJournal()->end();++it) + void updateCloseJournalButton () { - std::string a = it->getText(MWBase::Environment::get().getWorld()->getStore()); - journal = formatText(a,journal,10,17); - journal.endLine = journal.endLine +1; - journal.pages.back() = journal.pages.back() + std::string("\n"); + setVisible (CloseBTN, mStates.size () < 2); + setVisible (JournalBTN, mStates.size () >= 2); } - //std::string a = MWBase::Environment::get().getJournal()->begin()->getText(MWBase::Environment::get().getWorld()->getStore()); - //std::list journal = formatText(a,10,20,1); - bool left = true; - for(std::list::iterator it = journal.pages.begin(); it != journal.pages.end();++it) + + void updateShowingPages () { - if(left) + Book book; + unsigned int page; + unsigned int relPages; + + if (!mStates.empty ()) { - mLeftPages.push_back(*it); + book = mStates.top ().mBook; + page = mStates.top ().mPage; + relPages = book->pageCount () - page; } else { - mRightPages.push_back(*it); + page = 0; + relPages = 0; } - left = !left; + + setVisible (PrevPageBTN, page > 0); + setVisible (NextPageBTN, relPages > 2); + + setVisible (PageOneNum, relPages > 0); + setVisible (PageTwoNum, relPages > 1); + + getPage (LeftBookPage)->showPage ((relPages > 0) ? book : Book (), page+0); + getPage (RightBookPage)->showPage ((relPages > 0) ? book : Book (), page+1); + + setText (PageOneNum, page + 1); + setText (PageTwoNum, page + 2); } - if(!left) mRightPages.push_back(""); - mPageNumber = mLeftPages.size()-1; - displayLeftText(mLeftPages[mPageNumber]); - displayRightText(mRightPages[mPageNumber]); + void notifyTopicClicked (intptr_t linkId) + { + Book topicBook = createTopicBook (linkId); - } - else - { - //std::cout << MWBase::Environment::get().getJournal()->begin()->getText(MWBase::Environment::get().getWorld()->getStore()); - } -} + if (mStates.size () > 1) + replaceBook (topicBook, 0); + else + pushBook (topicBook, 0); -void MWGui::JournalWindow::displayLeftText(std::string text) -{ - mLeftTextWidget->eraseText(0,mLeftTextWidget->getTextLength()); - mLeftTextWidget->addText(text); -} + setVisible (OptionsOverlay, false); + setVisible (OptionsBTN, true); + setVisible (JournalBTN, true); + } -void MWGui::JournalWindow::displayRightText(std::string text) -{ - mRightTextWidget->eraseText(0,mRightTextWidget->getTextLength()); - mRightTextWidget->addText(text); -} + void notifyQuestClicked (intptr_t questId) + { + Book book = createQuestBook (questId); + if (mStates.size () > 1) + replaceBook (book, 0); + else + pushBook (book, 0); -void MWGui::JournalWindow::notifyNextPage(MyGUI::Widget* _sender) -{ - if(mPageNumber < int(mLeftPages.size())-1) - { - std::string nextSound = "book page2"; - MWBase::Environment::get().getSoundManager()->playSound (nextSound, 1.0, 1.0); - mPageNumber = mPageNumber + 1; - displayLeftText(mLeftPages[mPageNumber]); - displayRightText(mRightPages[mPageNumber]); - } + setVisible (OptionsOverlay, false); + setVisible (OptionsBTN, true); + setVisible (JournalBTN, true); + } + + void notifyOptions(MyGUI::Widget* _sender) + { + setOptionsMode (); + + if (!mTopicIndexBook) + mTopicIndexBook = createTopicIndexBook (); + + getPage (LeftTopicIndex)->showPage (mTopicIndexBook, 0); + getPage (RightTopicIndex)->showPage (mTopicIndexBook, 1); + } + + void notifyJournal(MyGUI::Widget* _sender) + { + assert (mStates.size () > 1); + popBook (); + } + + void showList (char const * listId, char const * pageId, Book book) + { + std::pair size = book->getSize (); + + getPage (pageId)->showPage (book, 0); + + getWidget (listId)->setCanvasSize (size.first, size.second); + } + + void notifyIndexLinkClicked (MWGui::TypesetBook::InteractiveId character) + { + setVisible (LeftTopicIndex, false); + setVisible (RightTopicIndex, false); + setVisible (TopicsList, true); + + showList (TopicsList, TopicsPage, createTopicIndexBook ((char)character)); + } + + void notifyTopics(MyGUI::Widget* _sender) + { + mQuestMode = false; + setVisible (LeftTopicIndex, true); + setVisible (RightTopicIndex, true); + setVisible (TopicsList, false); + setVisible (QuestsList, false); + setVisible (ShowAllBTN, false); + setVisible (ShowActiveBTN, false); + } + + void notifyQuests(MyGUI::Widget* _sender) + { + mQuestMode = true; + setVisible (LeftTopicIndex, false); + setVisible (RightTopicIndex, false); + setVisible (TopicsList, false); + setVisible (QuestsList, true); + setVisible (ShowAllBTN, !mAllQuests); + setVisible (ShowActiveBTN, mAllQuests); + + showList (QuestsList, QuestsPage, createQuestIndexBook (!mAllQuests)); + } + + void notifyShowAll(MyGUI::Widget* _sender) + { + mAllQuests = true; + setVisible (ShowAllBTN, !mAllQuests); + setVisible (ShowActiveBTN, mAllQuests); + showList (QuestsList, QuestsPage, createQuestIndexBook (!mAllQuests)); + } + + void notifyShowActive(MyGUI::Widget* _sender) + { + mAllQuests = false; + setVisible (ShowAllBTN, !mAllQuests); + setVisible (ShowActiveBTN, mAllQuests); + showList (QuestsList, QuestsPage, createQuestIndexBook (!mAllQuests)); + } + + void notifyCancel(MyGUI::Widget* _sender) + { + setBookMode (); + } + + void notifyClose(MyGUI::Widget* _sender) + { + MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); + + MWBase::Environment::get().getWindowManager ()->popGuiMode (); + } + + void notifyNextPage(MyGUI::Widget* _sender) + { + if (!mStates.empty ()) + { + unsigned int & page = mStates.top ().mPage; + Book book = mStates.top ().mBook; + + if (page < book->pageCount () - 2) + { + page += 2; + updateShowingPages (); + } + } + } + + void notifyPrevPage(MyGUI::Widget* _sender) + { + if (!mStates.empty ()) + { + unsigned int & page = mStates.top ().mPage; + + if(page > 0) + { + page -= 2; + updateShowingPages (); + } + } + } + }; } -void MWGui::JournalWindow::notifyPrevPage(MyGUI::Widget* _sender) +// glue the implementation to the interface +MWGui::JournalWindow * MWGui::JournalWindow::create (JournalViewModel::Ptr Model) { - if(mPageNumber > 0) - { - std::string prevSound = "book page"; - MWBase::Environment::get().getSoundManager()->playSound (prevSound, 1.0, 1.0); - mPageNumber = mPageNumber - 1; - displayLeftText(mLeftPages[mPageNumber]); - displayRightText(mRightPages[mPageNumber]); - } + return new JournalWindowImpl (Model); } diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index 044a2b2a4..63770ec1a 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -1,43 +1,26 @@ #ifndef MWGUI_JOURNAL_H #define MWGUI_JOURNAL_H -#include -#include -#include -#include +#include +#include -#include "window_base.hpp" -#include "imagebutton.hpp" +namespace MWBase { class WindowManager; } namespace MWGui { - class JournalWindow : public WindowBase - { - public: - JournalWindow(MWBase::WindowManager& parWindowManager); - virtual void open(); - virtual void close(); - - private: - void displayLeftText(std::string text); - void displayRightText(std::string text); + struct JournalViewModel; + struct JournalWindow + { + /// construct a new instance of the one JournalWindow implementation + static JournalWindow * create (boost::shared_ptr Model); - /** - *Called when next/prev button is used. - */ - void notifyNextPage(MyGUI::Widget* _sender); - void notifyPrevPage(MyGUI::Widget* _sender); + /// destroy this instance of the JournalWindow implementation + virtual ~JournalWindow () {}; - MyGUI::EditPtr mLeftTextWidget; - MyGUI::EditPtr mRightTextWidget; - MWGui::ImageButton* mPrevBtn; - MWGui::ImageButton* mNextBtn; - std::vector mLeftPages; - std::vector mRightPages; - int mPageNumber; //store the number of the current left page + /// show/hide the journal window + virtual void setVisible (bool newValue) = 0; }; - } #endif diff --git a/apps/openmw/mwgui/keywordsearch.cpp b/apps/openmw/mwgui/keywordsearch.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/apps/openmw/mwgui/keywordsearch.hpp b/apps/openmw/mwgui/keywordsearch.hpp new file mode 100644 index 000000000..13d3318e5 --- /dev/null +++ b/apps/openmw/mwgui/keywordsearch.hpp @@ -0,0 +1,199 @@ +#ifndef MWGUI_KEYWORDSEARCH_H +#define MWGUI_KEYWORDSEARCH_H + +#include +#include +#include +#include +#include // std::reverse + +#include + +namespace MWGui +{ + +template +class KeywordSearch +{ +public: + + typedef typename string_t::const_iterator Point; + + struct Match + { + Point mBeg; + Point mEnd; + value_t mValue; + }; + + void seed (string_t keyword, value_t value) + { + seed_impl (/*std::move*/ (keyword), /*std::move*/ (value), 0, mRoot); + } + + void clear () + { + mRoot.mChildren.clear (); + mRoot.mKeyword.clear (); + } + + bool containsKeyword (string_t keyword, value_t& value) + { + typename Entry::childen_t::iterator current; + typename Entry::childen_t::iterator next; + + current = mRoot.mChildren.find (std::tolower (*keyword.begin(), mLocale)); + if (current == mRoot.mChildren.end()) + return false; + else if (current->second.mKeyword.size() && Misc::StringUtils::ciEqual(current->second.mKeyword, keyword)) + { + value = current->second.mValue; + return true; + } + + for (Point i = ++keyword.begin(); i != keyword.end(); ++i) + { + next = current->second.mChildren.find(std::tolower (*i, mLocale)); + if (next == current->second.mChildren.end()) + return false; + if (Misc::StringUtils::ciEqual(next->second.mKeyword, keyword)) + { + value = next->second.mValue; + return true; + } + current = next; + } + return false; + } + + bool search (Point beg, Point end, Match & match) + { + for (Point i = beg; i != end; ++i) + { + // check first character + typename Entry::childen_t::iterator candidate = mRoot.mChildren.find (std::tolower (*i, mLocale)); + + // no match, on to next character + if (candidate == mRoot.mChildren.end ()) + continue; + + // see how far the match goes + Point j = i; + + // some keywords might be longer variations of other keywords, so we definitely need a list of candidates + // the first element in the pair is length of the match, i.e. depth from the first character on + std::vector< typename std::pair > candidates; + + while ((j + 1) != end) + { + typename Entry::childen_t::iterator next = candidate->second.mChildren.find (std::tolower (*++j, mLocale)); + + if (next == candidate->second.mChildren.end ()) + { + if (candidate->second.mKeyword.size() > 0) + candidates.push_back(std::make_pair((j-i), candidate)); + break; + } + + candidate = next; + + if (candidate->second.mKeyword.size() > 0) + candidates.push_back(std::make_pair((j-i), candidate)); + } + + if (!candidates.size()) + continue; // didn't match enough to disambiguate, on to next character + + // shorter candidates will be added to the vector first. however, we want to check against longer candidates first + std::reverse(candidates.begin(), candidates.end()); + + for (typename std::vector< std::pair >::iterator it = candidates.begin(); + it != candidates.end(); ++it) + { + candidate = it->second; + // try to match the rest of the keyword + Point k = i + it->first; + typename string_t::const_iterator t = candidate->second.mKeyword.begin () + (k - i); + + + while (k != end && t != candidate->second.mKeyword.end ()) + { + if (std::tolower (*k, mLocale) != std::tolower (*t, mLocale)) + break; + + ++k, ++t; + } + + // didn't match full keyword, try the next candidate + if (t != candidate->second.mKeyword.end ()) + continue; + + // we did it, report the good news + match.mValue = candidate->second.mValue; + match.mBeg = i; + match.mEnd = k; + + return true; + } + } + + // no match in range, report the bad news + return false; + } + +private: + + struct Entry + { + typedef std::map childen_t; + + string_t mKeyword; + value_t mValue; + childen_t mChildren; + }; + + void seed_impl (string_t keyword, value_t value, size_t depth, Entry & entry) + { + int ch = tolower (keyword.at (depth), mLocale); + + typename Entry::childen_t::iterator j = entry.mChildren.find (ch); + + if (j == entry.mChildren.end ()) + { + entry.mChildren [ch].mValue = /*std::move*/ (value); + entry.mChildren [ch].mKeyword = /*std::move*/ (keyword); + } + else + { + if (j->second.mKeyword.size () > 0) + { + if (keyword == j->second.mKeyword) + throw std::runtime_error ("duplicate keyword inserted"); + + value_t pushValue = /*std::move*/ (j->second.mValue); + string_t pushKeyword = /*std::move*/ (j->second.mKeyword); + + if (depth >= pushKeyword.size ()) + throw std::runtime_error ("unexpected"); + + if (depth+1 < pushKeyword.size()) + { + seed_impl (/*std::move*/ (pushKeyword), /*std::move*/ (pushValue), depth+1, j->second); + j->second.mKeyword.clear (); + } + } + if (depth+1 == keyword.size()) + j->second.mKeyword = value; + else // depth+1 < keyword.size() + seed_impl (/*std::move*/ (keyword), /*std::move*/ (value), depth+1, j->second); + } + + } + + Entry mRoot; + std::locale mLocale; +}; + +} + +#endif diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 45890b89f..5857db4d2 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -8,21 +8,22 @@ #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/esmstore.hpp" +#include "../mwworld/fallback.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" -#include "../mwmechanics/stat.hpp" namespace MWGui { - LevelupDialog::LevelupDialog(MWBase::WindowManager &parWindowManager) - : WindowBase("openmw_levelup_dialog.layout", parWindowManager) + LevelupDialog::LevelupDialog() + : WindowBase("openmw_levelup_dialog.layout") { getWidget(mOkButton, "OkButton"); getWidget(mClassImage, "ClassImage"); getWidget(mLevelText, "LevelText"); + getWidget(mLevelDescription, "LevelDescription"); + getWidget(mCoinBox, "Coins"); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &LevelupDialog::onOkButtonClicked); @@ -80,11 +81,13 @@ namespace MWGui void LevelupDialog::resetCoins () { - int curX = mMainWidget->getWidth()/2 - (16 + 2) * 1.5; + int curX = 0; for (int i=0; i<3; ++i) { MyGUI::ImageBox* image = mCoins[i]; - image->setCoord(MyGUI::IntCoord(curX,250,16,16)); + image->detachFromWidget(); + image->attachToWidget(mCoinBox); + image->setCoord(MyGUI::IntCoord(curX,0,16,16)); curX += 24+2; } } @@ -95,6 +98,9 @@ namespace MWGui for (unsigned int i=0; idetachFromWidget(); + image->attachToWidget(mMainWidget); + int attribute = mSpentAttributes[i]; int xdiff = mAttributeMultipliers[attribute]->getCaption() == "" ? 0 : 30; @@ -113,8 +119,6 @@ namespace MWGui MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats (player); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); - center(); - mSpentAttributes.clear(); resetCoins(); @@ -128,16 +132,25 @@ namespace MWGui mClassImage->setImageTexture ("textures\\levelup\\" + cls->mId + ".dds"); - /// \todo replace this with INI-imported texts int level = creatureStats.getLevel ()+1; mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + boost::lexical_cast(level)); + std::string levelupdescription; + if(level>20) + levelupdescription=world->getFallback()->getFallbackString("Level_Up_Default"); + else + levelupdescription=world->getFallback()->getFallbackString("Level_Up_Level"+boost::lexical_cast(level)); + + mLevelDescription->setCaption (levelupdescription); + for (int i=0; i<8; ++i) { MyGUI::TextBox* text = mAttributeMultipliers[i]; int mult = pcStats.getLevelupAttributeMultiplier (i); text->setCaption(mult <= 1 ? "" : "x" + boost::lexical_cast(mult)); } + + center(); } void LevelupDialog::onOkButtonClicked (MyGUI::Widget* sender) @@ -147,7 +160,7 @@ namespace MWGui MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); if (mSpentAttributes.size() < 3) - MWBase::Environment::get().getWindowManager ()->messageBox("#{sNotifyMessage36}", std::vector()); + MWBase::Environment::get().getWindowManager ()->messageBox("#{sNotifyMessage36}"); else { // increase attributes @@ -168,7 +181,7 @@ namespace MWGui creatureStats.setLevel (creatureStats.getLevel()+1); pcStats.levelUp (); - mWindowManager.removeGuiMode (GM_Levelup); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Levelup); } } diff --git a/apps/openmw/mwgui/levelupdialog.hpp b/apps/openmw/mwgui/levelupdialog.hpp index f5b24530d..69afbf089 100644 --- a/apps/openmw/mwgui/levelupdialog.hpp +++ b/apps/openmw/mwgui/levelupdialog.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_LEVELUPDIALOG_H #define MWGUI_LEVELUPDIALOG_H -#include "window_base.hpp" +#include "windowbase.hpp" namespace MWGui { @@ -9,7 +9,7 @@ namespace MWGui class LevelupDialog : public WindowBase { public: - LevelupDialog(MWBase::WindowManager& parWindowManager); + LevelupDialog(); virtual void open(); @@ -17,6 +17,9 @@ namespace MWGui MyGUI::Button* mOkButton; MyGUI::ImageBox* mClassImage; MyGUI::TextBox* mLevelText; + MyGUI::EditBox* mLevelDescription; + + MyGUI::Widget* mCoinBox; std::vector mAttributes; std::vector mAttributeValues; diff --git a/apps/openmw/mwgui/list.cpp b/apps/openmw/mwgui/list.cpp index 0bafced97..8dda041ca 100644 --- a/apps/openmw/mwgui/list.cpp +++ b/apps/openmw/mwgui/list.cpp @@ -1,137 +1,169 @@ #include "list.hpp" -#include +#include +#include +#include +#include -using namespace MWGui; -using namespace MWGui::Widgets; - -MWList::MWList() : - mClient(0) - , mScrollView(0) - , mItemHeight(0) +namespace MWGui { -} - -void MWList::initialiseOverride() -{ - Base::initialiseOverride(); - - assignWidget(mClient, "Client"); - if (mClient == 0) - mClient = this; - mScrollView = mClient->createWidgetReal( - "MW_ScrollView", MyGUI::FloatCoord(0.0, 0.0, 1.0, 1.0), - MyGUI::Align::Top | MyGUI::Align::Left | MyGUI::Align::Stretch, getName() + "_ScrollView"); -} - -void MWList::addItem(const std::string& name) -{ - mItems.push_back(name); -} + namespace Widgets + { -void MWList::addSeparator() -{ - mItems.push_back(""); -} + MWList::MWList() : + mClient(0) + , mScrollView(0) + , mItemHeight(0) + { + } -void MWList::adjustSize() -{ - redraw(); -} + void MWList::initialiseOverride() + { + Base::initialiseOverride(); -void MWList::redraw(bool scrollbarShown) -{ - const int _scrollBarWidth = 24; // fetch this from skin? - const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0; - const int spacing = 3; + assignWidget(mClient, "Client"); + if (mClient == 0) + mClient = this; - while (mScrollView->getChildCount()) - { - MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0)); - } + mScrollView = mClient->createWidgetReal( + "MW_ScrollView", MyGUI::FloatCoord(0.0, 0.0, 1.0, 1.0), + MyGUI::Align::Top | MyGUI::Align::Left | MyGUI::Align::Stretch, getName() + "_ScrollView"); + } - mItemHeight = 0; - for (std::vector::const_iterator it=mItems.begin(); - it!=mItems.end(); ++it) - { - if (*it != "") + void MWList::addItem(const std::string& name) { - MyGUI::Button* button = mScrollView->createWidget( - "MW_ListLine", MyGUI::IntCoord(0, mItemHeight, mScrollView->getSize().width - scrollBarWidth - 2, 24), - MyGUI::Align::Left | MyGUI::Align::Top, getName() + "_item_" + (*it)); - button->setCaption((*it)); - button->getSubWidgetText()->setWordWrap(true); - button->getSubWidgetText()->setTextAlign(MyGUI::Align::Left); - button->eventMouseWheel += MyGUI::newDelegate(this, &MWList::onMouseWheel); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWList::onItemSelected); + mItems.push_back(name); + } - int height = button->getTextSize().height; - button->setSize(MyGUI::IntSize(button->getSize().width, height)); + void MWList::addSeparator() + { + mItems.push_back(""); + } - mItemHeight += height + spacing; + void MWList::adjustSize() + { + redraw(); } - else + + void MWList::redraw(bool scrollbarShown) { - MyGUI::ImageBox* separator = mScrollView->createWidget("MW_HLine", - MyGUI::IntCoord(2, mItemHeight, mScrollView->getWidth()-4, 18), - MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); - separator->setNeedMouseFocus(false); + const int _scrollBarWidth = 24; // fetch this from skin? + const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0; + const int spacing = 3; + size_t scrollbarPosition = mScrollView->getScrollPosition(); + + while (mScrollView->getChildCount()) + { + MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0)); + } + + mItemHeight = 0; + int i=0; + for (std::vector::const_iterator it=mItems.begin(); + it!=mItems.end(); ++it) + { + if (*it != "") + { + MyGUI::Button* button = mScrollView->createWidget( + "MW_ListLine", MyGUI::IntCoord(0, mItemHeight, mScrollView->getSize().width - scrollBarWidth - 2, 24), + MyGUI::Align::Left | MyGUI::Align::Top, getName() + "_item_" + (*it)); + button->setCaption((*it)); + button->getSubWidgetText()->setWordWrap(true); + button->getSubWidgetText()->setTextAlign(MyGUI::Align::Left); + button->eventMouseWheel += MyGUI::newDelegate(this, &MWList::onMouseWheel); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWList::onItemSelected); + + int height = button->getTextSize().height; + button->setSize(MyGUI::IntSize(button->getSize().width, height)); + button->setUserData(i); + + mItemHeight += height + spacing; + } + else + { + MyGUI::ImageBox* separator = mScrollView->createWidget("MW_HLine", + MyGUI::IntCoord(2, mItemHeight, mScrollView->getWidth()-4, 18), + MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); + separator->setNeedMouseFocus(false); + + mItemHeight += 18 + spacing; + } + ++i; + } + mScrollView->setCanvasSize(mClient->getSize().width + (_scrollBarWidth-scrollBarWidth), std::max(mItemHeight, mClient->getSize().height)); + + if (!scrollbarShown && mItemHeight > mClient->getSize().height) + redraw(true); + + size_t scrollbarRange = mScrollView->getScrollRange(); + if(scrollbarPosition > scrollbarRange) + scrollbarPosition = scrollbarRange; + mScrollView->setScrollPosition(scrollbarPosition); + } - mItemHeight += 18 + spacing; + bool MWList::hasItem(const std::string& name) + { + return (std::find(mItems.begin(), mItems.end(), name) != mItems.end()); } - } - mScrollView->setCanvasSize(mClient->getSize().width + (_scrollBarWidth-scrollBarWidth), std::max(mItemHeight, mClient->getSize().height)); - if (!scrollbarShown && mItemHeight > mClient->getSize().height) - redraw(true); -} + unsigned int MWList::getItemCount() + { + return mItems.size(); + } -bool MWList::hasItem(const std::string& name) -{ - return (std::find(mItems.begin(), mItems.end(), name) != mItems.end()); -} + std::string MWList::getItemNameAt(unsigned int at) + { + assert(at < mItems.size() && "List item out of bounds"); + return mItems[at]; + } -unsigned int MWList::getItemCount() -{ - return mItems.size(); -} + void MWList::removeItem(const std::string& name) + { + assert( std::find(mItems.begin(), mItems.end(), name) != mItems.end() ); + mItems.erase( std::find(mItems.begin(), mItems.end(), name) ); + } -std::string MWList::getItemNameAt(unsigned int at) -{ - assert(at < mItems.size() && "List item out of bounds"); - return mItems[at]; -} + void MWList::clear() + { + mItems.clear(); + } -void MWList::removeItem(const std::string& name) -{ - assert( std::find(mItems.begin(), mItems.end(), name) != mItems.end() ); - mItems.erase( std::find(mItems.begin(), mItems.end(), name) ); -} + void MWList::onMouseWheel(MyGUI::Widget* _sender, int _rel) + { + //NB view offset is negative + if (mScrollView->getViewOffset().top + _rel*0.3 > 0) + mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mScrollView->setViewOffset(MyGUI::IntPoint(0, mScrollView->getViewOffset().top + _rel*0.3)); + } -void MWList::clear() -{ - mItems.clear(); -} + void MWList::onItemSelected(MyGUI::Widget* _sender) + { + std::string name = static_cast(_sender)->getCaption(); + int id = *_sender->getUserData(); + eventItemSelected(name, id); + eventWidgetSelected(_sender); + } -void MWList::onMouseWheel(MyGUI::Widget* _sender, int _rel) -{ - //NB view offset is negative - if (mScrollView->getViewOffset().top + _rel*0.3 > 0) - mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); - else - mScrollView->setViewOffset(MyGUI::IntPoint(0, mScrollView->getViewOffset().top + _rel*0.3)); -} + MyGUI::Widget* MWList::getItemWidget(const std::string& name) + { + return mScrollView->findWidget (getName() + "_item_" + name); + } -void MWList::onItemSelected(MyGUI::Widget* _sender) -{ - std::string name = static_cast(_sender)->getCaption(); + size_t MWScrollView::getScrollPosition() + { + return getVScroll()->getScrollPosition(); + } - eventItemSelected(name); - eventWidgetSelected(_sender); -} + void MWScrollView::setScrollPosition(size_t position) + { + getVScroll()->setScrollPosition(position); + } + size_t MWScrollView::getScrollRange() + { + return getVScroll()->getScrollRange(); + } -MyGUI::Widget* MWList::getItemWidget(const std::string& name) -{ - return mScrollView->findWidget (getName() + "_item_" + name); + } } diff --git a/apps/openmw/mwgui/list.hpp b/apps/openmw/mwgui/list.hpp index d07d49de6..956523c0d 100644 --- a/apps/openmw/mwgui/list.hpp +++ b/apps/openmw/mwgui/list.hpp @@ -1,12 +1,24 @@ #ifndef MWGUI_LIST_HPP #define MWGUI_LIST_HPP -#include +#include namespace MWGui { namespace Widgets { + /** + * \brief a custom ScrollView which has access to scrollbar properties + */ + class MWScrollView : public MyGUI::ScrollView + { + MYGUI_RTTI_DERIVED(MWScrollView) + public: + size_t getScrollPosition(); + void setScrollPosition(size_t); + size_t getScrollRange(); + }; + /** * \brief a very simple list widget that supports word-wrapping entries * \note if the width or height of the list changes, you must call adjustSize() method @@ -17,14 +29,14 @@ namespace MWGui public: MWList(); - typedef MyGUI::delegates::CMultiDelegate1 EventHandle_String; + typedef MyGUI::delegates::CMultiDelegate2 EventHandle_StringInt; typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Widget; /** * Event: Item selected with the mouse. * signature: void method(std::string itemName) */ - EventHandle_String eventItemSelected; + EventHandle_StringInt eventItemSelected; /** * Event: Item selected with the mouse. @@ -58,7 +70,7 @@ namespace MWGui void onItemSelected(MyGUI::Widget* _sender); private: - MyGUI::ScrollView* mScrollView; + MWGui::Widgets::MWScrollView* mScrollView; MyGUI::Widget* mClient; std::vector mItems; diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index dd5289edb..018f51feb 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -1,36 +1,28 @@ #include "loadingscreen.hpp" #include -#include #include #include -#include - - -#include #include #include "../mwbase/environment.hpp" -#include "../mwbase/inputmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include - - namespace MWGui { - LoadingScreen::LoadingScreen(Ogre::SceneManager* sceneMgr, Ogre::RenderWindow* rw, MWBase::WindowManager& parWindowManager) + LoadingScreen::LoadingScreen(Ogre::SceneManager* sceneMgr, Ogre::RenderWindow* rw) : mSceneMgr(sceneMgr) , mWindow(rw) - , WindowBase("openmw_loading_screen.layout", parWindowManager) + , WindowBase("openmw_loading_screen.layout") , mLoadingOn(false) , mLastRenderTime(0.f) , mLastWallpaperChangeTime(0.f) , mFirstLoad(true) + , mTotalRefsLoading(0) { getWidget(mLoadingText, "LoadingText"); getWidget(mProgressBar, "ProgressBar"); @@ -106,7 +98,7 @@ namespace MWGui float progress = (float(mCurrentCellLoading)+refProgress) / float(mTotalCellsLoading); assert(progress <= 1 && progress >= 0); - mLoadingText->setCaption(stage + "... "); + mLoadingText->setCaption(stage); mProgressBar->setProgressPosition (static_cast(progress * 1000)); static float loadingScreenFps = 30.f; @@ -195,12 +187,12 @@ namespace MWGui { changeWallpaper(); - mWindowManager.pushGuiMode(GM_LoadingWallpaper); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_LoadingWallpaper); } else { mBackgroundImage->setImageTexture(""); - mWindowManager.pushGuiMode(GM_Loading); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Loading); } } @@ -211,21 +203,27 @@ namespace MWGui mLoadingOn = false; mFirstLoad = false; - mWindowManager.removeGuiMode(GM_Loading); - mWindowManager.removeGuiMode(GM_LoadingWallpaper); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Loading); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_LoadingWallpaper); } void LoadingScreen::changeWallpaper () { - if (mResources.isNull ()) - mResources = Ogre::ResourceGroupManager::getSingleton ().findResourceNames ("General", "Splash_*.tga"); - + if (mResources.empty()) + { + Ogre::StringVector groups = Ogre::ResourceGroupManager::getSingleton().getResourceGroups (); + for (Ogre::StringVector::iterator it = groups.begin(); it != groups.end(); ++it) + { + Ogre::StringVectorPtr resourcesInThisGroup = Ogre::ResourceGroupManager::getSingleton ().findResourceNames (*it, "Splash_*.tga"); + mResources.insert(mResources.end(), resourcesInThisGroup->begin(), resourcesInThisGroup->end()); + } + } - if (mResources->size()) + if (!mResources.empty()) { - std::string const & randomSplash = mResources->at (rand() % mResources->size()); + std::string const & randomSplash = mResources.at (rand() % mResources.size()); - Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().load (randomSplash, "General"); + Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().load (randomSplash, Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME); mBackgroundImage->setImageTexture (randomSplash); } diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index c14087a3b..87cedaa98 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -2,16 +2,15 @@ #define MWGUI_LOADINGSCREEN_H #include -#include -#include "window_base.hpp" +#include "windowbase.hpp" namespace MWGui { class LoadingScreen : public WindowBase { public: - LoadingScreen(Ogre::SceneManager* sceneMgr, Ogre::RenderWindow* rw, MWBase::WindowManager& parWindowManager); + LoadingScreen(Ogre::SceneManager* sceneMgr, Ogre::RenderWindow* rw); virtual ~LoadingScreen(); void setLoadingProgress (const std::string& stage, int depth, int current, int total); @@ -19,6 +18,8 @@ namespace MWGui void onResChange(int w, int h); + void updateWindow(Ogre::RenderWindow* rw) { mWindow = rw; } + private: bool mFirstLoad; @@ -42,7 +43,7 @@ namespace MWGui Ogre::Rectangle2D* mRectangle; Ogre::MaterialPtr mBackgroundMaterial; - Ogre::StringVectorPtr mResources; + Ogre::StringVector mResources; bool mLoadingOn; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 14309abc5..88227c751 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -3,8 +3,11 @@ #include #include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/soundmanager.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/journal.hpp" +#include "../mwbase/dialoguemanager.hpp" namespace MWGui { @@ -29,7 +32,7 @@ namespace MWGui std::vector buttons; buttons.push_back("return"); - //buttons.push_back("newgame"); + buttons.push_back("newgame"); //buttons.push_back("loadgame"); //buttons.push_back("savegame"); buttons.push_back("options"); @@ -65,12 +68,20 @@ namespace MWGui void MainMenu::onButtonClicked(MyGUI::Widget *sender) { + MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); if (sender == mButtons["return"]) MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); else if (sender == mButtons["options"]) MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings); else if (sender == mButtons["exitgame"]) Ogre::Root::getSingleton ().queueEndRendering (); + 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(); + } } } diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp deleted file mode 100644 index 4e2ee517e..000000000 --- a/apps/openmw/mwgui/map_window.cpp +++ /dev/null @@ -1,427 +0,0 @@ -#include "map_window.hpp" - -#include - -#include -#include -#include - -#include "../mwbase/windowmanager.hpp" -#include "../mwbase/world.hpp" -#include "../mwbase/environment.hpp" -#include "../mwworld/player.hpp" - -#include "../mwrender/globalmap.hpp" - -using namespace MWGui; - -LocalMapBase::LocalMapBase() - : mCurX(0) - , mCurY(0) - , mInterior(false) - , mFogOfWar(true) - , mLocalMap(NULL) - , mMapDragAndDrop(false) - , mPrefix() - , mChanged(true) - , mLayout(NULL) - , mLastPositionX(0.0f) - , mLastPositionY(0.0f) - , mLastDirectionX(0.0f) - , mLastDirectionY(0.0f) - , mCompass(NULL) -{ -} - -void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, OEngine::GUI::Layout* layout, bool mapDragAndDrop) -{ - mLocalMap = widget; - mLayout = layout; - mMapDragAndDrop = mapDragAndDrop; - mCompass = compass; - - // create 3x3 map widgets, 512x512 each, holding a 1024x1024 texture each - const int widgetSize = 512; - for (int mx=0; mx<3; ++mx) - { - for (int my=0; my<3; ++my) - { - MyGUI::ImageBox* map = mLocalMap->createWidget("ImageBox", - MyGUI::IntCoord(mx*widgetSize, my*widgetSize, widgetSize, widgetSize), - MyGUI::Align::Top | MyGUI::Align::Left, "Map_" + boost::lexical_cast(mx) + "_" + boost::lexical_cast(my)); - - MyGUI::ImageBox* fog = map->createWidget("ImageBox", - MyGUI::IntCoord(0, 0, widgetSize, widgetSize), - MyGUI::Align::Top | MyGUI::Align::Left, "Map_" + boost::lexical_cast(mx) + "_" + boost::lexical_cast(my) + "_fog"); - - if (!mMapDragAndDrop) - { - map->setNeedMouseFocus(false); - fog->setNeedMouseFocus(false); - } - - mMapWidgets.push_back(map); - mFogWidgets.push_back(fog); - } - } -} - -void LocalMapBase::setCellPrefix(const std::string& prefix) -{ - mPrefix = prefix; - mChanged = true; -} - -void LocalMapBase::toggleFogOfWar() -{ - mFogOfWar = !mFogOfWar; - applyFogOfWar(); -} - -void LocalMapBase::applyFogOfWar() -{ - for (int mx=0; mx<3; ++mx) - { - for (int my=0; my<3; ++my) - { - std::string name = "Map_" + boost::lexical_cast(mx) + "_" - + boost::lexical_cast(my); - - std::string image = mPrefix+"_"+ boost::lexical_cast(mCurX + (mx-1)) + "_" - + boost::lexical_cast(mCurY + (mInterior ? (my-1) : -1*(my-1))); - MyGUI::ImageBox* fog = mFogWidgets[my + 3*mx]; - fog->setImageTexture(mFogOfWar ? - ((MyGUI::RenderManager::getInstance().getTexture(image+"_fog") != 0) ? image+"_fog" - : "black.png" ) - : ""); - } - } -} - -void LocalMapBase::onMarkerFocused (MyGUI::Widget* w1, MyGUI::Widget* w2) -{ - applyFogOfWar (); -} - -void LocalMapBase::onMarkerUnfocused (MyGUI::Widget* w1, MyGUI::Widget* w2) -{ - applyFogOfWar (); -} - -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 - - // 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)); - } - } - - for (int mx=0; mx<3; ++mx) - { - for (int my=0; my<3; ++my) - { - // map - std::string image = mPrefix+"_"+ boost::lexical_cast(x + (mx-1)) + "_" - + boost::lexical_cast(y + (interior ? (my-1) : -1*(my-1))); - - std::string name = "Map_" + boost::lexical_cast(mx) + "_" - + boost::lexical_cast(my); - - MyGUI::ImageBox* box = mMapWidgets[my + 3*mx]; - - if (MyGUI::RenderManager::getInstance().getTexture(image) != 0) - 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", "TextToolTip"); - markerWidget->setUserString("Caption_Text", 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 - applyFogOfWar(); - - // set the compass texture again, because MyGUI determines sorting of ImageBox widgets - // based on the last setImageTexture call - std::string tex = "textures\\compass.dds"; - mCompass->setImageTexture(""); - mCompass->setImageTexture(tex); -} - - -void LocalMapBase::setPlayerPos(const float x, const float y) -{ - if (x == mLastPositionX && y == mLastPositionY) - return; - - notifyPlayerUpdate (); - - MyGUI::IntSize size = mLocalMap->getCanvasSize(); - MyGUI::IntPoint middle = MyGUI::IntPoint((1/3.f + x/3.f)*size.width,(1/3.f + y/3.f)*size.height); - MyGUI::IntCoord viewsize = mLocalMap->getCoord(); - MyGUI::IntPoint pos(0.5*viewsize.width - middle.left, 0.5*viewsize.height - middle.top); - mLocalMap->setViewOffset(pos); - - mCompass->setPosition(MyGUI::IntPoint(512+x*512-16, 512+y*512-16)); - mLastPositionX = x; - mLastPositionY = y; -} - -void LocalMapBase::setPlayerDir(const float x, const float y) -{ - if (x == mLastDirectionX && y == mLastDirectionY) - return; - - notifyPlayerUpdate (); - - MyGUI::ISubWidget* main = mCompass->getSubWidgetMain(); - MyGUI::RotatingSkin* rotatingSubskin = main->castType(); - rotatingSubskin->setCenter(MyGUI::IntPoint(16,16)); - float angle = std::atan2(x,y); - rotatingSubskin->setAngle(angle); - - mLastDirectionX = x; - mLastDirectionY = y; -} - -// ------------------------------------------------------------------------------------------ - -MapWindow::MapWindow(MWBase::WindowManager& parWindowManager, const std::string& cacheDir) - : MWGui::WindowPinnableBase("openmw_map_window.layout", parWindowManager) - , mGlobal(false) -{ - setCoord(500,0,320,300); - - mGlobalMapRender = new MWRender::GlobalMap(cacheDir); - mGlobalMapRender->render(); - - getWidget(mLocalMap, "LocalMap"); - getWidget(mGlobalMap, "GlobalMap"); - getWidget(mGlobalMapImage, "GlobalMapImage"); - getWidget(mGlobalMapOverlay, "GlobalMapOverlay"); - getWidget(mPlayerArrowLocal, "CompassLocal"); - getWidget(mPlayerArrowGlobal, "CompassGlobal"); - - mGlobalMapImage->setImageTexture("GlobalMap.png"); - mGlobalMapOverlay->setImageTexture("GlobalMapOverlay"); - - mGlobalMap->setVisible (false); - - getWidget(mButton, "WorldButton"); - mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked); - mButton->setCaptionWithReplacing("#{sWorld}"); - - getWidget(mEventBoxGlobal, "EventBoxGlobal"); - mEventBoxGlobal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); - mEventBoxGlobal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); - getWidget(mEventBoxLocal, "EventBoxLocal"); - mEventBoxLocal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); - mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); - - LocalMapBase::init(mLocalMap, mPlayerArrowLocal, this); -} - -MapWindow::~MapWindow() -{ - delete mGlobalMapRender; -} - -void MapWindow::setCellName(const std::string& cellName) -{ - setTitle("#{sCell=" + cellName + "}"); -} - -void MapWindow::addVisitedLocation(const std::string& name, int x, int y) -{ - float worldX, worldY; - mGlobalMapRender->cellTopLeftCornerToImageSpace (x, y, worldX, worldY); - - MyGUI::IntCoord widgetCoord( - worldX * mGlobalMapRender->getWidth()+6, - worldY * mGlobalMapRender->getHeight()+6, - 12, 12); - - - static int _counter=0; - MyGUI::Button* markerWidget = mGlobalMapImage->createWidget("ButtonImage", - widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(_counter)); - markerWidget->setImageResource("DoorMarker"); - markerWidget->setUserString("ToolTipType", "Layout"); - markerWidget->setUserString("ToolTipLayout", "TextToolTip"); - markerWidget->setUserString("Caption_Text", name); - ++_counter; - - markerWidget = mEventBoxGlobal->createWidget("", - widgetCoord, MyGUI::Align::Default); - markerWidget->setNeedMouseFocus (true); - markerWidget->setUserString("ToolTipType", "Layout"); - markerWidget->setUserString("ToolTipLayout", "TextToolTip"); - markerWidget->setUserString("Caption_Text", name); -} - -void MapWindow::cellExplored(int x, int y) -{ - mGlobalMapRender->exploreCell(x,y); -} - -void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) -{ - if (_id!=MyGUI::MouseButton::Left) return; - mLastDragPos = MyGUI::IntPoint(_left, _top); -} - -void MapWindow::onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) -{ - if (_id!=MyGUI::MouseButton::Left) return; - - MyGUI::IntPoint diff = MyGUI::IntPoint(_left, _top) - mLastDragPos; - - if (!mGlobal) - mLocalMap->setViewOffset( mLocalMap->getViewOffset() + diff ); - else - mGlobalMap->setViewOffset( mGlobalMap->getViewOffset() + diff ); - - - mLastDragPos = MyGUI::IntPoint(_left, _top); -} - -void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender) -{ - mGlobal = !mGlobal; - mGlobalMap->setVisible(mGlobal); - mLocalMap->setVisible(!mGlobal); - - mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" : - "#{sWorld}"); - - if (mGlobal) - globalMapUpdatePlayer (); -} - -void MapWindow::onPinToggled() -{ - mWindowManager.setMinimapVisibility(!mPinned); -} - -void MapWindow::open() -{ - mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); - mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); - - for (unsigned int i=0; igetChildCount (); ++i) - { - if (mGlobalMapImage->getChildAt (i)->getName().substr(0,6) == "Marker") - mGlobalMapImage->getChildAt (i)->castType()->setImageResource("DoorMarker"); - } - - globalMapUpdatePlayer(); - - mPlayerArrowGlobal->setImageTexture ("textures\\compass.dds"); -} - -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().z); - - float worldX, worldY; - mGlobalMapRender->worldPosToImageSpace (pos.x, pos.z, 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? - if (MWBase::Environment::get().getWorld ()->isCellExterior ()) - { - mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(worldX - 16, worldY - 16)); - - MyGUI::ISubWidget* main = mPlayerArrowGlobal->getSubWidgetMain(); - MyGUI::RotatingSkin* rotatingSubskin = main->castType(); - rotatingSubskin->setCenter(MyGUI::IntPoint(16,16)); - float angle = std::atan2(dir.x, dir.y); - rotatingSubskin->setAngle(angle); - - // set the view offset so that player is in the center - MyGUI::IntSize viewsize = mGlobalMap->getSize(); - MyGUI::IntPoint viewoffs(0.5*viewsize.width - worldX, 0.5*viewsize.height - worldY); - mGlobalMap->setViewOffset(viewoffs); - } -} - -void MapWindow::notifyPlayerUpdate () -{ - globalMapUpdatePlayer (); -} diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp new file mode 100644 index 000000000..4a78238ce --- /dev/null +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -0,0 +1,445 @@ +#include "mapwindow.hpp" + +#include + +#include + +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwworld/player.hpp" + +#include "../mwrender/globalmap.hpp" + +#include "widgets.hpp" + +namespace MWGui +{ + + LocalMapBase::LocalMapBase() + : mCurX(0) + , mCurY(0) + , mInterior(false) + , mFogOfWar(true) + , mLocalMap(NULL) + , mMapDragAndDrop(false) + , mPrefix() + , mChanged(true) + , mLayout(NULL) + , mLastPositionX(0.0f) + , mLastPositionY(0.0f) + , mLastDirectionX(0.0f) + , mLastDirectionY(0.0f) + , mCompass(NULL) + { + } + + void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, OEngine::GUI::Layout* layout, bool mapDragAndDrop) + { + mLocalMap = widget; + mLayout = layout; + mMapDragAndDrop = mapDragAndDrop; + mCompass = compass; + + // create 3x3 map widgets, 512x512 each, holding a 1024x1024 texture each + const int widgetSize = 512; + for (int mx=0; mx<3; ++mx) + { + for (int my=0; my<3; ++my) + { + MyGUI::ImageBox* map = mLocalMap->createWidget("ImageBox", + MyGUI::IntCoord(mx*widgetSize, my*widgetSize, widgetSize, widgetSize), + MyGUI::Align::Top | MyGUI::Align::Left, "Map_" + boost::lexical_cast(mx) + "_" + boost::lexical_cast(my)); + + MyGUI::ImageBox* fog = map->createWidget("ImageBox", + MyGUI::IntCoord(0, 0, widgetSize, widgetSize), + MyGUI::Align::Top | MyGUI::Align::Left, "Map_" + boost::lexical_cast(mx) + "_" + boost::lexical_cast(my) + "_fog"); + + if (!mMapDragAndDrop) + { + map->setNeedMouseFocus(false); + fog->setNeedMouseFocus(false); + } + + mMapWidgets.push_back(map); + mFogWidgets.push_back(fog); + } + } + } + + void LocalMapBase::setCellPrefix(const std::string& prefix) + { + mPrefix = prefix; + mChanged = true; + } + + void LocalMapBase::toggleFogOfWar() + { + mFogOfWar = !mFogOfWar; + applyFogOfWar(); + } + + void LocalMapBase::applyFogOfWar() + { + for (int mx=0; mx<3; ++mx) + { + for (int my=0; my<3; ++my) + { + std::string name = "Map_" + boost::lexical_cast(mx) + "_" + + boost::lexical_cast(my); + + std::string image = mPrefix+"_"+ boost::lexical_cast(mCurX + (mx-1)) + "_" + + boost::lexical_cast(mCurY + (-1*(my-1))); + MyGUI::ImageBox* fog = mFogWidgets[my + 3*mx]; + fog->setImageTexture(mFogOfWar ? + ((MyGUI::RenderManager::getInstance().getTexture(image+"_fog") != 0) ? image+"_fog" + : "black.png" ) + : ""); + } + } + notifyMapChanged (); + } + + void LocalMapBase::onMarkerFocused (MyGUI::Widget* w1, MyGUI::Widget* w2) + { + applyFogOfWar (); + } + + void LocalMapBase::onMarkerUnfocused (MyGUI::Widget* w1, MyGUI::Widget* w2) + { + applyFogOfWar (); + } + + 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 + + // 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)); + } + } + + for (int mx=0; mx<3; ++mx) + { + for (int my=0; my<3; ++my) + { + // map + std::string image = mPrefix+"_"+ boost::lexical_cast(x + (mx-1)) + "_" + + boost::lexical_cast(y + (-1*(my-1))); + + std::string name = "Map_" + boost::lexical_cast(mx) + "_" + + boost::lexical_cast(my); + + MyGUI::ImageBox* box = mMapWidgets[my + 3*mx]; + + if (MyGUI::RenderManager::getInstance().getTexture(image) != 0) + 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 + applyFogOfWar(); + + // set the compass texture again, because MyGUI determines sorting of ImageBox widgets + // based on the last setImageTexture call + std::string tex = "textures\\compass.dds"; + mCompass->setImageTexture(""); + mCompass->setImageTexture(tex); + } + + + void LocalMapBase::setPlayerPos(const float x, const float y) + { + if (x == mLastPositionX && y == mLastPositionY) + return; + + notifyPlayerUpdate (); + + MyGUI::IntSize size = mLocalMap->getCanvasSize(); + MyGUI::IntPoint middle = MyGUI::IntPoint((1/3.f + x/3.f)*size.width,(1/3.f + y/3.f)*size.height); + MyGUI::IntCoord viewsize = mLocalMap->getCoord(); + MyGUI::IntPoint pos(0.5*viewsize.width - middle.left, 0.5*viewsize.height - middle.top); + mLocalMap->setViewOffset(pos); + + mCompass->setPosition(MyGUI::IntPoint(512+x*512-16, 512+y*512-16)); + mLastPositionX = x; + mLastPositionY = y; + } + + void LocalMapBase::setPlayerDir(const float x, const float y) + { + if (x == mLastDirectionX && y == mLastDirectionY) + return; + + notifyPlayerUpdate (); + + MyGUI::ISubWidget* main = mCompass->getSubWidgetMain(); + MyGUI::RotatingSkin* rotatingSubskin = main->castType(); + rotatingSubskin->setCenter(MyGUI::IntPoint(16,16)); + float angle = std::atan2(x,y); + rotatingSubskin->setAngle(angle); + + mLastDirectionX = x; + mLastDirectionY = y; + } + + // ------------------------------------------------------------------------------------------ + + MapWindow::MapWindow(const std::string& cacheDir) + : MWGui::WindowPinnableBase("openmw_map_window.layout") + , mGlobal(false) + { + setCoord(500,0,320,300); + + mGlobalMapRender = new MWRender::GlobalMap(cacheDir); + mGlobalMapRender->render(); + + getWidget(mLocalMap, "LocalMap"); + getWidget(mGlobalMap, "GlobalMap"); + getWidget(mGlobalMapImage, "GlobalMapImage"); + getWidget(mGlobalMapOverlay, "GlobalMapOverlay"); + getWidget(mPlayerArrowLocal, "CompassLocal"); + getWidget(mPlayerArrowGlobal, "CompassGlobal"); + + mGlobalMapImage->setImageTexture("GlobalMap.png"); + mGlobalMapOverlay->setImageTexture("GlobalMapOverlay"); + + mGlobalMap->setVisible (false); + + getWidget(mButton, "WorldButton"); + mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked); + mButton->setCaptionWithReplacing("#{sWorld}"); + + getWidget(mEventBoxGlobal, "EventBoxGlobal"); + mEventBoxGlobal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); + mEventBoxGlobal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); + getWidget(mEventBoxLocal, "EventBoxLocal"); + mEventBoxLocal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); + mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); + + LocalMapBase::init(mLocalMap, mPlayerArrowLocal, this); + } + + MapWindow::~MapWindow() + { + delete mGlobalMapRender; + } + + void MapWindow::setCellName(const std::string& cellName) + { + setTitle("#{sCell=" + cellName + "}"); + } + + void MapWindow::addVisitedLocation(const std::string& name, int x, int y) + { + float worldX, worldY; + mGlobalMapRender->cellTopLeftCornerToImageSpace (x, y, worldX, worldY); + + MyGUI::IntCoord widgetCoord( + worldX * mGlobalMapRender->getWidth()+6, + worldY * mGlobalMapRender->getHeight()+6, + 12, 12); + + + static int _counter=0; + MyGUI::Button* markerWidget = mGlobalMapImage->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", name); + ++_counter; + + markerWidget = mEventBoxGlobal->createWidget("", + widgetCoord, MyGUI::Align::Default); + markerWidget->setNeedMouseFocus (true); + markerWidget->setUserString("ToolTipType", "Layout"); + markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); + markerWidget->setUserString("Caption_TextOneLine", name); + } + + void MapWindow::cellExplored(int x, int y) + { + mGlobalMapRender->exploreCell(x,y); + } + + void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) + { + if (_id!=MyGUI::MouseButton::Left) return; + mLastDragPos = MyGUI::IntPoint(_left, _top); + } + + void MapWindow::onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) + { + if (_id!=MyGUI::MouseButton::Left) return; + + MyGUI::IntPoint diff = MyGUI::IntPoint(_left, _top) - mLastDragPos; + + if (!mGlobal) + mLocalMap->setViewOffset( mLocalMap->getViewOffset() + diff ); + else + mGlobalMap->setViewOffset( mGlobalMap->getViewOffset() + diff ); + + + mLastDragPos = MyGUI::IntPoint(_left, _top); + } + + void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender) + { + mGlobal = !mGlobal; + mGlobalMap->setVisible(mGlobal); + mLocalMap->setVisible(!mGlobal); + + mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" : + "#{sWorld}"); + + if (mGlobal) + globalMapUpdatePlayer (); + } + + void MapWindow::onPinToggled() + { + MWBase::Environment::get().getWindowManager()->setMinimapVisibility(!mPinned); + } + + void MapWindow::open() + { + mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); + mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); + + for (unsigned int i=0; igetChildCount (); ++i) + { + if (mGlobalMapImage->getChildAt (i)->getName().substr(0,6) == "Marker") + mGlobalMapImage->getChildAt (i)->castType()->setImageResource("DoorMarker"); + } + + globalMapUpdatePlayer(); + + mPlayerArrowGlobal->setImageTexture ("textures\\compass.dds"); + } + + 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? + if (MWBase::Environment::get().getWorld ()->isCellExterior ()) + { + mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(worldX - 16, worldY - 16)); + + MyGUI::ISubWidget* main = mPlayerArrowGlobal->getSubWidgetMain(); + MyGUI::RotatingSkin* rotatingSubskin = main->castType(); + rotatingSubskin->setCenter(MyGUI::IntPoint(16,16)); + float angle = std::atan2(dir.x, dir.y); + rotatingSubskin->setAngle(angle); + + // set the view offset so that player is in the center + MyGUI::IntSize viewsize = mGlobalMap->getSize(); + MyGUI::IntPoint viewoffs(0.5*viewsize.width - worldX, 0.5*viewsize.height - worldY); + mGlobalMap->setViewOffset(viewoffs); + } + } + + void MapWindow::notifyPlayerUpdate () + { + globalMapUpdatePlayer (); + } + + void MapWindow::notifyMapChanged () + { + // workaround to prevent the map from drawing on top of the button + MyGUI::IntCoord oldCoord = mButton->getCoord (); + MyGUI::Gui::getInstance().destroyWidget (mButton); + mButton = mMainWidget->createWidget("MW_Button", + oldCoord, MyGUI::Align::Bottom | MyGUI::Align::Right); + mButton->setProperty ("ExpandDirection", "Left"); + + mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked); + mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" : + "#{sWorld}"); + } + +} diff --git a/apps/openmw/mwgui/map_window.hpp b/apps/openmw/mwgui/mapwindow.hpp similarity index 94% rename from apps/openmw/mwgui/map_window.hpp rename to apps/openmw/mwgui/mapwindow.hpp index 4e2dd6756..18c81a060 100644 --- a/apps/openmw/mwgui/map_window.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_MAPWINDOW_H #define MWGUI_MAPWINDOW_H -#include "window_pinnable_base.hpp" +#include "windowpinnablebase.hpp" namespace MWRender { @@ -50,6 +50,7 @@ namespace MWGui void onMarkerUnfocused(MyGUI::Widget* w1, MyGUI::Widget* w2); virtual void notifyPlayerUpdate() {} + virtual void notifyMapChanged() {} OEngine::GUI::Layout* mLayout; @@ -64,7 +65,7 @@ namespace MWGui class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase { public: - MapWindow(MWBase::WindowManager& parWindowManager, const std::string& cacheDir); + MapWindow(const std::string& cacheDir); virtual ~MapWindow(); void setCellName(const std::string& cellName); @@ -99,6 +100,8 @@ namespace MWGui virtual void onPinToggled(); virtual void notifyPlayerUpdate(); + virtual void notifyMapChanged(); + }; } #endif diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp new file mode 100644 index 000000000..530594dda --- /dev/null +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -0,0 +1,132 @@ +#include "merchantrepair.hpp" + +#include + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/soundmanager.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/containerstore.hpp" + +#include "inventorywindow.hpp" +#include "tradewindow.hpp" + +namespace MWGui +{ + +MerchantRepair::MerchantRepair() + : WindowBase("openmw_merchantrepair.layout") +{ + getWidget(mList, "RepairView"); + getWidget(mOkButton, "OkButton"); + getWidget(mGoldLabel, "PlayerGold"); + + mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MerchantRepair::onOkButtonClick); +} + +void MerchantRepair::startRepair(const MWWorld::Ptr &actor) +{ + mActor = actor; + + while (mList->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mList->getChildAt(0)); + + int currentY = 0; + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + 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)); + iter!=store.end(); ++iter) + { + if (MWWorld::Class::get(*iter).hasItemHealth(*iter)) + { + int maxDurability = MWWorld::Class::get(*iter).getItemMaxHealth(*iter); + int durability = (iter->getCellRef().mCharge == -1) ? maxDurability : iter->getCellRef().mCharge; + if (maxDurability == durability) + continue; + + int basePrice = MWWorld::Class::get(*iter).getValue(*iter); + float fRepairMult = MWBase::Environment::get().getWorld()->getStore().get() + .find("fRepairMult")->getFloat(); + + float p = std::max(1, basePrice); + float r = std::max(1, static_cast(maxDurability / p)); + + int x = ((maxDurability - durability) / r); + x = (fRepairMult * x); + + int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mActor, x, true); + + + std::string name = MWWorld::Class::get(*iter).getName(*iter) + + " - " + boost::lexical_cast(price) + + MWBase::Environment::get().getWorld()->getStore().get() + .find("sgp")->getString();; + + + MyGUI::Button* button = + mList->createWidget( + (price>MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", + 0, + currentY, + 0, + 18, + MyGUI::Align::Default + ); + + currentY += 18; + + button->setEnabled(price<=MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()); + button->setUserString("Price", boost::lexical_cast(price)); + button->setUserData(*iter); + button->setCaptionWithReplacing(name); + button->setSize(button->getTextSize().width,18); + button->eventMouseWheel += MyGUI::newDelegate(this, &MerchantRepair::onMouseWheel); + button->setUserString("ToolTipType", "ItemPtr"); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MerchantRepair::onRepairButtonClick); + } + } + mList->setCanvasSize (MyGUI::IntSize(mList->getWidth(), std::max(mList->getHeight(), currentY))); + + mGoldLabel->setCaptionWithReplacing("#{sGold}: " + + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); +} + +void MerchantRepair::onMouseWheel(MyGUI::Widget* _sender, int _rel) +{ + if (mList->getViewOffset().top + _rel*0.3 > 0) + mList->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mList->setViewOffset(MyGUI::IntPoint(0, mList->getViewOffset().top + _rel*0.3)); +} + +void MerchantRepair::open() +{ + center(); +} + +void MerchantRepair::onRepairButtonClick(MyGUI::Widget *sender) +{ + // repair + MWWorld::Ptr item = *sender->getUserData(); + item.getCellRef().mCharge = MWWorld::Class::get(item).getItemMaxHealth(item); + + MWBase::Environment::get().getSoundManager()->playSound("Repair",1,1); + + int price = boost::lexical_cast(sender->getUserString("Price")); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-price); + + startRepair(mActor); +} + +void MerchantRepair::onOkButtonClick(MyGUI::Widget *sender) +{ + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_MerchantRepair); +} + +} diff --git a/apps/openmw/mwgui/merchantrepair.hpp b/apps/openmw/mwgui/merchantrepair.hpp new file mode 100644 index 000000000..4cb39fe01 --- /dev/null +++ b/apps/openmw/mwgui/merchantrepair.hpp @@ -0,0 +1,37 @@ +#ifndef OPENMW_MWGUI_MERCHANTREPAIR_H +#define OPENMW_MWGUI_MERCHANTREPAIR_H + +#include "windowbase.hpp" +#include "../mwworld/ptr.hpp" + + + +namespace MWGui +{ + +class MerchantRepair : public WindowBase +{ +public: + MerchantRepair(); + + virtual void open(); + + void startRepair(const MWWorld::Ptr& actor); + +private: + MyGUI::ScrollView* mList; + MyGUI::Button* mOkButton; + MyGUI::TextBox* mGoldLabel; + + MWWorld::Ptr mActor; + +protected: + void onMouseWheel(MyGUI::Widget* _sender, int _rel); + void onRepairButtonClick(MyGUI::Widget* sender); + void onOkButtonClick(MyGUI::Widget* sender); + +}; + +} + +#endif diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index b660af7dd..2fc50f257 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -1,383 +1,428 @@ -#include "messagebox.hpp" +#include -using namespace MWGui; +#include "messagebox.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/soundmanager.hpp" +#include "../mwbase/inputmanager.hpp" -MessageBoxManager::MessageBoxManager (MWBase::WindowManager *windowManager) +namespace MWGui { - mWindowManager = windowManager; - // defines - mMessageBoxSpeed = 0.1; - mInterMessageBoxe = NULL; -} -void MessageBoxManager::onFrame (float frameDuration) -{ - std::vector::iterator it; - for(it = mTimers.begin(); it != mTimers.end();) + MessageBoxManager::MessageBoxManager () { - // if this messagebox is already deleted, remove the timer and move on - if (std::find(mMessageBoxes.begin(), mMessageBoxes.end(), it->messageBox) == mMessageBoxes.end()) - { - it = mTimers.erase(it); - continue; - } + // defines + mMessageBoxSpeed = 0.1; + mInterMessageBoxe = NULL; + mStaticMessageBox = NULL; + } - it->current += frameDuration; - if(it->current >= it->max) + void MessageBoxManager::onFrame (float frameDuration) + { + std::vector::iterator it; + for(it = mTimers.begin(); it != mTimers.end();) { - it->messageBox->mMarkedToDelete = true; + // if this messagebox is already deleted, remove the timer and move on + if (std::find(mMessageBoxes.begin(), mMessageBoxes.end(), it->messageBox) == mMessageBoxes.end()) + { + it = mTimers.erase(it); + continue; + } - if(*mMessageBoxes.begin() == it->messageBox) // if this box is the last one + it->current += frameDuration; + if(it->current >= it->max) { - // collect all with mMarkedToDelete and delete them. - // and place the other messageboxes on the right position - int height = 0; - std::vector::iterator it2 = mMessageBoxes.begin(); - while(it2 != mMessageBoxes.end()) + it->messageBox->mMarkedToDelete = true; + + if(*mMessageBoxes.begin() == it->messageBox) // if this box is the last one { - if((*it2)->mMarkedToDelete) + // collect all with mMarkedToDelete and delete them. + // and place the other messageboxes on the right position + int height = 0; + std::vector::iterator it2 = mMessageBoxes.begin(); + while(it2 != mMessageBoxes.end()) { - delete (*it2); - it2 = mMessageBoxes.erase(it2); - } - else { - (*it2)->update(height); - height += (*it2)->getHeight(); - it2++; + if((*it2)->mMarkedToDelete) + { + delete (*it2); + it2 = mMessageBoxes.erase(it2); + } + else { + (*it2)->update(height); + height += (*it2)->getHeight(); + it2++; + } } } + it = mTimers.erase(it); + } + else + { + it++; } - it = mTimers.erase(it); } - else - { - it++; + + if(mInterMessageBoxe != NULL && mInterMessageBoxe->mMarkedToDelete) { + delete mInterMessageBoxe; + mInterMessageBoxe = NULL; + MWBase::Environment::get().getInputManager()->changeInputMode( + MWBase::Environment::get().getWindowManager()->isGuiMode()); } } - if(mInterMessageBoxe != NULL && mInterMessageBoxe->mMarkedToDelete) { - delete mInterMessageBoxe; - mInterMessageBoxe = NULL; - mWindowManager->removeGuiMode(GM_InterMessageBox); - } -} + void MessageBoxManager::createMessageBox (const std::string& message, bool stat) + { + MessageBox *box = new MessageBox(*this, message); -void MessageBoxManager::createMessageBox (const std::string& message) -{ - MessageBox *box = new MessageBox(*this, message); + if(stat) + mStaticMessageBox = box; + else + removeMessageBox(message.length()*mMessageBoxSpeed, box); - removeMessageBox(message.length()*mMessageBoxSpeed, box); + mMessageBoxes.push_back(box); + std::vector::iterator it; - mMessageBoxes.push_back(box); - std::vector::iterator it; + if(mMessageBoxes.size() > 3) { + delete *mMessageBoxes.begin(); + mMessageBoxes.erase(mMessageBoxes.begin()); + } - if(mMessageBoxes.size() > 3) { - delete *mMessageBoxes.begin(); - mMessageBoxes.erase(mMessageBoxes.begin()); + int height = 0; + for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it) + { + (*it)->update(height); + height += (*it)->getHeight(); + } } - int height = 0; - for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it) + void MessageBoxManager::removeStaticMessageBox () { - (*it)->update(height); - height += (*it)->getHeight(); + removeMessageBox(mStaticMessageBox); + mStaticMessageBox = NULL; } -} -bool MessageBoxManager::createInteractiveMessageBox (const std::string& message, const std::vector& buttons) -{ - /// \todo Don't write this kind of error message to cout. Either discard the old message box - /// silently or throw an exception. - if(mInterMessageBoxe != NULL) { - std::cout << "there is a MessageBox already" << std::endl; - return false; - } + bool MessageBoxManager::createInteractiveMessageBox (const std::string& message, const std::vector& buttons) + { + if(mInterMessageBoxe != NULL) { + throw std::runtime_error("There is a message box already"); + } - mInterMessageBoxe = new InteractiveMessageBox(*this, message, buttons); + mInterMessageBoxe = new InteractiveMessageBox(*this, message, buttons); - return true; -} + return true; + } -bool MessageBoxManager::isInteractiveMessageBox () -{ - return mInterMessageBoxe != NULL; -} + bool MessageBoxManager::isInteractiveMessageBox () + { + return mInterMessageBoxe != NULL; + } -void MessageBoxManager::removeMessageBox (float time, MessageBox *msgbox) -{ - MessageBoxManagerTimer timer; - timer.current = 0; - timer.max = time; - timer.messageBox = msgbox; + void MessageBoxManager::removeMessageBox (float time, MessageBox *msgbox) + { + MessageBoxManagerTimer timer; + timer.current = 0; + timer.max = time; + timer.messageBox = msgbox; - mTimers.insert(mTimers.end(), timer); -} + mTimers.insert(mTimers.end(), timer); + } -bool MessageBoxManager::removeMessageBox (MessageBox *msgbox) -{ - std::vector::iterator it; - for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it) + bool MessageBoxManager::removeMessageBox (MessageBox *msgbox) { - if((*it) == msgbox) + std::vector::iterator it; + for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it) { - delete (*it); - mMessageBoxes.erase(it); - return true; + if((*it) == msgbox) + { + delete (*it); + mMessageBoxes.erase(it); + return true; + } } + return false; } - return false; -} -void MessageBoxManager::setMessageBoxSpeed (int speed) -{ - mMessageBoxSpeed = speed; -} + void MessageBoxManager::setMessageBoxSpeed (int speed) + { + mMessageBoxSpeed = speed; + } + void MessageBoxManager::enterPressed () + { + if(mInterMessageBoxe != NULL) + mInterMessageBoxe->enterPressed(); + } -int MessageBoxManager::readPressedButton () -{ - if(mInterMessageBoxe != NULL) + int MessageBoxManager::readPressedButton () { - return mInterMessageBoxe->readPressedButton(); + if(mInterMessageBoxe != NULL) + { + return mInterMessageBoxe->readPressedButton(); + } + return -1; } - return -1; -} -MessageBox::MessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message) - : Layout("openmw_messagebox.layout") - , mMessageBoxManager(parMessageBoxManager) - , mMessage(message) -{ - // defines - mFixedWidth = 300; - mBottomPadding = 20; - mNextBoxPadding = 20; - mMarkedToDelete = false; + MessageBox::MessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message) + : Layout("openmw_messagebox.layout") + , mMessageBoxManager(parMessageBoxManager) + , mMessage(message) + { + // defines + mFixedWidth = 300; + mBottomPadding = 20; + mNextBoxPadding = 20; + mMarkedToDelete = false; - getWidget(mMessageWidget, "message"); + getWidget(mMessageWidget, "message"); - mMessageWidget->setOverflowToTheLeft(true); - mMessageWidget->setCaptionWithReplacing(mMessage); + mMessageWidget->setOverflowToTheLeft(true); + mMessageWidget->setCaptionWithReplacing(mMessage); - MyGUI::IntSize size; - size.width = mFixedWidth; - size.height = 100; // dummy + MyGUI::IntSize size; + size.width = mFixedWidth; + size.height = 100; // dummy - MyGUI::IntCoord coord; - coord.left = 10; // dummy - coord.top = 10; // dummy + MyGUI::IntCoord coord; + coord.left = 10; // dummy + coord.top = 10; // dummy - mMessageWidget->setSize(size); + mMessageWidget->setSize(size); - MyGUI::IntSize textSize = mMessageWidget->getTextSize(); + MyGUI::IntSize textSize = mMessageWidget->getTextSize(); - size.height = mHeight = textSize.height + 20; // this is the padding between the text and the box + 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); -} + 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::IntSize size; - size.width = mFixedWidth; - size.height = mHeight; - - mMainWidget->setCoord(coord); - mMainWidget->setSize(size); - mMainWidget->setVisible(true); -} + 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::IntSize size; + size.width = mFixedWidth; + size.height = mHeight; + + mMainWidget->setCoord(coord); + mMainWidget->setSize(size); + mMainWidget->setVisible(true); + } -int MessageBox::getHeight () -{ - return mHeight+mNextBoxPadding; // 20 is the padding between this and the next MessageBox -} + int MessageBox::getHeight () + { + return mHeight+mNextBoxPadding; // 20 is the padding between this and the next MessageBox + } -InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons) - : Layout("openmw_interactive_messagebox.layout") - , mMessageBoxManager(parMessageBoxManager) - , mButtonPressed(-1) -{ - int fixedWidth = 500; - int textPadding = 10; // padding between text-widget and main-widget - int textButtonPadding = 20; // padding between the text-widget und the button-widget - int buttonLeftPadding = 10; // padding between the buttons if horizontal - int buttonTopPadding = 5; // ^-- if vertical - int buttonPadding = 5; // padding between button label and button itself - int buttonMainPadding = 10; // padding between buttons and bottom of the main widget + InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons) + : WindowModal("openmw_interactive_messagebox.layout") + , mMessageBoxManager(parMessageBoxManager) + , mButtonPressed(-1) + { + WindowModal::open(); - mMarkedToDelete = false; + int fixedWidth = 500; + int textPadding = 10; // padding between text-widget and main-widget + int textButtonPadding = 20; // padding between the text-widget und the button-widget + int buttonLeftPadding = 10; // padding between the buttons if horizontal + int buttonTopPadding = 5; // ^-- if vertical + int buttonPadding = 5; // padding between button label and button itself + int buttonMainPadding = 10; // padding between buttons and bottom of the main widget + mMarkedToDelete = false; - getWidget(mMessageWidget, "message"); - getWidget(mButtonsWidget, "buttons"); - mMessageWidget->setOverflowToTheLeft(true); - mMessageWidget->addText(message); + getWidget(mMessageWidget, "message"); + getWidget(mButtonsWidget, "buttons"); - MyGUI::IntSize textSize = mMessageWidget->getTextSize(); + mMessageWidget->setOverflowToTheLeft(true); + mMessageWidget->setCaptionWithReplacing(message); - MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize(); + MyGUI::IntSize textSize = mMessageWidget->getTextSize(); - int biggestButtonWidth = 0; - int buttonWidth = 0; - int buttonsWidth = 0; - int buttonHeight = 0; - MyGUI::IntCoord dummyCoord(0, 0, 0, 0); + MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize(); - std::vector::const_iterator it; - for(it = buttons.begin(); it != buttons.end(); ++it) - { - MyGUI::ButtonPtr button = mButtonsWidget->createWidget( - MyGUI::WidgetStyle::Child, - std::string("MW_Button"), - dummyCoord, - MyGUI::Align::Default); - button->setCaption(*it); + int biggestButtonWidth = 0; + int buttonWidth = 0; + int buttonsWidth = 0; + int buttonHeight = 0; + MyGUI::IntCoord dummyCoord(0, 0, 0, 0); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &InteractiveMessageBox::mousePressed); + std::vector::const_iterator it; + for(it = buttons.begin(); it != buttons.end(); ++it) + { + MyGUI::Button* button = mButtonsWidget->createWidget( + MyGUI::WidgetStyle::Child, + std::string("MW_Button"), + dummyCoord, + MyGUI::Align::Default); + button->setCaptionWithReplacing(*it); - mButtons.push_back(button); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &InteractiveMessageBox::mousePressed); - buttonWidth = button->getTextSize().width + 2*buttonPadding + buttonLeftPadding; - buttonsWidth += buttonWidth; - buttonHeight = button->getTextSize().height + 2*buttonPadding + buttonTopPadding; + mButtons.push_back(button); - if(buttonWidth > biggestButtonWidth) - { - biggestButtonWidth = buttonWidth; - } - } - buttonsWidth += buttonLeftPadding; + buttonWidth = button->getTextSize().width + 2*buttonPadding + buttonLeftPadding; + buttonsWidth += buttonWidth; + buttonHeight = button->getTextSize().height + 2*buttonPadding + buttonTopPadding; - MyGUI::IntSize mainWidgetSize; - if(buttonsWidth < fixedWidth) - { - // on one line - if(textSize.width + 2*textPadding < buttonsWidth) - { - mainWidgetSize.width = buttonsWidth; + if(buttonWidth > biggestButtonWidth) + { + biggestButtonWidth = buttonWidth; + } } - else + buttonsWidth += buttonLeftPadding; + + MyGUI::IntSize mainWidgetSize; + if(buttonsWidth < fixedWidth) { - mainWidgetSize.width = textSize.width + 3*textPadding; - } - mainWidgetSize.height = textSize.height + textButtonPadding + buttonHeight + buttonMainPadding; + // on one line + if(textSize.width + 2*textPadding < buttonsWidth) + { + mainWidgetSize.width = buttonsWidth; + } + else + { + mainWidgetSize.width = textSize.width + 3*textPadding; + } + 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::IntCoord absCoord; + absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2; + absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2; - mMainWidget->setCoord(absCoord); - mMainWidget->setSize(mainWidgetSize); + mMainWidget->setCoord(absCoord); + mMainWidget->setSize(mainWidgetSize); - MyGUI::IntCoord messageWidgetCoord; - messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; - messageWidgetCoord.top = textPadding; - mMessageWidget->setCoord(messageWidgetCoord); + MyGUI::IntCoord messageWidgetCoord; + messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; + messageWidgetCoord.top = textPadding; + mMessageWidget->setCoord(messageWidgetCoord); - mMessageWidget->setSize(textSize); + mMessageWidget->setSize(textSize); - MyGUI::IntCoord buttonCord; - MyGUI::IntSize buttonSize(0, buttonHeight); - int left = (mainWidgetSize.width - buttonsWidth)/2 + buttonPadding; + MyGUI::IntCoord buttonCord; + MyGUI::IntSize buttonSize(0, buttonHeight); + int left = (mainWidgetSize.width - buttonsWidth)/2 + buttonPadding; - std::vector::const_iterator button; - for(button = mButtons.begin(); button != mButtons.end(); ++button) - { - buttonCord.left = left; - buttonCord.top = textSize.height + textButtonPadding; + std::vector::const_iterator button; + for(button = mButtons.begin(); button != mButtons.end(); ++button) + { + buttonCord.left = left; + buttonCord.top = textSize.height + textButtonPadding; - buttonSize.width = (*button)->getTextSize().width + 2*buttonPadding; - buttonSize.height = (*button)->getTextSize().height + 2*buttonPadding; + buttonSize.width = (*button)->getTextSize().width + 2*buttonPadding; + buttonSize.height = (*button)->getTextSize().height + 2*buttonPadding; - (*button)->setCoord(buttonCord); - (*button)->setSize(buttonSize); + (*button)->setCoord(buttonCord); + (*button)->setSize(buttonSize); - left += buttonSize.width + buttonLeftPadding; - } - } - else - { - // among each other - if(biggestButtonWidth > textSize.width) { - mainWidgetSize.width = biggestButtonWidth + buttonTopPadding; - } - else { - mainWidgetSize.width = textSize.width + 3*textPadding; + left += buttonSize.width + buttonLeftPadding; + } } - mainWidgetSize.height = textSize.height + 2*textPadding + textButtonPadding + buttonHeight * buttons.size() + buttonMainPadding; + else + { + // among each other + if(biggestButtonWidth > textSize.width) { + mainWidgetSize.width = biggestButtonWidth + buttonTopPadding; + } + else { + mainWidgetSize.width = textSize.width + 3*textPadding; + } + mainWidgetSize.height = textSize.height + 2*textPadding + textButtonPadding + buttonHeight * buttons.size() + buttonMainPadding; - mMainWidget->setSize(mainWidgetSize); + mMainWidget->setSize(mainWidgetSize); - MyGUI::IntCoord absCoord; - absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2; - absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2; + MyGUI::IntCoord absCoord; + absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2; + absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2; - mMainWidget->setCoord(absCoord); - mMainWidget->setSize(mainWidgetSize); + mMainWidget->setCoord(absCoord); + mMainWidget->setSize(mainWidgetSize); - MyGUI::IntCoord messageWidgetCoord; - messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; - messageWidgetCoord.top = textPadding; - mMessageWidget->setCoord(messageWidgetCoord); + MyGUI::IntCoord messageWidgetCoord; + messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; + messageWidgetCoord.top = textPadding; + mMessageWidget->setCoord(messageWidgetCoord); - mMessageWidget->setSize(textSize); + mMessageWidget->setSize(textSize); - MyGUI::IntCoord buttonCord; - MyGUI::IntSize buttonSize(0, buttonHeight); + MyGUI::IntCoord buttonCord; + MyGUI::IntSize buttonSize(0, buttonHeight); - int top = textButtonPadding + buttonTopPadding + textSize.height; + int top = textButtonPadding + buttonTopPadding + textSize.height; - std::vector::const_iterator button; - for(button = mButtons.begin(); button != mButtons.end(); ++button) - { - buttonSize.width = (*button)->getTextSize().width + buttonPadding*2; - buttonSize.height = (*button)->getTextSize().height + buttonPadding*2; + std::vector::const_iterator button; + for(button = mButtons.begin(); button != mButtons.end(); ++button) + { + buttonSize.width = (*button)->getTextSize().width + buttonPadding*2; + buttonSize.height = (*button)->getTextSize().height + buttonPadding*2; + + buttonCord.top = top; + buttonCord.left = (mainWidgetSize.width - buttonSize.width)/2 - 5; // FIXME: -5 is not so nice :/ - buttonCord.top = top; - buttonCord.left = (mainWidgetSize.width - buttonSize.width)/2 - 5; // FIXME: -5 is not so nice :/ + (*button)->setCoord(buttonCord); + (*button)->setSize(buttonSize); - (*button)->setCoord(buttonCord); - (*button)->setSize(buttonSize); + top += buttonSize.height + 2*buttonTopPadding; + } - top += buttonSize.height + 2*buttonTopPadding; + } + } + + void InteractiveMessageBox::enterPressed() + { + + 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); + break; + } } } -} -void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed) -{ - mMarkedToDelete = true; - int index = 0; - std::vector::const_iterator button; - for(button = mButtons.begin(); button != mButtons.end(); ++button) + void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed) { - if(*button == pressed) + buttonActivated (pressed); + } + + void InteractiveMessageBox::buttonActivated (MyGUI::Widget* pressed) + { + mMarkedToDelete = true; + int index = 0; + std::vector::const_iterator button; + for(button = mButtons.begin(); button != mButtons.end(); ++button) { - mButtonPressed = index; - return; + if(*button == pressed) + { + mButtonPressed = index; + mMessageBoxManager.onButtonPressed(mButtonPressed); + return; + } + index++; } - index++; } -} -int InteractiveMessageBox::readPressedButton () -{ - int pressed = mButtonPressed; - mButtonPressed = -1; - return pressed; + int InteractiveMessageBox::readPressedButton () + { + int pressed = mButtonPressed; + mButtonPressed = -1; + return pressed; + } + } diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index 5e4c468d5..0e47b0323 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -1,15 +1,19 @@ #ifndef MWGUI_MESSAGE_BOX_H #define MWGUI_MESSAGE_BOX_H -#include -#include - -#include "window_base.hpp" +#include "windowbase.hpp" #include "../mwbase/windowmanager.hpp" #undef MessageBox +namespace MyGUI +{ + class Widget; + class Button; + class EditBox; +} + namespace MWGui { class InteractiveMessageBox; @@ -25,9 +29,10 @@ namespace MWGui class MessageBoxManager { public: - MessageBoxManager (MWBase::WindowManager* windowManager); + MessageBoxManager (); void onFrame (float frameDuration); - void createMessageBox (const std::string& message); + void createMessageBox (const std::string& message, bool stat = false); + void removeStaticMessageBox (); bool createInteractiveMessageBox (const std::string& message, const std::vector& buttons); bool isInteractiveMessageBox (); @@ -35,13 +40,20 @@ namespace MWGui bool removeMessageBox (MessageBox *msgbox); void setMessageBoxSpeed (int speed); + void enterPressed(); int readPressedButton (); - MWBase::WindowManager *mWindowManager; + typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Int; + + // Note: this delegate unassigns itself after it was fired, i.e. works once. + EventHandle_Int eventButtonPressed; + + void onButtonPressed(int button) { eventButtonPressed(button); eventButtonPressed.clear(); } private: std::vector mMessageBoxes; InteractiveMessageBox* mInterMessageBoxe; + MessageBox* mStaticMessageBox; std::vector mTimers; float mMessageBoxSpeed; }; @@ -60,26 +72,29 @@ namespace MWGui MessageBoxManager& mMessageBoxManager; int mHeight; const std::string& mMessage; - MyGUI::EditPtr mMessageWidget; + MyGUI::EditBox* mMessageWidget; int mFixedWidth; int mBottomPadding; int mNextBoxPadding; }; - class InteractiveMessageBox : public OEngine::GUI::Layout + class InteractiveMessageBox : public WindowModal { public: InteractiveMessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons); + void enterPressed (); void mousePressed (MyGUI::Widget* _widget); int readPressedButton (); bool mMarkedToDelete; private: + void buttonActivated (MyGUI::Widget* _widget); + MessageBoxManager& mMessageBoxManager; - MyGUI::EditPtr mMessageWidget; - MyGUI::WidgetPtr mButtonsWidget; - std::vector mButtons; + MyGUI::EditBox* mMessageWidget; + MyGUI::Widget* mButtonsWidget; + std::vector mButtons; int mTextButtonPadding; int mButtonPressed; diff --git a/apps/openmw/mwgui/mode.hpp b/apps/openmw/mwgui/mode.hpp index 319537297..879fcb483 100644 --- a/apps/openmw/mwgui/mode.hpp +++ b/apps/openmw/mwgui/mode.hpp @@ -5,9 +5,11 @@ namespace MWGui { enum GuiMode { + GM_None, GM_Settings, // Settings window GM_Inventory, // Inventory mode GM_Container, + GM_Companion, GM_MainMenu, // Main menu mode GM_Console, // Console mode @@ -16,6 +18,7 @@ namespace MWGui GM_Scroll, // Read scroll GM_Book, // Read book GM_Alchemy, // Make potions + GM_Repair, GM_Dialogue, // NPC interaction GM_Barter, @@ -26,6 +29,7 @@ namespace MWGui GM_SpellCreation, GM_Enchanting, GM_Training, + GM_MerchantRepair, GM_Levelup, @@ -39,9 +43,6 @@ namespace MWGui GM_ClassCreate, GM_Review, - // interactive MessageBox - GM_InterMessageBox, - GM_Loading, GM_LoadingWallpaper, diff --git a/apps/openmw/mwgui/pickpocketitemmodel.cpp b/apps/openmw/mwgui/pickpocketitemmodel.cpp new file mode 100644 index 000000000..16be5f6cc --- /dev/null +++ b/apps/openmw/mwgui/pickpocketitemmodel.cpp @@ -0,0 +1,55 @@ +#include "pickpocketitemmodel.hpp" + +#include "../mwmechanics/npcstats.hpp" +#include "../mwworld/class.hpp" + +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(); + + mSourceModel->update(); + for (size_t i = 0; igetItemCount(); ++i) + { + if (std::rand() / static_cast(RAND_MAX) * 100 > chance) + mHiddenItems.push_back(mSourceModel->getItem(i)); + } + } + + ItemStack PickpocketItemModel::getItem (ModelIndex index) + { + if (index < 0) + throw std::runtime_error("Invalid index supplied"); + if (mItems.size() <= static_cast(index)) + throw std::runtime_error("Item index out of range"); + return mItems[index]; + } + + size_t PickpocketItemModel::getItemCount() + { + return mItems.size(); + } + + void PickpocketItemModel::update() + { + mSourceModel->update(); + mItems.clear(); + for (size_t i = 0; igetItemCount(); ++i) + { + const ItemStack& item = mSourceModel->getItem(i); + if (std::find(mHiddenItems.begin(), mHiddenItems.end(), item) == mHiddenItems.end() + && item.mType != ItemStack::Type_Equipped) + mItems.push_back(item); + } + } + + void PickpocketItemModel::removeItem (const ItemStack &item, size_t count) + { + ProxyItemModel::removeItem(item, count); + /// \todo check if player is detected + } + +} diff --git a/apps/openmw/mwgui/pickpocketitemmodel.hpp b/apps/openmw/mwgui/pickpocketitemmodel.hpp new file mode 100644 index 000000000..090d48d0e --- /dev/null +++ b/apps/openmw/mwgui/pickpocketitemmodel.hpp @@ -0,0 +1,26 @@ +#ifndef MWGUI_PICKPOCKET_ITEM_MODEL_H +#define MWGUI_PICKPOCKET_ITEM_MODEL_H + +#include "itemmodel.hpp" + +namespace MWGui +{ + + /// @brief The pickpocket item model randomly hides item stacks based on a specified chance. Equipped items are always hidden. + class PickpocketItemModel : public ProxyItemModel + { + public: + PickpocketItemModel (const MWWorld::Ptr& thief, ItemModel* sourceModel); + virtual ItemStack getItem (ModelIndex index); + virtual size_t getItemCount(); + virtual void update(); + virtual void removeItem (const ItemStack& item, size_t count); + + private: + std::vector mHiddenItems; + std::vector mItems; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 6d51420f0..877f7b700 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -2,13 +2,9 @@ #include -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" #include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/actionequip.hpp" -#include "../mwmechanics/spells.hpp" -#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spellsuccess.hpp" #include "../mwgui/inventorywindow.hpp" #include "../mwgui/bookwindow.hpp" @@ -43,8 +39,8 @@ namespace namespace MWGui { - QuickKeysMenu::QuickKeysMenu(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_quickkeys_menu.layout", parWindowManager) + QuickKeysMenu::QuickKeysMenu() + : WindowBase("openmw_quickkeys_menu.layout") , mAssignDialog(0) , mItemSelectionDialog(0) , mMagicSelectionDialog(0) @@ -109,14 +105,14 @@ namespace MWGui { // open assign dialog if (!mAssignDialog) - mAssignDialog = new QuickKeysMenuAssign(mWindowManager, this); + mAssignDialog = new QuickKeysMenuAssign(this); mAssignDialog->setVisible (true); } } void QuickKeysMenu::onOkButtonClicked (MyGUI::Widget *sender) { - mWindowManager.removeGuiMode(GM_QuickKeysMenu); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_QuickKeysMenu); } @@ -124,13 +120,12 @@ namespace MWGui { if (!mItemSelectionDialog ) { - mItemSelectionDialog = new ItemSelectionDialog("#{sQuickMenu6}", ContainerBase::Filter_All, mWindowManager); + mItemSelectionDialog = new ItemSelectionDialog("#{sQuickMenu6}"); mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItem); mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItemCancel); } mItemSelectionDialog->setVisible(true); mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); - mItemSelectionDialog->drawItems (); mAssignDialog->setVisible (false); } @@ -139,7 +134,7 @@ namespace MWGui { if (!mMagicSelectionDialog ) { - mMagicSelectionDialog = new MagicSelectionDialog(mWindowManager, this); + mMagicSelectionDialog = new MagicSelectionDialog(this); } mMagicSelectionDialog->setVisible(true); @@ -281,7 +276,7 @@ namespace MWGui std::string spellId = button->getChildAt(0)->getUserString("Spell"); spells.setSelectedSpell(spellId); store.setSelectedEnchantItem(store.end()); - mWindowManager.setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); + MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); } else if (type == Type_Item) { @@ -291,7 +286,7 @@ namespace MWGui if (item.getRefData ().getCount() == 0) { MWBase::Environment::get().getWindowManager ()->messageBox ( - "#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item), std::vector()); + "#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item)); return; } @@ -303,11 +298,11 @@ namespace MWGui // 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 - mWindowManager.getBookWindow()->setTakeButtonShow(false); - mWindowManager.getScrollWindow()->setTakeButtonShow(false); + MWBase::Environment::get().getWindowManager()->getBookWindow()->setTakeButtonShow(false); + MWBase::Environment::get().getWindowManager()->getScrollWindow()->setTakeButtonShow(false); // since we changed equipping status, update the inventory window - mWindowManager.getInventoryWindow()->drawItems(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); } else if (type == Type_MagicItem) { @@ -317,7 +312,7 @@ namespace MWGui if (item.getRefData ().getCount() == 0) { MWBase::Environment::get().getWindowManager ()->messageBox ( - "#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item), std::vector()); + "#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item)); return; } @@ -341,19 +336,19 @@ namespace MWGui action.execute (MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()); // since we changed equipping status, update the inventory window - mWindowManager.getInventoryWindow()->drawItems(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); } store.setSelectedEnchantItem(it); spells.setSelectedSpell(""); - mWindowManager.setSelectedEnchantItem(item, 100); /// \todo track charge % + MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item); } } // --------------------------------------------------------------------------------------------------------- - QuickKeysMenuAssign::QuickKeysMenuAssign (MWBase::WindowManager &parWindowManager, QuickKeysMenu* parent) - : WindowModal("openmw_quickkeys_menu_assign.layout", parWindowManager) + QuickKeysMenuAssign::QuickKeysMenuAssign (QuickKeysMenu* parent) + : WindowModal("openmw_quickkeys_menu_assign.layout") , mParent(parent) { getWidget(mLabel, "Label"); @@ -399,8 +394,8 @@ namespace MWGui // --------------------------------------------------------------------------------------------------------- - MagicSelectionDialog::MagicSelectionDialog(MWBase::WindowManager &parWindowManager, QuickKeysMenu* parent) - : WindowModal("openmw_magicselection_dialog.layout", parWindowManager) + MagicSelectionDialog::MagicSelectionDialog(QuickKeysMenu* parent) + : WindowModal("openmw_magicselection_dialog.layout") , mParent(parent) , mWidth(0) , mHeight(0) diff --git a/apps/openmw/mwgui/quickkeysmenu.hpp b/apps/openmw/mwgui/quickkeysmenu.hpp index 345ffa0c8..058519ece 100644 --- a/apps/openmw/mwgui/quickkeysmenu.hpp +++ b/apps/openmw/mwgui/quickkeysmenu.hpp @@ -1,10 +1,9 @@ #ifndef MWGUI_QUICKKEYS_H #define MWGUI_QUICKKEYS_H - #include "../mwworld/ptr.hpp" -#include "window_base.hpp" +#include "windowbase.hpp" namespace MWGui { @@ -16,7 +15,7 @@ namespace MWGui class QuickKeysMenu : public WindowBase { public: - QuickKeysMenu(MWBase::WindowManager& parWindowManager); + QuickKeysMenu(); ~QuickKeysMenu(); @@ -64,7 +63,7 @@ namespace MWGui class QuickKeysMenuAssign : public WindowModal { public: - QuickKeysMenuAssign(MWBase::WindowManager& parWindowManager, QuickKeysMenu* parent); + QuickKeysMenuAssign(QuickKeysMenu* parent); private: MyGUI::TextBox* mLabel; @@ -79,7 +78,7 @@ namespace MWGui class MagicSelectionDialog : public WindowModal { public: - MagicSelectionDialog(MWBase::WindowManager& parWindowManager, QuickKeysMenu* parent); + MagicSelectionDialog(QuickKeysMenu* parent); virtual void open(); diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index df6c99340..9065333f5 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -1,405 +1,385 @@ #include "race.hpp" -#include -#include - #include #include #include -#include "../mwworld/esmstore.hpp" - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include "widgets.hpp" #include "tooltips.hpp" -using namespace MWGui; -using namespace Widgets; - -RaceDialog::RaceDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_chargen_race.layout", parWindowManager) - , mGenderIndex(0) - , mFaceIndex(0) - , mHairIndex(0) - , mFaceCount(10) - , mHairCount(14) - , mCurrentAngle(0) +namespace { - // Centre dialog - center(); - - setText("AppearanceT", mWindowManager.getGameSettingString("sRaceMenu1", "Appearance")); - getWidget(mPreviewImage, "PreviewImage"); - - getWidget(mHeadRotate, "HeadRotate"); - mHeadRotate->setScrollRange(50); - mHeadRotate->setScrollPosition(25); - mHeadRotate->setScrollViewPage(10); - mHeadRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate); - - // Set up next/previous buttons - MyGUI::ButtonPtr prevButton, nextButton; - - setText("GenderChoiceT", mWindowManager.getGameSettingString("sRaceMenu2", "Change Sex")); - getWidget(prevButton, "PrevGenderButton"); - getWidget(nextButton, "NextGenderButton"); - prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousGender); - nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextGender); - - setText("FaceChoiceT", mWindowManager.getGameSettingString("sRaceMenu3", "Change Face")); - getWidget(prevButton, "PrevFaceButton"); - getWidget(nextButton, "NextFaceButton"); - prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousFace); - nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextFace); - - setText("HairChoiceT", mWindowManager.getGameSettingString("sRaceMenu4", "Change Hair")); - getWidget(prevButton, "PrevHairButton"); - getWidget(nextButton, "NextHairButton"); - prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousHair); - nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextHair); - - setText("RaceT", mWindowManager.getGameSettingString("sRaceMenu4", "Race")); - getWidget(mRaceList, "RaceList"); - mRaceList->setScrollVisible(true); - mRaceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); - mRaceList->eventListMouseItemActivate += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); - mRaceList->eventListChangePosition += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); - - setText("SkillsT", mWindowManager.getGameSettingString("sBonusSkillTitle", "Skill Bonus")); - getWidget(mSkillList, "SkillList"); - setText("SpellPowerT", mWindowManager.getGameSettingString("sRaceMenu7", "Specials")); - getWidget(mSpellPowerList, "SpellPowerList"); - - MyGUI::ButtonPtr backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onBackClicked); - - MyGUI::ButtonPtr okButton; - getWidget(okButton, "OKButton"); - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onOkClicked); - okButton->setEnabled(false); - - updateRaces(); - updateSkills(); - updateSpellPowers(); + int wrap(int index, int max) + { + if (index < 0) + return max - 1; + else if (index >= max) + return 0; + else + return index; + } } -void RaceDialog::setNextButtonShow(bool shown) +namespace MWGui { - MyGUI::ButtonPtr okButton; - getWidget(okButton, "OKButton"); - if (shown) - okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); - else - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); -} + RaceDialog::RaceDialog() + : WindowModal("openmw_chargen_race.layout") + , mGenderIndex(0) + , mFaceIndex(0) + , mHairIndex(0) + , mCurrentAngle(0) + { + // Centre dialog + center(); + + setText("AppearanceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu1", "Appearance")); + getWidget(mPreviewImage, "PreviewImage"); + + getWidget(mHeadRotate, "HeadRotate"); + mHeadRotate->setScrollRange(50); + mHeadRotate->setScrollPosition(25); + mHeadRotate->setScrollViewPage(10); + mHeadRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate); + + // Set up next/previous buttons + MyGUI::Button *prevButton, *nextButton; + + setText("GenderChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu2", "Change Sex")); + getWidget(prevButton, "PrevGenderButton"); + getWidget(nextButton, "NextGenderButton"); + prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousGender); + nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextGender); + + setText("FaceChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu3", "Change Face")); + getWidget(prevButton, "PrevFaceButton"); + getWidget(nextButton, "NextFaceButton"); + prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousFace); + nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextFace); + + setText("HairChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu4", "Change Hair")); + getWidget(prevButton, "PrevHairButton"); + getWidget(nextButton, "NextHairButton"); + prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousHair); + nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextHair); + + 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->eventListChangePosition += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); + + setText("SkillsT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sBonusSkillTitle", "Skill Bonus")); + getWidget(mSkillList, "SkillList"); + setText("SpellPowerT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu7", "Specials")); + getWidget(mSpellPowerList, "SpellPowerList"); + + MyGUI::Button* backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onBackClicked); + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onOkClicked); + + updateRaces(); + updateSkills(); + updateSpellPowers(); + } -void RaceDialog::open() -{ - updateRaces(); - updateSkills(); - updateSpellPowers(); + void RaceDialog::setNextButtonShow(bool shown) + { + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); - mPreview = new MWRender::RaceSelectionPreview(); - mPreview->setup(); - mPreview->update (0); + if (shown) + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); + else + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + } - const ESM::NPC proto = mPreview->getPrototype(); - setRaceId(proto.mRace); - recountParts(); - - std::string index = proto.mHead.substr(proto.mHead.size() - 2, 2); - mFaceIndex = boost::lexical_cast(index) - 1; + void RaceDialog::open() + { + WindowModal::open(); - index = proto.mHair.substr(proto.mHair.size() - 2, 2); - mHairIndex = boost::lexical_cast(index) - 1; + updateRaces(); + updateSkills(); + updateSpellPowers(); - mPreviewImage->setImageTexture ("CharacterHeadPreview"); -} + mPreview = new MWRender::RaceSelectionPreview(); + mPreview->setup(); + mPreview->update (0); + const ESM::NPC proto = mPreview->getPrototype(); + setRaceId(proto.mRace); + recountParts(); -void RaceDialog::setRaceId(const std::string &raceId) -{ - mCurrentRaceId = raceId; - mRaceList->setIndexSelected(MyGUI::ITEM_NONE); - size_t count = mRaceList->getItemCount(); - for (size_t i = 0; i < count; ++i) - { - if (boost::iequals(*mRaceList->getItemDataAt(i), raceId)) - { - mRaceList->setIndexSelected(i); - MyGUI::ButtonPtr okButton; - getWidget(okButton, "OKButton"); - okButton->setEnabled(true); - break; - } - } + std::string index = proto.mHead.substr(proto.mHead.size() - 2, 2); + mFaceIndex = boost::lexical_cast(index) - 1; - updateSkills(); - updateSpellPowers(); -} - -int wrap(int index, int max) -{ - if (index < 0) - return max - 1; - else if (index >= max) - return 0; - else - return index; -} + index = proto.mHair.substr(proto.mHair.size() - 2, 2); + mHairIndex = boost::lexical_cast(index) - 1; -int countParts(const std::string &part, const std::string &race, bool male) -{ - const MWWorld::Store &store = - MWBase::Environment::get().getWorld()->getStore().get(); - - std::string prefix = - "b_n_" + race + ((male) ? "_m_" : "_f_") + part; - - std::string suffix; - suffix.reserve(prefix.size() + 3); - - int count = -1; - do { - ++count; - suffix = "_" + (boost::format("%02d") % (count + 1)).str(); + mPreviewImage->setImageTexture ("CharacterHeadPreview"); } - while (store.search(prefix + suffix) != 0); - if (count == 0 && part == "hair") { - count = -1; - do { - ++count; - suffix = (boost::format("%02d") % (count + 1)).str(); + + void RaceDialog::setRaceId(const std::string &raceId) + { + mCurrentRaceId = raceId; + mRaceList->setIndexSelected(MyGUI::ITEM_NONE); + size_t count = mRaceList->getItemCount(); + for (size_t i = 0; i < count; ++i) + { + if (boost::iequals(*mRaceList->getItemDataAt(i), raceId)) + { + mRaceList->setIndexSelected(i); + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + break; + } } - while (store.search(prefix + suffix) != 0); - } - return count; -} -void RaceDialog::close() -{ - delete mPreview; - mPreview = 0; -} + updateSkills(); + updateSpellPowers(); + } -// widget controls + void RaceDialog::close() + { + delete mPreview; + mPreview = 0; + } -void RaceDialog::onOkClicked(MyGUI::Widget* _sender) -{ - if(mRaceList->getIndexSelected() == MyGUI::ITEM_NONE) - return; - eventDone(this); -} + // widget controls -void RaceDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} + void RaceDialog::onOkClicked(MyGUI::Widget* _sender) + { + if(mRaceList->getIndexSelected() == MyGUI::ITEM_NONE) + return; + eventDone(this); + } -void RaceDialog::onHeadRotate(MyGUI::ScrollBar*, size_t _position) -{ - float angle = (float(_position) / 49.f - 0.5) * 3.14 * 2; - float diff = angle - mCurrentAngle; - mPreview->update (diff); - mCurrentAngle += diff; -} + void RaceDialog::onBackClicked(MyGUI::Widget* _sender) + { + eventBack(); + } -void RaceDialog::onSelectPreviousGender(MyGUI::Widget*) -{ - mGenderIndex = wrap(mGenderIndex - 1, 2); + void RaceDialog::onHeadRotate(MyGUI::ScrollBar*, size_t _position) + { + float angle = (float(_position) / 49.f - 0.5) * 3.14 * 2; + float diff = angle - mCurrentAngle; + mPreview->update (diff); + mCurrentAngle += diff; + } - recountParts(); - updatePreview(); -} + void RaceDialog::onSelectPreviousGender(MyGUI::Widget*) + { + mGenderIndex = wrap(mGenderIndex - 1, 2); -void RaceDialog::onSelectNextGender(MyGUI::Widget*) -{ - mGenderIndex = wrap(mGenderIndex + 1, 2); + recountParts(); + updatePreview(); + } - recountParts(); - updatePreview(); -} + void RaceDialog::onSelectNextGender(MyGUI::Widget*) + { + mGenderIndex = wrap(mGenderIndex + 1, 2); -void RaceDialog::onSelectPreviousFace(MyGUI::Widget*) -{ - mFaceIndex = wrap(mFaceIndex - 1, mFaceCount); - updatePreview(); -} + recountParts(); + updatePreview(); + } -void RaceDialog::onSelectNextFace(MyGUI::Widget*) -{ - mFaceIndex = wrap(mFaceIndex + 1, mFaceCount); - updatePreview(); -} + void RaceDialog::onSelectPreviousFace(MyGUI::Widget*) + { + mFaceIndex = wrap(mFaceIndex - 1, mAvailableHeads.size()); + updatePreview(); + } -void RaceDialog::onSelectPreviousHair(MyGUI::Widget*) -{ - mHairIndex = wrap(mHairIndex - 1, mHairCount); - updatePreview(); -} + void RaceDialog::onSelectNextFace(MyGUI::Widget*) + { + mFaceIndex = wrap(mFaceIndex + 1, mAvailableHeads.size()); + updatePreview(); + } -void RaceDialog::onSelectNextHair(MyGUI::Widget*) -{ - mHairIndex = wrap(mHairIndex + 1, mHairCount); - updatePreview(); -} + void RaceDialog::onSelectPreviousHair(MyGUI::Widget*) + { + mHairIndex = wrap(mHairIndex - 1, mAvailableHairs.size()); + updatePreview(); + } -void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index) -{ - if (_index == MyGUI::ITEM_NONE) - return; + void RaceDialog::onSelectNextHair(MyGUI::Widget*) + { + mHairIndex = wrap(mHairIndex + 1, mAvailableHairs.size()); + updatePreview(); + } - MyGUI::ButtonPtr okButton; - getWidget(okButton, "OKButton"); - okButton->setEnabled(true); - const std::string *raceId = mRaceList->getItemDataAt(_index); - if (boost::iequals(mCurrentRaceId, *raceId)) - return; + void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index) + { + if (_index == MyGUI::ITEM_NONE) + return; - mCurrentRaceId = *raceId; + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + const std::string *raceId = mRaceList->getItemDataAt(_index); + if (boost::iequals(mCurrentRaceId, *raceId)) + return; - recountParts(); + mCurrentRaceId = *raceId; - updatePreview(); - updateSkills(); - updateSpellPowers(); -} + recountParts(); -void RaceDialog::recountParts() -{ - mFaceIndex = 0; - mHairIndex = 0; + updatePreview(); + updateSkills(); + updateSpellPowers(); + } - mFaceCount = countParts("head", mCurrentRaceId, mGenderIndex == 0); - mHairCount = countParts("hair", mCurrentRaceId, mGenderIndex == 0); -} + void RaceDialog::getBodyParts (int part, std::vector& out) + { + out.clear(); + const MWWorld::Store &store = + MWBase::Environment::get().getWorld()->getStore().get(); -// update widget content + for (MWWorld::Store::iterator it = store.begin(); it != store.end(); ++it) + { + const ESM::BodyPart& bodypart = *it; + if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable) + continue; + if (bodypart.mData.mType != ESM::BodyPart::MT_Skin) + continue; + if (bodypart.mData.mPart != static_cast(part)) + continue; + if (mGenderIndex != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female)) + continue; + bool firstPerson = (bodypart.mId.size() >= 3) + && bodypart.mId[bodypart.mId.size()-3] == '1' + && bodypart.mId[bodypart.mId.size()-2] == 's' + && bodypart.mId[bodypart.mId.size()-1] == 't'; + if (firstPerson) + continue; + if (Misc::StringUtils::ciEqual(bodypart.mRace, mCurrentRaceId)) + out.push_back(bodypart.mId); + } + } -void RaceDialog::updatePreview() -{ - ESM::NPC record = mPreview->getPrototype(); - record.mRace = mCurrentRaceId; - record.setIsMale(mGenderIndex == 0); + void RaceDialog::recountParts() + { + getBodyParts(ESM::BodyPart::MP_Hair, mAvailableHairs); + getBodyParts(ESM::BodyPart::MP_Head, mAvailableHeads); - std::string prefix = - "b_n_" + mCurrentRaceId + ((record.isMale()) ? "_m_" : "_f_"); + mFaceIndex = 0; + mHairIndex = 0; + } - std::string headIndex = (boost::format("%02d") % (mFaceIndex + 1)).str(); - std::string hairIndex = (boost::format("%02d") % (mHairIndex + 1)).str(); + // update widget content - record.mHead = prefix + "head_" + headIndex; - record.mHair = prefix + "hair_" + hairIndex; + void RaceDialog::updatePreview() + { + ESM::NPC record = mPreview->getPrototype(); + record.mRace = mCurrentRaceId; + record.setIsMale(mGenderIndex == 0); - const MWWorld::Store &parts = - MWBase::Environment::get().getWorld()->getStore().get(); + record.mHead = mAvailableHeads[mFaceIndex]; + record.mHair = mAvailableHairs[mHairIndex]; - if (parts.search(record.mHair) == 0) { - record.mHair = prefix + "hair" + hairIndex; + mPreview->setPrototype(record); } - mPreview->setPrototype(record); -} -void RaceDialog::updateRaces() -{ - mRaceList->removeAllItems(); + void RaceDialog::updateRaces() + { + mRaceList->removeAllItems(); - const MWWorld::Store &races = - MWBase::Environment::get().getWorld()->getStore().get(); + const MWWorld::Store &races = + MWBase::Environment::get().getWorld()->getStore().get(); - - int index = 0; - MWWorld::Store::iterator it = races.begin(); - for (; it != races.end(); ++it) - { - bool playable = it->mData.mFlags & ESM::Race::Playable; - if (!playable) // Only display playable races - continue; - - mRaceList->addItem(it->mName, it->mId); - if (boost::iequals(it->mId, mCurrentRaceId)) - mRaceList->setIndexSelected(index); - ++index; + + int index = 0; + MWWorld::Store::iterator it = races.begin(); + for (; it != races.end(); ++it) + { + bool playable = it->mData.mFlags & ESM::Race::Playable; + if (!playable) // Only display playable races + continue; + + mRaceList->addItem(it->mName, it->mId); + if (boost::iequals(it->mId, mCurrentRaceId)) + mRaceList->setIndexSelected(index); + ++index; + } } -} -void RaceDialog::updateSkills() -{ - for (std::vector::iterator it = mSkillItems.begin(); it != mSkillItems.end(); ++it) + void RaceDialog::updateSkills() { - MyGUI::Gui::getInstance().destroyWidget(*it); - } - mSkillItems.clear(); + for (std::vector::iterator it = mSkillItems.begin(); it != mSkillItems.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + mSkillItems.clear(); - if (mCurrentRaceId.empty()) - return; + if (mCurrentRaceId.empty()) + return; - MWSkillPtr skillWidget; - const int lineHeight = 18; - MyGUI::IntCoord coord1(0, 0, mSkillList->getWidth(), 18); + Widgets::MWSkillPtr skillWidget; + const int lineHeight = 18; + MyGUI::IntCoord coord1(0, 0, mSkillList->getWidth(), 18); - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - const ESM::Race *race = store.get().find(mCurrentRaceId); - int count = sizeof(race->mData.mBonus)/sizeof(race->mData.mBonus[0]); // TODO: Find a portable macro for this ARRAYSIZE? - for (int i = 0; i < count; ++i) - { - int skillId = race->mData.mBonus[i].mSkill; - if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes - continue; + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Race *race = store.get().find(mCurrentRaceId); + int count = sizeof(race->mData.mBonus)/sizeof(race->mData.mBonus[0]); // TODO: Find a portable macro for this ARRAYSIZE? + for (int i = 0; i < count; ++i) + { + int skillId = race->mData.mBonus[i].mSkill; + if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes + continue; - skillWidget = mSkillList->createWidget("MW_StatNameValue", coord1, MyGUI::Align::Default, - std::string("Skill") + boost::lexical_cast(i)); - skillWidget->setWindowManager(&mWindowManager); - skillWidget->setSkillNumber(skillId); - skillWidget->setSkillValue(MWSkill::SkillValue(race->mData.mBonus[i].mBonus)); - ToolTips::createSkillToolTip(skillWidget, skillId); + skillWidget = mSkillList->createWidget("MW_StatNameValue", coord1, MyGUI::Align::Default, + std::string("Skill") + boost::lexical_cast(i)); + skillWidget->setSkillNumber(skillId); + skillWidget->setSkillValue(Widgets::MWSkill::SkillValue(race->mData.mBonus[i].mBonus)); + ToolTips::createSkillToolTip(skillWidget, skillId); - mSkillItems.push_back(skillWidget); + mSkillItems.push_back(skillWidget); - coord1.top += lineHeight; + coord1.top += lineHeight; + } } -} -void RaceDialog::updateSpellPowers() -{ - for (std::vector::iterator it = mSpellPowerItems.begin(); it != mSpellPowerItems.end(); ++it) + void RaceDialog::updateSpellPowers() { - MyGUI::Gui::getInstance().destroyWidget(*it); - } - mSpellPowerItems.clear(); + for (std::vector::iterator it = mSpellPowerItems.begin(); it != mSpellPowerItems.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + mSpellPowerItems.clear(); - if (mCurrentRaceId.empty()) - return; + if (mCurrentRaceId.empty()) + return; - MWSpellPtr spellPowerWidget; - const int lineHeight = 18; - MyGUI::IntCoord coord(0, 0, mSpellPowerList->getWidth(), 18); + Widgets::MWSpellPtr spellPowerWidget; + const int lineHeight = 18; + MyGUI::IntCoord coord(0, 0, mSpellPowerList->getWidth(), 18); - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - const ESM::Race *race = store.get().find(mCurrentRaceId); + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Race *race = store.get().find(mCurrentRaceId); - std::vector::const_iterator it = race->mPowers.mList.begin(); - std::vector::const_iterator end = race->mPowers.mList.end(); - for (int i = 0; it != end; ++it) - { - const std::string &spellpower = *it; - spellPowerWidget = mSpellPowerList->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("SpellPower") + boost::lexical_cast(i)); - spellPowerWidget->setWindowManager(&mWindowManager); - spellPowerWidget->setSpellId(spellpower); - spellPowerWidget->setUserString("ToolTipType", "Spell"); - spellPowerWidget->setUserString("Spell", spellpower); + std::vector::const_iterator it = race->mPowers.mList.begin(); + std::vector::const_iterator end = race->mPowers.mList.end(); + for (int i = 0; it != end; ++it) + { + const std::string &spellpower = *it; + spellPowerWidget = mSpellPowerList->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("SpellPower") + boost::lexical_cast(i)); + spellPowerWidget->setSpellId(spellpower); + spellPowerWidget->setUserString("ToolTipType", "Spell"); + spellPowerWidget->setUserString("Spell", spellpower); - mSpellPowerItems.push_back(spellPowerWidget); + mSpellPowerItems.push_back(spellPowerWidget); - coord.top += lineHeight; - ++i; + coord.top += lineHeight; + ++i; + } } } diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index e0dc3306a..1d48c67cd 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -1,14 +1,9 @@ #ifndef MWGUI_RACE_H #define MWGUI_RACE_H - -#include - -#include "../mwworld/esmstore.hpp" - #include "../mwrender/characterpreview.hpp" -#include "window_base.hpp" +#include "windowbase.hpp" namespace MWGui @@ -23,10 +18,10 @@ namespace MWGui namespace MWGui { - class RaceDialog : public WindowBase + class RaceDialog : public WindowModal { public: - RaceDialog(MWBase::WindowManager& parWindowManager); + RaceDialog(); enum Gender { @@ -81,18 +76,22 @@ namespace MWGui void updatePreview(); void recountParts(); + void getBodyParts (int part, std::vector& out); + + std::vector mAvailableHeads; + std::vector mAvailableHairs; + MyGUI::ImageBox* mPreviewImage; MyGUI::ListBox* mRaceList; MyGUI::ScrollBar* mHeadRotate; - MyGUI::WidgetPtr mSkillList; - std::vector mSkillItems; + MyGUI::Widget* mSkillList; + std::vector mSkillItems; - MyGUI::WidgetPtr mSpellPowerList; - std::vector mSpellPowerItems; + MyGUI::Widget* mSpellPowerList; + std::vector mSpellPowerItems; int mGenderIndex, mFaceIndex, mHairIndex; - int mFaceCount, mHairCount; std::string mCurrentRaceId; diff --git a/apps/openmw/mwgui/referenceinterface.cpp b/apps/openmw/mwgui/referenceinterface.cpp index b1f7affb6..86a85be18 100644 --- a/apps/openmw/mwgui/referenceinterface.cpp +++ b/apps/openmw/mwgui/referenceinterface.cpp @@ -18,15 +18,18 @@ namespace MWGui void ReferenceInterface::checkReferenceAvailable() { - if (mPtr.isEmpty()) - return; - MWWorld::Ptr::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) - || mPtr.getRefData().getCount() == 0) - onReferenceUnavailable(); + || (!mPtr.isEmpty() && mPtr.getRefData().getCount() == 0)) + { + if (!mPtr.isEmpty()) + { + mPtr = MWWorld::Ptr(); + onReferenceUnavailable(); + } + } mCurrentPlayerCell = playerCell; } diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp new file mode 100644 index 000000000..0bd4b0995 --- /dev/null +++ b/apps/openmw/mwgui/repair.cpp @@ -0,0 +1,157 @@ +#include "repair.hpp" + +#include + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/containerstore.hpp" +#include "../mwworld/class.hpp" + +#include "widgets.hpp" + +namespace MWGui +{ + +Repair::Repair() + : WindowBase("openmw_repair.layout") +{ + getWidget(mRepairBox, "RepairBox"); + getWidget(mRepairView, "RepairView"); + getWidget(mToolBox, "ToolBox"); + getWidget(mToolIcon, "ToolIcon"); + getWidget(mUsesLabel, "UsesLabel"); + getWidget(mQualityLabel, "QualityLabel"); + getWidget(mCancelButton, "CancelButton"); + + mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onCancel); +} + +void Repair::open() +{ + center(); +} + +void Repair::startRepairItem(const MWWorld::Ptr &item) +{ + mRepair.setTool(item); + + std::string path = std::string("icons\\"); + path += MWWorld::Class::get(item).getInventoryIcon(item); + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + mToolIcon->setImageTexture (path); + mToolIcon->setUserString("ToolTipType", "ItemPtr"); + mToolIcon->setUserData(item); + + updateRepairView(); +} + +void Repair::updateRepairView() +{ + MWWorld::LiveCellRef *ref = + mRepair.getTool().get(); + + int uses = (mRepair.getTool().getCellRef().mCharge != -1) ? mRepair.getTool().getCellRef().mCharge : ref->mBase->mData.mUses; + + float quality = ref->mBase->mData.mQuality; + + std::stringstream qualityStr; + qualityStr << std::setprecision(3) << quality; + + mUsesLabel->setCaptionWithReplacing("#{sUses} " + boost::lexical_cast(uses)); + mQualityLabel->setCaptionWithReplacing("#{sQuality} " + qualityStr.str()); + + bool toolBoxVisible = (mRepair.getTool().getRefData().getCount() != 0); + mToolBox->setVisible(toolBoxVisible); + + bool toolBoxWasVisible = (mRepairBox->getPosition().top != mToolBox->getPosition().top); + + if (toolBoxVisible && !toolBoxWasVisible) + { + // shrink + mRepairBox->setPosition(mRepairBox->getPosition() + MyGUI::IntPoint(0,mToolBox->getSize().height)); + mRepairBox->setSize(mRepairBox->getSize() - MyGUI::IntSize(0,mToolBox->getSize().height)); + } + else if (!toolBoxVisible && toolBoxWasVisible) + { + // expand + mRepairBox->setPosition(MyGUI::IntPoint (mRepairBox->getPosition().left, mToolBox->getPosition().top)); + mRepairBox->setSize(mRepairBox->getSize() + MyGUI::IntSize(0,mToolBox->getSize().height)); + } + + while (mRepairView->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mRepairView->getChildAt(0)); + + int currentY = 0; + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + 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)); + iter!=store.end(); ++iter) + { + if (MWWorld::Class::get(*iter).hasItemHealth(*iter)) + { + int maxDurability = MWWorld::Class::get(*iter).getItemMaxHealth(*iter); + int durability = (iter->getCellRef().mCharge == -1) ? maxDurability : iter->getCellRef().mCharge; + if (maxDurability == durability) + continue; + + MyGUI::TextBox* text = mRepairView->createWidget ( + "SandText", MyGUI::IntCoord(8, currentY, mRepairView->getWidth()-8, 18), MyGUI::Align::Default); + text->setCaption(MWWorld::Class::get(*iter).getName(*iter)); + text->setNeedMouseFocus(false); + currentY += 19; + + MyGUI::ImageBox* icon = mRepairView->createWidget ( + "ImageBox", MyGUI::IntCoord(16, currentY, 32, 32), MyGUI::Align::Default); + std::string path = std::string("icons\\"); + path += MWWorld::Class::get(*iter).getInventoryIcon(*iter); + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + icon->setImageTexture (path); + icon->setUserString("ToolTipType", "ItemPtr"); + icon->setUserData(*iter); + icon->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onRepairItem); + icon->eventMouseWheel += MyGUI::newDelegate(this, &Repair::onMouseWheel); + + Widgets::MWDynamicStatPtr chargeWidget = mRepairView->createWidget + ("MW_ChargeBar", MyGUI::IntCoord(72, currentY+2, 199, 20), MyGUI::Align::Default); + chargeWidget->setValue(durability, maxDurability); + chargeWidget->setNeedMouseFocus(false); + + currentY += 32 + 4; + } + } + mRepairView->setCanvasSize (MyGUI::IntSize(mRepairView->getWidth(), std::max(mRepairView->getHeight(), currentY))); +} + +void Repair::onCancel(MyGUI::Widget *sender) +{ + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Repair); +} + +void Repair::onRepairItem(MyGUI::Widget *sender) +{ + if (!mRepair.getTool().getRefData().getCount()) + return; + + mRepair.repair(*sender->getUserData()); + + updateRepairView(); +} + +void Repair::onMouseWheel(MyGUI::Widget* _sender, int _rel) +{ + if (mRepairView->getViewOffset().top + _rel*0.3 > 0) + mRepairView->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mRepairView->setViewOffset(MyGUI::IntPoint(0, mRepairView->getViewOffset().top + _rel*0.3)); +} + +} diff --git a/apps/openmw/mwgui/repair.hpp b/apps/openmw/mwgui/repair.hpp new file mode 100644 index 000000000..d0f5c54c4 --- /dev/null +++ b/apps/openmw/mwgui/repair.hpp @@ -0,0 +1,45 @@ +#ifndef OPENMW_MWGUI_REPAIR_H +#define OPENMW_MWGUI_REPAIR_H + +#include "windowbase.hpp" + +#include "../mwmechanics/repair.hpp" + +namespace MWGui +{ + +class Repair : public WindowBase +{ +public: + Repair(); + + virtual void open(); + + void startRepairItem (const MWWorld::Ptr& item); + +protected: + MyGUI::Widget* mRepairBox; + MyGUI::ScrollView* mRepairView; + + MyGUI::Widget* mToolBox; + + MyGUI::ImageBox* mToolIcon; + + MyGUI::TextBox* mUsesLabel; + MyGUI::TextBox* mQualityLabel; + + MyGUI::Button* mCancelButton; + + MWMechanics::Repair mRepair; + + void updateRepairView(); + + void onRepairItem (MyGUI::Widget* sender); + void onCancel (MyGUI::Widget* sender); + void onMouseWheel(MyGUI::Widget* _sender, int _rel); + +}; + +} + +#endif diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 45adb5383..824929b67 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -1,373 +1,369 @@ #include "review.hpp" -#include - -#include #include -#include "../mwworld/esmstore.hpp" - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include "widgets.hpp" #include "tooltips.hpp" #undef min #undef max -using namespace MWGui; -using namespace Widgets; - -const int ReviewDialog::sLineHeight = 18; - -ReviewDialog::ReviewDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_chargen_review.layout", parWindowManager) - , mLastPos(0) +namespace MWGui { - // Centre dialog - center(); - - // Setup static stats - MyGUI::Button* button; - getWidget(mNameWidget, "NameText"); - getWidget(button, "NameButton"); - adjustButtonSize(button); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onNameClicked);; - - getWidget(mRaceWidget, "RaceText"); - getWidget(button, "RaceButton"); - adjustButtonSize(button); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onRaceClicked);; - - getWidget(mClassWidget, "ClassText"); - getWidget(button, "ClassButton"); - adjustButtonSize(button); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onClassClicked);; - - getWidget(mBirthSignWidget, "SignText"); - getWidget(button, "SignButton"); - adjustButtonSize(button); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBirthSignClicked);; - - // Setup dynamic stats - getWidget(mHealth, "Health"); - mHealth->setTitle(mWindowManager.getGameSettingString("sHealth", "")); - mHealth->setValue(45, 45); - - getWidget(mMagicka, "Magicka"); - mMagicka->setTitle(mWindowManager.getGameSettingString("sMagic", "")); - mMagicka->setValue(50, 50); - - getWidget(mFatigue, "Fatigue"); - mFatigue->setTitle(mWindowManager.getGameSettingString("sFatigue", "")); - mFatigue->setValue(160, 160); - - // Setup attributes - - MWAttributePtr attribute; - for (int idx = 0; idx < ESM::Attribute::Length; ++idx) - { - getWidget(attribute, std::string("Attribute") + boost::lexical_cast(idx)); - mAttributeWidgets.insert(std::make_pair(static_cast(ESM::Attribute::sAttributeIds[idx]), attribute)); - attribute->setWindowManager(&mWindowManager); - attribute->setAttributeId(ESM::Attribute::sAttributeIds[idx]); - attribute->setAttributeValue(MWAttribute::AttributeValue(0, 0)); - } - // Setup skills - getWidget(mSkillView, "SkillView"); - mSkillView->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); + const int ReviewDialog::sLineHeight = 18; - for (int i = 0; i < ESM::Skill::Length; ++i) + ReviewDialog::ReviewDialog() + : WindowModal("openmw_chargen_review.layout") + , mLastPos(0) { - mSkillValues.insert(std::make_pair(i, MWMechanics::Stat())); - mSkillWidgetMap.insert(std::make_pair(i, static_cast (0))); - } + // Centre dialog + center(); + + // Setup static stats + MyGUI::Button* button; + getWidget(mNameWidget, "NameText"); + getWidget(button, "NameButton"); + adjustButtonSize(button); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onNameClicked);; + + getWidget(mRaceWidget, "RaceText"); + getWidget(button, "RaceButton"); + adjustButtonSize(button); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onRaceClicked);; + + getWidget(mClassWidget, "ClassText"); + getWidget(button, "ClassButton"); + adjustButtonSize(button); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onClassClicked);; + + getWidget(mBirthSignWidget, "SignText"); + getWidget(button, "SignButton"); + adjustButtonSize(button); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBirthSignClicked);; + + // Setup dynamic stats + getWidget(mHealth, "Health"); + mHealth->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sHealth", "")); + mHealth->setValue(45, 45); + + getWidget(mMagicka, "Magicka"); + mMagicka->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sMagic", "")); + mMagicka->setValue(50, 50); + + getWidget(mFatigue, "Fatigue"); + mFatigue->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sFatigue", "")); + mFatigue->setValue(160, 160); + + // Setup attributes + + Widgets::MWAttributePtr attribute; + for (int idx = 0; idx < ESM::Attribute::Length; ++idx) + { + 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)); + } - MyGUI::ButtonPtr backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBackClicked); + // Setup skills + getWidget(mSkillView, "SkillView"); + mSkillView->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - MyGUI::ButtonPtr okButton; - getWidget(okButton, "OKButton"); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onOkClicked); -} + for (int i = 0; i < ESM::Skill::Length; ++i) + { + mSkillValues.insert(std::make_pair(i, MWMechanics::Stat())); + mSkillWidgetMap.insert(std::make_pair(i, static_cast (0))); + } -void ReviewDialog::open() -{ - updateSkillArea(); -} + MyGUI::Button* backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBackClicked); -void ReviewDialog::setPlayerName(const std::string &name) -{ - mNameWidget->setCaption(name); -} + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onOkClicked); + } -void ReviewDialog::setRace(const std::string &raceId) -{ - mRaceId = raceId; + void ReviewDialog::open() + { + WindowModal::open(); + updateSkillArea(); + } - const ESM::Race *race = - MWBase::Environment::get().getWorld()->getStore().get().search(mRaceId); - if (race) + void ReviewDialog::setPlayerName(const std::string &name) { - ToolTips::createRaceToolTip(mRaceWidget, race); - mRaceWidget->setCaption(race->mName); + mNameWidget->setCaption(name); } -} -void ReviewDialog::setClass(const ESM::Class& class_) -{ - mKlass = class_; - mClassWidget->setCaption(mKlass.mName); - ToolTips::createClassToolTip(mClassWidget, mKlass); -} + void ReviewDialog::setRace(const std::string &raceId) + { + mRaceId = raceId; -void ReviewDialog::setBirthSign(const std::string& signId) -{ - mBirthSignId = signId; + const ESM::Race *race = + MWBase::Environment::get().getWorld()->getStore().get().search(mRaceId); + if (race) + { + ToolTips::createRaceToolTip(mRaceWidget, race); + mRaceWidget->setCaption(race->mName); + } + } - const ESM::BirthSign *sign = - MWBase::Environment::get().getWorld()->getStore().get().search(mBirthSignId); - if (sign) + void ReviewDialog::setClass(const ESM::Class& class_) { - mBirthSignWidget->setCaption(sign->mName); - ToolTips::createBirthsignToolTip(mBirthSignWidget, mBirthSignId); + mKlass = class_; + mClassWidget->setCaption(mKlass.mName); + ToolTips::createClassToolTip(mClassWidget, mKlass); } -} -void ReviewDialog::setHealth(const MWMechanics::DynamicStat& value) -{ - mHealth->setValue(value.getCurrent(), value.getModified()); - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); - mHealth->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); -} - -void ReviewDialog::setMagicka(const MWMechanics::DynamicStat& value) -{ - mMagicka->setValue(value.getCurrent(), value.getModified()); - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); - mMagicka->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); -} + void ReviewDialog::setBirthSign(const std::string& signId) + { + mBirthSignId = signId; -void ReviewDialog::setFatigue(const MWMechanics::DynamicStat& value) -{ - mFatigue->setValue(value.getCurrent(), value.getModified()); - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); - mFatigue->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); -} + const ESM::BirthSign *sign = + MWBase::Environment::get().getWorld()->getStore().get().search(mBirthSignId); + if (sign) + { + mBirthSignWidget->setCaption(sign->mName); + ToolTips::createBirthsignToolTip(mBirthSignWidget, mBirthSignId); + } + } -void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::Stat& value) -{ - std::map::iterator attr = mAttributeWidgets.find(static_cast(attributeId)); - if (attr == mAttributeWidgets.end()) - return; + void ReviewDialog::setHealth(const MWMechanics::DynamicStat& value) + { + mHealth->setValue(value.getCurrent(), value.getModified()); + std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); + mHealth->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); + } - attr->second->setAttributeValue(value); -} + void ReviewDialog::setMagicka(const MWMechanics::DynamicStat& value) + { + mMagicka->setValue(value.getCurrent(), value.getModified()); + std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); + mMagicka->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); + } -void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::Stat& value) -{ - mSkillValues[skillId] = value; - MyGUI::TextBox* widget = mSkillWidgetMap[skillId]; - if (widget) + void ReviewDialog::setFatigue(const MWMechanics::DynamicStat& value) { - float modified = value.getModified(), base = value.getBase(); - std::string text = boost::lexical_cast(std::floor(modified)); - std::string state = "normal"; - if (modified > base) - state = "increased"; - else if (modified < base) - state = "decreased"; - - widget->setCaption(text); - widget->_setWidgetState(state); + mFatigue->setValue(value.getCurrent(), value.getModified()); + std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); + mFatigue->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); } -} + void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::Stat& value) + { + std::map::iterator attr = mAttributeWidgets.find(static_cast(attributeId)); + if (attr == mAttributeWidgets.end()) + return; -void ReviewDialog::configureSkills(const std::vector& major, const std::vector& minor) -{ - mMajorSkills = major; - mMinorSkills = minor; - - // Update misc skills with the remaining skills not in major or minor - std::set skillSet; - std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin())); - std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin())); - boost::array::const_iterator end = ESM::Skill::sSkillIds.end(); - mMiscSkills.clear(); - for (boost::array::const_iterator it = ESM::Skill::sSkillIds.begin(); it != end; ++it) + attr->second->setAttributeValue(value); + } + + void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::Stat& value) { - int skill = *it; - if (skillSet.find(skill) == skillSet.end()) - mMiscSkills.push_back(skill); + mSkillValues[skillId] = value; + MyGUI::TextBox* widget = mSkillWidgetMap[skillId]; + if (widget) + { + float modified = value.getModified(), base = value.getBase(); + std::string text = boost::lexical_cast(std::floor(modified)); + std::string state = "normal"; + if (modified > base) + state = "increased"; + else if (modified < base) + state = "decreased"; + + widget->setCaption(text); + widget->_setWidgetState(state); + } + } - updateSkillArea(); -} + void ReviewDialog::configureSkills(const std::vector& major, const std::vector& minor) + { + mMajorSkills = major; + mMinorSkills = minor; + + // Update misc skills with the remaining skills not in major or minor + std::set skillSet; + std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin())); + std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin())); + boost::array::const_iterator end = ESM::Skill::sSkillIds.end(); + mMiscSkills.clear(); + for (boost::array::const_iterator it = ESM::Skill::sSkillIds.begin(); it != end; ++it) + { + int skill = *it; + if (skillSet.find(skill) == skillSet.end()) + mMiscSkills.push_back(skill); + } -void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::ImageBox* separator = mSkillView->createWidget("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Default); - separator->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); + updateSkillArea(); + } - mSkillWidgets.push_back(separator); + void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::ImageBox* separator = mSkillView->createWidget("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Default); + separator->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - coord1.top += separator->getHeight(); - coord2.top += separator->getHeight(); -} + mSkillWidgets.push_back(separator); -void ReviewDialog::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::TextBox* groupWidget = mSkillView->createWidget("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Default); - groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - groupWidget->setCaption(label); - mSkillWidgets.push_back(groupWidget); + coord1.top += separator->getHeight(); + coord2.top += separator->getHeight(); + } - coord1.top += sLineHeight; - coord2.top += sLineHeight; -} + void ReviewDialog::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::TextBox* groupWidget = mSkillView->createWidget("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Default); + groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); + groupWidget->setCaption(label); + mSkillWidgets.push_back(groupWidget); -MyGUI::TextBox* ReviewDialog::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::TextBox* skillNameWidget; - MyGUI::TextBox* skillValueWidget; + coord1.top += sLineHeight; + coord2.top += sLineHeight; + } - skillNameWidget = mSkillView->createWidget("SandText", coord1, MyGUI::Align::Default); - skillNameWidget->setCaption(text); - skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); + MyGUI::TextBox* ReviewDialog::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::TextBox* skillNameWidget; + MyGUI::TextBox* skillValueWidget; - skillValueWidget = mSkillView->createWidget("SandTextRight", coord2, MyGUI::Align::Top | MyGUI::Align::Right); - skillValueWidget->setCaption(value); - skillValueWidget->_setWidgetState(state); - skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); + skillNameWidget = mSkillView->createWidget("SandText", coord1, MyGUI::Align::Default); + skillNameWidget->setCaption(text); + skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - mSkillWidgets.push_back(skillNameWidget); - mSkillWidgets.push_back(skillValueWidget); + skillValueWidget = mSkillView->createWidget("SandTextRight", coord2, MyGUI::Align::Top | MyGUI::Align::Right); + skillValueWidget->setCaption(value); + skillValueWidget->_setWidgetState(state); + skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + mSkillWidgets.push_back(skillNameWidget); + mSkillWidgets.push_back(skillValueWidget); - return skillValueWidget; -} + coord1.top += sLineHeight; + coord2.top += sLineHeight; -void ReviewDialog::addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::TextBox* skillNameWidget; + return skillValueWidget; + } - skillNameWidget = mSkillView->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); - skillNameWidget->setCaption(text); - skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); + void ReviewDialog::addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::TextBox* skillNameWidget; - mSkillWidgets.push_back(skillNameWidget); + skillNameWidget = mSkillView->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); + skillNameWidget->setCaption(text); + skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - coord1.top += sLineHeight; - coord2.top += sLineHeight; -} + mSkillWidgets.push_back(skillNameWidget); -void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - // Add a line separator if there are items above - if (!mSkillWidgets.empty()) - { - addSeparator(coord1, coord2); + coord1.top += sLineHeight; + coord2.top += sLineHeight; } - addGroup(mWindowManager.getGameSettingString(titleId, titleDefault), coord1, coord2); - - SkillList::const_iterator end = skills.end(); - for (SkillList::const_iterator it = skills.begin(); it != end; ++it) + void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { - int skillId = *it; - if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes - continue; - assert(skillId >= 0 && skillId < ESM::Skill::Length); - const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; - const MWMechanics::Stat &stat = mSkillValues.find(skillId)->second; - float base = stat.getBase(); - float modified = stat.getModified(); - - std::string state = "normal"; - if (modified > base) - state = "increased"; - else if (modified < base) - state = "decreased"; - MyGUI::TextBox* widget = addValueItem(mWindowManager.getGameSettingString(skillNameId, skillNameId), boost::lexical_cast(static_cast(modified)), state, coord1, coord2); - - for (int i=0; i<2; ++i) + // Add a line separator if there are items above + if (!mSkillWidgets.empty()) { - ToolTips::createSkillToolTip(mSkillWidgets[mSkillWidgets.size()-1-i], skillId); + addSeparator(coord1, coord2); } - mSkillWidgetMap[skillId] = widget; + addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString(titleId, titleDefault), coord1, coord2); + + SkillList::const_iterator end = skills.end(); + for (SkillList::const_iterator it = skills.begin(); it != end; ++it) + { + int skillId = *it; + if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes + continue; + assert(skillId >= 0 && skillId < ESM::Skill::Length); + const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; + const MWMechanics::Stat &stat = mSkillValues.find(skillId)->second; + float base = stat.getBase(); + float modified = stat.getModified(); + + std::string state = "normal"; + if (modified > base) + state = "increased"; + else if (modified < base) + state = "decreased"; + MyGUI::TextBox* widget = addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString(skillNameId, skillNameId), boost::lexical_cast(static_cast(modified)), state, coord1, coord2); + + for (int i=0; i<2; ++i) + { + ToolTips::createSkillToolTip(mSkillWidgets[mSkillWidgets.size()-1-i], skillId); + } + + mSkillWidgetMap[skillId] = widget; + } } -} -void ReviewDialog::updateSkillArea() -{ - for (std::vector::iterator it = mSkillWidgets.begin(); it != mSkillWidgets.end(); ++it) + void ReviewDialog::updateSkillArea() { - MyGUI::Gui::getInstance().destroyWidget(*it); - } - mSkillWidgets.clear(); + for (std::vector::iterator it = mSkillWidgets.begin(); it != mSkillWidgets.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + mSkillWidgets.clear(); - const int valueSize = 40; - MyGUI::IntCoord coord1(10, 0, mSkillView->getWidth() - (10 + valueSize) - 24, 18); - MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height); + const int valueSize = 40; + MyGUI::IntCoord coord1(10, 0, mSkillView->getWidth() - (10 + valueSize) - 24, 18); + MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height); - if (!mMajorSkills.empty()) - addSkills(mMajorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2); + if (!mMajorSkills.empty()) + addSkills(mMajorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2); - if (!mMinorSkills.empty()) - addSkills(mMinorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2); + if (!mMinorSkills.empty()) + addSkills(mMinorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2); - if (!mMiscSkills.empty()) - addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); + if (!mMiscSkills.empty()) + addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); - mClientHeight = coord1.top; + mClientHeight = coord1.top; - mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); -} + mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); + } -// widget controls + // widget controls -void ReviewDialog::onOkClicked(MyGUI::Widget* _sender) -{ - eventDone(this); -} + void ReviewDialog::onOkClicked(MyGUI::Widget* _sender) + { + eventDone(this); + } -void ReviewDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} + void ReviewDialog::onBackClicked(MyGUI::Widget* _sender) + { + eventBack(); + } -void ReviewDialog::onNameClicked(MyGUI::Widget* _sender) -{ - eventActivateDialog(NAME_DIALOG); -} + void ReviewDialog::onNameClicked(MyGUI::Widget* _sender) + { + eventActivateDialog(NAME_DIALOG); + } -void ReviewDialog::onRaceClicked(MyGUI::Widget* _sender) -{ - eventActivateDialog(RACE_DIALOG); -} + void ReviewDialog::onRaceClicked(MyGUI::Widget* _sender) + { + eventActivateDialog(RACE_DIALOG); + } -void ReviewDialog::onClassClicked(MyGUI::Widget* _sender) -{ - eventActivateDialog(CLASS_DIALOG); -} + void ReviewDialog::onClassClicked(MyGUI::Widget* _sender) + { + eventActivateDialog(CLASS_DIALOG); + } -void ReviewDialog::onBirthSignClicked(MyGUI::Widget* _sender) -{ - eventActivateDialog(BIRTHSIGN_DIALOG); -} + void ReviewDialog::onBirthSignClicked(MyGUI::Widget* _sender) + { + eventActivateDialog(BIRTHSIGN_DIALOG); + } + + void ReviewDialog::onMouseWheel(MyGUI::Widget* _sender, int _rel) + { + if (mSkillView->getViewOffset().top + _rel*0.3 > 0) + mSkillView->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mSkillView->setViewOffset(MyGUI::IntPoint(0, mSkillView->getViewOffset().top + _rel*0.3)); + } -void ReviewDialog::onMouseWheel(MyGUI::Widget* _sender, int _rel) -{ - if (mSkillView->getViewOffset().top + _rel*0.3 > 0) - mSkillView->setViewOffset(MyGUI::IntPoint(0, 0)); - else - mSkillView->setViewOffset(MyGUI::IntPoint(0, mSkillView->getViewOffset().top + _rel*0.3)); } diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index 2b0740234..87d6fedfa 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -1,8 +1,7 @@ #ifndef MWGUI_REVIEW_H #define MWGUI_REVIEW_H -#include "window_base.hpp" -#include "../mwmechanics/stat.hpp" +#include "windowbase.hpp" #include "widgets.hpp" namespace MWGui @@ -17,7 +16,7 @@ Layout is defined by resources/mygui/openmw_chargen_review.layout. namespace MWGui { - class ReviewDialog : public WindowBase + class ReviewDialog : public WindowModal { public: enum Dialogs { @@ -28,7 +27,7 @@ namespace MWGui }; typedef std::vector SkillList; - ReviewDialog(MWBase::WindowManager& parWindowManager); + ReviewDialog(); void setPlayerName(const std::string &name); void setRace(const std::string &raceId); @@ -91,7 +90,7 @@ namespace MWGui std::map mSkillWidgetMap; std::string mName, mRaceId, mBirthSignId; ESM::Class mKlass; - std::vector mSkillWidgets; //< Skills and other information + std::vector mSkillWidgets; //< Skills and other information }; } #endif diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 8317025e0..eb46b9c1e 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -10,62 +10,88 @@ #include "formatting.hpp" -using namespace MWGui; +namespace +{ + void adjustButton (MWGui::ImageButton* button) + { + MyGUI::IntSize diff = button->getSize() - button->getRequestedSize(); + button->setSize(button->getRequestedSize()); + + if (button->getAlign().isRight()) + button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0)); + } +} -ScrollWindow::ScrollWindow (MWBase::WindowManager& parWindowManager) : - WindowBase("openmw_scroll.layout", parWindowManager) +namespace MWGui { - getWidget(mTextView, "TextView"); - getWidget(mCloseButton, "CloseButton"); - mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onCloseButtonClicked); + ScrollWindow::ScrollWindow () + : WindowBase("openmw_scroll.layout") + , mTakeButtonShow(true) + , mTakeButtonAllowed(true) + { + getWidget(mTextView, "TextView"); - getWidget(mTakeButton, "TakeButton"); - mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onTakeButtonClicked); + getWidget(mCloseButton, "CloseButton"); + mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onCloseButtonClicked); - center(); -} + getWidget(mTakeButton, "TakeButton"); + mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onTakeButtonClicked); -void ScrollWindow::open (MWWorld::Ptr scroll) -{ - // no 3d sounds because the object could be in a container. - MWBase::Environment::get().getSoundManager()->playSound ("scroll", 1.0, 1.0); + adjustButton(mCloseButton); + adjustButton(mTakeButton); - mScroll = scroll; + center(); + } - MWWorld::LiveCellRef *ref = mScroll.get(); + void ScrollWindow::open (MWWorld::Ptr scroll) + { + // no 3d sounds because the object could be in a container. + MWBase::Environment::get().getSoundManager()->playSound ("scroll", 1.0, 1.0); - BookTextParser parser; - MyGUI::IntSize size = parser.parse(ref->mBase->mText, mTextView, 390); + mScroll = scroll; - if (size.height > mTextView->getSize().height) - mTextView->setCanvasSize(MyGUI::IntSize(410, size.height)); - else - mTextView->setCanvasSize(410, mTextView->getSize().height); + MWWorld::LiveCellRef *ref = mScroll.get(); - mTextView->setViewOffset(MyGUI::IntPoint(0,0)); + BookTextParser parser; + MyGUI::IntSize size = parser.parseScroll(ref->mBase->mText, mTextView, 390); - setTakeButtonShow(true); -} + if (size.height > mTextView->getSize().height) + mTextView->setCanvasSize(MyGUI::IntSize(410, size.height)); + else + mTextView->setCanvasSize(410, mTextView->getSize().height); -void ScrollWindow::setTakeButtonShow(bool show) -{ - mTakeButton->setVisible(show); -} + mTextView->setViewOffset(MyGUI::IntPoint(0,0)); -void ScrollWindow::onCloseButtonClicked (MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getSoundManager()->playSound ("scroll", 1.0, 1.0); + setTakeButtonShow(true); + } - mWindowManager.removeGuiMode(GM_Scroll); -} + void ScrollWindow::setTakeButtonShow(bool show) + { + mTakeButtonShow = show; + mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); + } -void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + void ScrollWindow::setInventoryAllowed(bool allowed) + { + mTakeButtonAllowed = allowed; + mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); + } + + void ScrollWindow::onCloseButtonClicked (MyGUI::Widget* _sender) + { + MWBase::Environment::get().getSoundManager()->playSound ("scroll", 1.0, 1.0); + + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll); + } + + void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender) + { + MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); - MWWorld::ActionTake take(mScroll); - take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + MWWorld::ActionTake take(mScroll); + take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); - mWindowManager.removeGuiMode(GM_Scroll); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll); + } } diff --git a/apps/openmw/mwgui/scrollwindow.hpp b/apps/openmw/mwgui/scrollwindow.hpp index 7932a215b..5feaff7bf 100644 --- a/apps/openmw/mwgui/scrollwindow.hpp +++ b/apps/openmw/mwgui/scrollwindow.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_SCROLLWINDOW_H #define MWGUI_SCROLLWINDOW_H -#include "window_base.hpp" +#include "windowbase.hpp" #include "imagebutton.hpp" #include "../mwworld/ptr.hpp" @@ -11,10 +11,11 @@ namespace MWGui class ScrollWindow : public WindowBase { public: - ScrollWindow (MWBase::WindowManager& parWindowManager); + ScrollWindow (); void open (MWWorld::Ptr scroll); void setTakeButtonShow(bool show); + void setInventoryAllowed(bool allowed); protected: void onCloseButtonClicked (MyGUI::Widget* _sender); @@ -26,6 +27,10 @@ namespace MWGui MyGUI::ScrollView* mTextView; MWWorld::Ptr mScroll; + + bool mTakeButtonShow; + bool mTakeButtonAllowed; + }; } diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index cdfe4d2b6..413171dd4 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -1,23 +1,18 @@ #include "settingswindow.hpp" #include -#include -#include +#include #include #include #include -#include - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/inputmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwrender/renderingmanager.hpp" - #include "confirmationdialog.hpp" namespace @@ -77,12 +72,23 @@ namespace { return (Ogre::Root::getSingleton ().getRenderSystem ()->getName ().find("OpenGL") != std::string::npos) ? "glsl" : "hlsl"; } + + bool cgAvailable () + { + Ogre::Root::PluginInstanceList list = Ogre::Root::getSingleton ().getInstalledPlugins (); + for (Ogre::Root::PluginInstanceList::const_iterator it = list.begin(); it != list.end(); ++it) + { + if ((*it)->getName() == "Cg Program Manager") + return true; + } + return false; + } } namespace MWGui { - SettingsWindow::SettingsWindow(MWBase::WindowManager& parWindowManager) : - WindowBase("openmw_settings_window.layout", parWindowManager) + SettingsWindow::SettingsWindow() : + WindowBase("openmw_settings_window.layout") { getWidget(mOkButton, "OkButton"); getWidget(mSubtitlesButton, "SubtitlesButton"); @@ -109,6 +115,7 @@ namespace MWGui getWidget(mReflectActorsButton, "ReflectActorsButton"); getWidget(mReflectTerrainButton, "ReflectTerrainButton"); getWidget(mShadersButton, "ShadersButton"); + getWidget(mShaderModeButton, "ShaderModeButton"); getWidget(mShadowsEnabledButton, "ShadowsEnabledButton"); getWidget(mShadowsLargeDistance, "ShadowsLargeDistance"); getWidget(mShadowsTextureSize, "ShadowsTextureSize"); @@ -116,22 +123,22 @@ namespace MWGui getWidget(mStaticsShadows, "StaticsShadows"); getWidget(mMiscShadows, "MiscShadows"); getWidget(mShadowsDebug, "ShadowsDebug"); - getWidget(mUnderwaterButton, "UnderwaterButton"); getWidget(mControlsBox, "ControlsBox"); getWidget(mResetControlsButton, "ResetControlsButton"); getWidget(mInvertYButton, "InvertYButton"); getWidget(mUISensitivitySlider, "UISensitivitySlider"); getWidget(mCameraSensitivitySlider, "CameraSensitivitySlider"); - getWidget(mGammaSlider, "GammaSlider"); + getWidget(mRefractionButton, "RefractionButton"); mSubtitlesButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mCrosshairButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mInvertYButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); - mUnderwaterButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mShadersButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShadersToggled); + mShaderModeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShaderModeToggled); mFullscreenButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mWaterShaderButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); + mRefractionButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mReflectObjectsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mReflectTerrainButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mReflectActorsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); @@ -144,7 +151,6 @@ namespace MWGui mViewDistanceSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); mResolutionList->eventListChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onResolutionSelected); mAnisotropySlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); - mGammaSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); mShadowsEnabledButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mShadowsLargeDistance->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); @@ -202,14 +208,6 @@ namespace MWGui getWidget(fovText, "FovText"); fovText->setCaption("Field of View (" + boost::lexical_cast(int(Settings::Manager::getFloat("field of view", "General"))) + ")"); - float gammaVal = (Settings::Manager::getFloat("gamma", "Video")-0.1f)/(3.f-0.1f); - mGammaSlider->setScrollPosition(gammaVal * (mGammaSlider->getScrollRange()-1)); - MyGUI::TextBox* gammaText; - getWidget(gammaText, "GammaText"); - std::stringstream gamma; - gamma << std::setprecision (2) << Settings::Manager::getFloat("gamma", "Video"); - gammaText->setCaption("Gamma (" + gamma.str() + ")"); - float anisotropyVal = Settings::Manager::getInt("anisotropy", "General") / 16.0; mAnisotropySlider->setScrollPosition(anisotropyVal * (mAnisotropySlider->getScrollRange()-1)); std::string tf = Settings::Manager::getString("texture filtering", "General"); @@ -230,10 +228,10 @@ namespace MWGui mReflectObjectsButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect statics", "Water") ? "#{sOn}" : "#{sOff}"); mReflectActorsButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect actors", "Water") ? "#{sOn}" : "#{sOff}"); mReflectTerrainButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect terrain", "Water") ? "#{sOn}" : "#{sOff}"); - mUnderwaterButton->setCaptionWithReplacing(Settings::Manager::getBool("underwater effect", "Water") ? "#{sOn}" : "#{sOff}"); mShadowsTextureSize->setCaption (Settings::Manager::getString ("texture size", "Shadows")); mShadowsLargeDistance->setCaptionWithReplacing(Settings::Manager::getBool("split", "Shadows") ? "#{sOn}" : "#{sOff}"); + mShadowsEnabledButton->setCaptionWithReplacing(Settings::Manager::getBool("enabled", "Shadows") ? "#{sOn}" : "#{sOff}"); mActorShadows->setCaptionWithReplacing(Settings::Manager::getBool("actor shadows", "Shadows") ? "#{sOn}" : "#{sOff}"); mStaticsShadows->setCaptionWithReplacing(Settings::Manager::getBool("statics shadows", "Shadows") ? "#{sOn}" : "#{sOff}"); @@ -250,26 +248,14 @@ namespace MWGui mInvertYButton->setCaptionWithReplacing(Settings::Manager::getBool("invert y axis", "Input") ? "#{sOn}" : "#{sOff}"); - std::string shaders; - if (!Settings::Manager::getBool("shaders", "Objects")) - shaders = "off"; - else - { - shaders = Settings::Manager::getString("shader mode", "General"); - } - mShadersButton->setCaption (shaders); + mShadersButton->setCaptionWithReplacing (Settings::Manager::getBool("shaders", "Objects") ? "#{sOn}" : "#{sOff}"); + mShaderModeButton->setCaption (Settings::Manager::getString("shader mode", "General")); - if (!MWRender::RenderingManager::waterShaderSupported()) - { - mWaterShaderButton->setEnabled(false); - mReflectObjectsButton->setEnabled(false); - mReflectActorsButton->setEnabled(false); - mReflectTerrainButton->setEnabled(false); - } + mRefractionButton->setCaptionWithReplacing (Settings::Manager::getBool("refraction", "Water") ? "#{sOn}" : "#{sOff}"); - if (shaders == "off") + if (!Settings::Manager::getBool("shaders", "Objects")) { - mUnderwaterButton->setEnabled (false); + mRefractionButton->setEnabled(false); mShadowsEnabledButton->setEnabled(false); } @@ -280,7 +266,7 @@ namespace MWGui void SettingsWindow::onOkButtonClicked(MyGUI::Widget* _sender) { - mWindowManager.removeGuiMode(GM_Settings); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Settings); } void SettingsWindow::onResolutionSelected(MyGUI::ListBox* _sender, size_t index) @@ -288,7 +274,7 @@ namespace MWGui if (index == MyGUI::ITEM_NONE) return; - ConfirmationDialog* dialog = mWindowManager.getConfirmationDialog(); + ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); dialog->open("#{sNotifyMessage67}"); dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionAccept); @@ -335,8 +321,8 @@ namespace MWGui void SettingsWindow::onButtonToggled(MyGUI::Widget* _sender) { - std::string on = mWindowManager.getGameSettingString("sOn", "On"); - std::string off = mWindowManager.getGameSettingString("sOff", "On"); + std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString("sOn", "On"); + std::string off = MWBase::Environment::get().getWindowManager()->getGameSettingString("sOff", "On"); bool newState; if (_sender->castType()->getCaption() == on) { @@ -368,7 +354,7 @@ namespace MWGui { std::string msg = "This resolution is not supported in Fullscreen mode. Please select a resolution from the list."; MWBase::Environment::get().getWindowManager()-> - messageBox(msg, std::vector()); + messageBox(msg); _sender->castType()->setCaption(off); } else @@ -385,12 +371,12 @@ namespace MWGui } else { + if (_sender == mVSyncButton) + Settings::Manager::setBool("vsync", "Video", newState); if (_sender == mWaterShaderButton) Settings::Manager::setBool("shader", "Water", newState); - else if (_sender == mUnderwaterButton) - { - Settings::Manager::setBool("underwater effect", "Water", newState); - } + else if (_sender == mRefractionButton) + Settings::Manager::setBool("refraction", "Water", newState); else if (_sender == mReflectObjectsButton) { Settings::Manager::setBool("reflect misc", "Water", newState); @@ -424,33 +410,44 @@ namespace MWGui } } - void SettingsWindow::onShadersToggled(MyGUI::Widget* _sender) + void SettingsWindow::onShaderModeToggled(MyGUI::Widget* _sender) { std::string val = static_cast(_sender)->getCaption(); - if (val == "off") + if (val == "cg") { val = hlslGlsl(); } - else if (val == hlslGlsl()) + else if (cgAvailable ()) val = "cg"; - else - val = "off"; static_cast(_sender)->setCaption(val); - if (val == "off") + Settings::Manager::setString("shader mode", "General", val); + + apply(); + } + + void SettingsWindow::onShadersToggled(MyGUI::Widget* _sender) + { + std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString("sOn", "On"); + std::string off = MWBase::Environment::get().getWindowManager()->getGameSettingString("sOff", "On"); + + std::string val = static_cast(_sender)->getCaption(); + if (val == off) + val = on; + else + val = off; + static_cast(_sender)->setCaptionWithReplacing (val); + + if (val == off) { Settings::Manager::setBool("shaders", "Objects", false); - // water shader not supported with object shaders off - mWaterShaderButton->setCaptionWithReplacing("#{sOff}"); - mUnderwaterButton->setCaptionWithReplacing("#{sOff}"); - mWaterShaderButton->setEnabled(false); - mReflectObjectsButton->setEnabled(false); - mReflectActorsButton->setEnabled(false); - mReflectTerrainButton->setEnabled(false); - mUnderwaterButton->setEnabled(false); - Settings::Manager::setBool("shader", "Water", false); + // refraction needs shaders to display underwater fog + mRefractionButton->setCaptionWithReplacing("#{sOff}"); + mRefractionButton->setEnabled(false); + + Settings::Manager::setBool("refraction", "Water", false); Settings::Manager::setBool("underwater effect", "Water", false); // shadows not supported @@ -461,17 +458,13 @@ namespace MWGui else { Settings::Manager::setBool("shaders", "Objects", true); - Settings::Manager::setString("shader mode", "General", val); // re-enable - if (MWRender::RenderingManager::waterShaderSupported()) - { - mWaterShaderButton->setEnabled(true); - mReflectObjectsButton->setEnabled(true); - mReflectActorsButton->setEnabled(true); - mReflectTerrainButton->setEnabled(true); - } - mUnderwaterButton->setEnabled(true); + mReflectObjectsButton->setEnabled(true); + mReflectActorsButton->setEnabled(true); + mReflectTerrainButton->setEnabled(true); + mRefractionButton->setEnabled(true); + mShadowsEnabledButton->setEnabled(true); } @@ -521,15 +514,6 @@ namespace MWGui fovText->setCaption("Field of View (" + boost::lexical_cast(int((1-val) * sFovMin + val * sFovMax)) + ")"); Settings::Manager::setFloat("field of view", "General", (1-val) * sFovMin + val * sFovMax); } - else if (scroller == mGammaSlider) - { - Settings::Manager::setFloat("gamma", "Video", (1-val) * 0.1f + val * 3.f); - MyGUI::TextBox* gammaText; - getWidget(gammaText, "GammaText"); - std::stringstream gamma; - gamma << std::setprecision (2) << Settings::Manager::getFloat("gamma", "Video"); - gammaText->setCaption("Gamma (" + gamma.str() + ")"); - } else if (scroller == mAnisotropySlider) { mAnisotropyLabel->setCaption("Anisotropy (" + boost::lexical_cast(int(val*16)) + ")"); @@ -567,6 +551,8 @@ namespace MWGui while (mControlsBox->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(mControlsBox->getChildAt(0)); + MWBase::Environment::get().getWindowManager ()->removeStaticMessageBox(); + std::vector actions = MWBase::Environment::get().getInputManager()->getActionSorting (); const int h = 18; @@ -601,7 +587,7 @@ namespace MWGui static_cast(_sender)->setCaptionWithReplacing("#{sNone}"); - MWBase::Environment::get().getWindowManager ()->messageBox ("#{sControlsMenu3}", std::vector()); + MWBase::Environment::get().getWindowManager ()->staticMessageBox ("#{sControlsMenu3}"); MWBase::Environment::get().getWindowManager ()->disallowMouse(); MWBase::Environment::get().getInputManager ()->enableDetectingBindingMode (actionId); @@ -618,7 +604,7 @@ namespace MWGui void SettingsWindow::onResetDefaultBindings(MyGUI::Widget* _sender) { - ConfirmationDialog* dialog = mWindowManager.getConfirmationDialog(); + ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); dialog->open("#{sNotifyMessage66}"); dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindingsAccept); diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index e878d0abe..6dcef2422 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_SETTINGS_H #define MWGUI_SETTINGS_H -#include "window_base.hpp" +#include "windowbase.hpp" namespace MWGui { @@ -13,7 +13,7 @@ namespace MWGui class SettingsWindow : public WindowBase { public: - SettingsWindow(MWBase::WindowManager& parWindowManager); + SettingsWindow(); virtual void open(); @@ -40,7 +40,6 @@ namespace MWGui MyGUI::Button* mFPSButton; MyGUI::ScrollBar* mViewDistanceSlider; MyGUI::ScrollBar* mFOVSlider; - MyGUI::ScrollBar* mGammaSlider; MyGUI::ScrollBar* mAnisotropySlider; MyGUI::Button* mTextureFilteringButton; MyGUI::TextBox* mAnisotropyLabel; @@ -50,7 +49,8 @@ namespace MWGui MyGUI::Button* mReflectActorsButton; MyGUI::Button* mReflectTerrainButton; MyGUI::Button* mShadersButton; - MyGUI::Button* mUnderwaterButton; + MyGUI::Button* mShaderModeButton; + MyGUI::Button* mRefractionButton; MyGUI::Button* mShadowsEnabledButton; MyGUI::Button* mShadowsLargeDistance; @@ -84,6 +84,7 @@ namespace MWGui void onResolutionCancel(); void onShadersToggled(MyGUI::Widget* _sender); + void onShaderModeToggled(MyGUI::Widget* _sender); void onShadowTextureSize(MyGUI::Widget* _sender); void onRebindAction(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwgui/sortfilteritemmodel.cpp b/apps/openmw/mwgui/sortfilteritemmodel.cpp new file mode 100644 index 000000000..3cf514dc5 --- /dev/null +++ b/apps/openmw/mwgui/sortfilteritemmodel.cpp @@ -0,0 +1,171 @@ +#include "sortfilteritemmodel.hpp" + +#include "../mwworld/class.hpp" + +namespace +{ + bool compareType(const std::string& type1, const std::string& type2) + { + // this defines the sorting order of types. types that are first in the vector appear before other types. + std::vector mapping; + mapping.push_back( typeid(ESM::Weapon).name() ); + mapping.push_back( typeid(ESM::Armor).name() ); + mapping.push_back( typeid(ESM::Clothing).name() ); + mapping.push_back( typeid(ESM::Potion).name() ); + mapping.push_back( typeid(ESM::Ingredient).name() ); + mapping.push_back( typeid(ESM::Apparatus).name() ); + mapping.push_back( typeid(ESM::Book).name() ); + mapping.push_back( typeid(ESM::Light).name() ); + mapping.push_back( typeid(ESM::Miscellaneous).name() ); + mapping.push_back( typeid(ESM::Lockpick).name() ); + mapping.push_back( typeid(ESM::Repair).name() ); + mapping.push_back( typeid(ESM::Probe).name() ); + + assert( std::find(mapping.begin(), mapping.end(), type1) != mapping.end() ); + assert( std::find(mapping.begin(), mapping.end(), type2) != mapping.end() ); + + return std::find(mapping.begin(), mapping.end(), type1) < std::find(mapping.begin(), mapping.end(), type2); + } + + bool compare (const MWGui::ItemStack& left, const MWGui::ItemStack& right) + { + if (left.mType != right.mType) + return left.mType < right.mType; + + if (left.mBase.getTypeName() == right.mBase.getTypeName()) + { + int cmp = MWWorld::Class::get(left.mBase).getName(left.mBase).compare( + MWWorld::Class::get(right.mBase).getName(right.mBase)); + return cmp < 0; + } + else + return compareType(left.mBase.getTypeName(), right.mBase.getTypeName()); + } +} + +namespace MWGui +{ + + SortFilterItemModel::SortFilterItemModel(ItemModel *sourceModel) + : mCategory(Category_All) + , mShowEquipped(true) + , mFilter(0) + { + mSourceModel = sourceModel; + } + + void SortFilterItemModel::addDragItem (const MWWorld::Ptr& dragItem, size_t count) + { + mDragItems.push_back(std::make_pair(dragItem, count)); + } + + void SortFilterItemModel::clearDragItems() + { + mDragItems.clear(); + } + + bool SortFilterItemModel::filterAccepts (const ItemStack& item) + { + MWWorld::Ptr base = item.mBase; + + if (item.mType == ItemStack::Type_Equipped && !mShowEquipped) + return false; + + int category = 0; + if (base.getTypeName() == typeid(ESM::Armor).name() + || base.getTypeName() == typeid(ESM::Clothing).name()) + category = Category_Apparel; + else if (base.getTypeName() == typeid(ESM::Weapon).name()) + category = Category_Weapon; + else if (base.getTypeName() == typeid(ESM::Ingredient).name() + || base.getTypeName() == typeid(ESM::Potion).name()) + category = Category_Magic; + else if (base.getTypeName() == typeid(ESM::Miscellaneous).name() + || base.getTypeName() == typeid(ESM::Ingredient).name() + || base.getTypeName() == typeid(ESM::Repair).name() + || base.getTypeName() == typeid(ESM::Lockpick).name() + || base.getTypeName() == typeid(ESM::Light).name() + || base.getTypeName() == typeid(ESM::Apparatus).name() + || base.getTypeName() == typeid(ESM::Book).name() + || base.getTypeName() == typeid(ESM::Probe).name()) + category = Category_Misc; + + if (item.mFlags & ItemStack::Flag_Enchanted) + category |= Category_Magic; + + if (!(category & mCategory)) + return false; + + if ((mFilter & Filter_OnlyIngredients) && base.getTypeName() != typeid(ESM::Ingredient).name()) + return false; + if ((mFilter & Filter_OnlyEnchanted) && !(item.mFlags & ItemStack::Flag_Enchanted)) + return false; + if ((mFilter & Filter_OnlyChargedSoulstones) && (base.getTypeName() != typeid(ESM::Miscellaneous).name() + || base.getCellRef().mSoul == "")) + return false; + if ((mFilter & Filter_OnlyEnchantable) && (item.mFlags & ItemStack::Flag_Enchanted + || (base.getTypeName() != typeid(ESM::Armor).name() + && base.getTypeName() != typeid(ESM::Clothing).name() + && base.getTypeName() != typeid(ESM::Weapon).name() + && base.getTypeName() != typeid(ESM::Book).name()))) + return false; + if ((mFilter & Filter_OnlyEnchantable) && base.getTypeName() == typeid(ESM::Book).name() + && !base.get()->mBase->mData.mIsScroll) + return false; + + return true; + } + + ItemStack SortFilterItemModel::getItem (ModelIndex index) + { + if (index < 0) + throw std::runtime_error("Invalid index supplied"); + if (mItems.size() <= static_cast(index)) + throw std::runtime_error("Item index out of range"); + return mItems[index]; + } + + size_t SortFilterItemModel::getItemCount() + { + return mItems.size(); + } + + void SortFilterItemModel::setCategory (int category) + { + mCategory = category; + } + + void SortFilterItemModel::setFilter (int filter) + { + mFilter = filter; + } + + void SortFilterItemModel::update() + { + mSourceModel->update(); + + size_t count = mSourceModel->getItemCount(); + + mItems.clear(); + for (size_t i=0; igetItem(i); + + for (std::vector >::iterator it = mDragItems.begin(); it != mDragItems.end(); ++it) + { + if (item.mBase == it->first) + { + if (item.mCount < it->second) + throw std::runtime_error("Dragging more than present in the model"); + item.mCount -= it->second; + } + } + + if (item.mCount > 0 && filterAccepts(item)) + mItems.push_back(item); + } + + std::sort(mItems.begin(), mItems.end(), compare); + } + +} diff --git a/apps/openmw/mwgui/sortfilteritemmodel.hpp b/apps/openmw/mwgui/sortfilteritemmodel.hpp new file mode 100644 index 000000000..c7feaa3b9 --- /dev/null +++ b/apps/openmw/mwgui/sortfilteritemmodel.hpp @@ -0,0 +1,53 @@ +#ifndef MWGUI_SORT_FILTER_ITEM_MODEL_H +#define MWGUI_SORT_FILTER_ITEM_MODEL_H + +#include "itemmodel.hpp" + +namespace MWGui +{ + + class SortFilterItemModel : public ProxyItemModel + { + public: + SortFilterItemModel (ItemModel* sourceModel); + + virtual void update(); + + bool filterAccepts (const ItemStack& item); + + virtual ItemStack getItem (ModelIndex index); + virtual size_t getItemCount(); + + /// Dragged items are not displayed. + void addDragItem (const MWWorld::Ptr& dragItem, size_t count); + void clearDragItems(); + + void setCategory (int category); + void setFilter (int filter); + void setShowEquipped (bool show) { mShowEquipped = show; } + + static const int Category_Weapon = (1<<1); + static const int Category_Apparel = (1<<2); + static const int Category_Misc = (1<<3); + static const int Category_Magic = (1<<4); + static const int Category_All = 255; + + static const int Filter_OnlyIngredients = (1<<0); + static const int Filter_OnlyEnchanted = (1<<1); + static const int Filter_OnlyEnchantable = (1<<2); + static const int Filter_OnlyChargedSoulstones = (1<<3); + + + private: + std::vector mItems; + + std::vector > mDragItems; + + int mCategory; + int mFilter; + bool mShowEquipped; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/soulgemdialog.cpp b/apps/openmw/mwgui/soulgemdialog.cpp new file mode 100644 index 000000000..b95eec0b6 --- /dev/null +++ b/apps/openmw/mwgui/soulgemdialog.cpp @@ -0,0 +1,33 @@ +#include "soulgemdialog.hpp" + +#include "../mwbase/environment.hpp" + +#include "messagebox.hpp" + +namespace MWGui +{ + + void SoulgemDialog::show(const MWWorld::Ptr &soulgem) + { + mSoulgem = soulgem; + std::vector buttons; + buttons.push_back("#{sRechargeEnchantment}"); + buttons.push_back("#{sMake Enchantment}"); + mManager->createInteractiveMessageBox("#{sDoYouWantTo}", buttons); + mManager->eventButtonPressed += MyGUI::newDelegate(this, &SoulgemDialog::onButtonPressed); + } + + void SoulgemDialog::onButtonPressed(int button) + { + if (button == 0) + { + /// \todo show recharge enchanted item dialog here + } + else + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting); + MWBase::Environment::get().getWindowManager()->startSelfEnchanting(mSoulgem); + } + } + +} diff --git a/apps/openmw/mwgui/soulgemdialog.hpp b/apps/openmw/mwgui/soulgemdialog.hpp new file mode 100644 index 000000000..9aea1f339 --- /dev/null +++ b/apps/openmw/mwgui/soulgemdialog.hpp @@ -0,0 +1,28 @@ +#ifndef OPENMW_MWGUI_SOULGEMDIALOG_H +#define OPENMW_MWGUI_SOULGEMDIALOG_H + +#include "../mwworld/ptr.hpp" + +namespace MWGui +{ + + class MessageBoxManager; + + class SoulgemDialog + { + public: + SoulgemDialog (MessageBoxManager* manager) + : mManager(manager) {} + + void show (const MWWorld::Ptr& soulgem); + + void onButtonPressed(int button); + + private: + MessageBoxManager* mManager; + MWWorld::Ptr mSoulgem; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 11f090494..a7fcfdd02 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -1,7 +1,5 @@ #include "spellbuyingwindow.hpp" -#include - #include #include "../mwbase/environment.hpp" @@ -11,9 +9,8 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/player.hpp" -#include "../mwworld/manualref.hpp" +#include "../mwworld/class.hpp" -#include "../mwmechanics/spells.hpp" #include "../mwmechanics/creaturestats.hpp" #include "inventorywindow.hpp" @@ -23,8 +20,8 @@ namespace MWGui { const int SpellBuyingWindow::sLineHeight = 18; - SpellBuyingWindow::SpellBuyingWindow(MWBase::WindowManager& parWindowManager) : - WindowBase("openmw_spell_buying_window.layout", parWindowManager) + SpellBuyingWindow::SpellBuyingWindow() : + WindowBase("openmw_spell_buying_window.layout") , mCurrentY(0) , mLastPos(0) { @@ -32,20 +29,9 @@ namespace MWGui getWidget(mCancelButton, "CancelButton"); getWidget(mPlayerGold, "PlayerGold"); - getWidget(mSelect, "Select"); - getWidget(mSpells, "Spells"); getWidget(mSpellsView, "SpellsView"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellBuyingWindow::onCancelButtonClicked); - - mSpells->setCoord(450/2-mSpells->getTextSize().width/2, - mSpells->getTop(), - mSpells->getTextSize().width, - mSpells->getHeight()); - mSelect->setCoord(8, - mSelect->getTop(), - mSelect->getTextSize().width, - mSelect->getHeight()); } void SpellBuyingWindow::addSpell(const std::string& spellId) @@ -59,7 +45,7 @@ namespace MWGui MyGUI::Button* toAdd = mSpellsView->createWidget( - (price>mWindowManager.getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", + (price>MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", 0, mCurrentY, 200, @@ -94,9 +80,6 @@ namespace MWGui mPtr = actor; clearSpells(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - - MWMechanics::Spells& playerSpells = MWWorld::Class::get (player).getCreatureStats (player).getSpells(); MWMechanics::Spells& merchantSpells = MWWorld::Class::get (actor).getCreatureStats (actor).getSpells(); for (MWMechanics::Spells::TIterator iter = merchantSpells.begin(); iter!=merchantSpells.end(); ++iter) @@ -107,8 +90,8 @@ namespace MWGui if (spell->mData.mType!=ESM::Spell::ST_Spell) continue; // don't try to sell diseases, curses or powers - if (std::find (playerSpells.begin(), playerSpells.end(), *iter)!=playerSpells.end()) - continue; // we have that spell already + if (playerHasSpell(iter->first)) + continue; addSpell (iter->first); } @@ -118,17 +101,29 @@ namespace MWGui mSpellsView->setCanvasSize (MyGUI::IntSize(mSpellsView->getWidth(), std::max(mSpellsView->getHeight(), mCurrentY))); } + bool SpellBuyingWindow::playerHasSpell(const std::string &id) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::Spells& playerSpells = MWWorld::Class::get (player).getCreatureStats (player).getSpells(); + for (MWMechanics::Spells::TIterator it = playerSpells.begin(); it != playerSpells.end(); ++it) + { + if (Misc::StringUtils::ciEqual(id, it->first)) + return true; + } + return false; + } + void SpellBuyingWindow::onSpellButtonClick(MyGUI::Widget* _sender) { int price = *_sender->getUserData(); - if (mWindowManager.getInventoryWindow()->getPlayerGold()>=price) + if (MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()>=price) { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); spells.add (mSpellsWidgetMap.find(_sender)->second); - mWindowManager.getTradeWindow()->addOrRemoveGold(-price); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-price); startSpellBuying(mPtr); MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); @@ -137,12 +132,12 @@ namespace MWGui void SpellBuyingWindow::onCancelButtonClicked(MyGUI::Widget* _sender) { - mWindowManager.removeGuiMode(GM_SpellBuying); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_SpellBuying); } void SpellBuyingWindow::updateLabels() { - mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(mWindowManager.getInventoryWindow()->getPlayerGold())); + mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); mPlayerGold->setCoord(8, mPlayerGold->getTop(), mPlayerGold->getTextSize().width, @@ -152,8 +147,8 @@ namespace MWGui void SpellBuyingWindow::onReferenceUnavailable() { // remove both Spells and Dialogue (since you always trade with the NPC/creature that you have previously talked to) - mWindowManager.removeGuiMode(GM_SpellBuying); - mWindowManager.removeGuiMode(GM_Dialogue); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_SpellBuying); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); } void SpellBuyingWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) diff --git a/apps/openmw/mwgui/spellbuyingwindow.hpp b/apps/openmw/mwgui/spellbuyingwindow.hpp index 1d0ac28e0..f7ea54c89 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.hpp +++ b/apps/openmw/mwgui/spellbuyingwindow.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_SpellBuyingWINDOW_H #define MWGUI_SpellBuyingWINDOW_H -#include "window_base.hpp" +#include "windowbase.hpp" #include "referenceinterface.hpp" namespace MyGUI @@ -21,15 +21,13 @@ namespace MWGui class SpellBuyingWindow : public ReferenceInterface, public WindowBase { public: - SpellBuyingWindow(MWBase::WindowManager& parWindowManager); + SpellBuyingWindow(); void startSpellBuying(const MWWorld::Ptr& actor); protected: MyGUI::Button* mCancelButton; MyGUI::TextBox* mPlayerGold; - MyGUI::TextBox* mSpells; - MyGUI::TextBox* mSelect; MyGUI::ScrollView* mSpellsView; @@ -47,6 +45,8 @@ namespace MWGui void updateLabels(); virtual void onReferenceUnavailable(); + + bool playerHasSpell (const std::string& id); }; } diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 839586452..45cf1b0aa 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -4,22 +4,14 @@ #include "../mwbase/windowmanager.hpp" -#include "../mwbase/world.hpp" -#include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwworld/esmstore.hpp" #include "../mwworld/player.hpp" -#include "../mwworld/class.hpp" -#include "../mwmechanics/spells.hpp" -#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spellsuccess.hpp" - #include "tooltips.hpp" -#include "widgets.hpp" #include "class.hpp" #include "inventorywindow.hpp" #include "tradewindow.hpp" @@ -40,8 +32,8 @@ namespace namespace MWGui { - EditEffectDialog::EditEffectDialog(MWBase::WindowManager &parWindowManager) - : WindowModal("openmw_edit_effect.layout", parWindowManager) + EditEffectDialog::EditEffectDialog() + : WindowModal("openmw_edit_effect.layout") , mEditing(false) { getWidget(mCancelButton, "CancelButton"); @@ -72,6 +64,7 @@ namespace MWGui mMagnitudeMaxSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onMagnitudeMaxChanged); mDurationSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onDurationChanged); mAreaSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onAreaChanged); + constantEffect=false; } void EditEffectDialog::open() @@ -164,7 +157,7 @@ namespace MWGui mMagnitudeBox->setVisible (true); curY += mMagnitudeBox->getSize().height; } - if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) + if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)&&constantEffect==false) { mDurationBox->setPosition(mDurationBox->getPosition().left, curY); mDurationBox->setVisible (true); @@ -273,9 +266,9 @@ namespace MWGui // ------------------------------------------------------------------------------------------------ - SpellCreationDialog::SpellCreationDialog(MWBase::WindowManager &parWindowManager) - : WindowBase("openmw_spellcreation_dialog.layout", parWindowManager) - , EffectEditorBase(parWindowManager) + SpellCreationDialog::SpellCreationDialog() + : WindowBase("openmw_spellcreation_dialog.layout") + , EffectEditorBase() { getWidget(mNameEdit, "NameEdit"); getWidget(mMagickaCost, "MagickaCost"); @@ -302,38 +295,38 @@ namespace MWGui void SpellCreationDialog::onCancelButtonClicked (MyGUI::Widget* sender) { - mWindowManager.removeGuiMode (MWGui::GM_SpellCreation); + MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_SpellCreation); } void SpellCreationDialog::onBuyButtonClicked (MyGUI::Widget* sender) { if (mEffects.size() <= 0) { - mWindowManager.messageBox ("#{sNotifyMessage30}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage30}"); return; } if (mNameEdit->getCaption () == "") { - mWindowManager.messageBox ("#{sNotifyMessage10}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage10}"); return; } if (mMagickaCost->getCaption() == "0") { - mWindowManager.messageBox ("#{sEnchantmentMenu8}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sEnchantmentMenu8}"); return; } - if (boost::lexical_cast(mPriceLabel->getCaption()) > mWindowManager.getInventoryWindow()->getPlayerGold()) + if (boost::lexical_cast(mPriceLabel->getCaption()) > MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) { - mWindowManager.messageBox ("#{sNotifyMessage18}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage18}"); return; } mSpell.mName = mNameEdit->getCaption(); - mWindowManager.getTradeWindow()->addOrRemoveGold(-boost::lexical_cast(mPriceLabel->getCaption())); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-boost::lexical_cast(mPriceLabel->getCaption())); MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); @@ -346,7 +339,7 @@ namespace MWGui MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); - mWindowManager.removeGuiMode (GM_SpellCreation); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_SpellCreation); } void SpellCreationDialog::open() @@ -356,8 +349,8 @@ namespace MWGui void SpellCreationDialog::onReferenceUnavailable () { - mWindowManager.removeGuiMode (GM_Dialogue); - mWindowManager.removeGuiMode (GM_SpellCreation); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_SpellCreation); } void SpellCreationDialog::notifyEffectsChanged () @@ -411,8 +404,8 @@ namespace MWGui // ------------------------------------------------------------------------------------------------ - EffectEditorBase::EffectEditorBase(MWBase::WindowManager& parWindowManager) - : mAddEffectDialog(parWindowManager) + EffectEditorBase::EffectEditorBase() + : mAddEffectDialog() , mSelectAttributeDialog(NULL) , mSelectSkillDialog(NULL) { @@ -454,10 +447,13 @@ namespace MWGui mAvailableEffectsList->clear (); + int i=0; for (std::vector::const_iterator it = knownEffects.begin(); it != knownEffects.end(); ++it) { mAvailableEffectsList->addItem(MWBase::Environment::get().getWorld ()->getStore ().get().find( ESM::MagicEffect::effectIdToString (*it))->getString()); + mButtonMapping[i] = *it; + ++i; } mAvailableEffectsList->adjustSize (); @@ -466,7 +462,6 @@ namespace MWGui std::string name = MWBase::Environment::get().getWorld ()->getStore ().get().find( ESM::MagicEffect::effectIdToString (*it))->getString(); MyGUI::Widget* w = mAvailableEffectsList->getItemWidget(name); - w->setUserData(*it); ToolTips::createMagicEffectToolTip (w, *it); } @@ -514,17 +509,18 @@ namespace MWGui { if (mEffects.size() >= 8) { - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage28}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage28}"); return; } - short effectId = *sender->getUserData(); + int buttonId = *sender->getUserData(); + short effectId = mButtonMapping[buttonId]; for (std::vector::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) { if (it->mEffectID == effectId) { - MWBase::Environment::get().getWindowManager()->messageBox ("#{sOnetypeEffectMessage}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sOnetypeEffectMessage}"); return; } } @@ -537,7 +533,7 @@ namespace MWGui if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill) { delete mSelectSkillDialog; - mSelectSkillDialog = new SelectSkillDialog(*MWBase::Environment::get().getWindowManager ()); + mSelectSkillDialog = new SelectSkillDialog(); mSelectSkillDialog->eventCancel += MyGUI::newDelegate(this, &SpellCreationDialog::onAttributeOrSkillCancel); mSelectSkillDialog->eventItemSelected += MyGUI::newDelegate(this, &SpellCreationDialog::onSelectSkill); mSelectSkillDialog->setVisible (true); @@ -545,7 +541,7 @@ namespace MWGui else if (effect->mData.mFlags & ESM::MagicEffect::TargetAttribute) { delete mSelectAttributeDialog; - mSelectAttributeDialog = new SelectAttributeDialog(*MWBase::Environment::get().getWindowManager ()); + mSelectAttributeDialog = new SelectAttributeDialog(); mSelectAttributeDialog->eventCancel += MyGUI::newDelegate(this, &SpellCreationDialog::onAttributeOrSkillCancel); mSelectAttributeDialog->eventItemSelected += MyGUI::newDelegate(this, &SpellCreationDialog::onSelectAttribute); mSelectAttributeDialog->setVisible (true); @@ -597,7 +593,6 @@ namespace MWGui Widgets::MWSpellEffectPtr effect = button->createWidget("MW_EffectImage", MyGUI::IntCoord(0,0,0,24), MyGUI::Align::Default); effect->setNeedMouseFocus (false); - effect->setWindowManager (MWBase::Environment::get().getWindowManager ()); effect->setSpellEffect (params); effect->setSize(effect->getRequestedWidth (), 24); diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index 4d27ec1c6..5ad306fbe 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_SPELLCREATION_H #define MWGUI_SPELLCREATION_H -#include "window_base.hpp" +#include "windowbase.hpp" #include "referenceinterface.hpp" #include "list.hpp" #include "widgets.hpp" @@ -15,7 +15,7 @@ namespace MWGui class EditEffectDialog : public WindowModal { public: - EditEffectDialog(MWBase::WindowManager& parWindowManager); + EditEffectDialog(); virtual void open(); @@ -24,7 +24,7 @@ namespace MWGui void newEffect (const ESM::MagicEffect* effect); void editEffect (ESM::ENAMstruct effect); - + bool constantEffect; typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Effect; EventHandle_Effect eventEffectAdded; @@ -69,7 +69,6 @@ namespace MWGui void onMagnitudeMaxChanged (MyGUI::ScrollBar* sender, size_t pos); void onDurationChanged (MyGUI::ScrollBar* sender, size_t pos); void onAreaChanged (MyGUI::ScrollBar* sender, size_t pos); - void setMagicEffect(const ESM::MagicEffect* effect); void updateBoxes(); @@ -84,10 +83,12 @@ namespace MWGui class EffectEditorBase { public: - EffectEditorBase(MWBase::WindowManager& parWindowManager); + EffectEditorBase(); protected: + std::map mButtonMapping; // maps button ID to effect ID + Widgets::MWList* mAvailableEffectsList; MyGUI::ScrollView* mUsedEffectsView; @@ -122,7 +123,7 @@ namespace MWGui class SpellCreationDialog : public WindowBase, public ReferenceInterface, public EffectEditorBase { public: - SpellCreationDialog(MWBase::WindowManager& parWindowManager); + SpellCreationDialog(); virtual void open(); diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp new file mode 100644 index 000000000..e762cc610 --- /dev/null +++ b/apps/openmw/mwgui/spellicons.cpp @@ -0,0 +1,289 @@ +#include "spellicons.hpp" + +#include + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/inventorystore.hpp" + +#include "../mwmechanics/creaturestats.hpp" + +#include "tooltips.hpp" + + +namespace MWGui +{ + + void SpellIcons::updateWidgets(MyGUI::Widget *parent, bool adjustSize) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + const MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); + + std::map > effects; + + // add permanent item enchantments + MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); + for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) + { + MWWorld::ContainerStoreIterator it = store.getSlot(slot); + if (it == store.end()) + continue; + std::string enchantment = MWWorld::Class::get(*it).getEnchantment(*it); + if (enchantment.empty()) + continue; + const ESM::Enchantment* enchant = MWBase::Environment::get().getWorld()->getStore().get().find(enchantment); + if (enchant->mData.mType != ESM::Enchantment::ConstantEffect) + continue; + + const ESM::EffectList& list = enchant->mEffects; + for (std::vector::const_iterator effectIt = list.mList.begin(); + effectIt != list.mList.end(); ++effectIt) + { + const ESM::MagicEffect* magicEffect = + MWBase::Environment::get().getWorld ()->getStore ().get().find(effectIt->mEffectID); + + MagicEffectInfo effectInfo; + effectInfo.mSource = MWWorld::Class::get(*it).getName(*it); + effectInfo.mKey = MWMechanics::EffectKey (effectIt->mEffectID); + if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) + effectInfo.mKey.mArg = effectIt->mSkill; + else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) + effectInfo.mKey.mArg = effectIt->mAttribute; + // just using the min magnitude here, permanent enchantments with a random magnitude just wouldn't make any sense + effectInfo.mMagnitude = effectIt->mMagnMin; + effectInfo.mPermanent = true; + effects[effectIt->mEffectID].push_back (effectInfo); + } + } + + // add permanent spells + const MWMechanics::Spells& spells = stats.getSpells(); + for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it) + { + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(it->first); + + // these are the spell types that are permanently in effect + if (!(spell->mData.mType == ESM::Spell::ST_Ability) + && !(spell->mData.mType == ESM::Spell::ST_Disease) + && !(spell->mData.mType == ESM::Spell::ST_Curse) + && !(spell->mData.mType == ESM::Spell::ST_Blight)) + continue; + const ESM::EffectList& list = spell->mEffects; + for (std::vector::const_iterator effectIt = list.mList.begin(); + effectIt != list.mList.end(); ++effectIt) + { + const ESM::MagicEffect* magicEffect = + MWBase::Environment::get().getWorld ()->getStore ().get().find(effectIt->mEffectID); + MagicEffectInfo effectInfo; + effectInfo.mSource = getSpellDisplayName (it->first); + effectInfo.mKey = MWMechanics::EffectKey (effectIt->mEffectID); + if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) + effectInfo.mKey.mArg = effectIt->mSkill; + else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) + effectInfo.mKey.mArg = effectIt->mAttribute; + // just using the min magnitude here, permanent spells with a random magnitude just wouldn't make any sense + effectInfo.mMagnitude = effectIt->mMagnMin; + effectInfo.mPermanent = true; + + effects[effectIt->mEffectID].push_back (effectInfo); + } + } + + // add lasting effect spells/potions etc + const MWMechanics::ActiveSpells::TContainer& activeSpells = stats.getActiveSpells().getActiveSpells(); + for (MWMechanics::ActiveSpells::TContainer::const_iterator it = activeSpells.begin(); + it != activeSpells.end(); ++it) + { + const ESM::EffectList& list = getSpellEffectList(it->first); + + float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor(); + + for (std::vector::const_iterator effectIt = list.mList.begin(); + effectIt != list.mList.end(); ++effectIt) + { + const ESM::MagicEffect* magicEffect = + MWBase::Environment::get().getWorld ()->getStore ().get().find(effectIt->mEffectID); + + MagicEffectInfo effectInfo; + effectInfo.mSource = getSpellDisplayName (it->first); + effectInfo.mKey = MWMechanics::EffectKey (effectIt->mEffectID); + if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) + effectInfo.mKey.mArg = effectIt->mSkill; + else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) + effectInfo.mKey.mArg = effectIt->mAttribute; + effectInfo.mMagnitude = effectIt->mMagnMin + (effectIt->mMagnMax-effectIt->mMagnMin) * it->second.second; + effectInfo.mRemainingTime = effectIt->mDuration + + (it->second.first - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale; + + // ingredients need special casing for their magnitude / duration + /// \todo duplicated from ActiveSpells, helper function? + if (MWBase::Environment::get().getWorld()->getStore().get().search (it->first)) + { + effectInfo.mRemainingTime = effectIt->mDuration * it->second.second + + (it->second.first - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale; + + effectInfo.mMagnitude = static_cast (0.05*it->second.second / (0.1 * magicEffect->mData.mBaseCost)); + } + + + effects[effectIt->mEffectID].push_back (effectInfo); + } + } + + int w=2; + + if (adjustSize) + { + int s = effects.size() * 16+4; + if (!effects.size()) + s = 0; + int diff = parent->getWidth() - s; + parent->setSize(s, parent->getHeight()); + parent->setPosition(parent->getLeft()+diff, parent->getTop()); + } + + + for (std::map >::const_iterator it = effects.begin(); it != effects.end(); ++it) + { + MyGUI::ImageBox* image; + if (mWidgetMap.find(it->first) == mWidgetMap.end()) + image = parent->createWidget + ("ImageBox", MyGUI::IntCoord(w,2,16,16), MyGUI::Align::Default); + else + image = mWidgetMap[it->first]; + mWidgetMap[it->first] = image; + image->setPosition(w,2); + image->setVisible(true); + + const ESM::MagicEffect* effect = + MWBase::Environment::get().getWorld ()->getStore ().get().find(it->first); + + std::string icon = effect->mIcon; + icon[icon.size()-3] = 'd'; + icon[icon.size()-2] = 'd'; + icon[icon.size()-1] = 's'; + icon = "icons\\" + icon; + + image->setImageTexture(icon); + w += 16; + + float remainingDuration = 0; + + std::string sourcesDescription; + + const float fadeTime = 5.f; + + for (std::vector::const_iterator effectIt = it->second.begin(); + effectIt != it->second.end(); ++effectIt) + { + if (effectIt != it->second.begin()) + sourcesDescription += "\n"; + + // if at least one of the effect sources is permanent, the effect will never wear off + if (effectIt->mPermanent) + remainingDuration = fadeTime; + else + remainingDuration = std::max(remainingDuration, effectIt->mRemainingTime); + + sourcesDescription += effectIt->mSource; + + if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill) + sourcesDescription += " (" + + MWBase::Environment::get().getWindowManager()->getGameSettingString( + ESM::Skill::sSkillNameIds[effectIt->mKey.mArg], "") + ")"; + if (effect->mData.mFlags & ESM::MagicEffect::TargetAttribute) + sourcesDescription += " (" + + MWBase::Environment::get().getWindowManager()->getGameSettingString( + ESM::Attribute::sGmstAttributeIds[effectIt->mKey.mArg], "") + ")"; + + if (!(effect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) + { + std::string pt = MWBase::Environment::get().getWindowManager()->getGameSettingString("spoint", ""); + std::string pts = MWBase::Environment::get().getWindowManager()->getGameSettingString("spoints", ""); + + sourcesDescription += ": " + boost::lexical_cast(effectIt->mMagnitude); + sourcesDescription += " " + ((effectIt->mMagnitude > 1) ? pts : pt); + } + } + + std::string name = ESM::MagicEffect::effectIdToString (it->first); + + ToolTipInfo tooltipInfo; + tooltipInfo.caption = "#{" + name + "}"; + tooltipInfo.icon = effect->mIcon; + tooltipInfo.text = sourcesDescription; + tooltipInfo.imageSize = 16; + tooltipInfo.wordWrap = false; + + image->setUserData(tooltipInfo); + image->setUserString("ToolTipType", "ToolTipInfo"); + + // Fade out during the last 5 seconds + image->setAlpha(std::min(remainingDuration/fadeTime, 1.f)); + } + + // hide inactive effects + for (std::map::iterator it = mWidgetMap.begin(); it != mWidgetMap.end(); ++it) + { + if (effects.find(it->first) == effects.end()) + it->second->setVisible(false); + } + + } + + + std::string SpellIcons::getSpellDisplayName (const std::string& id) + { + if (const ESM::Spell *spell = + MWBase::Environment::get().getWorld()->getStore().get().search (id)) + return spell->mName; + + if (const ESM::Potion *potion = + MWBase::Environment::get().getWorld()->getStore().get().search (id)) + return potion->mName; + + if (const ESM::Ingredient *ingredient = + MWBase::Environment::get().getWorld()->getStore().get().search (id)) + return ingredient->mName; + + throw std::runtime_error ("ID " + id + " has no display name"); + } + + ESM::EffectList SpellIcons::getSpellEffectList (const std::string& id) + { + if (const ESM::Spell *spell = + MWBase::Environment::get().getWorld()->getStore().get().search (id)) + return spell->mEffects; + + if (const ESM::Potion *potion = + MWBase::Environment::get().getWorld()->getStore().get().search (id)) + return potion->mEffects; + + if (const ESM::Ingredient *ingredient = + MWBase::Environment::get().getWorld()->getStore().get().search (id)) + { + const ESM::MagicEffect *magicEffect = + MWBase::Environment::get().getWorld()->getStore().get().find ( + ingredient->mData.mEffectID[0]); + + ESM::ENAMstruct effect; + effect.mEffectID = ingredient->mData.mEffectID[0]; + effect.mSkill = ingredient->mData.mSkills[0]; + effect.mAttribute = ingredient->mData.mAttributes[0]; + effect.mRange = 0; + effect.mArea = 0; + effect.mDuration = magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration ? 0 : 1; + effect.mMagnMin = 1; + effect.mMagnMax = 1; + ESM::EffectList result; + result.mList.push_back (effect); + return result; + } + throw std::runtime_error("ID " + id + " does not have effects"); + } + +} diff --git a/apps/openmw/mwgui/spellicons.hpp b/apps/openmw/mwgui/spellicons.hpp new file mode 100644 index 000000000..af600e347 --- /dev/null +++ b/apps/openmw/mwgui/spellicons.hpp @@ -0,0 +1,47 @@ +#ifndef MWGUI_SPELLICONS_H +#define MWGUI_SPELLICONS_H + +#include + +#include "../mwmechanics/magiceffects.hpp" + +namespace MyGUI +{ + class Widget; + class ImageBox; +} +namespace ESM +{ + struct ENAMstruct; + struct EffectList; +} + +namespace MWGui +{ + + // information about a single magic effect source as required for display in the tooltip + struct MagicEffectInfo + { + MagicEffectInfo() : mPermanent(false) {} + std::string mSource; // display name for effect source (e.g. potion name) + MWMechanics::EffectKey mKey; + int mMagnitude; + float mRemainingTime; + bool mPermanent; // the effect is permanent + }; + + class SpellIcons + { + public: + void updateWidgets(MyGUI::Widget* parent, bool adjustSize); + + private: + std::string getSpellDisplayName (const std::string& id); + ESM::EffectList getSpellEffectList (const std::string& id); + + std::map mWidgetMap; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 47e1d739a..d5e3abc11 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -1,24 +1,17 @@ #include "spellwindow.hpp" -#include #include #include -#include "../mwworld/esmstore.hpp" - -#include "../mwbase/world.hpp" -#include "../mwbase/environment.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/actionequip.hpp" -#include "../mwmechanics/spells.hpp" -#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spellsuccess.hpp" +#include "spellicons.hpp" #include "inventorywindow.hpp" #include "confirmationdialog.hpp" @@ -46,11 +39,13 @@ namespace namespace MWGui { - SpellWindow::SpellWindow(MWBase::WindowManager& parWindowManager) - : WindowPinnableBase("openmw_spell_window.layout", parWindowManager) + SpellWindow::SpellWindow() + : WindowPinnableBase("openmw_spell_window.layout") , mHeight(0) , mWidth(0) { + mSpellIcons = new SpellIcons(); + getWidget(mSpellView, "SpellView"); getWidget(mEffectBox, "EffectsBox"); @@ -61,9 +56,14 @@ namespace MWGui mMainWidget->castType()->eventWindowChangeCoord += MyGUI::newDelegate(this, &SpellWindow::onWindowResize); } + SpellWindow::~SpellWindow() + { + delete mSpellIcons; + } + void SpellWindow::onPinToggled() { - mWindowManager.setSpellVisibility(!mPinned); + MWBase::Environment::get().getWindowManager()->setSpellVisibility(!mPinned); } void SpellWindow::open() @@ -73,6 +73,8 @@ namespace MWGui void SpellWindow::updateSpells() { + mSpellIcons->updateWidgets(mEffectBox, false); + const int spellHeight = 18; mHeight = 0; @@ -130,7 +132,7 @@ namespace MWGui { store.setSelectedEnchantItem(store.end()); spells.setSelectedSpell(""); - mWindowManager.unsetSelectedSpell(); + MWBase::Environment::get().getWindowManager()->unsetSelectedSpell(); selectedItem = MWWorld::Ptr(); } } @@ -367,12 +369,12 @@ namespace MWGui action.execute (MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()); // since we changed equipping status, update the inventory window - mWindowManager.getInventoryWindow()->drawItems(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); } store.setSelectedEnchantItem(it); spells.setSelectedSpell(""); - mWindowManager.setSelectedEnchantItem(item, 100); /// \todo track charge % + MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item); updateSpells(); } @@ -394,14 +396,14 @@ namespace MWGui if (spell->mData.mFlags & ESM::Spell::F_Always || spell->mData.mType == ESM::Spell::ST_Power) { - mWindowManager.messageBox("#{sDeleteSpellError}", std::vector()); + MWBase::Environment::get().getWindowManager()->messageBox("#{sDeleteSpellError}"); } else { // ask for confirmation mSpellToDelete = spellId; - ConfirmationDialog* dialog = mWindowManager.getConfirmationDialog(); - std::string question = mWindowManager.getGameSettingString("sQuestionDeleteSpell", "Delete %s?"); + ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); + std::string question = MWBase::Environment::get().getWindowManager()->getGameSettingString("sQuestionDeleteSpell", "Delete %s?"); question = boost::str(boost::format(question) % spell->mName); dialog->open(question); dialog->eventOkClicked.clear(); @@ -413,7 +415,7 @@ namespace MWGui { spells.setSelectedSpell(spellId); store.setSelectedEnchantItem(store.end()); - mWindowManager.setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); + MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); } updateSpells(); @@ -444,7 +446,7 @@ namespace MWGui if (spells.getSelectedSpell() == mSpellToDelete) { spells.setSelectedSpell(""); - mWindowManager.unsetSelectedSpell(); + MWBase::Environment::get().getWindowManager()->unsetSelectedSpell(); } spells.remove(mSpellToDelete); diff --git a/apps/openmw/mwgui/spellwindow.hpp b/apps/openmw/mwgui/spellwindow.hpp index caa67fd74..521e73d76 100644 --- a/apps/openmw/mwgui/spellwindow.hpp +++ b/apps/openmw/mwgui/spellwindow.hpp @@ -1,14 +1,17 @@ #ifndef MWGUI_SPELLWINDOW_H #define MWGUI_SPELLWINDOW_H -#include "window_pinnable_base.hpp" +#include "windowpinnablebase.hpp" namespace MWGui { + class SpellIcons; + class SpellWindow : public WindowPinnableBase { public: - SpellWindow(MWBase::WindowManager& parWindowManager); + SpellWindow(); + virtual ~SpellWindow(); void updateSpells(); @@ -33,6 +36,8 @@ namespace MWGui virtual void onPinToggled(); virtual void open(); + + SpellIcons* mSpellIcons; }; } diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp deleted file mode 100644 index 70ceed857..000000000 --- a/apps/openmw/mwgui/stats_window.cpp +++ /dev/null @@ -1,580 +0,0 @@ -#include "stats_window.hpp" - -#include -#include -#include - -#include - -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" -#include "../mwbase/mechanicsmanager.hpp" -#include "../mwbase/windowmanager.hpp" - -#include "../mwworld/player.hpp" -#include "../mwworld/class.hpp" - -#include "../mwmechanics/npcstats.hpp" - -#include "tooltips.hpp" - - -using namespace MWGui; -const int StatsWindow::sLineHeight = 18; - -StatsWindow::StatsWindow (MWBase::WindowManager& parWindowManager) - : WindowPinnableBase("openmw_stats_window.layout", parWindowManager) - , mSkillView(NULL) - , mClientHeight(0) - , mMajorSkills() - , mMinorSkills() - , mMiscSkills() - , mSkillValues() - , mSkillWidgetMap() - , mFactionWidgetMap() - , mFactions() - , mBirthSignId() - , mReputation(0) - , mBounty(0) - , mSkillWidgets() - , mChanged(true) -{ - setCoord(0,0,498, 342); - - const char *names[][2] = - { - { "Attrib1", "sAttributeStrength" }, - { "Attrib2", "sAttributeIntelligence" }, - { "Attrib3", "sAttributeWillpower" }, - { "Attrib4", "sAttributeAgility" }, - { "Attrib5", "sAttributeSpeed" }, - { "Attrib6", "sAttributeEndurance" }, - { "Attrib7", "sAttributePersonality" }, - { "Attrib8", "sAttributeLuck" }, - { 0, 0 } - }; - - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - for (int i=0; names[i][0]; ++i) - { - setText (names[i][0], store.get().find (names[i][1])->getString()); - } - - getWidget(mSkillView, "SkillView"); - getWidget(mLeftPane, "LeftPane"); - getWidget(mRightPane, "RightPane"); - - for (int i = 0; i < ESM::Skill::Length; ++i) - { - mSkillValues.insert(std::pair >(i, MWMechanics::Stat())); - mSkillWidgetMap.insert(std::pair(i, (MyGUI::TextBox*)nullptr)); - } - - MyGUI::WindowPtr t = static_cast(mMainWidget); - t->eventWindowChangeCoord += MyGUI::newDelegate(this, &StatsWindow::onWindowResize); -} - -void StatsWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) -{ - if (mSkillView->getViewOffset().top + _rel*0.3 > 0) - mSkillView->setViewOffset(MyGUI::IntPoint(0, 0)); - else - mSkillView->setViewOffset(MyGUI::IntPoint(0, mSkillView->getViewOffset().top + _rel*0.3)); -} - -void StatsWindow::onWindowResize(MyGUI::Window* window) -{ - mLeftPane->setCoord( MyGUI::IntCoord(0, 0, 0.44*window->getSize().width, window->getSize().height) ); - mRightPane->setCoord( MyGUI::IntCoord(0.44*window->getSize().width, 0, 0.56*window->getSize().width, window->getSize().height) ); - mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); -} - -void StatsWindow::setBar(const std::string& name, const std::string& tname, int val, int max) -{ - MyGUI::ProgressPtr pt; - getWidget(pt, name); - pt->setProgressRange(max); - pt->setProgressPosition(val); - - std::stringstream out; - out << val << "/" << max; - setText(tname, out.str().c_str()); -} - -void StatsWindow::setPlayerName(const std::string& playerName) -{ - static_cast(mMainWidget)->setCaption(playerName); - adjustWindowCaption(); -} - -void StatsWindow::setValue (const std::string& id, const MWMechanics::Stat& value) -{ - static const char *ids[] = - { - "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", - "AttribVal6", "AttribVal7", "AttribVal8", - 0 - }; - - for (int i=0; ids[i]; ++i) - if (ids[i]==id) - { - std::ostringstream valueString; - valueString << value.getModified(); - setText (id, valueString.str()); - - MyGUI::TextBox* box; - getWidget(box, id); - - if (value.getModified()>value.getBase()) - box->_setWidgetState("increased"); - else if (value.getModified()_setWidgetState("decreased"); - else - box->_setWidgetState("normal"); - - break; - } -} - -void StatsWindow::setValue (const std::string& id, const MWMechanics::DynamicStat& value) -{ - static const char *ids[] = - { - "HBar", "MBar", "FBar", - 0 - }; - - 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())); - - // health, magicka, fatigue tooltip - MyGUI::Widget* w; - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(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); - } - } - } -} - -void StatsWindow::setValue (const std::string& id, const std::string& value) -{ - if (id=="name") - setPlayerName (value); - else if (id=="race") - setText ("RaceText", value); - else if (id=="class") - setText ("ClassText", value); -} - -void StatsWindow::setValue (const std::string& id, int value) -{ - if (id=="level") - { - std::ostringstream text; - text << value; - setText("LevelText", text.str()); - } -} - -void StatsWindow::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value) -{ - mSkillValues[parSkill] = value; - MyGUI::TextBox* widget = mSkillWidgetMap[(int)parSkill]; - if (widget) - { - float modified = value.getModified(), base = value.getBase(); - std::string text = boost::lexical_cast(std::floor(modified)); - std::string state = "normal"; - if (modified > base) - state = "increased"; - else if (modified < base) - state = "decreased"; - - widget->setCaption(text); - widget->_setWidgetState(state); - } -} - -void StatsWindow::configureSkills (const std::vector& major, const std::vector& minor) -{ - mMajorSkills = major; - mMinorSkills = minor; - - // Update misc skills with the remaining skills not in major or minor - std::set skillSet; - std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin())); - std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin())); - boost::array::const_iterator end = ESM::Skill::sSkillIds.end(); - mMiscSkills.clear(); - for (boost::array::const_iterator it = ESM::Skill::sSkillIds.begin(); it != end; ++it) - { - int skill = *it; - if (skillSet.find(skill) == skillSet.end()) - mMiscSkills.push_back(skill); - } - - updateSkillArea(); -} - -void StatsWindow::onFrame () -{ - if (!mMainWidget->getVisible()) - return; - - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWMechanics::NpcStats PCstats = MWWorld::Class::get(player).getNpcStats(player); - - // level progress - MyGUI::Widget* levelWidget; - for (int i=0; i<2; ++i) - { - 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"); - } - - setFactions(PCstats.getFactionRanks()); - setExpelled(PCstats.getExpelled ()); - - const std::string &signId = - MWBase::Environment::get().getWorld()->getPlayer().getBirthSign(); - - setBirthSign(signId); - setReputation (PCstats.getReputation ()); - setBounty (PCstats.getBounty ()); - - if (mChanged) - updateSkillArea(); -} - -void StatsWindow::setFactions (const FactionList& factions) -{ - if (mFactions != factions) - { - mFactions = factions; - mChanged = true; - } -} - -void StatsWindow::setExpelled (const std::set& expelled) -{ - if (mExpelled != expelled) - { - mExpelled = expelled; - mChanged = true; - } -} - -void StatsWindow::setBirthSign (const std::string& signId) -{ - if (signId != mBirthSignId) - { - mBirthSignId = signId; - mChanged = true; - } -} - -void StatsWindow::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::ImageBox* separator = mSkillView->createWidget("MW_HLine", - MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), - MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); - separator->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - mSkillWidgets.push_back(separator); - - coord1.top += separator->getHeight(); - coord2.top += separator->getHeight(); -} - -void StatsWindow::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::TextBox* groupWidget = mSkillView->createWidget("SandBrightText", - MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), - MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); - groupWidget->setCaption(label); - groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - mSkillWidgets.push_back(groupWidget); - - coord1.top += sLineHeight; - coord2.top += sLineHeight; -} - -MyGUI::TextBox* StatsWindow::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::TextBox *skillNameWidget, *skillValueWidget; - - skillNameWidget = mSkillView->createWidget("SandText", coord1, MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); - skillNameWidget->setCaption(text); - skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - - skillValueWidget = mSkillView->createWidget("SandTextRight", coord2, MyGUI::Align::Right | MyGUI::Align::Top); - skillValueWidget->setCaption(value); - skillValueWidget->_setWidgetState(state); - skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - - mSkillWidgets.push_back(skillNameWidget); - mSkillWidgets.push_back(skillValueWidget); - - coord1.top += sLineHeight; - coord2.top += sLineHeight; - - return skillValueWidget; -} - -MyGUI::Widget* StatsWindow::addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::TextBox* skillNameWidget; - - skillNameWidget = mSkillView->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); - skillNameWidget->setCaption(text); - skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - - mSkillWidgets.push_back(skillNameWidget); - - coord1.top += sLineHeight; - coord2.top += sLineHeight; - - return skillNameWidget; -} - -void StatsWindow::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - // Add a line separator if there are items above - if (!mSkillWidgets.empty()) - { - addSeparator(coord1, coord2); - } - - addGroup(mWindowManager.getGameSettingString(titleId, titleDefault), coord1, coord2); - - SkillList::const_iterator end = skills.end(); - for (SkillList::const_iterator it = skills.begin(); it != end; ++it) - { - int skillId = *it; - if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes - continue; - assert(skillId >= 0 && skillId < ESM::Skill::Length); - const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; - const MWMechanics::Stat &stat = mSkillValues.find(skillId)->second; - float base = stat.getBase(); - float modified = stat.getModified(); - int progressPercent = (modified - float(static_cast(modified))) * 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) - state = "increased"; - else if (modified < base) - state = "decreased"; - MyGUI::TextBox* widget = addValueItem(mWindowManager.getGameSettingString(skillNameId, skillNameId), - boost::lexical_cast(static_cast(modified)), state, coord1, coord2); - - for (int i=0; i<2; ++i) - { - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "SkillToolTip"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillName", "#{"+skillNameId+"}"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillDescription", skill->mDescription); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillAttribute", "#{sGoverningAttribute}: #{" + attr->mName + "}"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ImageTexture_SkillImage", icon); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillProgressText", boost::lexical_cast(progressPercent)+"/100"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Range_SkillProgress", "100"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("RangePosition_SkillProgress", boost::lexical_cast(progressPercent)); - } - - mSkillWidgetMap[skillId] = widget; - } -} - -void StatsWindow::updateSkillArea() -{ - mChanged = false; - - for (std::vector::iterator it = mSkillWidgets.begin(); it != mSkillWidgets.end(); ++it) - { - MyGUI::Gui::getInstance().destroyWidget(*it); - } - mSkillWidgets.clear(); - - mSkillView->setViewOffset (MyGUI::IntPoint(0,0)); - mClientHeight = 0; - - const int valueSize = 40; - MyGUI::IntCoord coord1(10, 0, mSkillView->getWidth() - (10 + valueSize) - 24, 18); - MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height); - - if (!mMajorSkills.empty()) - addSkills(mMajorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2); - - if (!mMinorSkills.empty()) - addSkills(mMinorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2); - - if (!mMiscSkills.empty()) - addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); - - MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::ESMStore &store = world->getStore(); - const ESM::NPC *player = - world->getPlayer().getPlayer().get()->mBase; - - // race tooltip - const ESM::Race* playerRace = store.get().find(player->mRace); - - MyGUI::Widget* raceWidget; - getWidget(raceWidget, "RaceText"); - ToolTips::createRaceToolTip(raceWidget, playerRace); - getWidget(raceWidget, "Race_str"); - ToolTips::createRaceToolTip(raceWidget, playerRace); - - // class tooltip - MyGUI::Widget* classWidget; - - const ESM::Class *playerClass = - store.get().find(player->mClass); - - getWidget(classWidget, "ClassText"); - ToolTips::createClassToolTip(classWidget, *playerClass); - getWidget(classWidget, "Class_str"); - ToolTips::createClassToolTip(classWidget, *playerClass); - - if (!mFactions.empty()) - { - // Add a line separator if there are items above - if (!mSkillWidgets.empty()) - addSeparator(coord1, coord2); - - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWMechanics::NpcStats PCstats = MWWorld::Class::get(player).getNpcStats(player); - std::set& expelled = PCstats.getExpelled (); - - addGroup(mWindowManager.getGameSettingString("sFaction", "Faction"), coord1, coord2); - FactionList::const_iterator end = mFactions.end(); - for (FactionList::const_iterator it = mFactions.begin(); it != end; ++it) - { - const ESM::Faction *faction = - store.get().find(it->first); - MyGUI::Widget* w = addItem(faction->mName, coord1, coord2); - - std::string text; - - text += std::string("#DDC79E") + faction->mName; - - if (expelled.find(it->first) != expelled.end()) - text += "\n#{sExpelled}"; - else - { - text += std::string("\n#BF9959") + faction->mRanks[it->second]; - - if (it->second < 9) - { - // player doesn't have max rank yet - text += std::string("\n\n#DDC79E#{sNextRank} ") + faction->mRanks[it->second+1]; - - ESM::RankData rankData = faction->mData.mRankData[it->second+1]; - const ESM::Attribute* attr1 = store.get().find(faction->mData.mAttribute1); - const ESM::Attribute* attr2 = store.get().find(faction->mData.mAttribute2); - assert(attr1 && attr2); - - text += "\n#BF9959#{" + attr1->mName + "}: " + boost::lexical_cast(rankData.mAttribute1) - + ", #{" + attr2->mName + "}: " + boost::lexical_cast(rankData.mAttribute2); - - text += "\n\n#DDC79E#{sFavoriteSkills}"; - text += "\n#BF9959"; - for (int i=0; i<6; ++i) - { - text += "#{"+ESM::Skill::sSkillNameIds[faction->mData.mSkillID[i]]+"}"; - if (i<5) - text += ", "; - } - - text += "\n"; - - if (rankData.mSkill1 > 0) - text += "\n#{sNeedOneSkill} " + boost::lexical_cast(rankData.mSkill1); - if (rankData.mSkill2 > 0) - text += "\n#{sNeedTwoSkills} " + boost::lexical_cast(rankData.mSkill2); - } - } - - w->setUserString("ToolTipType", "Layout"); - w->setUserString("ToolTipLayout", "TextToolTip"); - w->setUserString("Caption_Text", text); - } - } - - if (!mBirthSignId.empty()) - { - // Add a line separator if there are items above - if (!mSkillWidgets.empty()) - addSeparator(coord1, coord2); - - addGroup(mWindowManager.getGameSettingString("sBirthSign", "Sign"), coord1, coord2); - const ESM::BirthSign *sign = - store.get().find(mBirthSignId); - MyGUI::Widget* w = addItem(sign->mName, coord1, coord2); - - ToolTips::createBirthsignToolTip(w, mBirthSignId); - } - - // Add a line separator if there are items above - if (!mSkillWidgets.empty()) - addSeparator(coord1, coord2); - - addValueItem(mWindowManager.getGameSettingString("sReputation", "Reputation"), - boost::lexical_cast(static_cast(mReputation)), "normal", coord1, coord2); - - for (int i=0; i<2; ++i) - { - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "TextToolTip"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sSkillsMenuReputationHelp}"); - } - - addValueItem(mWindowManager.getGameSettingString("sBounty", "Bounty"), - boost::lexical_cast(static_cast(mBounty)), "normal", coord1, coord2); - - for (int i=0; i<2; ++i) - { - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "TextToolTip"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sCrimeHelp}"); - } - - mClientHeight = coord1.top; - - mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); -} - -void StatsWindow::onPinToggled() -{ - mWindowManager.setHMSVisibility(!mPinned); -} diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp new file mode 100644 index 000000000..1134767f1 --- /dev/null +++ b/apps/openmw/mwgui/statswindow.cpp @@ -0,0 +1,577 @@ +#include "statswindow.hpp" + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" + +#include "../mwmechanics/npcstats.hpp" + +#include "tooltips.hpp" + +namespace MWGui +{ + + const int StatsWindow::sLineHeight = 18; + + StatsWindow::StatsWindow () + : WindowPinnableBase("openmw_stats_window.layout") + , mSkillView(NULL) + , mClientHeight(0) + , mMajorSkills() + , mMinorSkills() + , mMiscSkills() + , mSkillValues() + , mSkillWidgetMap() + , mFactionWidgetMap() + , mFactions() + , mBirthSignId() + , mReputation(0) + , mBounty(0) + , mSkillWidgets() + , mChanged(true) + { + setCoord(0,0,498, 342); + + const char *names[][2] = + { + { "Attrib1", "sAttributeStrength" }, + { "Attrib2", "sAttributeIntelligence" }, + { "Attrib3", "sAttributeWillpower" }, + { "Attrib4", "sAttributeAgility" }, + { "Attrib5", "sAttributeSpeed" }, + { "Attrib6", "sAttributeEndurance" }, + { "Attrib7", "sAttributePersonality" }, + { "Attrib8", "sAttributeLuck" }, + { 0, 0 } + }; + + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + for (int i=0; names[i][0]; ++i) + { + setText (names[i][0], store.get().find (names[i][1])->getString()); + } + + getWidget(mSkillView, "SkillView"); + getWidget(mLeftPane, "LeftPane"); + getWidget(mRightPane, "RightPane"); + + for (int i = 0; i < ESM::Skill::Length; ++i) + { + mSkillValues.insert(std::pair >(i, MWMechanics::Stat())); + mSkillWidgetMap.insert(std::pair(i, (MyGUI::TextBox*)NULL)); + } + + MyGUI::WindowPtr t = static_cast(mMainWidget); + t->eventWindowChangeCoord += MyGUI::newDelegate(this, &StatsWindow::onWindowResize); + } + + void StatsWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) + { + if (mSkillView->getViewOffset().top + _rel*0.3 > 0) + mSkillView->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mSkillView->setViewOffset(MyGUI::IntPoint(0, mSkillView->getViewOffset().top + _rel*0.3)); + } + + void StatsWindow::onWindowResize(MyGUI::Window* window) + { + mLeftPane->setCoord( MyGUI::IntCoord(0, 0, 0.44*window->getSize().width, window->getSize().height) ); + mRightPane->setCoord( MyGUI::IntCoord(0.44*window->getSize().width, 0, 0.56*window->getSize().width, window->getSize().height) ); + mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); + } + + void StatsWindow::setBar(const std::string& name, const std::string& tname, int val, int max) + { + MyGUI::ProgressPtr pt; + getWidget(pt, name); + pt->setProgressRange(max); + pt->setProgressPosition(val); + + std::stringstream out; + out << val << "/" << max; + setText(tname, out.str().c_str()); + } + + void StatsWindow::setPlayerName(const std::string& playerName) + { + static_cast(mMainWidget)->setCaption(playerName); + adjustWindowCaption(); + } + + void StatsWindow::setValue (const std::string& id, const MWMechanics::Stat& value) + { + static const char *ids[] = + { + "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", + "AttribVal6", "AttribVal7", "AttribVal8", + 0 + }; + + for (int i=0; ids[i]; ++i) + if (ids[i]==id) + { + std::ostringstream valueString; + valueString << value.getModified(); + setText (id, valueString.str()); + + MyGUI::TextBox* box; + getWidget(box, id); + + if (value.getModified()>value.getBase()) + box->_setWidgetState("increased"); + else if (value.getModified()_setWidgetState("decreased"); + else + box->_setWidgetState("normal"); + + break; + } + } + + void StatsWindow::setValue (const std::string& id, const MWMechanics::DynamicStat& value) + { + static const char *ids[] = + { + "HBar", "MBar", "FBar", + 0 + }; + + 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())); + + // health, magicka, fatigue tooltip + MyGUI::Widget* w; + std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(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); + } + } + } + } + + void StatsWindow::setValue (const std::string& id, const std::string& value) + { + if (id=="name") + setPlayerName (value); + else if (id=="race") + setText ("RaceText", value); + else if (id=="class") + setText ("ClassText", value); + } + + void StatsWindow::setValue (const std::string& id, int value) + { + if (id=="level") + { + std::ostringstream text; + text << value; + setText("LevelText", text.str()); + } + } + + void StatsWindow::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value) + { + mSkillValues[parSkill] = value; + MyGUI::TextBox* widget = mSkillWidgetMap[(int)parSkill]; + if (widget) + { + float modified = value.getModified(), base = value.getBase(); + std::string text = boost::lexical_cast(std::floor(modified)); + std::string state = "normal"; + if (modified > base) + state = "increased"; + else if (modified < base) + state = "decreased"; + + widget->setCaption(text); + widget->_setWidgetState(state); + } + } + + void StatsWindow::configureSkills (const std::vector& major, const std::vector& minor) + { + mMajorSkills = major; + mMinorSkills = minor; + + // Update misc skills with the remaining skills not in major or minor + std::set skillSet; + std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin())); + std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin())); + boost::array::const_iterator end = ESM::Skill::sSkillIds.end(); + mMiscSkills.clear(); + for (boost::array::const_iterator it = ESM::Skill::sSkillIds.begin(); it != end; ++it) + { + int skill = *it; + if (skillSet.find(skill) == skillSet.end()) + mMiscSkills.push_back(skill); + } + + updateSkillArea(); + } + + void StatsWindow::onFrame () + { + if (!mMainWidget->getVisible()) + return; + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::NpcStats PCstats = MWWorld::Class::get(player).getNpcStats(player); + + // level progress + MyGUI::Widget* levelWidget; + for (int i=0; i<2; ++i) + { + 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"); + } + + setFactions(PCstats.getFactionRanks()); + setExpelled(PCstats.getExpelled ()); + + const std::string &signId = + MWBase::Environment::get().getWorld()->getPlayer().getBirthSign(); + + setBirthSign(signId); + setReputation (PCstats.getReputation ()); + setBounty (PCstats.getBounty ()); + + if (mChanged) + updateSkillArea(); + } + + void StatsWindow::setFactions (const FactionList& factions) + { + if (mFactions != factions) + { + mFactions = factions; + mChanged = true; + } + } + + void StatsWindow::setExpelled (const std::set& expelled) + { + if (mExpelled != expelled) + { + mExpelled = expelled; + mChanged = true; + } + } + + void StatsWindow::setBirthSign (const std::string& signId) + { + if (signId != mBirthSignId) + { + mBirthSignId = signId; + mChanged = true; + } + } + + void StatsWindow::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::ImageBox* separator = mSkillView->createWidget("MW_HLine", + MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), + MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); + separator->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); + mSkillWidgets.push_back(separator); + + coord1.top += separator->getHeight(); + coord2.top += separator->getHeight(); + } + + void StatsWindow::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::TextBox* groupWidget = mSkillView->createWidget("SandBrightText", + MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), + MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); + groupWidget->setCaption(label); + groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); + mSkillWidgets.push_back(groupWidget); + + coord1.top += sLineHeight; + coord2.top += sLineHeight; + } + + MyGUI::TextBox* StatsWindow::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::TextBox *skillNameWidget, *skillValueWidget; + + skillNameWidget = mSkillView->createWidget("SandText", coord1, MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); + skillNameWidget->setCaption(text); + skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); + + skillValueWidget = mSkillView->createWidget("SandTextRight", coord2, MyGUI::Align::Right | MyGUI::Align::Top); + skillValueWidget->setCaption(value); + skillValueWidget->_setWidgetState(state); + skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); + + mSkillWidgets.push_back(skillNameWidget); + mSkillWidgets.push_back(skillValueWidget); + + coord1.top += sLineHeight; + coord2.top += sLineHeight; + + return skillValueWidget; + } + + MyGUI::Widget* StatsWindow::addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::TextBox* skillNameWidget; + + skillNameWidget = mSkillView->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); + skillNameWidget->setCaption(text); + skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); + + mSkillWidgets.push_back(skillNameWidget); + + coord1.top += sLineHeight; + coord2.top += sLineHeight; + + return skillNameWidget; + } + + void StatsWindow::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + // Add a line separator if there are items above + if (!mSkillWidgets.empty()) + { + addSeparator(coord1, coord2); + } + + addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString(titleId, titleDefault), coord1, coord2); + + SkillList::const_iterator end = skills.end(); + for (SkillList::const_iterator it = skills.begin(); it != end; ++it) + { + int skillId = *it; + if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes + continue; + assert(skillId >= 0 && skillId < ESM::Skill::Length); + const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; + const MWMechanics::Stat &stat = mSkillValues.find(skillId)->second; + float base = stat.getBase(); + float modified = stat.getModified(); + int progressPercent = (modified - float(static_cast(modified))) * 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) + state = "increased"; + else if (modified < base) + state = "decreased"; + MyGUI::TextBox* widget = addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString(skillNameId, skillNameId), + boost::lexical_cast(static_cast(modified)), state, coord1, coord2); + + for (int i=0; i<2; ++i) + { + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "SkillToolTip"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillName", "#{"+skillNameId+"}"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillDescription", skill->mDescription); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillAttribute", "#{sGoverningAttribute}: #{" + attr->mName + "}"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ImageTexture_SkillImage", icon); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillProgressText", boost::lexical_cast(progressPercent)+"/100"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Range_SkillProgress", "100"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("RangePosition_SkillProgress", boost::lexical_cast(progressPercent)); + } + + mSkillWidgetMap[skillId] = widget; + } + } + + void StatsWindow::updateSkillArea() + { + mChanged = false; + + for (std::vector::iterator it = mSkillWidgets.begin(); it != mSkillWidgets.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + mSkillWidgets.clear(); + + mSkillView->setViewOffset (MyGUI::IntPoint(0,0)); + mClientHeight = 0; + + const int valueSize = 40; + MyGUI::IntCoord coord1(10, 0, mSkillView->getWidth() - (10 + valueSize) - 24, 18); + MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height); + + if (!mMajorSkills.empty()) + addSkills(mMajorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2); + + if (!mMinorSkills.empty()) + addSkills(mMinorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2); + + if (!mMiscSkills.empty()) + addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); + + MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::ESMStore &store = world->getStore(); + const ESM::NPC *player = + world->getPlayer().getPlayer().get()->mBase; + + // race tooltip + const ESM::Race* playerRace = store.get().find(player->mRace); + + MyGUI::Widget* raceWidget; + getWidget(raceWidget, "RaceText"); + ToolTips::createRaceToolTip(raceWidget, playerRace); + getWidget(raceWidget, "Race_str"); + ToolTips::createRaceToolTip(raceWidget, playerRace); + + // class tooltip + MyGUI::Widget* classWidget; + + const ESM::Class *playerClass = + store.get().find(player->mClass); + + getWidget(classWidget, "ClassText"); + ToolTips::createClassToolTip(classWidget, *playerClass); + getWidget(classWidget, "Class_str"); + ToolTips::createClassToolTip(classWidget, *playerClass); + + if (!mFactions.empty()) + { + // Add a line separator if there are items above + if (!mSkillWidgets.empty()) + addSeparator(coord1, coord2); + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::NpcStats PCstats = MWWorld::Class::get(player).getNpcStats(player); + std::set& expelled = PCstats.getExpelled (); + + addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString("sFaction", "Faction"), coord1, coord2); + FactionList::const_iterator end = mFactions.end(); + for (FactionList::const_iterator it = mFactions.begin(); it != end; ++it) + { + const ESM::Faction *faction = + store.get().find(it->first); + MyGUI::Widget* w = addItem(faction->mName, coord1, coord2); + + std::string text; + + text += std::string("#DDC79E") + faction->mName; + + if (expelled.find(it->first) != expelled.end()) + text += "\n#{sExpelled}"; + else + { + text += std::string("\n#BF9959") + faction->mRanks[it->second]; + + if (it->second < 9) + { + // player doesn't have max rank yet + text += std::string("\n\n#DDC79E#{sNextRank} ") + faction->mRanks[it->second+1]; + + 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); + + text += "\n\n#DDC79E#{sFavoriteSkills}"; + text += "\n#BF9959"; + for (int i=0; i<6; ++i) + { + text += "#{"+ESM::Skill::sSkillNameIds[faction->mData.mSkills[i]]+"}"; + if (i<5) + text += ", "; + } + + text += "\n"; + + if (rankData.mSkill1 > 0) + text += "\n#{sNeedOneSkill} " + boost::lexical_cast(rankData.mSkill1); + if (rankData.mSkill2 > 0) + text += "\n#{sNeedTwoSkills} " + boost::lexical_cast(rankData.mSkill2); + } + } + + w->setUserString("ToolTipType", "Layout"); + w->setUserString("ToolTipLayout", "TextToolTip"); + w->setUserString("Caption_Text", text); + } + } + + if (!mBirthSignId.empty()) + { + // Add a line separator if there are items above + if (!mSkillWidgets.empty()) + addSeparator(coord1, coord2); + + addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString("sBirthSign", "Sign"), coord1, coord2); + const ESM::BirthSign *sign = + store.get().find(mBirthSignId); + MyGUI::Widget* w = addItem(sign->mName, coord1, coord2); + + ToolTips::createBirthsignToolTip(w, mBirthSignId); + } + + // Add a line separator if there are items above + if (!mSkillWidgets.empty()) + addSeparator(coord1, coord2); + + addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString("sReputation", "Reputation"), + boost::lexical_cast(static_cast(mReputation)), "normal", coord1, coord2); + + for (int i=0; i<2; ++i) + { + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "TextToolTip"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sSkillsMenuReputationHelp}"); + } + + addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString("sBounty", "Bounty"), + boost::lexical_cast(static_cast(mBounty)), "normal", coord1, coord2); + + for (int i=0; i<2; ++i) + { + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "TextToolTip"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sCrimeHelp}"); + } + + mClientHeight = coord1.top; + + mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); + } + + void StatsWindow::onPinToggled() + { + MWBase::Environment::get().getWindowManager()->setHMSVisibility(!mPinned); + } +} diff --git a/apps/openmw/mwgui/stats_window.hpp b/apps/openmw/mwgui/statswindow.hpp similarity index 90% rename from apps/openmw/mwgui/stats_window.hpp rename to apps/openmw/mwgui/statswindow.hpp index 6619680fa..bec42d029 100644 --- a/apps/openmw/mwgui/stats_window.hpp +++ b/apps/openmw/mwgui/statswindow.hpp @@ -3,13 +3,8 @@ #include "../mwworld/esmstore.hpp" -#include -#include -#include -#include - #include "../mwmechanics/stat.hpp" -#include "window_pinnable_base.hpp" +#include "windowpinnablebase.hpp" namespace MWGui { @@ -22,7 +17,7 @@ namespace MWGui typedef std::vector SkillList; - StatsWindow(MWBase::WindowManager& parWindowManager); + StatsWindow(); /// automatically updates all the data in the stats window, but only if it has changed. void onFrame(); @@ -67,11 +62,11 @@ namespace MWGui SkillList mMajorSkills, mMinorSkills, mMiscSkills; std::map > mSkillValues; std::map mSkillWidgetMap; - std::map mFactionWidgetMap; + std::map mFactionWidgetMap; FactionList mFactions; ///< Stores a list of factions and the current rank std::string mBirthSignId; int mReputation, mBounty; - std::vector mSkillWidgets; //< Skills and other information + std::vector mSkillWidgets; //< Skills and other information std::set mExpelled; bool mChanged; diff --git a/apps/openmw/mwgui/text_input.cpp b/apps/openmw/mwgui/text_input.cpp deleted file mode 100644 index 3dbe75165..000000000 --- a/apps/openmw/mwgui/text_input.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "text_input.hpp" - -#include "../mwbase/windowmanager.hpp" - -using namespace MWGui; - -TextInputDialog::TextInputDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_text_input.layout", parWindowManager) -{ - // Centre dialog - center(); - - getWidget(mTextEdit, "TextEdit"); - mTextEdit->eventEditSelectAccept += newDelegate(this, &TextInputDialog::onTextAccepted); - - MyGUI::ButtonPtr okButton; - getWidget(okButton, "OKButton"); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TextInputDialog::onOkClicked); - - // Make sure the edit box has focus - MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); -} - -void TextInputDialog::setNextButtonShow(bool shown) -{ - MyGUI::ButtonPtr okButton; - getWidget(okButton, "OKButton"); - - if (shown) - okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); - else - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); -} - -void TextInputDialog::setTextLabel(const std::string &label) -{ - setText("LabelT", label); -} - -void TextInputDialog::open() -{ - // Make sure the edit box has focus - MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); -} - -// widget controls - -void TextInputDialog::onOkClicked(MyGUI::Widget* _sender) -{ - if (mTextEdit->getCaption() == "") - { - mWindowManager.messageBox ("#{sNotifyMessage37}", std::vector()); - MyGUI::InputManager::getInstance ().setKeyFocusWidget (mTextEdit); - } - else - eventDone(this); -} - -void TextInputDialog::onTextAccepted(MyGUI::Edit* _sender) -{ - if (mTextEdit->getCaption() == "") - { - mWindowManager.messageBox ("#{sNotifyMessage37}", std::vector()); - MyGUI::InputManager::getInstance ().setKeyFocusWidget (mTextEdit); - } - else - eventDone(this); -} diff --git a/apps/openmw/mwgui/textinput.cpp b/apps/openmw/mwgui/textinput.cpp new file mode 100644 index 000000000..d4f8a2533 --- /dev/null +++ b/apps/openmw/mwgui/textinput.cpp @@ -0,0 +1,73 @@ +#include "textinput.hpp" + +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/environment.hpp" + +namespace MWGui +{ + + TextInputDialog::TextInputDialog() + : WindowModal("openmw_text_input.layout") + { + // Centre dialog + center(); + + getWidget(mTextEdit, "TextEdit"); + mTextEdit->eventEditSelectAccept += newDelegate(this, &TextInputDialog::onTextAccepted); + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TextInputDialog::onOkClicked); + + // Make sure the edit box has focus + MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); + } + + void TextInputDialog::setNextButtonShow(bool shown) + { + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + + if (shown) + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); + else + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + } + + void TextInputDialog::setTextLabel(const std::string &label) + { + setText("LabelT", label); + } + + void TextInputDialog::open() + { + WindowModal::open(); + // Make sure the edit box has focus + MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); + } + + // widget controls + + void TextInputDialog::onOkClicked(MyGUI::Widget* _sender) + { + if (mTextEdit->getCaption() == "") + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage37}"); + MyGUI::InputManager::getInstance ().setKeyFocusWidget (mTextEdit); + } + else + eventDone(this); + } + + void TextInputDialog::onTextAccepted(MyGUI::Edit* _sender) + { + if (mTextEdit->getCaption() == "") + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage37}"); + MyGUI::InputManager::getInstance ().setKeyFocusWidget (mTextEdit); + } + else + eventDone(this); + } + +} diff --git a/apps/openmw/mwgui/text_input.hpp b/apps/openmw/mwgui/textinput.hpp similarity index 77% rename from apps/openmw/mwgui/text_input.hpp rename to apps/openmw/mwgui/textinput.hpp index 848310369..1f53263ec 100644 --- a/apps/openmw/mwgui/text_input.hpp +++ b/apps/openmw/mwgui/textinput.hpp @@ -1,22 +1,19 @@ #ifndef MWGUI_TEXT_INPUT_H #define MWGUI_TEXT_INPUT_H -#include "window_base.hpp" +#include "windowbase.hpp" namespace MWGui { class WindowManager; } -/* - */ - namespace MWGui { - class TextInputDialog : public WindowBase + class TextInputDialog : public WindowModal { public: - TextInputDialog(MWBase::WindowManager& parWindowManager); + TextInputDialog(); std::string getTextInput() const { return mTextEdit ? mTextEdit->getOnlyText() : ""; } void setTextInput(const std::string &text) { if (mTextEdit) mTextEdit->setOnlyText(text); } @@ -30,7 +27,7 @@ namespace MWGui void onTextAccepted(MyGUI::Edit* _sender); private: - MyGUI::EditPtr mTextEdit; + MyGUI::EditBox* mTextEdit; }; } #endif diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index c7acf568d..e73ed6b5e 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -2,789 +2,784 @@ #include -#include - -#include - #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" -#include "map_window.hpp" -#include "widgets.hpp" +#include "mapwindow.hpp" #include "inventorywindow.hpp" -using namespace MWGui; -using namespace MyGUI; - -ToolTips::ToolTips(MWBase::WindowManager* windowManager) : - Layout("openmw_tooltips.layout") - , mGameMode(true) - , mWindowManager(windowManager) - , mFullHelp(false) - , mEnabled(true) - , mFocusToolTipX(0.0) - , mFocusToolTipY(0.0) - , mDelay(0.0) - , mRemainingDelay(0.0) - , mLastMouseX(0) - , mLastMouseY(0) - , mHorizontalScrollIndex(0) -{ - getWidget(mDynamicToolTipBox, "DynamicToolTipBox"); +#include "itemmodel.hpp" - mDynamicToolTipBox->setVisible(false); - - // turn off mouse focus so that getMouseFocusWidget returns the correct widget, - // even if the mouse is over the tooltip - mDynamicToolTipBox->setNeedMouseFocus(false); - mMainWidget->setNeedMouseFocus(false); +namespace MWGui +{ - mDelay = Settings::Manager::getFloat("tooltip delay", "GUI"); - mRemainingDelay = mDelay; -} + ToolTips::ToolTips() : + Layout("openmw_tooltips.layout") + , mGameMode(true) + , mFullHelp(false) + , mEnabled(true) + , mFocusToolTipX(0.0) + , mFocusToolTipY(0.0) + , mDelay(0.0) + , mRemainingDelay(0.0) + , mLastMouseX(0) + , mLastMouseY(0) + , mHorizontalScrollIndex(0) + { + getWidget(mDynamicToolTipBox, "DynamicToolTipBox"); -void ToolTips::setEnabled(bool enabled) -{ - mEnabled = enabled; -} + mDynamicToolTipBox->setVisible(false); -void ToolTips::onFrame(float frameDuration) -{ + // turn off mouse focus so that getMouseFocusWidget returns the correct widget, + // even if the mouse is over the tooltip + mDynamicToolTipBox->setNeedMouseFocus(false); + mMainWidget->setNeedMouseFocus(false); - while (mDynamicToolTipBox->getChildCount()) - { - MyGUI::Gui::getInstance().destroyWidget(mDynamicToolTipBox->getChildAt(0)); - } + mDelay = Settings::Manager::getFloat("tooltip delay", "GUI"); + mRemainingDelay = mDelay; - // start by hiding everything - for (unsigned int i=0; i < mMainWidget->getChildCount(); ++i) - { - mMainWidget->getChildAt(i)->setVisible(false); + for (unsigned int i=0; i < mMainWidget->getChildCount(); ++i) + { + mMainWidget->getChildAt(i)->setVisible(false); + } } - const IntSize &viewSize = RenderManager::getInstance().getViewSize(); - - if (!mEnabled) + void ToolTips::setEnabled(bool enabled) { - return; + mEnabled = enabled; } - if (!mGameMode) + void ToolTips::onFrame(float frameDuration) { - const MyGUI::IntPoint& mousePos = InputManager::getInstance().getMousePosition(); - if (mWindowManager->getWorldMouseOver() && ((mWindowManager->getMode() == GM_Console) - || (mWindowManager->getMode() == GM_Container) - || (mWindowManager->getMode() == GM_Inventory))) + while (mDynamicToolTipBox->getChildCount()) { - mFocusObject = MWBase::Environment::get().getWorld()->getFacedObject(); - - if (mFocusObject.isEmpty ()) - return; - - MyGUI::IntSize tooltipSize = getToolTipViaPtr(true); + MyGUI::Gui::getInstance().destroyWidget(mDynamicToolTipBox->getChildAt(0)); + } - IntPoint tooltipPosition = InputManager::getInstance().getMousePosition() + IntPoint(0, 24); + // start by hiding everything + for (unsigned int i=0; i < mMainWidget->getChildCount(); ++i) + { + mMainWidget->getChildAt(i)->setVisible(false); + } - // make the tooltip stay completely in the viewport - if ((tooltipPosition.left + tooltipSize.width) > viewSize.width) - { - tooltipPosition.left = viewSize.width - tooltipSize.width; - } - if ((tooltipPosition.top + tooltipSize.height) > viewSize.height) - { - tooltipPosition.top = viewSize.height - tooltipSize.height; - } + const MyGUI::IntSize &viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - setCoord(tooltipPosition.left, tooltipPosition.top, tooltipSize.width, tooltipSize.height); + if (!mEnabled) + { + return; } - else + if (!mGameMode) { - const MyGUI::IntPoint& lastPressed = InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left); - - if (mousePos == lastPressed) // mouseclick makes tooltip disappear - return; + const MyGUI::IntPoint& mousePos = MyGUI::InputManager::getInstance().getMousePosition(); - if (mousePos.left == mLastMouseX && mousePos.top == mLastMouseY) + if (MWBase::Environment::get().getWindowManager()->getWorldMouseOver() && ((MWBase::Environment::get().getWindowManager()->getMode() == GM_Console) + || (MWBase::Environment::get().getWindowManager()->getMode() == GM_Container) + || (MWBase::Environment::get().getWindowManager()->getMode() == GM_Inventory))) { - mRemainingDelay -= frameDuration; - } - else - { - mHorizontalScrollIndex = 0; - mRemainingDelay = mDelay; - } - mLastMouseX = mousePos.left; - mLastMouseY = mousePos.top; + mFocusObject = MWBase::Environment::get().getWorld()->getFacedObject(); - - if (mRemainingDelay > 0) - return; + if (mFocusObject.isEmpty ()) + return; - Widget* focus = InputManager::getInstance().getMouseFocusWidget(); - if (focus == 0) - { - return; - } + const MWWorld::Class& objectclass = MWWorld::Class::get (mFocusObject); - IntSize tooltipSize; + MyGUI::IntSize tooltipSize; + if ((!objectclass.hasToolTip(mFocusObject))&&(MWBase::Environment::get().getWindowManager()->getMode() == GM_Console)) + { + setCoord(0, 0, 300, 300); + mDynamicToolTipBox->setVisible(true); + ToolTipInfo info; + info.caption=mFocusObject.getCellRef().mRefID; + info.icon=""; + tooltipSize = createToolTip(info); + } + else + tooltipSize = getToolTipViaPtr(true); - // try to go 1 level up until there is a widget that has tooltip - // this is necessary because some skin elements are actually separate widgets - int i=0; - while (!focus->isUserString("ToolTipType")) - { - focus = focus->getParent(); - if (!focus) - return; - ++i; - } + MyGUI::IntPoint tooltipPosition = MyGUI::InputManager::getInstance().getMousePosition() + MyGUI::IntPoint(0, 24); - std::string type = focus->getUserString("ToolTipType"); - std::string text = focus->getUserString("ToolTipText"); + // make the tooltip stay completely in the viewport + if ((tooltipPosition.left + tooltipSize.width) > viewSize.width) + { + tooltipPosition.left = viewSize.width - tooltipSize.width; + } + if ((tooltipPosition.top + tooltipSize.height) > viewSize.height) + { + tooltipPosition.top = viewSize.height - tooltipSize.height; + } - if (type == "") - { - return; + setCoord(tooltipPosition.left, tooltipPosition.top, tooltipSize.width, tooltipSize.height); } - - - // special handling for markers on the local map: the tooltip should only be visible - // if the marker is not hidden due to the fog of war. - if (focus->getUserString ("IsMarker") == "true") + + else { - LocalMapBase::MarkerPosition pos = *focus->getUserData(); + const MyGUI::IntPoint& lastPressed = MyGUI::InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left); - if (!MWBase::Environment::get().getWorld ()->isPositionExplored (pos.nX, pos.nY, pos.cellX, pos.cellY, pos.interior)) + if (mousePos == lastPressed) // mouseclick makes tooltip disappear return; - } - if (type == "ItemPtr") - { - mFocusObject = *focus->getUserData(); - tooltipSize = getToolTipViaPtr(false); - } - else if (type == "AvatarItemSelection") - { - MyGUI::IntCoord avatarPos = mWindowManager->getInventoryWindow ()->getAvatarScreenCoord (); - 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 ); - MWWorld::Ptr item = mWindowManager->getInventoryWindow ()->getAvatarSelectedItem (realX, realY); - - mFocusObject = item; - if (!mFocusObject.isEmpty ()) - tooltipSize = getToolTipViaPtr(false); - } - else if (type == "Spell") - { - ToolTipInfo info; - - const ESM::Spell *spell = - MWBase::Environment::get().getWorld()->getStore().get().find(focus->getUserString("Spell")); - info.caption = spell->mName; - Widgets::SpellEffectList effects; - std::vector::const_iterator end = spell->mEffects.mList.end(); - for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != end; ++it) + if (mousePos.left == mLastMouseX && mousePos.top == mLastMouseY) { - Widgets::SpellEffectParams params; - params.mEffectID = it->mEffectID; - params.mSkill = it->mSkill; - params.mAttribute = it->mAttribute; - params.mDuration = it->mDuration; - params.mMagnMin = it->mMagnMin; - params.mMagnMax = it->mMagnMax; - params.mRange = it->mRange; - params.mIsConstant = (spell->mData.mType == ESM::Spell::ST_Ability); - params.mNoTarget = false; - effects.push_back(params); + mRemainingDelay -= frameDuration; } - info.effects = effects; - tooltipSize = createToolTip(info); - } - else if (type == "Layout") - { - // tooltip defined in the layout - MyGUI::Widget* tooltip; - getWidget(tooltip, focus->getUserString("ToolTipLayout")); - - tooltip->setVisible(true); - if (!tooltip->isUserString("DontResize")) + else { - tooltip->setCoord(0, 0, 450, 300); // this is the maximum width of the tooltip before it starts word-wrapping + mHorizontalScrollIndex = 0; + mRemainingDelay = mDelay; + } + mLastMouseX = mousePos.left; + mLastMouseY = mousePos.top; - tooltipSize = MyGUI::IntSize(0, tooltip->getSize().height); + + if (mRemainingDelay > 0) + return; + + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getMouseFocusWidget(); + if (focus == 0) + return; + + MyGUI::IntSize tooltipSize; + + // try to go 1 level up until there is a widget that has tooltip + // this is necessary because some skin elements are actually separate widgets + int i=0; + while (!focus->isUserString("ToolTipType")) + { + focus = focus->getParent(); + if (!focus) + return; + ++i; } - else - tooltipSize = tooltip->getSize(); - std::map userStrings = focus->getUserStrings(); - for (std::map::iterator it = userStrings.begin(); - it != userStrings.end(); ++it) + std::string type = focus->getUserString("ToolTipType"); + std::string text = focus->getUserString("ToolTipText"); + + if (type == "") { - if (it->first == "ToolTipType" - || it->first == "ToolTipLayout" - || it->first == "IsMarker") - continue; + return; + } - size_t underscorePos = it->first.find("_"); - std::string propertyKey = it->first.substr(0, underscorePos); - std::string widgetName = it->first.substr(underscorePos+1, it->first.size()-(underscorePos+1)); + // special handling for markers on the local map: the tooltip should only be visible + // if the marker is not hidden due to the fog of war. + if (focus->getUserString ("IsMarker") == "true") + { + LocalMapBase::MarkerPosition pos = *focus->getUserData(); - MyGUI::Widget* w; - getWidget(w, widgetName); - w->setProperty(propertyKey, it->second); + if (!MWBase::Environment::get().getWorld ()->isPositionExplored (pos.nX, pos.nY, pos.cellX, pos.cellY, pos.interior)) + return; } - for (unsigned int i=0; igetChildCount(); ++i) + if (type == "ItemPtr") { - MyGUI::Widget* w = tooltip->getChildAt(i); - - if (w->isUserString("AutoResizeHorizontal")) + mFocusObject = *focus->getUserData(); + tooltipSize = getToolTipViaPtr(false); + } + else if (type == "ItemModelIndex") + { + std::pair pair = *focus->getUserData >(); + mFocusObject = pair.second->getItem(pair.first).mBase; + // HACK: To get the correct count for multiple item stack sources + int oldCount = mFocusObject.getRefData().getCount(); + mFocusObject.getRefData().setCount(pair.second->getItem(pair.first).mCount); + tooltipSize = getToolTipViaPtr(false); + mFocusObject.getRefData().setCount(oldCount); + } + else if (type == "ToolTipInfo") + { + tooltipSize = createToolTip(*focus->getUserData()); + } + else if (type == "AvatarItemSelection") + { + MyGUI::IntCoord avatarPos = MWBase::Environment::get().getWindowManager()->getInventoryWindow ()->getAvatarScreenCoord (); + 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 ); + MWWorld::Ptr item = MWBase::Environment::get().getWindowManager()->getInventoryWindow ()->getAvatarSelectedItem (realX, realY); + + mFocusObject = item; + if (!mFocusObject.isEmpty ()) + tooltipSize = getToolTipViaPtr(false); + } + else if (type == "Spell") + { + ToolTipInfo info; + + const ESM::Spell *spell = + MWBase::Environment::get().getWorld()->getStore().get().find(focus->getUserString("Spell")); + info.caption = spell->mName; + Widgets::SpellEffectList effects; + std::vector::const_iterator end = spell->mEffects.mList.end(); + for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != end; ++it) { - MyGUI::TextBox* text = w->castType(); - tooltipSize.width = std::max(tooltipSize.width, w->getLeft() + text->getTextSize().width + 8); + Widgets::SpellEffectParams params; + params.mEffectID = it->mEffectID; + params.mSkill = it->mSkill; + params.mAttribute = it->mAttribute; + params.mDuration = it->mDuration; + params.mMagnMin = it->mMagnMin; + params.mMagnMax = it->mMagnMax; + params.mRange = it->mRange; + params.mIsConstant = (spell->mData.mType == ESM::Spell::ST_Ability); + params.mNoTarget = false; + effects.push_back(params); } - else if (!tooltip->isUserString("DontResize")) - tooltipSize.width = std::max(tooltipSize.width, w->getLeft() + w->getWidth() + 8); + info.effects = effects; + tooltipSize = createToolTip(info); + } + else if (type == "Layout") + { + // tooltip defined in the layout + MyGUI::Widget* tooltip; + getWidget(tooltip, focus->getUserString("ToolTipLayout")); - if (w->isUserString("AutoResizeVertical")) + tooltip->setVisible(true); + + std::map userStrings = focus->getUserStrings(); + for (std::map::iterator it = userStrings.begin(); + it != userStrings.end(); ++it) { - MyGUI::TextBox* text = w->castType(); - int height = text->getTextSize().height; - if (height > w->getHeight()) - { - tooltipSize += MyGUI::IntSize(0, height - w->getHeight()); - } - if (height < w->getHeight()) - { - tooltipSize -= MyGUI::IntSize(0, w->getHeight() - height); - } + if (it->first == "ToolTipType" + || it->first == "ToolTipLayout" + || it->first == "IsMarker") + continue; + + + size_t underscorePos = it->first.find("_"); + std::string propertyKey = it->first.substr(0, underscorePos); + std::string widgetName = it->first.substr(underscorePos+1, it->first.size()-(underscorePos+1)); + + MyGUI::Widget* w; + getWidget(w, widgetName); + w->setProperty(propertyKey, it->second); } + + tooltipSize = tooltip->getSize(); + + tooltip->setCoord(0, 0, tooltipSize.width, tooltipSize.height); } - tooltip->setCoord(0, 0, tooltipSize.width, tooltipSize.height); - } - else - throw std::runtime_error ("unknown tooltip type"); + else + throw std::runtime_error ("unknown tooltip type"); - IntPoint tooltipPosition = InputManager::getInstance().getMousePosition() + IntPoint(0, 24); + MyGUI::IntPoint tooltipPosition = MyGUI::InputManager::getInstance().getMousePosition() + MyGUI::IntPoint(0, 24); - // make the tooltip stay completely in the viewport - if ((tooltipPosition.left + tooltipSize.width) > viewSize.width) - { - tooltipPosition.left = viewSize.width - tooltipSize.width; + // make the tooltip stay completely in the viewport + if ((tooltipPosition.left + tooltipSize.width) > viewSize.width) + { + tooltipPosition.left = viewSize.width - tooltipSize.width; + } + if ((tooltipPosition.top + tooltipSize.height) > viewSize.height) + { + tooltipPosition.top = viewSize.height - tooltipSize.height; + } + + setCoord(tooltipPosition.left, tooltipPosition.top, tooltipSize.width, tooltipSize.height); } - if ((tooltipPosition.top + tooltipSize.height) > viewSize.height) + } + else + { + if (!mFocusObject.isEmpty()) { - tooltipPosition.top = viewSize.height - tooltipSize.height; - } + MyGUI::IntSize tooltipSize = getToolTipViaPtr(); - setCoord(tooltipPosition.left, tooltipPosition.top, tooltipSize.width, tooltipSize.height); + setCoord(viewSize.width/2 - tooltipSize.width/2, + std::max(0, int(mFocusToolTipY*viewSize.height - tooltipSize.height)), + tooltipSize.width, + tooltipSize.height); + + mDynamicToolTipBox->setVisible(true); + } } } - else + + void ToolTips::enterGameMode() { - if (!mFocusObject.isEmpty()) - { - IntSize tooltipSize = getToolTipViaPtr(); + mGameMode = true; + } - setCoord(viewSize.width/2 - tooltipSize.width/2, - std::max(0, int(mFocusToolTipY*viewSize.height - tooltipSize.height)), - tooltipSize.width, - tooltipSize.height); + void ToolTips::enterGuiMode() + { + mGameMode = false; + } - mDynamicToolTipBox->setVisible(true); - } + void ToolTips::setFocusObject(const MWWorld::Ptr& focus) + { + mFocusObject = focus; } -} -void ToolTips::enterGameMode() -{ - mGameMode = true; -} + MyGUI::IntSize ToolTips::getToolTipViaPtr (bool image) + { + // this the maximum width of the tooltip before it starts word-wrapping + setCoord(0, 0, 300, 300); -void ToolTips::enterGuiMode() -{ - mGameMode = false; -} + MyGUI::IntSize tooltipSize; -void ToolTips::setFocusObject(const MWWorld::Ptr& focus) -{ - mFocusObject = focus; -} + const MWWorld::Class& object = MWWorld::Class::get (mFocusObject); + if (!object.hasToolTip(mFocusObject)) + { + mDynamicToolTipBox->setVisible(false); + } + else + { + mDynamicToolTipBox->setVisible(true); -IntSize ToolTips::getToolTipViaPtr (bool image) -{ - // this the maximum width of the tooltip before it starts word-wrapping - setCoord(0, 0, 300, 300); + ToolTipInfo info = object.getToolTipInfo(mFocusObject); + if (!image) + info.icon = ""; + tooltipSize = createToolTip(info); + } - IntSize tooltipSize; + return tooltipSize; + } - const MWWorld::Class& object = MWWorld::Class::get (mFocusObject); - if (!object.hasToolTip(mFocusObject)) + void ToolTips::findImageExtension(std::string& image) { - mDynamicToolTipBox->setVisible(false); + int len = image.size(); + if (len < 4) return; + + if (!Ogre::ResourceGroupManager::getSingleton().resourceExists(Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME, image)) + { + // Change texture extension to .dds + image[len-3] = 'd'; + image[len-2] = 'd'; + image[len-1] = 's'; + } } - else + + MyGUI::IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) { mDynamicToolTipBox->setVisible(true); - ToolTipInfo info = object.getToolTipInfo(mFocusObject); - if (!image) - info.icon = ""; - tooltipSize = createToolTip(info); - } + std::string caption = info.caption; + std::string image = info.icon; + int imageSize = (image != "") ? info.imageSize : 0; + std::string text = info.text; - return tooltipSize; -} + // remove the first newline (easier this way) + if (text.size() > 0 && text[0] == '\n') + text.erase(0, 1); -void ToolTips::findImageExtension(std::string& image) -{ - int len = image.size(); - if (len < 4) return; + if(caption.size() > 0 && isalnum(caption[0])) + caption[0] = toupper(caption[0]); - if (!Ogre::ResourceGroupManager::getSingleton().resourceExists(Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME, image)) - { - // Change texture extension to .dds - image[len-3] = 'd'; - image[len-2] = 'd'; - image[len-1] = 's'; - } -} + const ESM::Enchantment* enchant = 0; + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + if (info.enchant != "") + { + enchant = store.get().find(info.enchant); + if (enchant->mData.mType == ESM::Enchantment::CastOnce) + text += "\n#{sItemCastOnce}"; + else if (enchant->mData.mType == ESM::Enchantment::WhenStrikes) + text += "\n#{sItemCastWhenStrikes}"; + else if (enchant->mData.mType == ESM::Enchantment::WhenUsed) + text += "\n#{sItemCastWhenUsed}"; + else if (enchant->mData.mType == ESM::Enchantment::ConstantEffect) + text += "\n#{sItemCastConstant}"; + } -IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) -{ - mDynamicToolTipBox->setVisible(true); + // this the maximum width of the tooltip before it starts word-wrapping + setCoord(0, 0, 300, 300); - std::string caption = info.caption; - std::string image = info.icon; - int imageSize = (image != "") ? 32 : 0; - std::string text = info.text; + const MyGUI::IntPoint padding(8, 8); - // remove the first newline (easier this way) - if (text.size() > 0 && text[0] == '\n') - text.erase(0, 1); + const int maximumWidth = 500; - if(caption.size() > 0 && isalnum(caption[0])) - caption[0] = toupper(caption[0]); + const int imageCaptionHPadding = (caption != "" ? 8 : 0); + const int imageCaptionVPadding = (caption != "" ? 4 : 0); - const ESM::Enchantment* enchant = 0; - const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - if (info.enchant != "") - { - enchant = store.get().find(info.enchant); - if (enchant->mData.mType == ESM::Enchantment::CastOnce) - text += "\n#{sItemCastOnce}"; - else if (enchant->mData.mType == ESM::Enchantment::WhenStrikes) - text += "\n#{sItemCastWhenStrikes}"; - else if (enchant->mData.mType == ESM::Enchantment::WhenUsed) - text += "\n#{sItemCastWhenUsed}"; - else if (enchant->mData.mType == ESM::Enchantment::ConstantEffect) - text += "\n#{sItemCastConstant}"; - } + std::string realImage = "icons\\" + image; + findImageExtension(realImage); - // this the maximum width of the tooltip before it starts word-wrapping - setCoord(0, 0, 300, 300); + MyGUI::EditBox* captionWidget = mDynamicToolTipBox->createWidget("NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption"); + captionWidget->setProperty("Static", "true"); + captionWidget->setCaptionWithReplacing(caption); + MyGUI::IntSize captionSize = captionWidget->getTextSize(); - const IntPoint padding(8, 8); - - const int maximumWidth = 500; + int captionHeight = std::max(caption != "" ? captionSize.height : 0, imageSize); - const int imageCaptionHPadding = (caption != "" ? 8 : 0); - const int imageCaptionVPadding = (caption != "" ? 4 : 0); + MyGUI::EditBox* textWidget = mDynamicToolTipBox->createWidget("SandText", MyGUI::IntCoord(0, captionHeight+imageCaptionVPadding, 300, 300-captionHeight-imageCaptionVPadding), MyGUI::Align::Stretch, "ToolTipText"); + textWidget->setProperty("Static", "true"); + textWidget->setProperty("MultiLine", "true"); + textWidget->setProperty("WordWrap", info.wordWrap ? "true" : "false"); + textWidget->setCaptionWithReplacing(text); + textWidget->setTextAlign(MyGUI::Align::HCenter | MyGUI::Align::Top); + MyGUI::IntSize textSize = textWidget->getTextSize(); - std::string realImage = "icons\\" + image; - findImageExtension(realImage); + captionSize += MyGUI::IntSize(imageSize, 0); // adjust for image + MyGUI::IntSize totalSize = MyGUI::IntSize( std::min(std::max(textSize.width,captionSize.width + ((image != "") ? imageCaptionHPadding : 0)),maximumWidth), + ((text != "") ? textSize.height + imageCaptionVPadding : 0) + captionHeight ); - EditBox* captionWidget = mDynamicToolTipBox->createWidget("NormalText", IntCoord(0, 0, 300, 300), Align::Left | Align::Top, "ToolTipCaption"); - captionWidget->setProperty("Static", "true"); - captionWidget->setCaption(caption); - IntSize captionSize = captionWidget->getTextSize(); + if (!info.effects.empty()) + { + MyGUI::Widget* effectArea = mDynamicToolTipBox->createWidget("", + MyGUI::IntCoord(0, totalSize.height, 300, 300-totalSize.height), + MyGUI::Align::Stretch, "ToolTipEffectArea"); + + MyGUI::IntCoord coord(0, 6, totalSize.width, 24); + + /** + * \todo + * the various potion effects should appear in the tooltip depending if the player + * has enough skill in alchemy to know about the effects of this potion. + */ + + Widgets::MWEffectListPtr effectsWidget = effectArea->createWidget + ("MW_StatName", coord, MyGUI::Align::Default, "ToolTipEffectsWidget"); + effectsWidget->setEffectList(info.effects); + + std::vector effectItems; + effectsWidget->createEffectWidgets(effectItems, effectArea, coord, true, info.isPotion ? Widgets::MWEffectList::EF_NoTarget : 0); + totalSize.height += coord.top-6; + totalSize.width = std::max(totalSize.width, coord.width); + } - int captionHeight = std::max(caption != "" ? captionSize.height : 0, imageSize); + if (info.enchant != "") + { + assert(enchant); + MyGUI::Widget* enchantArea = mDynamicToolTipBox->createWidget("", + MyGUI::IntCoord(0, totalSize.height, 300, 300-totalSize.height), + MyGUI::Align::Stretch, "ToolTipEnchantArea"); - EditBox* textWidget = mDynamicToolTipBox->createWidget("SandText", IntCoord(0, captionHeight+imageCaptionVPadding, 300, 300-captionHeight-imageCaptionVPadding), Align::Stretch, "ToolTipText"); - textWidget->setProperty("Static", "true"); - textWidget->setProperty("MultiLine", "true"); - textWidget->setProperty("WordWrap", "true"); - textWidget->setCaptionWithReplacing(text); - textWidget->setTextAlign(Align::HCenter | Align::Top); - IntSize textSize = textWidget->getTextSize(); + MyGUI::IntCoord coord(0, 6, totalSize.width, 24); - captionSize += IntSize(imageSize, 0); // adjust for image - IntSize totalSize = IntSize( std::min(std::max(textSize.width,captionSize.width + ((image != "") ? imageCaptionHPadding : 0)),maximumWidth), - ((text != "") ? textSize.height + imageCaptionVPadding : 0) + captionHeight ); + Widgets::MWEffectListPtr enchantWidget = enchantArea->createWidget + ("MW_StatName", coord, MyGUI::Align::Default, "ToolTipEnchantWidget"); + enchantWidget->setEffectList(Widgets::MWEffectList::effectListFromESM(&enchant->mEffects)); - if (!info.effects.empty()) - { - Widget* effectArea = mDynamicToolTipBox->createWidget("", - IntCoord(0, totalSize.height, 300, 300-totalSize.height), - Align::Stretch, "ToolTipEffectArea"); - - IntCoord coord(0, 6, totalSize.width, 24); - - /** - * \todo - * the various potion effects should appear in the tooltip depending if the player - * has enough skill in alchemy to know about the effects of this potion. - */ - - Widgets::MWEffectListPtr effectsWidget = effectArea->createWidget - ("MW_StatName", coord, Align::Default, "ToolTipEffectsWidget"); - effectsWidget->setWindowManager(mWindowManager); - effectsWidget->setEffectList(info.effects); - - std::vector effectItems; - effectsWidget->createEffectWidgets(effectItems, effectArea, coord, true, info.isPotion ? Widgets::MWEffectList::EF_NoTarget : 0); - totalSize.height += coord.top-6; - totalSize.width = std::max(totalSize.width, coord.width); - } + std::vector enchantEffectItems; + int flag = (enchant->mData.mType == ESM::Enchantment::ConstantEffect) ? Widgets::MWEffectList::EF_Constant : 0; + enchantWidget->createEffectWidgets(enchantEffectItems, enchantArea, coord, true, flag); + totalSize.height += coord.top-6; + totalSize.width = std::max(totalSize.width, coord.width); - if (info.enchant != "") - { - assert(enchant); - Widget* enchantArea = mDynamicToolTipBox->createWidget("", - IntCoord(0, totalSize.height, 300, 300-totalSize.height), - Align::Stretch, "ToolTipEnchantArea"); - - IntCoord coord(0, 6, totalSize.width, 24); - - Widgets::MWEffectListPtr enchantWidget = enchantArea->createWidget - ("MW_StatName", coord, Align::Default, "ToolTipEnchantWidget"); - enchantWidget->setWindowManager(mWindowManager); - enchantWidget->setEffectList(Widgets::MWEffectList::effectListFromESM(&enchant->mEffects)); - - std::vector enchantEffectItems; - int flag = (enchant->mData.mType == ESM::Enchantment::ConstantEffect) ? Widgets::MWEffectList::EF_Constant : 0; - enchantWidget->createEffectWidgets(enchantEffectItems, enchantArea, coord, true, flag); - totalSize.height += coord.top-6; - totalSize.width = std::max(totalSize.width, coord.width); - - if (enchant->mData.mType == ESM::Enchantment::WhenStrikes - || enchant->mData.mType == ESM::Enchantment::WhenUsed) - { - /// \todo store the current enchantment charge somewhere - int charge = enchant->mData.mCharge; + if (enchant->mData.mType == ESM::Enchantment::WhenStrikes + || enchant->mData.mType == ESM::Enchantment::WhenUsed) + { + int maxCharge = enchant->mData.mCharge; + int charge = (info.remainingEnchantCharge == -1) ? maxCharge : info.remainingEnchantCharge; - const int chargeWidth = 204; + const int chargeWidth = 204; - TextBox* chargeText = enchantArea->createWidget("SandText", IntCoord(0, 0, 10, 18), Align::Default, "ToolTipEnchantChargeText"); - chargeText->setCaptionWithReplacing("#{sCharges}"); + MyGUI::TextBox* chargeText = enchantArea->createWidget("SandText", MyGUI::IntCoord(0, 0, 10, 18), MyGUI::Align::Default, "ToolTipEnchantChargeText"); + chargeText->setCaptionWithReplacing("#{sCharges}"); - const int chargeTextWidth = chargeText->getTextSize().width + 5; + const int chargeTextWidth = chargeText->getTextSize().width + 5; - const int chargeAndTextWidth = chargeWidth + chargeTextWidth; + const int chargeAndTextWidth = chargeWidth + chargeTextWidth; - totalSize.width = std::max(totalSize.width, chargeAndTextWidth); + totalSize.width = std::max(totalSize.width, chargeAndTextWidth); - chargeText->setCoord((totalSize.width - chargeAndTextWidth)/2, coord.top+6, chargeTextWidth, 18); + chargeText->setCoord((totalSize.width - chargeAndTextWidth)/2, coord.top+6, chargeTextWidth, 18); - IntCoord chargeCoord; - if (totalSize.width < chargeWidth) - { - totalSize.width = chargeWidth; - chargeCoord = IntCoord(0, coord.top+6, chargeWidth, 18); - } - else - { - chargeCoord = IntCoord((totalSize.width - chargeAndTextWidth)/2 + chargeTextWidth, coord.top+6, chargeWidth, 18); + MyGUI::IntCoord chargeCoord; + if (totalSize.width < chargeWidth) + { + totalSize.width = chargeWidth; + chargeCoord = MyGUI::IntCoord(0, coord.top+6, chargeWidth, 18); + } + else + { + chargeCoord = MyGUI::IntCoord((totalSize.width - chargeAndTextWidth)/2 + chargeTextWidth, coord.top+6, chargeWidth, 18); + } + Widgets::MWDynamicStatPtr chargeWidget = enchantArea->createWidget + ("MW_ChargeBar", chargeCoord, MyGUI::Align::Default, "ToolTipEnchantCharge"); + chargeWidget->setValue(charge, charge); + totalSize.height += 24; } - Widgets::MWDynamicStatPtr chargeWidget = enchantArea->createWidget - ("MW_ChargeBar", chargeCoord, Align::Default, "ToolTipEnchantCharge"); - chargeWidget->setValue(charge, charge); - totalSize.height += 24; } - } - captionWidget->setCoord( (totalSize.width - captionSize.width)/2 + imageSize, - (captionHeight-captionSize.height)/2, - captionSize.width-imageSize, - captionSize.height); - - //if its too long we do hscroll with the caption - if (captionSize.width > maximumWidth){ - mHorizontalScrollIndex = mHorizontalScrollIndex + 2; - if (mHorizontalScrollIndex > captionSize.width){ - mHorizontalScrollIndex = -totalSize.width; - } - int horizontal_scroll = mHorizontalScrollIndex; - if (horizontal_scroll < 40){ - horizontal_scroll = 40; - }else{ - horizontal_scroll = 80 - mHorizontalScrollIndex; - } - captionWidget->setPosition (IntPoint(horizontal_scroll, captionWidget->getPosition().top + padding.top)); - } else { - captionWidget->setPosition (captionWidget->getPosition() + padding); - } + captionWidget->setCoord( (totalSize.width - captionSize.width)/2 + imageSize, + (captionHeight-captionSize.height)/2, + captionSize.width-imageSize, + captionSize.height); - textWidget->setPosition (textWidget->getPosition() + IntPoint(0, padding.top)); // only apply vertical padding, the horizontal works automatically due to Align::HCenter + //if its too long we do hscroll with the caption + if (captionSize.width > maximumWidth) + { + mHorizontalScrollIndex = mHorizontalScrollIndex + 2; + if (mHorizontalScrollIndex > captionSize.width){ + mHorizontalScrollIndex = -totalSize.width; + } + int horizontal_scroll = mHorizontalScrollIndex; + if (horizontal_scroll < 40){ + horizontal_scroll = 40; + }else{ + horizontal_scroll = 80 - mHorizontalScrollIndex; + } + captionWidget->setPosition (MyGUI::IntPoint(horizontal_scroll, captionWidget->getPosition().top + padding.top)); + } else { + captionWidget->setPosition (captionWidget->getPosition() + padding); + } - if (image != "") - { - ImageBox* imageWidget = mDynamicToolTipBox->createWidget("ImageBox", - IntCoord((totalSize.width - captionSize.width - imageCaptionHPadding)/2, 0, imageSize, imageSize), - Align::Left | Align::Top, "ToolTipImage"); - imageWidget->setImageTexture(realImage); - imageWidget->setPosition (imageWidget->getPosition() + padding); - } + textWidget->setPosition (textWidget->getPosition() + MyGUI::IntPoint(0, padding.top)); // only apply vertical padding, the horizontal works automatically due to Align::HCenter - totalSize += IntSize(padding.left*2, padding.top*2); + if (image != "") + { + MyGUI::ImageBox* imageWidget = mDynamicToolTipBox->createWidget("ImageBox", + MyGUI::IntCoord((totalSize.width - captionSize.width - imageCaptionHPadding)/2, 0, imageSize, imageSize), + MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipImage"); + imageWidget->setImageTexture(realImage); + imageWidget->setPosition (imageWidget->getPosition() + padding); + } - return totalSize; -} + totalSize += MyGUI::IntSize(padding.left*2, padding.top*2); -std::string ToolTips::toString(const float value) -{ - std::ostringstream stream; - stream << std::setprecision(3) << value; - return stream.str(); -} + return totalSize; + } -std::string ToolTips::toString(const int value) -{ - std::ostringstream stream; - stream << value; - return stream.str(); -} + std::string ToolTips::toString(const float value) + { + std::ostringstream stream; + stream << std::setprecision(3) << value; + return stream.str(); + } -std::string ToolTips::getValueString(const int value, const std::string& prefix) -{ - if (value == 0) - return ""; - else - return "\n" + prefix + ": " + toString(value); -} + std::string ToolTips::toString(const int value) + { + std::ostringstream stream; + stream << value; + return stream.str(); + } -std::string ToolTips::getMiscString(const std::string& text, const std::string& prefix) -{ - if (text == "") - return ""; - else - return "\n" + prefix + ": " + text; -} + std::string ToolTips::getValueString(const int value, const std::string& prefix) + { + if (value == 0) + return ""; + else + return "\n" + prefix + ": " + toString(value); + } -std::string ToolTips::getCountString(const int value) -{ - if (value == 1) - return ""; - else - return " (" + boost::lexical_cast(value) + ")"; -} + std::string ToolTips::getMiscString(const std::string& text, const std::string& prefix) + { + if (text == "") + return ""; + else + return "\n" + prefix + ": " + text; + } -void ToolTips::toggleFullHelp() -{ - mFullHelp = !mFullHelp; -} + std::string ToolTips::getCountString(const int value) + { + if (value == 1) + return ""; + else + return " (" + boost::lexical_cast(value) + ")"; + } -bool ToolTips::getFullHelp() const -{ - return mFullHelp; -} + void ToolTips::toggleFullHelp() + { + mFullHelp = !mFullHelp; + } -void ToolTips::setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) -{ - mFocusToolTipX = (min_x + max_x) / 2; - mFocusToolTipY = min_y; -} + bool ToolTips::getFullHelp() const + { + return mFullHelp; + } -void ToolTips::createSkillToolTip(MyGUI::Widget* widget, int skillId) -{ - if (skillId == -1) - return; - - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); - - const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; - const ESM::Skill* skill = store.get().find(skillId); - assert(skill); - - const ESM::Attribute* attr = - store.get().find(skill->mData.mAttribute); - assert(attr); - std::string icon = "icons\\k\\" + ESM::Skill::sIconNames[skillId]; - - widget->setUserString("ToolTipType", "Layout"); - widget->setUserString("ToolTipLayout", "SkillNoProgressToolTip"); - widget->setUserString("Caption_SkillNoProgressName", "#{"+skillNameId+"}"); - widget->setUserString("Caption_SkillNoProgressDescription", skill->mDescription); - widget->setUserString("Caption_SkillNoProgressAttribute", "#{sGoverningAttribute}: #{" + attr->mName + "}"); - widget->setUserString("ImageTexture_SkillNoProgressImage", icon); -} + void ToolTips::setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) + { + mFocusToolTipX = (min_x + max_x) / 2; + mFocusToolTipY = min_y; + } -void ToolTips::createAttributeToolTip(MyGUI::Widget* widget, int attributeId) -{ - if (attributeId == -1) - return; - - std::string icon = ESM::Attribute::sAttributeIcons[attributeId]; - std::string name = ESM::Attribute::sGmstAttributeIds[attributeId]; - std::string desc = ESM::Attribute::sGmstAttributeDescIds[attributeId]; - - widget->setUserString("ToolTipType", "Layout"); - widget->setUserString("ToolTipLayout", "AttributeToolTip"); - widget->setUserString("Caption_AttributeName", "#{"+name+"}"); - widget->setUserString("Caption_AttributeDescription", "#{"+desc+"}"); - widget->setUserString("ImageTexture_AttributeImage", icon); -} + void ToolTips::createSkillToolTip(MyGUI::Widget* widget, int skillId) + { + if (skillId == -1) + return; + + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); + + const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; + const ESM::Skill* skill = store.get().find(skillId); + assert(skill); + + const ESM::Attribute* attr = + store.get().find(skill->mData.mAttribute); + assert(attr); + std::string icon = "icons\\k\\" + ESM::Skill::sIconNames[skillId]; + + widget->setUserString("ToolTipType", "Layout"); + widget->setUserString("ToolTipLayout", "SkillNoProgressToolTip"); + widget->setUserString("Caption_SkillNoProgressName", "#{"+skillNameId+"}"); + widget->setUserString("Caption_SkillNoProgressDescription", skill->mDescription); + widget->setUserString("Caption_SkillNoProgressAttribute", "#{sGoverningAttribute}: #{" + attr->mName + "}"); + widget->setUserString("ImageTexture_SkillNoProgressImage", icon); + } -void ToolTips::createSpecializationToolTip(MyGUI::Widget* widget, const std::string& name, int specId) -{ - widget->setUserString("Caption_CenteredCaption", name); - std::string specText; - // get all skills of this specialisation - const MWWorld::Store &skills = - MWBase::Environment::get().getWorld()->getStore().get(); - - MWWorld::Store::iterator it = skills.begin(); - for (; it != skills.end(); ++it) + void ToolTips::createAttributeToolTip(MyGUI::Widget* widget, int attributeId) { - if (it->mData.mSpecialization == specId) - specText += std::string("\n#{") + ESM::Skill::sSkillNameIds[it->mIndex] + "}"; + if (attributeId == -1) + return; + + std::string icon = ESM::Attribute::sAttributeIcons[attributeId]; + std::string name = ESM::Attribute::sGmstAttributeIds[attributeId]; + std::string desc = ESM::Attribute::sGmstAttributeDescIds[attributeId]; + + widget->setUserString("ToolTipType", "Layout"); + widget->setUserString("ToolTipLayout", "AttributeToolTip"); + widget->setUserString("Caption_AttributeName", "#{"+name+"}"); + widget->setUserString("Caption_AttributeDescription", "#{"+desc+"}"); + widget->setUserString("ImageTexture_AttributeImage", icon); } - widget->setUserString("Caption_CenteredCaptionText", specText); - widget->setUserString("ToolTipLayout", "TextWithCenteredCaptionToolTip"); - widget->setUserString("ToolTipType", "Layout"); -} -void ToolTips::createBirthsignToolTip(MyGUI::Widget* widget, const std::string& birthsignId) -{ - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); + void ToolTips::createSpecializationToolTip(MyGUI::Widget* widget, const std::string& name, int specId) + { + widget->setUserString("Caption_CenteredCaption", name); + std::string specText; + // get all skills of this specialisation + const MWWorld::Store &skills = + MWBase::Environment::get().getWorld()->getStore().get(); + + MWWorld::Store::iterator it = skills.begin(); + for (; it != skills.end(); ++it) + { + if (it->mData.mSpecialization == specId) + specText += std::string("\n#{") + ESM::Skill::sSkillNameIds[it->mIndex] + "}"; + } + widget->setUserString("Caption_CenteredCaptionText", specText); + widget->setUserString("ToolTipLayout", "TextWithCenteredCaptionToolTip"); + widget->setUserString("ToolTipType", "Layout"); + } - const ESM::BirthSign *sign = store.get().find(birthsignId); + void ToolTips::createBirthsignToolTip(MyGUI::Widget* widget, const std::string& birthsignId) + { + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); - widget->setUserString("ToolTipType", "Layout"); - widget->setUserString("ToolTipLayout", "BirthSignToolTip"); - std::string image = sign->mTexture; - image.replace(image.size()-3, 3, "dds"); - widget->setUserString("ImageTexture_BirthSignImage", "textures\\" + image); - std::string text; + const ESM::BirthSign *sign = store.get().find(birthsignId); - text += sign->mName; - text += "\n#BF9959" + sign->mDescription; + widget->setUserString("ToolTipType", "Layout"); + widget->setUserString("ToolTipLayout", "BirthSignToolTip"); + std::string image = sign->mTexture; + image.replace(image.size()-3, 3, "dds"); + widget->setUserString("ImageTexture_BirthSignImage", "textures\\" + image); + std::string text; - std::vector abilities, powers, spells; + text += sign->mName; + text += "\n#BF9959" + sign->mDescription; - std::vector::const_iterator it = sign->mPowers.mList.begin(); - std::vector::const_iterator end = sign->mPowers.mList.end(); - for (; it != end; ++it) - { - const std::string &spellId = *it; - const ESM::Spell *spell = store.get().search(spellId); - if (!spell) - continue; // Skip spells which cannot be found - ESM::Spell::SpellType type = static_cast(spell->mData.mType); - if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Ability && type != ESM::Spell::ST_Power) - continue; // We only want spell, ability and powers. - - if (type == ESM::Spell::ST_Ability) - abilities.push_back(spellId); - else if (type == ESM::Spell::ST_Power) - powers.push_back(spellId); - else if (type == ESM::Spell::ST_Spell) - spells.push_back(spellId); - } + std::vector abilities, powers, spells; - struct { - const std::vector &spells; - std::string label; - } - categories[3] = { - {abilities, "sBirthsignmenu1"}, - {powers, "sPowers"}, - {spells, "sBirthsignmenu2"} - }; + std::vector::const_iterator it = sign->mPowers.mList.begin(); + std::vector::const_iterator end = sign->mPowers.mList.end(); + for (; it != end; ++it) + { + const std::string &spellId = *it; + const ESM::Spell *spell = store.get().search(spellId); + if (!spell) + continue; // Skip spells which cannot be found + ESM::Spell::SpellType type = static_cast(spell->mData.mType); + if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Ability && type != ESM::Spell::ST_Power) + continue; // We only want spell, ability and powers. + + if (type == ESM::Spell::ST_Ability) + abilities.push_back(spellId); + else if (type == ESM::Spell::ST_Power) + powers.push_back(spellId); + else if (type == ESM::Spell::ST_Spell) + spells.push_back(spellId); + } - for (int category = 0; category < 3; ++category) - { - for (std::vector::const_iterator it = categories[category].spells.begin(); it != categories[category].spells.end(); ++it) + struct { + const std::vector &spells; + std::string label; + } + categories[3] = { + {abilities, "sBirthsignmenu1"}, + {powers, "sPowers"}, + {spells, "sBirthsignmenu2"} + }; + + for (int category = 0; category < 3; ++category) { - if (it == categories[category].spells.begin()) + for (std::vector::const_iterator it = categories[category].spells.begin(); it != categories[category].spells.end(); ++it) { - text += std::string("\n#DDC79E") + std::string("#{") + categories[category].label + "}"; - } + if (it == categories[category].spells.begin()) + { + text += std::string("\n#DDC79E") + std::string("#{") + categories[category].label + "}"; + } - const std::string &spellId = *it; + const std::string &spellId = *it; - const ESM::Spell *spell = store.get().find(spellId); - text += "\n#BF9959" + spell->mName; + const ESM::Spell *spell = store.get().find(spellId); + text += "\n#BF9959" + spell->mName; + } } + + widget->setUserString("Caption_BirthSignText", text); } - widget->setUserString("Caption_BirthSignText", text); -} + void ToolTips::createRaceToolTip(MyGUI::Widget* widget, const ESM::Race* playerRace) + { + widget->setUserString("Caption_CenteredCaption", playerRace->mName); + widget->setUserString("Caption_CenteredCaptionText", playerRace->mDescription); + widget->setUserString("ToolTipType", "Layout"); + widget->setUserString("ToolTipLayout", "TextWithCenteredCaptionToolTip"); + } -void ToolTips::createRaceToolTip(MyGUI::Widget* widget, const ESM::Race* playerRace) -{ - widget->setUserString("Caption_CenteredCaption", playerRace->mName); - widget->setUserString("Caption_CenteredCaptionText", playerRace->mDescription); - widget->setUserString("ToolTipType", "Layout"); - widget->setUserString("ToolTipLayout", "TextWithCenteredCaptionToolTip"); -} + void ToolTips::createClassToolTip(MyGUI::Widget* widget, const ESM::Class& playerClass) + { + if (playerClass.mName == "") + return; + + int spec = playerClass.mData.mSpecialization; + std::string specStr; + if (spec == 0) + specStr = "#{sSpecializationCombat}"; + else if (spec == 1) + specStr = "#{sSpecializationMagic}"; + else if (spec == 2) + specStr = "#{sSpecializationStealth}"; + + widget->setUserString("Caption_ClassName", playerClass.mName); + widget->setUserString("Caption_ClassDescription", playerClass.mDescription); + widget->setUserString("Caption_ClassSpecialisation", "#{sSpecialization}: " + specStr); + widget->setUserString("ToolTipType", "Layout"); + widget->setUserString("ToolTipLayout", "ClassToolTip"); + } -void ToolTips::createClassToolTip(MyGUI::Widget* widget, const ESM::Class& playerClass) -{ - if (playerClass.mName == "") - return; - - int spec = playerClass.mData.mSpecialization; - std::string specStr; - if (spec == 0) - specStr = "#{sSpecializationCombat}"; - else if (spec == 1) - specStr = "#{sSpecializationMagic}"; - else if (spec == 2) - specStr = "#{sSpecializationStealth}"; - - widget->setUserString("Caption_ClassName", playerClass.mName); - widget->setUserString("Caption_ClassDescription", playerClass.mDescription); - widget->setUserString("Caption_ClassSpecialisation", "#{sSpecialization}: " + specStr); - widget->setUserString("ToolTipType", "Layout"); - widget->setUserString("ToolTipLayout", "ClassToolTip"); -} + void ToolTips::createMagicEffectToolTip(MyGUI::Widget* widget, short id) + { + const ESM::MagicEffect* effect = + MWBase::Environment::get().getWorld ()->getStore ().get().find(id); + const std::string &name = ESM::MagicEffect::effectIdToString (id); + + std::string icon = effect->mIcon; + + int slashPos = icon.find("\\"); + icon.insert(slashPos+1, "b_"); + + icon[icon.size()-3] = 'd'; + icon[icon.size()-2] = 'd'; + icon[icon.size()-1] = 's'; + + icon = "icons\\" + icon; + + std::vector schools; + schools.push_back ("#{sSchoolAlteration}"); + schools.push_back ("#{sSchoolConjuration}"); + schools.push_back ("#{sSchoolDestruction}"); + schools.push_back ("#{sSchoolIllusion}"); + schools.push_back ("#{sSchoolMysticism}"); + schools.push_back ("#{sSchoolRestoration}"); + + widget->setUserString("ToolTipType", "Layout"); + widget->setUserString("ToolTipLayout", "MagicEffectToolTip"); + widget->setUserString("Caption_MagicEffectName", "#{" + name + "}"); + widget->setUserString("Caption_MagicEffectDescription", effect->mDescription); + widget->setUserString("Caption_MagicEffectSchool", "#{sSchool}: " + schools[effect->mData.mSchool]); + widget->setUserString("ImageTexture_MagicEffectImage", icon); + } -void ToolTips::createMagicEffectToolTip(MyGUI::Widget* widget, short id) -{ - const ESM::MagicEffect* effect = - MWBase::Environment::get().getWorld ()->getStore ().get().find(id); - const std::string &name = ESM::MagicEffect::effectIdToString (id); - - std::string icon = effect->mIcon; - - int slashPos = icon.find("\\"); - icon.insert(slashPos+1, "b_"); - - icon[icon.size()-3] = 'd'; - icon[icon.size()-2] = 'd'; - icon[icon.size()-1] = 's'; - - icon = "icons\\" + icon; - - std::vector schools; - schools.push_back ("#{sSchoolAlteration}"); - schools.push_back ("#{sSchoolConjuration}"); - schools.push_back ("#{sSchoolDestruction}"); - schools.push_back ("#{sSchoolIllusion}"); - schools.push_back ("#{sSchoolMysticism}"); - schools.push_back ("#{sSchoolRestoration}"); - - widget->setUserString("ToolTipType", "Layout"); - widget->setUserString("ToolTipLayout", "MagicEffectToolTip"); - widget->setUserString("Caption_MagicEffectName", "#{" + name + "}"); - widget->setUserString("Caption_MagicEffectDescription", effect->mDescription); - widget->setUserString("Caption_MagicEffectSchool", "#{sSchool}: " + schools[effect->mData.mSchool]); - widget->setUserString("ImageTexture_MagicEffectImage", icon); -} + void ToolTips::setDelay(float delay) + { + mDelay = delay; + mRemainingDelay = mDelay; + } -void ToolTips::setDelay(float delay) -{ - mDelay = delay; - mRemainingDelay = mDelay; } diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index 4048d0d5a..f8f256167 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -15,25 +15,31 @@ namespace MWGui public: ToolTipInfo() : isPotion(false) + , imageSize(32) + , wordWrap(true) + , remainingEnchantCharge(-1) {} std::string caption; std::string text; std::string icon; + int imageSize; // enchantment (for cloth, armor, weapons) std::string enchant; + int remainingEnchantCharge; // effects (for potions, ingredients) Widgets::SpellEffectList effects; bool isPotion; // potions do not show target in the tooltip + bool wordWrap; }; class ToolTips : public OEngine::GUI::Layout { public: - ToolTips(MWBase::WindowManager* windowManager); + ToolTips(); void onFrame(float frameDuration); @@ -76,8 +82,6 @@ namespace MWGui private: MyGUI::Widget* mDynamicToolTipBox; - MWBase::WindowManager* mWindowManager; - MWWorld::Ptr mFocusObject; void findImageExtension(std::string& image); @@ -90,9 +94,9 @@ namespace MWGui float mFocusToolTipX; float mFocusToolTipY; - + int mHorizontalScrollIndex; - + float mDelay; float mRemainingDelay; // remaining time until tooltip will show diff --git a/apps/openmw/mwgui/tradeitemmodel.cpp b/apps/openmw/mwgui/tradeitemmodel.cpp new file mode 100644 index 000000000..b4fa4e16f --- /dev/null +++ b/apps/openmw/mwgui/tradeitemmodel.cpp @@ -0,0 +1,198 @@ +#include "tradeitemmodel.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/containerstore.hpp" +#include "../mwworld/inventorystore.hpp" + +namespace MWGui +{ + + TradeItemModel::TradeItemModel(ItemModel *sourceModel, const MWWorld::Ptr& merchant) + : mMerchant(merchant) + { + mSourceModel = sourceModel; + } + + ItemStack TradeItemModel::getItem (ModelIndex index) + { + if (index < 0) + throw std::runtime_error("Invalid index supplied"); + if (mItems.size() <= static_cast(index)) + throw std::runtime_error("Item index out of range"); + return mItems[index]; + } + + size_t TradeItemModel::getItemCount() + { + return mItems.size(); + } + + void TradeItemModel::borrowImpl(const ItemStack &item, std::vector &out) + { + std::vector::iterator it = out.begin(); + bool found = false; + for (; it != out.end(); ++it) + { + if (it->stacks(item)) + { + it->mCount += item.mCount; + found = true; + break; + } + } + if (!found) + out.push_back(item); + } + + void TradeItemModel::unborrowImpl(const ItemStack &item, size_t count, std::vector &out) + { + std::vector::iterator it = out.begin(); + bool found = false; + for (; it != out.end(); ++it) + { + if (it->stacks(item)) + { + if (it->mCount < count) + throw std::runtime_error("Not enough borrowed items to return"); + it->mCount -= count; + if (it->mCount == 0) + out.erase(it); + found = true; + break; + } + } + if (!found) + throw std::runtime_error("Can't find borrowed item to return"); + } + + void TradeItemModel::borrowItemFromUs (ModelIndex itemIndex, size_t count) + { + ItemStack item = getItem(itemIndex); + item.mCount = count; + borrowImpl(item, mBorrowedFromUs); + } + + void TradeItemModel::borrowItemToUs (ModelIndex itemIndex, ItemModel* source, size_t count) + { + ItemStack item = source->getItem(itemIndex); + item.mCount = count; + borrowImpl(item, mBorrowedToUs); + } + + void TradeItemModel::returnItemBorrowedToUs (ModelIndex itemIndex, size_t count) + { + ItemStack item = getItem(itemIndex); + unborrowImpl(item, count, mBorrowedToUs); + } + + void TradeItemModel::returnItemBorrowedFromUs (ModelIndex itemIndex, ItemModel* source, size_t count) + { + ItemStack item = source->getItem(itemIndex); + unborrowImpl(item, count, mBorrowedFromUs); + } + + void TradeItemModel::abort() + { + mBorrowedFromUs.clear(); + mBorrowedToUs.clear(); + } + + std::vector TradeItemModel::getItemsBorrowedToUs() + { + return mBorrowedToUs; + } + + void TradeItemModel::transferItems() + { + std::vector::iterator it = mBorrowedToUs.begin(); + for (; it != mBorrowedToUs.end(); ++it) + { + // get index in the source model + ItemModel* sourceModel = it->mCreator; + size_t i=0; + for (; igetItemCount(); ++i) + { + if (it->stacks(sourceModel->getItem(i))) + break; + } + if (i == sourceModel->getItemCount()) + throw std::runtime_error("The borrowed item disappeared"); + + // reset owner before copying + const ItemStack& item = sourceModel->getItem(i); + std::string owner = item.mBase.getCellRef().mOwner; + if (mMerchant.isEmpty()) // only for items bought by player + item.mBase.getCellRef().mOwner = ""; + // copy the borrowed items to our model + copyItem(item, it->mCount); + item.mBase.getCellRef().mOwner = owner; + // then remove them from the source model + sourceModel->removeItem(item, it->mCount); + } + mBorrowedToUs.clear(); + mBorrowedFromUs.clear(); + } + + void TradeItemModel::update() + { + mSourceModel->update(); + + int services = 0; + if (!mMerchant.isEmpty()) + services = MWWorld::Class::get(mMerchant).getServices(mMerchant); + + mItems.clear(); + // add regular items + for (size_t i=0; igetItemCount(); ++i) + { + ItemStack item = mSourceModel->getItem(i); + MWWorld::Ptr base = item.mBase; + if (!mMerchant.isEmpty() && Misc::StringUtils::ciEqual(base.getCellRef().mRefID, "gold_001")) + continue; + if (!mMerchant.isEmpty() && !MWWorld::Class::get(base).canSell(base, services)) + continue; + + // don't show equipped items + if (mMerchant.getTypeName() == typeid(ESM::NPC).name()) + { + bool isEquipped = false; + MWWorld::InventoryStore& store = MWWorld::Class::get(mMerchant).getInventoryStore(mMerchant); + for (int slot=0; slot::iterator it = mBorrowedFromUs.begin(); + for (; it != mBorrowedFromUs.end(); ++it) + { + if (it->stacks(item)) + { + if (item.mCount < it->mCount) + throw std::runtime_error("Lent more items than present"); + item.mCount -= it->mCount; + } + } + + if (item.mCount > 0) + mItems.push_back(item); + } + + // add items borrowed to us + std::vector::iterator it = mBorrowedToUs.begin(); + for (; it != mBorrowedToUs.end(); ++it) + { + ItemStack item = *it; + item.mType = ItemStack::Type_Barter; + mItems.push_back(item); + } + } + +} diff --git a/apps/openmw/mwgui/tradeitemmodel.hpp b/apps/openmw/mwgui/tradeitemmodel.hpp new file mode 100644 index 000000000..5cfaaafc7 --- /dev/null +++ b/apps/openmw/mwgui/tradeitemmodel.hpp @@ -0,0 +1,53 @@ +#ifndef MWGUI_TRADE_ITEM_MODEL_H +#define MWGUI_TRADE_ITEM_MODEL_H + +#include "itemmodel.hpp" + +namespace MWGui +{ + + class ItemModel; + + /// @brief An item model that allows 'borrowing' items from another item model. Used for previewing barter offers. + /// Also filters items that the merchant does not sell. + class TradeItemModel : public ProxyItemModel + { + public: + TradeItemModel (ItemModel* sourceModel, const MWWorld::Ptr& merchant); + + virtual ItemStack getItem (ModelIndex index); + virtual size_t getItemCount(); + + virtual void update(); + + void borrowItemFromUs (ModelIndex itemIndex, size_t count); + + void borrowItemToUs (ModelIndex itemIndex, ItemModel* source, size_t count); + ///< @note itemIndex points to an item in \a source + + void returnItemBorrowedToUs (ModelIndex itemIndex, size_t count); + + void returnItemBorrowedFromUs (ModelIndex itemIndex, ItemModel* source, size_t count); + + /// Permanently transfers items that were borrowed to us from another model to this model + void transferItems (); + /// Aborts trade + void abort(); + + std::vector getItemsBorrowedToUs(); + + private: + void borrowImpl(const ItemStack& item, std::vector& out); + void unborrowImpl(const ItemStack& item, size_t count, std::vector& out); + + std::vector mItems; + + std::vector mBorrowedToUs; + std::vector mBorrowedFromUs; + + MWWorld::Ptr mMerchant; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 51eb3d87e..01f7e2419 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -9,8 +9,9 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.hpp" -#include "../mwworld/inventorystore.hpp" #include "../mwworld/manualref.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/containerstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" @@ -18,20 +19,24 @@ #include "../mwworld/player.hpp" #include "inventorywindow.hpp" +#include "itemview.hpp" +#include "sortfilteritemmodel.hpp" +#include "containeritemmodel.hpp" +#include "tradeitemmodel.hpp" +#include "countdialog.hpp" namespace MWGui { - TradeWindow::TradeWindow(MWBase::WindowManager& parWindowManager) : - WindowBase("openmw_trade_window.layout", parWindowManager) - , ContainerBase(NULL) // no drag&drop + const float TradeWindow::sBalanceChangeInitialPause = 0.5; + const float TradeWindow::sBalanceChangeInterval = 0.1; + + TradeWindow::TradeWindow() + : WindowBase("openmw_trade_window.layout") , mCurrentBalance(0) + , mBalanceButtonsState(BBS_None) + , mBalanceChangePause(0.0) + , mItemToSell(-1) { - MyGUI::ScrollView* itemView; - MyGUI::Widget* containerWidget; - getWidget(containerWidget, "Items"); - getWidget(itemView, "ItemView"); - setWidgets(containerWidget, itemView); - getWidget(mFilterAll, "AllButton"); getWidget(mFilterWeapon, "WeaponButton"); getWidget(mFilterApparel, "ApparelButton"); @@ -49,6 +54,9 @@ namespace MWGui getWidget(mTotalBalanceLabel, "TotalBalanceLabel"); getWidget(mBottomPane, "BottomPane"); + getWidget(mItemView, "ItemView"); + mItemView->eventItemClicked += MyGUI::newDelegate(this, &TradeWindow::onItemSelected); + mFilterAll->setStateSelected(true); mFilterAll->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged); @@ -59,44 +67,49 @@ namespace MWGui mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onCancelButtonClicked); mOfferButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onOfferButtonClicked); - mIncreaseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onIncreaseButtonClicked); - mDecreaseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onDecreaseButtonClicked); + mMaxSaleButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onMaxSaleButtonClicked); + mIncreaseButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &TradeWindow::onIncreaseButtonPressed); + mIncreaseButton->eventMouseButtonReleased += MyGUI::newDelegate(this, &TradeWindow::onBalanceButtonReleased); + mDecreaseButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &TradeWindow::onDecreaseButtonPressed); + mDecreaseButton->eventMouseButtonReleased += MyGUI::newDelegate(this, &TradeWindow::onBalanceButtonReleased); setCoord(400, 0, 400, 300); - - static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &TradeWindow::onWindowResize); } - void TradeWindow::startTrade(MWWorld::Ptr actor) + void TradeWindow::startTrade(const MWWorld::Ptr& actor) { + mPtr = actor; setTitle(MWWorld::Class::get(actor).getName(actor)); mCurrentBalance = 0; mCurrentMerchantOffer = 0; - mWindowManager.getInventoryWindow()->startTrade(); + std::vector itemSources; + MWBase::Environment::get().getWorld()->getContainersOwnedBy(actor, itemSources); + // Important: actor goes last, so that items purchased by the merchant go into his inventory + itemSources.push_back(actor); + std::vector worldItems; + MWBase::Environment::get().getWorld()->getItemsOwnedBy(actor, worldItems); - mBoughtItems.clear(); - - ContainerBase::openContainer(actor); + mTradeModel = new TradeItemModel(new ContainerItemModel(itemSources, worldItems), mPtr); + mSortModel = new SortFilterItemModel(mTradeModel); + mItemView->setModel (mSortModel); updateLabels(); - - drawItems(); } void TradeWindow::onFilterChanged(MyGUI::Widget* _sender) { if (_sender == mFilterAll) - setFilter(ContainerBase::Filter_All); + mSortModel->setCategory(SortFilterItemModel::Category_All); else if (_sender == mFilterWeapon) - setFilter(ContainerBase::Filter_Weapon); + mSortModel->setCategory(SortFilterItemModel::Category_Weapon); else if (_sender == mFilterApparel) - setFilter(ContainerBase::Filter_Apparel); + mSortModel->setCategory(SortFilterItemModel::Category_Apparel); else if (_sender == mFilterMagic) - setFilter(ContainerBase::Filter_Magic); + mSortModel->setCategory(SortFilterItemModel::Category_Magic); else if (_sender == mFilterMisc) - setFilter(ContainerBase::Filter_Misc); + mSortModel->setCategory(SortFilterItemModel::Category_Misc); mFilterAll->setStateSelected(false); mFilterWeapon->setStateSelected(false); @@ -105,26 +118,96 @@ namespace MWGui mFilterMisc->setStateSelected(false); static_cast(_sender)->setStateSelected(true); + + mItemView->update(); + } + + int TradeWindow::getMerchantServices() + { + return MWWorld::Class::get(mPtr).getServices(mPtr); + } + + void TradeWindow::onItemSelected (int index) + { + const ItemStack& item = mSortModel->getItem(index); + + MWWorld::Ptr object = item.mBase; + int count = item.mCount; + bool shift = MyGUI::InputManager::getInstance().isShiftPressed(); + if (MyGUI::InputManager::getInstance().isControlPressed()) + count = 1; + + if (count > 1 && !shift) + { + CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); + std::string message = "#{sQuanityMenuMessage02}"; + dialog->open(MWWorld::Class::get(object).getName(object), message, count); + dialog->eventOkClicked.clear(); + dialog->eventOkClicked += MyGUI::newDelegate(this, &TradeWindow::sellItem); + mItemToSell = mSortModel->mapToSource(index); + } + else + { + mItemToSell = mSortModel->mapToSource(index); + sellItem (NULL, count); + } + } + + void TradeWindow::sellItem(MyGUI::Widget* sender, int count) + { + const ItemStack& item = mTradeModel->getItem(mItemToSell); + std::string sound = MWWorld::Class::get(item.mBase).getDownSoundId(item.mBase); + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); + + TradeItemModel* playerTradeModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel(); + + if (item.mType == ItemStack::Type_Barter) + { + // this was an item borrowed to us by the player + mTradeModel->returnItemBorrowedToUs(mItemToSell, count); + playerTradeModel->returnItemBorrowedFromUs(mItemToSell, mTradeModel, count); + buyFromNpc(item.mBase, count, true); + } + else + { + // borrow item to player + playerTradeModel->borrowItemToUs(mItemToSell, mTradeModel, count); + mTradeModel->borrowItemFromUs(mItemToSell, count); + buyFromNpc(item.mBase, count, false); + } + + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); + mItemView->update(); } - void TradeWindow::onWindowResize(MyGUI::Window* _sender) + void TradeWindow::borrowItem (int index, size_t count) { - drawItems(); + TradeItemModel* playerTradeModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel(); + mTradeModel->borrowItemToUs(index, playerTradeModel, count); + mItemView->update(); + sellToNpc(playerTradeModel->getItem(index).mBase, count, false); + } + + void TradeWindow::returnItem (int index, size_t count) + { + TradeItemModel* playerTradeModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel(); + const ItemStack& item = playerTradeModel->getItem(index); + mTradeModel->returnItemBorrowedFromUs(index, playerTradeModel, count); + mItemView->update(); + sellToNpc(item.mBase, count, true); } void TradeWindow::addOrRemoveGold(int amount) { bool goldFound = false; MWWorld::Ptr gold; - MWWorld::ContainerStore& playerStore = mWindowManager.getInventoryWindow()->getContainerStore(); - - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::ContainerStore& playerStore = MWWorld::Class::get(player).getContainerStore(player); for (MWWorld::ContainerStoreIterator it = playerStore.begin(); it != playerStore.end(); ++it) { - if (MWWorld::Class::get(*it).getName(*it) == gmst.find("sGold")->getString()) + if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, "gold_001")) { goldFound = true; gold = *it; @@ -143,51 +226,54 @@ namespace MWGui } } + void TradeWindow::onFrame(float frameDuration) + { + if (!mMainWidget->getVisible() || mBalanceButtonsState == BBS_None) + return; + + mBalanceChangePause -= frameDuration; + if (mBalanceChangePause < 0.0) { + mBalanceChangePause += sBalanceChangeInterval; + if (mBalanceButtonsState == BBS_Increase) + onIncreaseButtonTriggered(); + else if (mBalanceButtonsState == BBS_Decrease) + onDecreaseButtonTriggered(); + } + } + void TradeWindow::onOfferButtonClicked(MyGUI::Widget* _sender) { + TradeItemModel* playerItemModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel(); + const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); // were there any items traded at all? - MWWorld::ContainerStore& playerBought = mWindowManager.getInventoryWindow()->getBoughtItems(); - MWWorld::ContainerStore& merchantBought = getBoughtItems(); - if (playerBought.begin() == playerBought.end() && merchantBought.begin() == merchantBought.end()) + std::vector playerBought = playerItemModel->getItemsBorrowedToUs(); + std::vector merchantBought = mTradeModel->getItemsBorrowedToUs(); + if (!playerBought.size() && !merchantBought.size()) { // user notification MWBase::Environment::get().getWindowManager()-> - messageBox("#{sBarterDialog11}", std::vector()); + messageBox("#{sBarterDialog11}"); return; } // check if the player can afford this - if (mCurrentBalance < 0 && mWindowManager.getInventoryWindow()->getPlayerGold() < std::abs(mCurrentBalance)) + if (mCurrentBalance < 0 && MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold() < std::abs(mCurrentBalance)) { // user notification MWBase::Environment::get().getWindowManager()-> - messageBox("#{sBarterDialog1}", std::vector()); + messageBox("#{sBarterDialog1}"); return; } // check if the merchant can afford this - int merchantgold; - if (mPtr.getTypeName() == typeid(ESM::NPC).name()) - { - MWWorld::LiveCellRef* ref = mPtr.get(); - if (ref->mBase->mNpdt52.mGold == -10) - merchantgold = ref->mBase->mNpdt12.mGold; - else - merchantgold = ref->mBase->mNpdt52.mGold; - } - else // ESM::Creature - { - MWWorld::LiveCellRef* ref = mPtr.get(); - merchantgold = ref->mBase->mData.mGold; - } - if (mCurrentBalance > 0 && merchantgold < mCurrentBalance) + if (mCurrentBalance > 0 && getMerchantGold() < mCurrentBalance) { // user notification MWBase::Environment::get().getWindowManager()-> - messageBox("#{sBarterDialog2}", std::vector()); + messageBox("#{sBarterDialog2}"); return; } @@ -197,15 +283,17 @@ namespace MWGui if (mPtr.getTypeName() != typeid(ESM::NPC).name()) { MWBase::Environment::get().getWindowManager()-> - messageBox("#{sNotifyMessage9}", std::vector()); + messageBox("#{sNotifyMessage9}"); return; } int a = abs(mCurrentMerchantOffer); int b = abs(mCurrentBalance); int d = 0; - if (mCurrentMerchantOffer<0) d = int(100 * (a - b) / a); - else d = int(100 * (b - a) / a); + if (mCurrentBalance<0) + d = int(100 * (a - b) / a); + else + d = int(100 * (b - a) / a); float clampedDisposition = std::max(0,std::min(int(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr) + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange()),100)); @@ -226,14 +314,16 @@ namespace MWGui float pcTerm = (clampedDisposition - 50 + a1 + b1 + c1) * playerStats.getFatigueTerm(); float npcTerm = (d1 + e1 + f1) * sellerStats.getFatigueTerm(); float x = gmst.find("fBargainOfferMulti")->getFloat() * d + gmst.find("fBargainOfferBase")->getFloat(); - if (mCurrentMerchantOffer<0) x += abs(int(pcTerm - npcTerm)); - else x += abs(int(npcTerm - pcTerm)); + if (mCurrentBalance<0) + x += abs(int(pcTerm - npcTerm)); + else + x += abs(int(npcTerm - pcTerm)); int roll = std::rand()%100 + 1; if(roll > x) //trade refused { MWBase::Environment::get().getWindowManager()-> - messageBox("#{sNotifyMessage9}", std::vector()); + messageBox("#{sNotifyMessage9}"); int iBarterFailDisposition = gmst.find("iBarterFailDisposition")->getInt(); MWBase::Environment::get().getDialogueManager()->applyTemporaryDispositionChange(iBarterFailDisposition); @@ -242,14 +332,14 @@ namespace MWGui //skill use! MWWorld::Class::get(playerPtr).skillUsageSucceeded(playerPtr, ESM::Skill::Mercantile, 0); - } + } int iBarterSuccessDisposition = gmst.find("iBarterSuccessDisposition")->getInt(); MWBase::Environment::get().getDialogueManager()->applyTemporaryDispositionChange(iBarterSuccessDisposition); - // success! make the item transfer. - transferBoughtItems(); - mWindowManager.getInventoryWindow()->transferBoughtItems(); + // make the item transfer + mTradeModel->transferItems(); + playerItemModel->transferItems(); // add or remove gold from the player. if (mCurrentBalance != 0) @@ -258,27 +348,49 @@ namespace MWGui std::string sound = "Item Gold Up"; MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - mWindowManager.removeGuiMode(GM_Barter); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); } void TradeWindow::onCancelButtonClicked(MyGUI::Widget* _sender) { - // i give you back your stuff! - returnBoughtItems(mWindowManager.getInventoryWindow()->getContainerStore()); - // now gimme back my stuff! - mWindowManager.getInventoryWindow()->returnBoughtItems(MWWorld::Class::get(mPtr).getContainerStore(mPtr)); + mTradeModel->abort(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel()->abort(); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); + } + + void TradeWindow::onMaxSaleButtonClicked(MyGUI::Widget* _sender) + { + mCurrentBalance = getMerchantGold(); + updateLabels(); + } - mWindowManager.removeGuiMode(GM_Barter); + void TradeWindow::onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) + { + mBalanceButtonsState = BBS_Increase; + mBalanceChangePause = sBalanceChangeInitialPause; + onIncreaseButtonTriggered(); } - void TradeWindow::onIncreaseButtonClicked(MyGUI::Widget* _sender) + void TradeWindow::onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) + { + mBalanceButtonsState = BBS_Decrease; + mBalanceChangePause = sBalanceChangeInitialPause; + onDecreaseButtonTriggered(); + } + + void TradeWindow::onBalanceButtonReleased(MyGUI::Widget *_sender, int _left, int _top, MyGUI::MouseButton _id) + { + mBalanceButtonsState = BBS_None; + } + + void TradeWindow::onIncreaseButtonTriggered() { if(mCurrentBalance<=-1) mCurrentBalance -= 1; if(mCurrentBalance>=1) mCurrentBalance += 1; updateLabels(); } - void TradeWindow::onDecreaseButtonClicked(MyGUI::Widget* _sender) + void TradeWindow::onDecreaseButtonTriggered() { if(mCurrentBalance<-1) mCurrentBalance += 1; if(mCurrentBalance>1) mCurrentBalance -= 1; @@ -287,7 +399,7 @@ namespace MWGui void TradeWindow::updateLabels() { - mPlayerGold->setCaptionWithReplacing("#{sYourGold} " + boost::lexical_cast(mWindowManager.getInventoryWindow()->getPlayerGold())); + mPlayerGold->setCaptionWithReplacing("#{sYourGold} " + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); if (mCurrentBalance > 0) { @@ -300,108 +412,10 @@ namespace MWGui mTotalBalance->setCaption(boost::lexical_cast(-mCurrentBalance)); } - int merchantgold; - if (mPtr.getTypeName() == typeid(ESM::NPC).name()) - { - MWWorld::LiveCellRef* ref = mPtr.get(); - if (ref->mBase->mNpdt52.mGold == -10) - merchantgold = ref->mBase->mNpdt12.mGold; - else - merchantgold = ref->mBase->mNpdt52.mGold; - } - else // ESM::Creature - { - MWWorld::LiveCellRef* ref = mPtr.get(); - merchantgold = ref->mBase->mData.mGold; - } - - mMerchantGold->setCaptionWithReplacing("#{sSellerGold} " + boost::lexical_cast(merchantgold)); - } - - std::vector TradeWindow::getEquippedItems() - { - std::vector items; - - if (mPtr.getTypeName() == typeid(ESM::Creature).name()) - { - // creatures don't have equipment slots. - return items; - } - - MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); - - for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) - { - MWWorld::ContainerStoreIterator it = invStore.getSlot(slot); - if (it != invStore.end()) - { - items.push_back(*it); - } - } - - return items; + mMerchantGold->setCaptionWithReplacing("#{sSellerGold} " + boost::lexical_cast(getMerchantGold())); } - bool TradeWindow::npcAcceptsItem(MWWorld::Ptr item) - { - int services = 0; - if (mPtr.getTypeName() == typeid(ESM::NPC).name()) - { - MWWorld::LiveCellRef* ref = mPtr.get(); - if (ref->mBase->mHasAI) - services = ref->mBase->mAiData.mServices; - } - else if (mPtr.getTypeName() == typeid(ESM::Creature).name()) - { - MWWorld::LiveCellRef* ref = mPtr.get(); - if (ref->mBase->mHasAI) - services = ref->mBase->mAiData.mServices; - } - - /// \todo what about potions, there doesn't seem to be a flag for them?? - - if (item.getTypeName() == typeid(ESM::Weapon).name()) - return services & ESM::NPC::Weapon; - else if (item.getTypeName() == typeid(ESM::Armor).name()) - return services & ESM::NPC::Armor; - else if (item.getTypeName() == typeid(ESM::Clothing).name()) - return services & ESM::NPC::Clothing; - else if (item.getTypeName() == typeid(ESM::Book).name()) - return services & ESM::NPC::Books; - else if (item.getTypeName() == typeid(ESM::Ingredient).name()) - return services & ESM::NPC::Ingredients; - else if (item.getTypeName() == typeid(ESM::Tool).name()) - return services & ESM::NPC::Picks; - else if (item.getTypeName() == typeid(ESM::Probe).name()) - return services & ESM::NPC::Probes; - else if (item.getTypeName() == typeid(ESM::Light).name()) - return services & ESM::NPC::Lights; - else if (item.getTypeName() == typeid(ESM::Apparatus).name()) - return services & ESM::NPC::Apparatus; - else if (item.getTypeName() == typeid(ESM::Repair).name()) - return services & ESM::NPC::RepairItem; - else if (item.getTypeName() == typeid(ESM::Miscellaneous).name()) - return services & ESM::NPC::Misc; - - return false; - } - - std::vector TradeWindow::itemsToIgnore() - { - std::vector items; - MWWorld::ContainerStore& invStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - - for (MWWorld::ContainerStoreIterator it = invStore.begin(); - it != invStore.end(); ++it) - { - if (!npcAcceptsItem(*it)) - items.push_back(*it); - } - - return items; - } - - void TradeWindow::sellToNpc(MWWorld::Ptr item, int count, bool boughtItem) + void TradeWindow::sellToNpc(const MWWorld::Ptr& item, int count, bool boughtItem) { int diff = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, MWWorld::Class::get(item).getValue(item) * count, boughtItem); @@ -411,7 +425,7 @@ namespace MWGui updateLabels(); } - void TradeWindow::buyFromNpc(MWWorld::Ptr item, int count, bool soldItem) + void TradeWindow::buyFromNpc(const MWWorld::Ptr& item, int count, bool soldItem) { int diff = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, MWWorld::Class::get(item).getValue(item) * count, !soldItem); @@ -424,7 +438,28 @@ namespace MWGui void TradeWindow::onReferenceUnavailable() { // remove both Trade and Dialogue (since you always trade with the NPC/creature that you have previously talked to) - mWindowManager.removeGuiMode(GM_Barter); - mWindowManager.removeGuiMode(GM_Dialogue); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); + } + + int TradeWindow::getMerchantGold() + { + int merchantGold; + + if (mPtr.getTypeName() == typeid(ESM::NPC).name()) + { + MWWorld::LiveCellRef* ref = mPtr.get(); + if (ref->mBase->mNpdt52.mGold == -10) + merchantGold = ref->mBase->mNpdt12.mGold; + else + merchantGold = ref->mBase->mNpdt52.mGold; + } + else // ESM::Creature + { + MWWorld::LiveCellRef* ref = mPtr.get(); + merchantGold = ref->mBase->mData.mGold; + } + + return merchantGold; } } diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index 219e44036..4e905915a 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -2,9 +2,6 @@ #define MWGUI_TRADEWINDOW_H #include "container.hpp" -#include "window_base.hpp" - -#include "../mwworld/ptr.hpp" namespace MyGUI { @@ -20,21 +17,35 @@ namespace MWGui namespace MWGui { - class TradeWindow : public ContainerBase, public WindowBase + class ItemView; + class SortFilterItemModel; + class TradeItemModel; + + class TradeWindow : public WindowBase, public ReferenceInterface { public: - TradeWindow(MWBase::WindowManager& parWindowManager); + TradeWindow(); - void startTrade(MWWorld::Ptr actor); + void startTrade(const MWWorld::Ptr& actor); - void sellToNpc(MWWorld::Ptr item, int count, bool boughtItem); ///< only used for adjusting the gold balance - void buyFromNpc(MWWorld::Ptr item, int count, bool soldItem); ///< only used for adjusting the gold balance + void addOrRemoveGold(int gold); - bool npcAcceptsItem(MWWorld::Ptr item); + void onFrame(float frameDuration); - void addOrRemoveGold(int gold); + void borrowItem (int index, size_t count); + void returnItem (int index, size_t count); + + int getMerchantServices(); + + + private: + ItemView* mItemView; + SortFilterItemModel* mSortModel; + TradeItemModel* mTradeModel; + + static const float sBalanceChangeInitialPause; // in seconds + static const float sBalanceChangeInterval; // in seconds - protected: MyGUI::Button* mFilterAll; MyGUI::Button* mFilterWeapon; MyGUI::Button* mFilterApparel; @@ -54,28 +65,41 @@ namespace MWGui MyGUI::TextBox* mPlayerGold; MyGUI::TextBox* mMerchantGold; + int mItemToSell; + int mCurrentBalance; int mCurrentMerchantOffer; - void onWindowResize(MyGUI::Window* _sender); + enum BalanceButtonsState { + BBS_None, + BBS_Increase, + BBS_Decrease + } mBalanceButtonsState; + /// pause before next balance change will trigger while user holds +/- button pressed + float mBalanceChangePause; + + void sellToNpc(const MWWorld::Ptr& item, int count, bool boughtItem); ///< only used for adjusting the gold balance + void buyFromNpc(const MWWorld::Ptr& item, int count, bool soldItem); ///< only used for adjusting the gold balance + + void onItemSelected (int index); + void sellItem (MyGUI::Widget* sender, int count); + void onFilterChanged(MyGUI::Widget* _sender); void onOfferButtonClicked(MyGUI::Widget* _sender); void onCancelButtonClicked(MyGUI::Widget* _sender); - void onIncreaseButtonClicked(MyGUI::Widget* _sender); - void onDecreaseButtonClicked(MyGUI::Widget* _sender); + void onMaxSaleButtonClicked(MyGUI::Widget* _sender); + void onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); + void onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); + void onBalanceButtonReleased(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); - // don't show items that the NPC has equipped in his trade-window. - virtual bool ignoreEquippedItems() { return true; } - virtual std::vector getEquippedItems(); - - virtual bool isTrading() { return true; } - virtual bool isTradeWindow() { return true; } - - virtual std::vector itemsToIgnore(); + void onIncreaseButtonTriggered(); + void onDecreaseButtonTriggered(); void updateLabels(); virtual void onReferenceUnavailable(); + + int getMerchantGold(); }; } diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index ba39ee601..7ddac38f5 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -10,6 +10,7 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" #include "../mwmechanics/npcstats.hpp" @@ -20,8 +21,8 @@ namespace MWGui { - TrainingWindow::TrainingWindow(MWBase::WindowManager &parWindowManager) - : WindowBase("openmw_trainingwindow.layout", parWindowManager) + TrainingWindow::TrainingWindow() + : WindowBase("openmw_trainingwindow.layout") , mFadeTimeRemaining(0) { getWidget(mTrainingOptions, "TrainingOptions"); @@ -40,7 +41,7 @@ namespace MWGui { mPtr = actor; - mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(mWindowManager.getInventoryWindow()->getPlayerGold())); + mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); MWMechanics::NpcStats& npcStats = MWWorld::Class::get(actor).getNpcStats (actor); @@ -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 > mWindowManager.getInventoryWindow ()->getPlayerGold ()) ? "SandTextGreyedOut" : "SandTextButton"; + std::string skin = (price > MWBase::Environment::get().getWindowManager()->getInventoryWindow ()->getPlayerGold ()) ? "SandTextGreyedOut" : "SandTextButton"; MyGUI::Button* button = mTrainingOptions->createWidget(skin, MyGUI::IntCoord(5, 5+i*18, mTrainingOptions->getWidth()-10, 18), MyGUI::Align::Default); @@ -102,12 +103,12 @@ namespace MWGui void TrainingWindow::onReferenceUnavailable () { - mWindowManager.removeGuiMode(GM_Training); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Training); } void TrainingWindow::onCancelButtonClicked (MyGUI::Widget *sender) { - mWindowManager.removeGuiMode (GM_Training); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Training); } void TrainingWindow::onTrainingSelected (MyGUI::Widget *sender) @@ -123,13 +124,13 @@ namespace MWGui int price = pcStats.getSkill (skillId).getBase() * store.get().find("iTrainingMod")->getInt (); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); - if (mWindowManager.getInventoryWindow()->getPlayerGold()getInventoryWindow()->getPlayerGold()()); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sServiceTrainingWords}"); return; } @@ -141,11 +142,11 @@ namespace MWGui pcStats.increaseSkill (skillId, *class_, true); // remove gold - mWindowManager.getTradeWindow()->addOrRemoveGold(-price); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-price); // go back to game mode - mWindowManager.removeGuiMode (GM_Training); - mWindowManager.removeGuiMode (GM_Dialogue); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Training); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue); // advance time MWBase::Environment::get().getWorld ()->advanceTime (2); diff --git a/apps/openmw/mwgui/trainingwindow.hpp b/apps/openmw/mwgui/trainingwindow.hpp index f2ef1714e..740115cdf 100644 --- a/apps/openmw/mwgui/trainingwindow.hpp +++ b/apps/openmw/mwgui/trainingwindow.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_TRAININGWINDOW_H #define MWGUI_TRAININGWINDOW_H -#include "window_base.hpp" +#include "windowbase.hpp" #include "referenceinterface.hpp" namespace MWGui @@ -10,7 +10,7 @@ namespace MWGui class TrainingWindow : public WindowBase, public ReferenceInterface { public: - TrainingWindow(MWBase::WindowManager& parWindowManager); + TrainingWindow(); virtual void open(); diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 9615e95f7..83e158e4a 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -1,22 +1,16 @@ #include "travelwindow.hpp" -#include - #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/player.hpp" -#include "../mwworld/manualref.hpp" - -#include "../mwmechanics/spells.hpp" -#include "../mwmechanics/creaturestats.hpp" +#include "../mwworld/class.hpp" #include "inventorywindow.hpp" #include "tradewindow.hpp" @@ -25,8 +19,8 @@ namespace MWGui { const int TravelWindow::sLineHeight = 18; - TravelWindow::TravelWindow(MWBase::WindowManager& parWindowManager) : - WindowBase("openmw_travel_window.layout", parWindowManager) + TravelWindow::TravelWindow() : + WindowBase("openmw_travel_window.layout") , mCurrentY(0) , mLastPos(0) { @@ -71,7 +65,7 @@ namespace MWGui price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); - MyGUI::Button* toAdd = mDestinationsView->createWidget((price>mWindowManager.getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); + MyGUI::Button* toAdd = mDestinationsView->createWidget((price>MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); mCurrentY += sLineHeight; if(interior) toAdd->setUserString("interior","y"); @@ -129,9 +123,11 @@ namespace MWGui int price; iss >> price; - if (mWindowManager.getInventoryWindow()->getPlayerGold()getInventoryWindow()->getPlayerGold()getTradeWindow ()->addOrRemoveGold (-price); + MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(1); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); ESM::Position pos = *_sender->getUserData(); @@ -139,35 +135,38 @@ namespace MWGui int x,y; bool interior = _sender->getUserString("interior") == "y"; MWBase::Environment::get().getWorld()->positionToIndex(pos.pos[0],pos.pos[1],x,y); - MWWorld::CellStore* cell; - if(interior) cell = MWBase::Environment::get().getWorld()->getInterior(cellname); + if(interior) + MWBase::Environment::get().getWorld()->changeToInteriorCell(cellname, pos); else { - cell = MWBase::Environment::get().getWorld()->getExterior(x,y); - 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) ); - int time = int(d /MWBase::Environment::get().getWorld()->getStore().get().find("fTravelTimeMult")->getFloat()); - for(int i = 0;i < time;i++) + ESM::Position playerPos = player.getRefData().getPosition(); + float d = Ogre::Vector3(pos.pos[0], pos.pos[1], 0).distance( + Ogre::Vector3(playerPos.pos[0], playerPos.pos[1], 0)); + 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().getWorld()->advanceTime(time); + MWBase::Environment::get().getWorld()->advanceTime(hours); + + MWBase::Environment::get().getWorld()->changeToExteriorCell(pos); } - MWBase::Environment::get().getWorld()->moveObject(player,*cell,pos.pos[0],pos.pos[1],pos.pos[2]); - mWindowManager.removeGuiMode(GM_Travel); - mWindowManager.removeGuiMode(GM_Dialogue); + + MWWorld::Class::get(player).adjustPosition(player); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0); MWBase::Environment::get().getWorld ()->getFader ()->fadeIn(1); } void TravelWindow::onCancelButtonClicked(MyGUI::Widget* _sender) { - mWindowManager.removeGuiMode(GM_Travel); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); } void TravelWindow::updateLabels() { - mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(mWindowManager.getInventoryWindow()->getPlayerGold())); + mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); mPlayerGold->setCoord(8, mPlayerGold->getTop(), mPlayerGold->getTextSize().width, @@ -176,8 +175,8 @@ namespace MWGui void TravelWindow::onReferenceUnavailable() { - mWindowManager.removeGuiMode(GM_Travel); - mWindowManager.removeGuiMode(GM_Dialogue); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); } void TravelWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) diff --git a/apps/openmw/mwgui/travelwindow.hpp b/apps/openmw/mwgui/travelwindow.hpp index cc3d6a31f..f2a23b048 100644 --- a/apps/openmw/mwgui/travelwindow.hpp +++ b/apps/openmw/mwgui/travelwindow.hpp @@ -2,9 +2,6 @@ #define MWGUI_TravelWINDOW_H #include "container.hpp" -#include "window_base.hpp" - -#include "../mwworld/ptr.hpp" namespace MyGUI { @@ -23,7 +20,7 @@ namespace MWGui class TravelWindow : public ReferenceInterface, public WindowBase { public: - TravelWindow(MWBase::WindowManager& parWindowManager); + TravelWindow(); void startTravel(const MWWorld::Ptr& actor); diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 4f2c98c08..ad2b4710c 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -9,11 +9,10 @@ #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwworld/timestamp.hpp" #include "../mwworld/player.hpp" -#include "../mwworld/ptr.hpp" #include "../mwworld/class.hpp" +#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" #include "widgets.hpp" @@ -22,8 +21,8 @@ namespace MWGui { - WaitDialogProgressBar::WaitDialogProgressBar(MWBase::WindowManager &parWindowManager) - : WindowBase("openmw_wait_dialog_progressbar.layout", parWindowManager) + WaitDialogProgressBar::WaitDialogProgressBar() + : WindowBase("openmw_wait_dialog_progressbar.layout") { getWidget(mProgressBar, "ProgressBar"); getWidget(mProgressText, "ProgressText"); @@ -43,9 +42,9 @@ namespace MWGui // --------------------------------------------------------------------------------------------------------- - WaitDialog::WaitDialog(MWBase::WindowManager &parWindowManager) - : WindowBase("openmw_wait_dialog.layout", parWindowManager) - , mProgressBar(parWindowManager) + WaitDialog::WaitDialog() + : WindowBase("openmw_wait_dialog.layout") + , mProgressBar() , mWaiting(false) , mSleeping(false) , mHours(1) @@ -72,7 +71,7 @@ namespace MWGui { if (!MWBase::Environment::get().getWindowManager ()->getRestEnabled ()) { - mWindowManager.popGuiMode (); + MWBase::Environment::get().getWindowManager()->popGuiMode (); } int canRest = MWBase::Environment::get().getWorld ()->canRest (); @@ -80,8 +79,8 @@ namespace MWGui if (canRest == 2) { // resting underwater or mid-air not allowed - mWindowManager.messageBox ("#{sNotifyMessage1}", std::vector()); - mWindowManager.popGuiMode (); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage1}"); + MWBase::Environment::get().getWindowManager()->popGuiMode (); } setCanRest(canRest == 0); @@ -92,31 +91,46 @@ namespace MWGui // http://www.uesp.net/wiki/Lore:Calendar std::string month; int m = MWBase::Environment::get().getWorld ()->getMonth (); - if (m == 0) - month = "#{sMonthMorningstar}"; - else if (m == 1) - month = "#{sMonthSunsdawn}"; - else if (m == 2) - month = "#{sMonthFirstseed}"; - else if (m == 3) - month = "#{sMonthRainshand}"; - else if (m == 4) - month = "#{sMonthSecondseed}"; - else if (m == 5) - month = "#{sMonthMidyear}"; - else if (m == 6) - month = "#{sMonthSunsheight}"; - else if (m == 7) - month = "#{sMonthLastseed}"; - else if (m == 8) - month = "#{sMonthHeartfire}"; - else if (m == 9) - month = "#{sMonthFrostfall}"; - else if (m == 10) - month = "#{sMonthSunsdusk}"; - else if (m == 11) - month = "#{sMonthEveningstar}"; - + 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; + } int hour = MWBase::Environment::get().getWorld ()->getTimeStamp ().getHour (); bool pm = hour >= 12; if (hour >= 13) hour -= 12; @@ -124,7 +138,7 @@ namespace MWGui std::string dateTimeText = boost::lexical_cast(MWBase::Environment::get().getWorld ()->getDay ()) + " " - + month + " (#{sDay} " + boost::lexical_cast(MWBase::Environment::get().getWorld ()->getTimeStamp ().getDay ()+1) + + month + " (#{sDay} " + boost::lexical_cast(MWBase::Environment::get().getWorld ()->getTimeStamp ().getDay()) + ") " + boost::lexical_cast(hour) + " " + (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); mDateTimeText->setCaptionWithReplacing (dateTimeText); @@ -132,34 +146,75 @@ namespace MWGui void WaitDialog::onUntilHealedButtonClicked(MyGUI::Widget* sender) { - startWaiting(); + // 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()->getPlayer().getPlayer(); + 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(MWMechanics::EffectKey(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 + + startWaiting(autoHours); } void WaitDialog::onWaitButtonClicked(MyGUI::Widget* sender) { - startWaiting(); + startWaiting(mManualHours); } - void WaitDialog::startWaiting () + void WaitDialog::startWaiting(int hoursToWait) { MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0.2); setVisible(false); mProgressBar.setVisible (true); + mWaiting = true; mCurHour = 0; + mHours = hoursToWait; + mRemainingTime = 0.05; mProgressBar.setProgress (0, mHours); } void WaitDialog::onCancelButtonClicked(MyGUI::Widget* sender) { - mWindowManager.popGuiMode (); + MWBase::Environment::get().getWindowManager()->popGuiMode (); } void WaitDialog::onHourSliderChangedPosition(MyGUI::ScrollBar* sender, size_t position) { mHourText->setCaptionWithReplacing (boost::lexical_cast(position+1) + " #{sRestMenu2}"); - mHours = position+1; + mManualHours = position+1; } void WaitDialog::setCanRest (bool canRest) @@ -181,9 +236,9 @@ namespace MWGui mRemainingTime -= dt; - if (mRemainingTime < 0) + while (mRemainingTime < 0) { - mRemainingTime = 0.05; + mRemainingTime += 0.05; ++mCurHour; mProgressBar.setProgress (mCurHour, mHours); @@ -197,14 +252,15 @@ namespace MWGui if (mCurHour > mHours) stopWaiting(); + } void WaitDialog::stopWaiting () { MWBase::Environment::get().getWorld ()->getFader ()->fadeIn(0.2); mProgressBar.setVisible (false); - mWindowManager.removeGuiMode (GM_Rest); - mWindowManager.removeGuiMode (GM_RestBed); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Rest); + MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_RestBed); mWaiting = false; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); @@ -213,7 +269,7 @@ namespace MWGui // trigger levelup if possible if (mSleeping && pcstats.getLevelProgress () >= 10) { - mWindowManager.pushGuiMode (GM_Levelup); + MWBase::Environment::get().getWindowManager()->pushGuiMode (GM_Levelup); } } diff --git a/apps/openmw/mwgui/waitdialog.hpp b/apps/openmw/mwgui/waitdialog.hpp index 6af565c6e..d06d7d112 100644 --- a/apps/openmw/mwgui/waitdialog.hpp +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_WAIT_DIALOG_H #define MWGUI_WAIT_DIALOG_H -#include "window_base.hpp" +#include "windowbase.hpp" namespace MWGui { @@ -9,7 +9,7 @@ namespace MWGui class WaitDialogProgressBar : public WindowBase { public: - WaitDialogProgressBar(MWBase::WindowManager& parWindowManager); + WaitDialogProgressBar(); virtual void open(); @@ -23,7 +23,7 @@ namespace MWGui class WaitDialog : public WindowBase { public: - WaitDialog(MWBase::WindowManager& parWindowManager); + WaitDialog(); virtual void open(); @@ -47,6 +47,7 @@ namespace MWGui bool mSleeping; int mCurHour; int mHours; + int mManualHours; // stores the hours to rest selected via slider float mRemainingTime; WaitDialogProgressBar mProgressBar; @@ -58,7 +59,7 @@ namespace MWGui void setCanRest(bool canRest); - void startWaiting(); + void startWaiting(int hoursToWait); void stopWaiting(); }; diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index f932c1f03..1662c0597 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -2,887 +2,896 @@ #include +#include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/esmstore.hpp" - #undef min #undef max -using namespace MWGui; -using namespace MWGui::Widgets; - -/* Helper functions */ - -/* - * Fixes the filename of a texture path to use the correct .dds extension. - * This is needed on some ESM entries which point to a .tga file instead. - */ -void MWGui::Widgets::fixTexturePath(std::string &path) -{ - int offset = path.rfind("."); - if (offset < 0) - return; - path.replace(offset, path.length() - offset, ".dds"); -} - -/* MWSkill */ - -MWSkill::MWSkill() - : mManager(nullptr) - , mSkillId(ESM::Skill::Length) - , mSkillNameWidget(nullptr) - , mSkillValueWidget(nullptr) +namespace MWGui { -} + namespace Widgets + { -void MWSkill::setSkillId(ESM::Skill::SkillEnum skill) -{ - mSkillId = skill; - updateWidgets(); -} + /* Helper functions */ -void MWSkill::setSkillNumber(int skill) -{ - if (skill < 0) - setSkillId(ESM::Skill::Length); - else if (skill < ESM::Skill::Length) - setSkillId(static_cast(skill)); - else - throw new std::runtime_error("Skill number out of range"); -} + /* + * Fixes the filename of a texture path to use the correct .dds extension. + * This is needed on some ESM entries which point to a .tga file instead. + */ + void fixTexturePath(std::string &path) + { + int offset = path.rfind("."); + if (offset < 0) + return; + path.replace(offset, path.length() - offset, ".dds"); + } -void MWSkill::setSkillValue(const SkillValue& value) -{ - mValue = value; - updateWidgets(); -} + /* MWSkill */ -void MWSkill::updateWidgets() -{ - if (mSkillNameWidget && mManager) - { - if (mSkillId == ESM::Skill::Length) + MWSkill::MWSkill() + : mSkillId(ESM::Skill::Length) + , mSkillNameWidget(NULL) + , mSkillValueWidget(NULL) { - static_cast(mSkillNameWidget)->setCaption(""); } - else + + void MWSkill::setSkillId(ESM::Skill::SkillEnum skill) { - const std::string &name = mManager->getGameSettingString(ESM::Skill::sSkillNameIds[mSkillId], ""); - static_cast(mSkillNameWidget)->setCaption(name); + mSkillId = skill; + updateWidgets(); } - } - if (mSkillValueWidget) - { - SkillValue::Type modified = mValue.getModified(), base = mValue.getBase(); - static_cast(mSkillValueWidget)->setCaption(boost::lexical_cast(modified)); - if (modified > base) - mSkillValueWidget->_setWidgetState("increased"); - else if (modified < base) - mSkillValueWidget->_setWidgetState("decreased"); - else - mSkillValueWidget->_setWidgetState("normal"); - } -} - -void MWSkill::onClicked(MyGUI::Widget* _sender) -{ - eventClicked(this); -} -MWSkill::~MWSkill() -{ -} + void MWSkill::setSkillNumber(int skill) + { + if (skill < 0) + setSkillId(ESM::Skill::Length); + else if (skill < ESM::Skill::Length) + setSkillId(static_cast(skill)); + else + throw new std::runtime_error("Skill number out of range"); + } -void MWSkill::initialiseOverride() -{ - Base::initialiseOverride(); + void MWSkill::setSkillValue(const SkillValue& value) + { + mValue = value; + updateWidgets(); + } - assignWidget(mSkillNameWidget, "StatName"); - assignWidget(mSkillValueWidget, "StatValue"); + void MWSkill::updateWidgets() + { + if (mSkillNameWidget) + { + if (mSkillId == ESM::Skill::Length) + { + static_cast(mSkillNameWidget)->setCaption(""); + } + else + { + const std::string &name = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Skill::sSkillNameIds[mSkillId], ""); + static_cast(mSkillNameWidget)->setCaption(name); + } + } + if (mSkillValueWidget) + { + SkillValue::Type modified = mValue.getModified(), base = mValue.getBase(); + static_cast(mSkillValueWidget)->setCaption(boost::lexical_cast(modified)); + if (modified > base) + mSkillValueWidget->_setWidgetState("increased"); + else if (modified < base) + mSkillValueWidget->_setWidgetState("decreased"); + else + mSkillValueWidget->_setWidgetState("normal"); + } + } - MyGUI::ButtonPtr button; - assignWidget(button, "StatNameButton"); - if (button) - { - mSkillNameWidget = button; - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); - } + void MWSkill::onClicked(MyGUI::Widget* _sender) + { + eventClicked(this); + } - button = 0; - assignWidget(button, "StatValueButton"); - if (button) - { - mSkillNameWidget = button; - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); - } -} + MWSkill::~MWSkill() + { + } -/* MWAttribute */ + void MWSkill::initialiseOverride() + { + Base::initialiseOverride(); -MWAttribute::MWAttribute() - : mManager(nullptr) - , mId(-1) - , mAttributeNameWidget(nullptr) - , mAttributeValueWidget(nullptr) -{ -} + assignWidget(mSkillNameWidget, "StatName"); + assignWidget(mSkillValueWidget, "StatValue"); -void MWAttribute::setAttributeId(int attributeId) -{ - mId = attributeId; - updateWidgets(); -} + MyGUI::Button* button; + assignWidget(button, "StatNameButton"); + if (button) + { + mSkillNameWidget = button; + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); + } -void MWAttribute::setAttributeValue(const AttributeValue& value) -{ - mValue = value; - updateWidgets(); -} + button = 0; + assignWidget(button, "StatValueButton"); + if (button) + { + mSkillNameWidget = button; + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); + } + } -void MWAttribute::onClicked(MyGUI::Widget* _sender) -{ - eventClicked(this); -} + /* MWAttribute */ -void MWAttribute::updateWidgets() -{ - if (mAttributeNameWidget && mManager) - { - if (mId < 0 || mId >= 8) + MWAttribute::MWAttribute() + : mId(-1) + , mAttributeNameWidget(NULL) + , mAttributeValueWidget(NULL) { - static_cast(mAttributeNameWidget)->setCaption(""); } - else + + void MWAttribute::setAttributeId(int attributeId) { - static const char *attributes[8] = { - "sAttributeStrength", - "sAttributeIntelligence", - "sAttributeWillpower", - "sAttributeAgility", - "sAttributeSpeed", - "sAttributeEndurance", - "sAttributePersonality", - "sAttributeLuck" - }; - const std::string &name = mManager->getGameSettingString(attributes[mId], ""); - static_cast(mAttributeNameWidget)->setCaption(name); + mId = attributeId; + updateWidgets(); } - } - if (mAttributeValueWidget) - { - AttributeValue::Type modified = mValue.getModified(), base = mValue.getBase(); - static_cast(mAttributeValueWidget)->setCaption(boost::lexical_cast(modified)); - if (modified > base) - mAttributeValueWidget->_setWidgetState("increased"); - else if (modified < base) - mAttributeValueWidget->_setWidgetState("decreased"); - else - mAttributeValueWidget->_setWidgetState("normal"); - } -} - -MWAttribute::~MWAttribute() -{ -} -void MWAttribute::initialiseOverride() -{ - Base::initialiseOverride(); + void MWAttribute::setAttributeValue(const AttributeValue& value) + { + mValue = value; + updateWidgets(); + } - assignWidget(mAttributeNameWidget, "StatName"); - assignWidget(mAttributeValueWidget, "StatValue"); + void MWAttribute::onClicked(MyGUI::Widget* _sender) + { + eventClicked(this); + } - MyGUI::ButtonPtr button; - assignWidget(button, "StatNameButton"); - if (button) - { - mAttributeNameWidget = button; - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked); - } + void MWAttribute::updateWidgets() + { + if (mAttributeNameWidget) + { + if (mId < 0 || mId >= 8) + { + static_cast(mAttributeNameWidget)->setCaption(""); + } + else + { + static const char *attributes[8] = { + "sAttributeStrength", + "sAttributeIntelligence", + "sAttributeWillpower", + "sAttributeAgility", + "sAttributeSpeed", + "sAttributeEndurance", + "sAttributePersonality", + "sAttributeLuck" + }; + const std::string &name = MWBase::Environment::get().getWindowManager()->getGameSettingString(attributes[mId], ""); + static_cast(mAttributeNameWidget)->setCaption(name); + } + } + if (mAttributeValueWidget) + { + AttributeValue::Type modified = mValue.getModified(), base = mValue.getBase(); + static_cast(mAttributeValueWidget)->setCaption(boost::lexical_cast(modified)); + if (modified > base) + mAttributeValueWidget->_setWidgetState("increased"); + else if (modified < base) + mAttributeValueWidget->_setWidgetState("decreased"); + else + mAttributeValueWidget->_setWidgetState("normal"); + } + } - button = 0; - assignWidget(button, "StatValueButton"); - if (button) - { - mAttributeValueWidget = button; - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked); - } -} + MWAttribute::~MWAttribute() + { + } -/* MWSpell */ + void MWAttribute::initialiseOverride() + { + Base::initialiseOverride(); -MWSpell::MWSpell() - : mWindowManager(nullptr) - , mSpellNameWidget(nullptr) -{ -} + assignWidget(mAttributeNameWidget, "StatName"); + assignWidget(mAttributeValueWidget, "StatValue"); -void MWSpell::setSpellId(const std::string &spellId) -{ - mId = spellId; - updateWidgets(); -} + MyGUI::Button* button; + assignWidget(button, "StatNameButton"); + if (button) + { + mAttributeNameWidget = button; + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked); + } -void MWSpell::createEffectWidgets(std::vector &effects, MyGUI::WidgetPtr creator, MyGUI::IntCoord &coord, int flags) -{ - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); + button = 0; + assignWidget(button, "StatValueButton"); + if (button) + { + mAttributeValueWidget = button; + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked); + } + } - const ESM::Spell *spell = store.get().search(mId); - MYGUI_ASSERT(spell, "spell with id '" << mId << "' not found"); + /* MWSpell */ - MWSpellEffectPtr effect = nullptr; - std::vector::const_iterator end = spell->mEffects.mList.end(); - for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != end; ++it) - { - effect = creator->createWidget("MW_EffectImage", coord, MyGUI::Align::Default); - effect->setWindowManager(mWindowManager); - SpellEffectParams params; - params.mEffectID = it->mEffectID; - params.mSkill = it->mSkill; - params.mAttribute = it->mAttribute; - params.mDuration = it->mDuration; - params.mMagnMin = it->mMagnMin; - params.mMagnMax = it->mMagnMax; - params.mRange = it->mRange; - params.mIsConstant = (flags & MWEffectList::EF_Constant); - params.mNoTarget = (flags & MWEffectList::EF_NoTarget); - effect->setSpellEffect(params); - effects.push_back(effect); - coord.top += effect->getHeight(); - coord.width = std::max(coord.width, effect->getRequestedWidth()); - } -} + MWSpell::MWSpell() + : mSpellNameWidget(NULL) + { + } -void MWSpell::updateWidgets() -{ - if (mSpellNameWidget && mWindowManager) - { - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); - - const ESM::Spell *spell = store.get().search(mId); - if (spell) - static_cast(mSpellNameWidget)->setCaption(spell->mName); - else - static_cast(mSpellNameWidget)->setCaption(""); - } -} + void MWSpell::setSpellId(const std::string &spellId) + { + mId = spellId; + updateWidgets(); + } -void MWSpell::initialiseOverride() -{ - Base::initialiseOverride(); + void MWSpell::createEffectWidgets(std::vector &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, int flags) + { + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); - assignWidget(mSpellNameWidget, "StatName"); -} + const ESM::Spell *spell = store.get().search(mId); + MYGUI_ASSERT(spell, "spell with id '" << mId << "' not found"); -MWSpell::~MWSpell() -{ -} + MWSpellEffectPtr effect = NULL; + std::vector::const_iterator end = spell->mEffects.mList.end(); + for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != end; ++it) + { + effect = creator->createWidget("MW_EffectImage", coord, MyGUI::Align::Default); + SpellEffectParams params; + params.mEffectID = it->mEffectID; + params.mSkill = it->mSkill; + params.mAttribute = it->mAttribute; + params.mDuration = it->mDuration; + params.mMagnMin = it->mMagnMin; + params.mMagnMax = it->mMagnMax; + params.mRange = it->mRange; + params.mIsConstant = (flags & MWEffectList::EF_Constant); + params.mNoTarget = (flags & MWEffectList::EF_NoTarget); + effect->setSpellEffect(params); + effects.push_back(effect); + coord.top += effect->getHeight(); + coord.width = std::max(coord.width, effect->getRequestedWidth()); + } + } -/* MWEffectList */ + void MWSpell::updateWidgets() + { + if (mSpellNameWidget && MWBase::Environment::get().getWindowManager()) + { + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); + + const ESM::Spell *spell = store.get().search(mId); + if (spell) + static_cast(mSpellNameWidget)->setCaption(spell->mName); + else + static_cast(mSpellNameWidget)->setCaption(""); + } + } -MWEffectList::MWEffectList() - : mWindowManager(nullptr) - , mEffectList(0) -{ -} + void MWSpell::initialiseOverride() + { + Base::initialiseOverride(); -void MWEffectList::setEffectList(const SpellEffectList& list) -{ - mEffectList = list; - updateWidgets(); -} + assignWidget(mSpellNameWidget, "StatName"); + } -void MWEffectList::createEffectWidgets(std::vector &effects, MyGUI::WidgetPtr creator, MyGUI::IntCoord &coord, bool center, int flags) -{ - // We don't know the width of all the elements beforehand, so we do it in - // 2 steps: first, create all widgets and check their width.... - MWSpellEffectPtr effect = nullptr; - int maxwidth = coord.width; + MWSpell::~MWSpell() + { + } - for (SpellEffectList::iterator it=mEffectList.begin(); - it != mEffectList.end(); ++it) - { - effect = creator->createWidget("MW_EffectImage", coord, MyGUI::Align::Default); - effect->setWindowManager(mWindowManager); - it->mIsConstant = (flags & EF_Constant) || it->mIsConstant; - it->mNoTarget = (flags & EF_NoTarget) || it->mNoTarget; - effect->setSpellEffect(*it); - effects.push_back(effect); - if (effect->getRequestedWidth() > maxwidth) - maxwidth = effect->getRequestedWidth(); - - coord.top += effect->getHeight(); - } + /* MWEffectList */ - // ... then adjust the size for all widgets - for (std::vector::iterator it = effects.begin(); it != effects.end(); ++it) - { - effect = static_cast(*it); - bool needcenter = center && (maxwidth > effect->getRequestedWidth()); - int diff = maxwidth - effect->getRequestedWidth(); - if (needcenter) + MWEffectList::MWEffectList() + : mEffectList(0) { - effect->setCoord(diff/2, effect->getCoord().top, effect->getRequestedWidth(), effect->getCoord().height); } - else + + void MWEffectList::setEffectList(const SpellEffectList& list) { - effect->setCoord(0, effect->getCoord().top, effect->getRequestedWidth(), effect->getCoord().height); + mEffectList = list; + updateWidgets(); } - } - - // inform the parent about width - coord.width = maxwidth; -} -void MWEffectList::updateWidgets() -{ -} - -void MWEffectList::initialiseOverride() -{ - Base::initialiseOverride(); -} - -MWEffectList::~MWEffectList() -{ -} - -SpellEffectList MWEffectList::effectListFromESM(const ESM::EffectList* effects) -{ - SpellEffectList result; - std::vector::const_iterator end = effects->mList.end(); - for (std::vector::const_iterator it = effects->mList.begin(); it != end; ++it) - { - SpellEffectParams params; - params.mEffectID = it->mEffectID; - params.mSkill = it->mSkill; - params.mAttribute = it->mAttribute; - params.mDuration = it->mDuration; - params.mMagnMin = it->mMagnMin; - params.mMagnMax = it->mMagnMax; - params.mRange = it->mRange; - params.mArea = it->mArea; - result.push_back(params); - } - return result; -} - -/* MWSpellEffect */ - -MWSpellEffect::MWSpellEffect() - : mWindowManager(nullptr) - , mImageWidget(nullptr) - , mTextWidget(nullptr) - , mRequestedWidth(0) -{ -} - -void MWSpellEffect::setSpellEffect(const SpellEffectParams& params) -{ - mEffectParams = params; - updateWidgets(); -} - -void MWSpellEffect::updateWidgets() -{ - if (!mEffectParams.mKnown) - { - mTextWidget->setCaption ("?"); - mRequestedWidth = mTextWidget->getTextSize().width + 24; - mImageWidget->setImageTexture (""); - return; - } - - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); + void MWEffectList::createEffectWidgets(std::vector &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, bool center, int flags) + { + // We don't know the width of all the elements beforehand, so we do it in + // 2 steps: first, create all widgets and check their width.... + MWSpellEffectPtr effect = NULL; + int maxwidth = coord.width; - const ESM::MagicEffect *magicEffect = - store.get().search(mEffectParams.mEffectID); + for (SpellEffectList::iterator it=mEffectList.begin(); + it != mEffectList.end(); ++it) + { + effect = creator->createWidget("MW_EffectImage", coord, MyGUI::Align::Default); + it->mIsConstant = (flags & EF_Constant) || it->mIsConstant; + it->mNoTarget = (flags & EF_NoTarget) || it->mNoTarget; + effect->setSpellEffect(*it); + effects.push_back(effect); + if (effect->getRequestedWidth() > maxwidth) + maxwidth = effect->getRequestedWidth(); + + coord.top += effect->getHeight(); + } - assert(magicEffect); + // ... then adjust the size for all widgets + for (std::vector::iterator it = effects.begin(); it != effects.end(); ++it) + { + effect = static_cast(*it); + bool needcenter = center && (maxwidth > effect->getRequestedWidth()); + int diff = maxwidth - effect->getRequestedWidth(); + if (needcenter) + { + effect->setCoord(diff/2, effect->getCoord().top, effect->getRequestedWidth(), effect->getCoord().height); + } + else + { + effect->setCoord(0, effect->getCoord().top, effect->getRequestedWidth(), effect->getCoord().height); + } + } - std::string pt = mWindowManager->getGameSettingString("spoint", ""); - std::string pts = mWindowManager->getGameSettingString("spoints", ""); - std::string to = " " + mWindowManager->getGameSettingString("sTo", "") + " "; - std::string sec = " " + mWindowManager->getGameSettingString("ssecond", ""); - std::string secs = " " + mWindowManager->getGameSettingString("sseconds", ""); + // inform the parent about width + coord.width = maxwidth; + } - std::string effectIDStr = ESM::MagicEffect::effectIdToString(mEffectParams.mEffectID); - std::string spellLine = mWindowManager->getGameSettingString(effectIDStr, ""); + void MWEffectList::updateWidgets() + { + } - if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) - { - spellLine += " " + mWindowManager->getGameSettingString(ESM::Skill::sSkillNameIds[mEffectParams.mSkill], ""); - } - if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) - { - static const char *attributes[8] = { - "sAttributeStrength", - "sAttributeIntelligence", - "sAttributeWillpower", - "sAttributeAgility", - "sAttributeSpeed", - "sAttributeEndurance", - "sAttributePersonality", - "sAttributeLuck" - }; - spellLine += " " + mWindowManager->getGameSettingString(attributes[mEffectParams.mAttribute], ""); - } + void MWEffectList::initialiseOverride() + { + Base::initialiseOverride(); + } - if ((mEffectParams.mMagnMin >= 0 || mEffectParams.mMagnMax >= 0) && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) - { - if (mEffectParams.mMagnMin == mEffectParams.mMagnMax) - spellLine += " " + boost::lexical_cast(mEffectParams.mMagnMin) + " " + ((mEffectParams.mMagnMin == 1) ? pt : pts); - else + MWEffectList::~MWEffectList() { - spellLine += " " + boost::lexical_cast(mEffectParams.mMagnMin) + to + boost::lexical_cast(mEffectParams.mMagnMax) + " " + pts; } - } - // constant effects have no duration and no target - if (!mEffectParams.mIsConstant) - { - if (mEffectParams.mDuration >= 0 && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) + SpellEffectList MWEffectList::effectListFromESM(const ESM::EffectList* effects) { - spellLine += " " + mWindowManager->getGameSettingString("sfor", "") + " " + boost::lexical_cast(mEffectParams.mDuration) + ((mEffectParams.mDuration == 1) ? sec : secs); + SpellEffectList result; + std::vector::const_iterator end = effects->mList.end(); + for (std::vector::const_iterator it = effects->mList.begin(); it != end; ++it) + { + SpellEffectParams params; + params.mEffectID = it->mEffectID; + params.mSkill = it->mSkill; + params.mAttribute = it->mAttribute; + params.mDuration = it->mDuration; + params.mMagnMin = it->mMagnMin; + params.mMagnMax = it->mMagnMax; + params.mRange = it->mRange; + params.mArea = it->mArea; + result.push_back(params); + } + return result; } - if (mEffectParams.mArea > 0) + /* MWSpellEffect */ + + MWSpellEffect::MWSpellEffect() + : mImageWidget(NULL) + , mTextWidget(NULL) + , mRequestedWidth(0) { - spellLine += " #{sin} " + boost::lexical_cast(mEffectParams.mArea) + " #{sfootarea}"; } - // potions have no target - if (!mEffectParams.mNoTarget) + void MWSpellEffect::setSpellEffect(const SpellEffectParams& params) { - std::string on = mWindowManager->getGameSettingString("sonword", ""); - if (mEffectParams.mRange == ESM::RT_Self) - spellLine += " " + on + " " + mWindowManager->getGameSettingString("sRangeSelf", ""); - else if (mEffectParams.mRange == ESM::RT_Touch) - spellLine += " " + on + " " + mWindowManager->getGameSettingString("sRangeTouch", ""); - else if (mEffectParams.mRange == ESM::RT_Target) - spellLine += " " + on + " " + mWindowManager->getGameSettingString("sRangeTarget", ""); + mEffectParams = params; + updateWidgets(); } - } - static_cast(mTextWidget)->setCaptionWithReplacing(spellLine); - mRequestedWidth = mTextWidget->getTextSize().width + 24; + void MWSpellEffect::updateWidgets() + { + if (!mEffectParams.mKnown) + { + mTextWidget->setCaption ("?"); + mRequestedWidth = mTextWidget->getTextSize().width + 24; + mImageWidget->setImageTexture (""); + return; + } + + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); - std::string path = std::string("icons\\") + magicEffect->mIcon; - fixTexturePath(path); - mImageWidget->setImageTexture(path); -} + const ESM::MagicEffect *magicEffect = + store.get().search(mEffectParams.mEffectID); -MWSpellEffect::~MWSpellEffect() -{ -} + assert(magicEffect); -void MWSpellEffect::initialiseOverride() -{ - Base::initialiseOverride(); + std::string pt = MWBase::Environment::get().getWindowManager()->getGameSettingString("spoint", ""); + std::string pts = MWBase::Environment::get().getWindowManager()->getGameSettingString("spoints", ""); + std::string to = " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "") + " "; + std::string sec = " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("ssecond", ""); + std::string secs = " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sseconds", ""); - assignWidget(mTextWidget, "Text"); - assignWidget(mImageWidget, "Image"); -} + std::string effectIDStr = ESM::MagicEffect::effectIdToString(mEffectParams.mEffectID); + std::string spellLine = MWBase::Environment::get().getWindowManager()->getGameSettingString(effectIDStr, ""); -/* MWDynamicStat */ + if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) + { + spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Skill::sSkillNameIds[mEffectParams.mSkill], ""); + } + if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) + { + spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Attribute::sGmstAttributeIds[mEffectParams.mAttribute], ""); + } -MWDynamicStat::MWDynamicStat() -: mValue(0) -, mMax(1) -, mTextWidget(nullptr) -, mBarWidget(nullptr) -, mBarTextWidget(nullptr) -{ -} + if ((mEffectParams.mMagnMin >= 0 || mEffectParams.mMagnMax >= 0) && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) + { + if (mEffectParams.mMagnMin == mEffectParams.mMagnMax) + spellLine += " " + boost::lexical_cast(mEffectParams.mMagnMin) + " " + ((mEffectParams.mMagnMin == 1) ? pt : pts); + else + { + spellLine += " " + boost::lexical_cast(mEffectParams.mMagnMin) + to + boost::lexical_cast(mEffectParams.mMagnMax) + " " + pts; + } + } -void MWDynamicStat::setValue(int cur, int max) -{ - mValue = cur; - mMax = max; + // constant effects have no duration and no target + if (!mEffectParams.mIsConstant) + { + if (mEffectParams.mDuration >= 0 && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) + { + spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sfor", "") + " " + boost::lexical_cast(mEffectParams.mDuration) + ((mEffectParams.mDuration == 1) ? sec : secs); + } + + if (mEffectParams.mArea > 0) + { + spellLine += " #{sin} " + boost::lexical_cast(mEffectParams.mArea) + " #{sfootarea}"; + } + + // potions have no target + if (!mEffectParams.mNoTarget) + { + std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString("sonword", ""); + if (mEffectParams.mRange == ESM::RT_Self) + spellLine += " " + on + " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sRangeSelf", ""); + else if (mEffectParams.mRange == ESM::RT_Touch) + spellLine += " " + on + " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sRangeTouch", ""); + else if (mEffectParams.mRange == ESM::RT_Target) + spellLine += " " + on + " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sRangeTarget", ""); + } + } - if (mBarWidget) - { - mBarWidget->setProgressRange(mMax); - mBarWidget->setProgressPosition(mValue); - } + static_cast(mTextWidget)->setCaptionWithReplacing(spellLine); + mRequestedWidth = mTextWidget->getTextSize().width + 24; + std::string path = std::string("icons\\") + magicEffect->mIcon; + fixTexturePath(path); + mImageWidget->setImageTexture(path); + } - if (mBarTextWidget) - { - if (mValue >= 0 && mMax > 0) + MWSpellEffect::~MWSpellEffect() { - std::stringstream out; - out << mValue << "/" << mMax; - static_cast(mBarTextWidget)->setCaption(out.str().c_str()); } - else - static_cast(mBarTextWidget)->setCaption(""); - } -} -void MWDynamicStat::setTitle(const std::string& text) -{ - if (mTextWidget) - static_cast(mTextWidget)->setCaption(text); -} -MWDynamicStat::~MWDynamicStat() -{ -} + void MWSpellEffect::initialiseOverride() + { + Base::initialiseOverride(); -void MWDynamicStat::initialiseOverride() -{ - Base::initialiseOverride(); + assignWidget(mTextWidget, "Text"); + assignWidget(mImageWidget, "Image"); + } - assignWidget(mTextWidget, "Text"); - assignWidget(mBarWidget, "Bar"); - assignWidget(mBarTextWidget, "BarText"); -} + /* MWDynamicStat */ + MWDynamicStat::MWDynamicStat() + : mValue(0) + , mMax(1) + , mTextWidget(NULL) + , mBarWidget(NULL) + , mBarTextWidget(NULL) + { + } + void MWDynamicStat::setValue(int cur, int max) + { + mValue = cur; + mMax = max; + if (mBarWidget) + { + mBarWidget->setProgressRange(mMax); + mBarWidget->setProgressPosition(mValue); + } -// --------------------------------------------------------------------------------------------------------------------- -void AutoSizedWidget::notifySizeChange (MyGUI::Widget* w) -{ - if (w->getParent () != 0) - { - Box* b = dynamic_cast(w->getParent()); - if (b) - b->notifyChildrenSizeChanged (); - else - { - if (mExpandDirection == MyGUI::Align::Left) + if (mBarTextWidget) { - int hdiff = getRequestedSize ().width - w->getSize().width; - w->setPosition(w->getPosition() - MyGUI::IntPoint(hdiff, 0)); + if (mValue >= 0 && mMax > 0) + { + std::stringstream out; + out << mValue << "/" << mMax; + static_cast(mBarTextWidget)->setCaption(out.str().c_str()); + } + else + static_cast(mBarTextWidget)->setCaption(""); } - w->setSize(getRequestedSize ()); } - } -} - - -MyGUI::IntSize AutoSizedTextBox::getRequestedSize() -{ - return getTextSize(); -} + void MWDynamicStat::setTitle(const std::string& text) + { + if (mTextWidget) + static_cast(mTextWidget)->setCaption(text); + } -void AutoSizedTextBox::setCaption(const MyGUI::UString& _value) -{ - TextBox::setCaption(_value); + MWDynamicStat::~MWDynamicStat() + { + } - notifySizeChange (this); -} + void MWDynamicStat::initialiseOverride() + { + Base::initialiseOverride(); -void AutoSizedTextBox::setPropertyOverride(const std::string& _key, const std::string& _value) -{ - if (_key == "ExpandDirection") - { - mExpandDirection = MyGUI::Align::parse (_value); - } - else - { - TextBox::setPropertyOverride (_key, _value); - } -} + assignWidget(mTextWidget, "Text"); + assignWidget(mBarWidget, "Bar"); + assignWidget(mBarTextWidget, "BarText"); + } -MyGUI::IntSize AutoSizedButton::getRequestedSize() -{ - MyGUI::IntSize size = getTextSize() + MyGUI::IntSize(24,0); - size.height = std::max(24, size.height); - return size; -} -void AutoSizedButton::setCaption(const MyGUI::UString& _value) -{ - Button::setCaption(_value); - notifySizeChange (this); -} + // --------------------------------------------------------------------------------------------------------------------- -void AutoSizedButton::setPropertyOverride(const std::string& _key, const std::string& _value) -{ - if (_key == "ExpandDirection") - { - mExpandDirection = MyGUI::Align::parse (_value); - } - else - { - Button::setPropertyOverride (_key, _value); - } -} + void AutoSizedWidget::notifySizeChange (MyGUI::Widget* w) + { + if (w->getParent () != 0) + { + Box* b = dynamic_cast(w->getParent()); + if (b) + b->notifyChildrenSizeChanged (); + else + { + if (mExpandDirection == MyGUI::Align::Left) + { + int hdiff = getRequestedSize ().width - w->getSize().width; + w->setPosition(w->getPosition() - MyGUI::IntPoint(hdiff, 0)); + } + w->setSize(getRequestedSize ()); + } + } + } -Box::Box() - : mSpacing(4) - , mPadding(0) - , mAutoResize(false) -{ -} + MyGUI::IntSize AutoSizedTextBox::getRequestedSize() + { + return getTextSize(); + } -void Box::notifyChildrenSizeChanged () -{ - align(); -} + void AutoSizedTextBox::setCaption(const MyGUI::UString& _value) + { + TextBox::setCaption(_value); -void Box::_setPropertyImpl(const std::string& _key, const std::string& _value) -{ - if (_key == "Spacing") - mSpacing = MyGUI::utility::parseValue(_value); - else if (_key == "Padding") - mPadding = MyGUI::utility::parseValue(_value); - else if (_key == "AutoResize") - mAutoResize = MyGUI::utility::parseValue(_value); -} + notifySizeChange (this); + } -void HBox::align () -{ - unsigned int count = getChildCount (); - size_t h_stretched_count = 0; - int total_width = 0; - int total_height = 0; - std::vector< std::pair > sizes; + void AutoSizedTextBox::setPropertyOverride(const std::string& _key, const std::string& _value) + { + if (_key == "ExpandDirection") + { + mExpandDirection = MyGUI::Align::parse (_value); + } + else + { + TextBox::setPropertyOverride (_key, _value); + } + } - for (unsigned int i = 0; i < count; ++i) - { - MyGUI::Widget* w = getChildAt(i); - bool hstretch = w->getUserString ("HStretch") == "true"; - h_stretched_count += hstretch; - AutoSizedWidget* aw = dynamic_cast(w); - if (aw) + MyGUI::IntSize AutoSizedEditBox::getRequestedSize() { - sizes.push_back(std::make_pair(aw->getRequestedSize (), hstretch)); - total_width += aw->getRequestedSize ().width; - total_height = std::max(total_height, aw->getRequestedSize ().height); + if (getAlign().isHStretch()) + throw std::runtime_error("AutoSizedEditBox can't have HStretch align (" + getName() + ")"); + return MyGUI::IntSize(getSize().width, getTextSize().height); } - else + + void AutoSizedEditBox::setCaption(const MyGUI::UString& _value) { - sizes.push_back (std::make_pair(w->getSize(), hstretch)); - total_width += w->getSize().width; + EditBox::setCaption(_value); + + notifySizeChange (this); } - if (i != count-1) - total_width += mSpacing; - } + void AutoSizedEditBox::setPropertyOverride(const std::string& _key, const std::string& _value) + { + if (_key == "ExpandDirection") + { + mExpandDirection = MyGUI::Align::parse (_value); + } + else + { + EditBox::setPropertyOverride (_key, _value); + } + } - if (mAutoResize && (total_width+mPadding*2 != getSize().width || total_height+mPadding*2 != getSize().height)) - { - setSize(MyGUI::IntSize(total_width+mPadding*2, total_height+mPadding*2)); - return; - } + MyGUI::IntSize AutoSizedButton::getRequestedSize() + { + MyGUI::IntSize size = getTextSize() + MyGUI::IntSize(24,0); + size.height = std::max(24, size.height); + return size; + } - int curX = 0; - for (unsigned int i = 0; i < count; ++i) - { - if (i == 0) - curX += mPadding; - - MyGUI::Widget* w = getChildAt(i); - - bool vstretch = w->getUserString ("VStretch") == "true"; - int height = vstretch ? total_height : sizes[i].first.height; - - MyGUI::IntCoord widgetCoord; - widgetCoord.left = curX; - widgetCoord.top = mPadding + (getSize().height-mPadding*2 - height) / 2; - int width = sizes[i].second ? sizes[i].first.width + (getSize().width-mPadding*2 - total_width)/h_stretched_count - : sizes[i].first.width; - widgetCoord.width = width; - widgetCoord.height = height; - w->setCoord(widgetCoord); - curX += width; - - if (i != count-1) - curX += mSpacing; - } -} + void AutoSizedButton::setCaption(const MyGUI::UString& _value) + { + Button::setCaption(_value); -void HBox::setPropertyOverride(const std::string& _key, const std::string& _value) -{ - Box::_setPropertyImpl (_key, _value); -} + notifySizeChange (this); + } -void HBox::setSize (const MyGUI::IntSize& _value) -{ - MyGUI::Widget::setSize (_value); - align(); -} + void AutoSizedButton::setPropertyOverride(const std::string& _key, const std::string& _value) + { + if (_key == "ExpandDirection") + { + mExpandDirection = MyGUI::Align::parse (_value); + } + else + { + Button::setPropertyOverride (_key, _value); + } + } -void HBox::setCoord (const MyGUI::IntCoord& _value) -{ - MyGUI::Widget::setCoord (_value); - align(); -} + Box::Box() + : mSpacing(4) + , mPadding(0) + , mAutoResize(false) + { -void HBox::onWidgetCreated(MyGUI::Widget* _widget) -{ - align(); -} + } -void HBox::onWidgetDestroy(MyGUI::Widget* _widget) -{ - align(); -} + void Box::notifyChildrenSizeChanged () + { + align(); + } -MyGUI::IntSize HBox::getRequestedSize () -{ - MyGUI::IntSize size(0,0); - for (unsigned int i = 0; i < getChildCount (); ++i) - { - AutoSizedWidget* w = dynamic_cast(getChildAt(i)); - if (w) + void Box::_setPropertyImpl(const std::string& _key, const std::string& _value) { - MyGUI::IntSize requested = w->getRequestedSize (); - size.height = std::max(size.height, requested.height); - size.width = size.width + requested.width; - if (i != getChildCount()-1) - size.width += mSpacing; + if (_key == "Spacing") + mSpacing = MyGUI::utility::parseValue(_value); + else if (_key == "Padding") + mPadding = MyGUI::utility::parseValue(_value); + else if (_key == "AutoResize") + mAutoResize = MyGUI::utility::parseValue(_value); } - else + + void HBox::align () { - MyGUI::IntSize requested = getChildAt(i)->getSize (); - size.height = std::max(size.height, requested.height); + unsigned int count = getChildCount (); + size_t h_stretched_count = 0; + int total_width = 0; + int total_height = 0; + std::vector< std::pair > sizes; + + for (unsigned int i = 0; i < count; ++i) + { + MyGUI::Widget* w = getChildAt(i); + bool hstretch = w->getUserString ("HStretch") == "true"; + h_stretched_count += hstretch; + AutoSizedWidget* aw = dynamic_cast(w); + if (aw) + { + sizes.push_back(std::make_pair(aw->getRequestedSize (), hstretch)); + total_width += aw->getRequestedSize ().width; + total_height = std::max(total_height, aw->getRequestedSize ().height); + } + else + { + sizes.push_back (std::make_pair(w->getSize(), hstretch)); + total_width += w->getSize().width; + if (!(w->getUserString("VStretch") == "true")) + total_height = std::max(total_height, w->getSize().height); + } + + if (i != count-1) + total_width += mSpacing; + } + + if (mAutoResize && (total_width+mPadding*2 != getSize().width || total_height+mPadding*2 != getSize().height)) + { + setSize(MyGUI::IntSize(total_width+mPadding*2, total_height+mPadding*2)); + return; + } - if (getChildAt(i)->getUserString("HStretch") != "true") - size.width = size.width + requested.width; - if (i != getChildCount()-1) - size.width += mSpacing; + int curX = 0; + for (unsigned int i = 0; i < count; ++i) + { + if (i == 0) + curX += mPadding; + + MyGUI::Widget* w = getChildAt(i); + + bool vstretch = w->getUserString ("VStretch") == "true"; + int height = vstretch ? total_height : sizes[i].first.height; + + MyGUI::IntCoord widgetCoord; + widgetCoord.left = curX; + widgetCoord.top = mPadding + (getSize().height-mPadding*2 - height) / 2; + int width = sizes[i].second ? sizes[i].first.width + (getSize().width-mPadding*2 - total_width)/h_stretched_count + : sizes[i].first.width; + widgetCoord.width = width; + widgetCoord.height = height; + w->setCoord(widgetCoord); + curX += width; + + if (i != count-1) + curX += mSpacing; + } } - size.height += mPadding*2; - size.width += mPadding*2; - } - return size; -} + void HBox::setPropertyOverride(const std::string& _key, const std::string& _value) + { + Box::_setPropertyImpl (_key, _value); + } + void HBox::setSize (const MyGUI::IntSize& _value) + { + MyGUI::Widget::setSize (_value); + align(); + } + void HBox::setCoord (const MyGUI::IntCoord& _value) + { + MyGUI::Widget::setCoord (_value); + align(); + } -void VBox::align () -{ - unsigned int count = getChildCount (); - size_t v_stretched_count = 0; - int total_height = 0; - int total_width = 0; - std::vector< std::pair > sizes; - for (unsigned int i = 0; i < count; ++i) - { - MyGUI::Widget* w = getChildAt(i); - bool vstretch = w->getUserString ("VStretch") == "true"; - v_stretched_count += vstretch; - AutoSizedWidget* aw = dynamic_cast(w); - if (aw) + void HBox::onWidgetCreated(MyGUI::Widget* _widget) { - sizes.push_back(std::make_pair(aw->getRequestedSize (), vstretch)); - total_height += aw->getRequestedSize ().height; - total_width = std::max(total_width, aw->getRequestedSize ().width); + align(); } - else + + MyGUI::IntSize HBox::getRequestedSize () { - sizes.push_back (std::make_pair(w->getSize(), vstretch)); - total_height += w->getSize().height; + MyGUI::IntSize size(0,0); + for (unsigned int i = 0; i < getChildCount (); ++i) + { + AutoSizedWidget* w = dynamic_cast(getChildAt(i)); + if (w) + { + MyGUI::IntSize requested = w->getRequestedSize (); + size.height = std::max(size.height, requested.height); + size.width = size.width + requested.width; + if (i != getChildCount()-1) + size.width += mSpacing; + } + else + { + MyGUI::IntSize requested = getChildAt(i)->getSize (); + size.height = std::max(size.height, requested.height); + + if (getChildAt(i)->getUserString("HStretch") != "true") + size.width = size.width + requested.width; + + if (i != getChildCount()-1) + size.width += mSpacing; + } + size.height += mPadding*2; + size.width += mPadding*2; + } + return size; } - if (i != count-1) - total_height += mSpacing; - } - if (mAutoResize && (total_width+mPadding*2 != getSize().width || total_height+mPadding*2 != getSize().height)) - { - setSize(MyGUI::IntSize(total_width+mPadding*2, total_height+mPadding*2)); - return; - } - int curY = 0; - for (unsigned int i = 0; i < count; ++i) - { - if (i==0) - curY += mPadding; - - MyGUI::Widget* w = getChildAt(i); - - bool hstretch = w->getUserString ("HStretch") == "true"; - int width = hstretch ? total_width : sizes[i].first.width; - - MyGUI::IntCoord widgetCoord; - widgetCoord.top = curY; - widgetCoord.left = mPadding + (getSize().width-mPadding*2 - width) / 2; - int height = sizes[i].second ? sizes[i].first.height + (getSize().height-mPadding*2 - total_height)/v_stretched_count - : sizes[i].first.height; - widgetCoord.height = height; - widgetCoord.width = width; - w->setCoord(widgetCoord); - curY += height; - - if (i != count-1) - curY += mSpacing; - } -} + void VBox::align () + { + unsigned int count = getChildCount (); + size_t v_stretched_count = 0; + int total_height = 0; + int total_width = 0; + std::vector< std::pair > sizes; + for (unsigned int i = 0; i < count; ++i) + { + MyGUI::Widget* w = getChildAt(i); + bool vstretch = w->getUserString ("VStretch") == "true"; + v_stretched_count += vstretch; + AutoSizedWidget* aw = dynamic_cast(w); + if (aw) + { + sizes.push_back(std::make_pair(aw->getRequestedSize (), vstretch)); + total_height += aw->getRequestedSize ().height; + total_width = std::max(total_width, aw->getRequestedSize ().width); + } + else + { + sizes.push_back (std::make_pair(w->getSize(), vstretch)); + total_height += w->getSize().height; + + if (!(w->getUserString("HStretch") == "true")) + total_width = std::max(total_width, w->getSize().width); + } + + if (i != count-1) + total_height += mSpacing; + } -void VBox::setPropertyOverride(const std::string& _key, const std::string& _value) -{ - Box::_setPropertyImpl (_key, _value); -} + if (mAutoResize && (total_width+mPadding*2 != getSize().width || total_height+mPadding*2 != getSize().height)) + { + setSize(MyGUI::IntSize(total_width+mPadding*2, total_height+mPadding*2)); + return; + } -void VBox::setSize (const MyGUI::IntSize& _value) -{ - MyGUI::Widget::setSize (_value); - align(); -} -void VBox::setCoord (const MyGUI::IntCoord& _value) -{ - MyGUI::Widget::setCoord (_value); - align(); -} + int curY = 0; + for (unsigned int i = 0; i < count; ++i) + { + if (i==0) + curY += mPadding; + + MyGUI::Widget* w = getChildAt(i); + + bool hstretch = w->getUserString ("HStretch") == "true"; + int width = hstretch ? total_width : sizes[i].first.width; + + MyGUI::IntCoord widgetCoord; + widgetCoord.top = curY; + widgetCoord.left = mPadding + (getSize().width-mPadding*2 - width) / 2; + int height = sizes[i].second ? sizes[i].first.height + (getSize().height-mPadding*2 - total_height)/v_stretched_count + : sizes[i].first.height; + widgetCoord.height = height; + widgetCoord.width = width; + w->setCoord(widgetCoord); + curY += height; + + if (i != count-1) + curY += mSpacing; + } + } -MyGUI::IntSize VBox::getRequestedSize () -{ - MyGUI::IntSize size(0,0); - for (unsigned int i = 0; i < getChildCount (); ++i) - { - AutoSizedWidget* w = dynamic_cast(getChildAt(i)); - if (w) + void VBox::setPropertyOverride(const std::string& _key, const std::string& _value) { - MyGUI::IntSize requested = w->getRequestedSize (); - size.width = std::max(size.width, requested.width); - size.height = size.height + requested.height; - if (i != getChildCount()-1) - size.height += mSpacing; + Box::_setPropertyImpl (_key, _value); } - else - { - MyGUI::IntSize requested = getChildAt(i)->getSize (); - size.width = std::max(size.width, requested.width); - if (getChildAt(i)->getUserString("VStretch") != "true") - size.height = size.height + requested.height; + void VBox::setSize (const MyGUI::IntSize& _value) + { + MyGUI::Widget::setSize (_value); + align(); + } - if (i != getChildCount()-1) - size.height += mSpacing; + void VBox::setCoord (const MyGUI::IntCoord& _value) + { + MyGUI::Widget::setCoord (_value); + align(); } - size.height += mPadding*2; - size.width += mPadding*2; - } - return size; -} -void VBox::onWidgetCreated(MyGUI::Widget* _widget) -{ - align(); -} + MyGUI::IntSize VBox::getRequestedSize () + { + MyGUI::IntSize size(0,0); + for (unsigned int i = 0; i < getChildCount (); ++i) + { + AutoSizedWidget* w = dynamic_cast(getChildAt(i)); + if (w) + { + MyGUI::IntSize requested = w->getRequestedSize (); + size.width = std::max(size.width, requested.width); + size.height = size.height + requested.height; + if (i != getChildCount()-1) + size.height += mSpacing; + } + else + { + MyGUI::IntSize requested = getChildAt(i)->getSize (); + size.width = std::max(size.width, requested.width); + + if (getChildAt(i)->getUserString("VStretch") != "true") + size.height = size.height + requested.height; + + if (i != getChildCount()-1) + size.height += mSpacing; + } + size.height += mPadding*2; + size.width += mPadding*2; + } + return size; + } -void VBox::onWidgetDestroy(MyGUI::Widget* _widget) -{ - align(); + void VBox::onWidgetCreated(MyGUI::Widget* _widget) + { + align(); + } + } } diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index 7cbb5e53a..156794691 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -2,10 +2,15 @@ #define MWGUI_WIDGETS_H #include "../mwworld/esmstore.hpp" +#include "../mwmechanics/stat.hpp" -#include +#include +#include -#include "../mwmechanics/stat.hpp" +namespace MyGUI +{ + class ImageBox; +} namespace MWBase { @@ -87,12 +92,10 @@ namespace MWGui typedef MWMechanics::Stat SkillValue; - void setWindowManager(MWBase::WindowManager *m) { mManager = m; } /// \todo remove void setSkillId(ESM::Skill::SkillEnum skillId); void setSkillNumber(int skillId); void setSkillValue(const SkillValue& value); - MWBase::WindowManager *getWindowManager() const { return mManager; } ESM::Skill::SkillEnum getSkillId() const { return mSkillId; } const SkillValue& getSkillValue() const { return mValue; } @@ -115,10 +118,10 @@ namespace MWGui void updateWidgets(); - MWBase::WindowManager *mManager; ESM::Skill::SkillEnum mSkillId; SkillValue mValue; - MyGUI::WidgetPtr mSkillNameWidget, mSkillValueWidget; + MyGUI::Widget* mSkillNameWidget; + MyGUI::Widget* mSkillValueWidget; }; typedef MWSkill* MWSkillPtr; @@ -130,11 +133,9 @@ namespace MWGui typedef MWMechanics::Stat AttributeValue; - void setWindowManager(MWBase::WindowManager *m) { mManager = m; } void setAttributeId(int attributeId); void setAttributeValue(const AttributeValue& value); - MWBase::WindowManager *getWindowManager() const { return mManager; } int getAttributeId() const { return mId; } const AttributeValue& getAttributeValue() const { return mValue; } @@ -157,10 +158,10 @@ namespace MWGui void updateWidgets(); - MWBase::WindowManager *mManager; int mId; AttributeValue mValue; - MyGUI::WidgetPtr mAttributeNameWidget, mAttributeValueWidget; + MyGUI::Widget* mAttributeNameWidget; + MyGUI::Widget* mAttributeValueWidget; }; typedef MWAttribute* MWAttributePtr; @@ -176,7 +177,6 @@ namespace MWGui typedef MWMechanics::Stat SpellValue; - void setWindowManager(MWBase::WindowManager* parWindowManager) { mWindowManager = parWindowManager; } void setSpellId(const std::string &id); /** @@ -186,7 +186,7 @@ namespace MWGui * @param spell category, if this is 0, this means the spell effects are permanent and won't display e.g. duration * @param various flags, see MWEffectList::EffectFlags */ - void createEffectWidgets(std::vector &effects, MyGUI::WidgetPtr creator, MyGUI::IntCoord &coord, int flags); + void createEffectWidgets(std::vector &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, int flags); const std::string &getSpellId() const { return mId; } @@ -198,7 +198,6 @@ namespace MWGui private: void updateWidgets(); - MWBase::WindowManager* mWindowManager; std::string mId; MyGUI::TextBox* mSpellNameWidget; }; @@ -218,7 +217,6 @@ namespace MWGui EF_Constant = 0x02 // constant effect means that duration will not be displayed }; - void setWindowManager(MWBase::WindowManager* parWindowManager) { mWindowManager = parWindowManager; } void setEffectList(const SpellEffectList& list); static SpellEffectList effectListFromESM(const ESM::EffectList* effects); @@ -230,7 +228,7 @@ namespace MWGui * @param center the effect widgets horizontally * @param various flags, see MWEffectList::EffectFlags */ - void createEffectWidgets(std::vector &effects, MyGUI::WidgetPtr creator, MyGUI::IntCoord &coord, bool center, int flags); + void createEffectWidgets(std::vector &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, bool center, int flags); protected: virtual ~MWEffectList(); @@ -240,7 +238,6 @@ namespace MWGui private: void updateWidgets(); - MWBase::WindowManager* mWindowManager; SpellEffectList mEffectList; }; typedef MWEffectList* MWEffectListPtr; @@ -253,7 +250,6 @@ namespace MWGui typedef ESM::ENAMstruct SpellEffectValue; - void setWindowManager(MWBase::WindowManager* parWindowManager) { mWindowManager = parWindowManager; } void setSpellEffect(const SpellEffectParams& params); int getRequestedWidth() const { return mRequestedWidth; } @@ -267,7 +263,6 @@ namespace MWGui void updateWidgets(); - MWBase::WindowManager* mWindowManager; SpellEffectParams mEffectParams; MyGUI::ImageBox* mImageWidget; MyGUI::TextBox* mTextWidget; @@ -332,6 +327,18 @@ namespace MWGui virtual void setPropertyOverride(const std::string& _key, const std::string& _value); }; + class AutoSizedEditBox : public AutoSizedWidget, public MyGUI::EditBox + { + MYGUI_RTTI_DERIVED( AutoSizedEditBox ) + + public: + virtual MyGUI::IntSize getRequestedSize(); + virtual void setCaption(const MyGUI::UString& _value); + + protected: + virtual void setPropertyOverride(const std::string& _key, const std::string& _value); + }; + class AutoSizedButton : public AutoSizedWidget, public MyGUI::Button { MYGUI_RTTI_DERIVED( AutoSizedButton ) @@ -382,7 +389,6 @@ namespace MWGui virtual void setPropertyOverride(const std::string& _key, const std::string& _value); virtual void onWidgetCreated(MyGUI::Widget* _widget); - virtual void onWidgetDestroy(MyGUI::Widget* _widget); }; class VBox : public Box, public MyGUI::Widget @@ -400,7 +406,6 @@ namespace MWGui virtual void setPropertyOverride(const std::string& _key, const std::string& _value); virtual void onWidgetCreated(MyGUI::Widget* _widget); - virtual void onWidgetDestroy(MyGUI::Widget* _widget); }; } } diff --git a/apps/openmw/mwgui/window_pinnable_base.cpp b/apps/openmw/mwgui/window_pinnable_base.cpp deleted file mode 100644 index 4ddf49d27..000000000 --- a/apps/openmw/mwgui/window_pinnable_base.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "window_pinnable_base.hpp" - -#include "../mwbase/windowmanager.hpp" - -using namespace MWGui; - -WindowPinnableBase::WindowPinnableBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager) - : WindowBase(parLayout, parWindowManager), mPinned(false), mVisible(false) -{ - MyGUI::WindowPtr t = static_cast(mMainWidget); - t->eventWindowButtonPressed += MyGUI::newDelegate(this, &WindowPinnableBase::onWindowButtonPressed); -} - -void WindowPinnableBase::setVisible(bool b) -{ - // Pinned windows can not be hidden - if (mPinned && !b) - return; - - WindowBase::setVisible(b); - mVisible = b; -} - -void WindowPinnableBase::onWindowButtonPressed(MyGUI::Window* sender, const std::string& eventName) -{ - if ("PinToggle" == eventName) - { - mPinned = !mPinned; - onPinToggled(); - } - - eventDone(this); -} diff --git a/apps/openmw/mwgui/window_base.cpp b/apps/openmw/mwgui/windowbase.cpp similarity index 77% rename from apps/openmw/mwgui/window_base.cpp rename to apps/openmw/mwgui/windowbase.cpp index 38bee9ea3..cc74579ab 100644 --- a/apps/openmw/mwgui/window_base.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -1,14 +1,11 @@ -#include "window_base.hpp" - -#include +#include "windowbase.hpp" #include "../mwbase/windowmanager.hpp" using namespace MWGui; -WindowBase::WindowBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager) +WindowBase::WindowBase(const std::string& parLayout) : Layout(parLayout) - , mWindowManager(parWindowManager) { } @@ -39,8 +36,8 @@ void WindowBase::center() mMainWidget->setCoord(coord); } -WindowModal::WindowModal(const std::string& parLayout, MWBase::WindowManager& parWindowManager) - : WindowBase(parLayout, parWindowManager) +WindowModal::WindowModal(const std::string& parLayout) + : WindowBase(parLayout) { } diff --git a/apps/openmw/mwgui/window_base.hpp b/apps/openmw/mwgui/windowbase.hpp similarity index 76% rename from apps/openmw/mwgui/window_base.hpp rename to apps/openmw/mwgui/windowbase.hpp index afdf4d065..2c014baf0 100644 --- a/apps/openmw/mwgui/window_base.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -15,7 +15,7 @@ namespace MWGui class WindowBase: public OEngine::GUI::Layout { public: - WindowBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager); + WindowBase(const std::string& parLayout); // Events typedef MyGUI::delegates::CMultiDelegate1 EventHandle_WindowBase; @@ -29,10 +29,6 @@ namespace MWGui signature : void method()\n */ EventHandle_WindowBase eventDone; - - protected: - /// \todo remove - MWBase::WindowManager& mWindowManager; }; @@ -42,7 +38,7 @@ namespace MWGui class WindowModal : public WindowBase { public: - WindowModal(const std::string& parLayout, MWBase::WindowManager& parWindowManager); + WindowModal(const std::string& parLayout); virtual void open(); virtual void close(); }; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index dbd8e01a2..48561936d 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -18,27 +18,21 @@ #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" + #include "../mwbase/inputmanager.hpp" -#include "../mwworld/ptr.hpp" -#include "../mwworld/cellstore.hpp" +#include "../mwworld/class.hpp" #include "console.hpp" #include "journalwindow.hpp" +#include "journalviewmodel.hpp" #include "charactercreation.hpp" -#include "text_input.hpp" -#include "review.hpp" #include "dialogue.hpp" -#include "dialogue_history.hpp" -#include "map_window.hpp" -#include "stats_window.hpp" +#include "statswindow.hpp" #include "messagebox.hpp" -#include "container.hpp" -#include "inventorywindow.hpp" #include "tooltips.hpp" #include "scrollwindow.hpp" #include "bookwindow.hpp" -#include "list.hpp" #include "hud.hpp" #include "mainmenu.hpp" #include "countdialog.hpp" @@ -53,1097 +47,1259 @@ #include "loadingscreen.hpp" #include "levelupdialog.hpp" #include "waitdialog.hpp" -#include "spellcreationdialog.hpp" #include "enchantingdialog.hpp" #include "trainingwindow.hpp" -#include "imagebutton.hpp" - -#include "cursorreplace.hpp" - -using namespace MWGui; - -WindowManager::WindowManager( - const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, - const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, - Translation::Storage& translationDataStorage) - : mGuiManager(NULL) - , mHud(NULL) - , mMap(NULL) - , mMenu(NULL) - , mStatsWindow(NULL) - , mToolTips(NULL) - , mMessageBoxManager(NULL) - , mConsole(NULL) - , mJournal(NULL) - , mDialogueWindow(NULL) - , mBookWindow(NULL) - , mScrollWindow(NULL) - , mCountDialog(NULL) - , mTradeWindow(NULL) - , mSpellBuyingWindow(NULL) - , mTravelWindow(NULL) - , mSettingsWindow(NULL) - , mConfirmationDialog(NULL) - , mAlchemyWindow(NULL) - , mSpellWindow(NULL) - , mLoadingScreen(NULL) - , mCharGen(NULL) - , mLevelupDialog(NULL) - , mWaitDialog(NULL) - , mSpellCreationDialog(NULL) - , mEnchantingDialog(NULL) - , mTrainingWindow(NULL) - , mPlayerName() - , mPlayerRaceId() - , mPlayerAttributes() - , mPlayerMajorSkills() - , mPlayerMinorSkills() - , mPlayerSkillValues() - , mPlayerHealth() - , mPlayerMagicka() - , mPlayerFatigue() - , mGui(NULL) - , mGarbageDialogs() - , mShown(GW_ALL) - , mAllowed(newGame ? GW_None : GW_ALL) - , mRestAllowed(newGame ? false : true) - , mShowFPSLevel(fpsLevel) - , mFPS(0.0f) - , mTriangleCount(0) - , mBatchCount(0) - , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) - , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) - , mHudEnabled(true) - , mTranslationDataStorage (translationDataStorage) - , mCursorManager(NULL) - , mUseHardwareCursors(Settings::Manager::getBool("hardware cursors", "GUI")) -{ +#include "exposedwindow.hpp" +#include "cursor.hpp" +#include "merchantrepair.hpp" +#include "repair.hpp" +#include "soulgemdialog.hpp" +#include "companionwindow.hpp" +#include "inventorywindow.hpp" +#include "bookpage.hpp" +#include "itemview.hpp" +#include "fontloader.hpp" + +namespace MWGui +{ + + WindowManager::WindowManager( + const Compiler::Extensions& extensions, int fpsLevel, OEngine::Render::OgreRenderer *ogre, + const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, + Translation::Storage& translationDataStorage, ToUTF8::FromType encoding) + : mGuiManager(NULL) + , mRendering(ogre) + , mHud(NULL) + , mMap(NULL) + , mMenu(NULL) + , mStatsWindow(NULL) + , mToolTips(NULL) + , mMessageBoxManager(NULL) + , mConsole(NULL) + , mJournal(NULL) + , mDialogueWindow(NULL) + , mBookWindow(NULL) + , mScrollWindow(NULL) + , mCountDialog(NULL) + , mTradeWindow(NULL) + , mSpellBuyingWindow(NULL) + , mTravelWindow(NULL) + , mSettingsWindow(NULL) + , mConfirmationDialog(NULL) + , mAlchemyWindow(NULL) + , mSpellWindow(NULL) + , mLoadingScreen(NULL) + , mCharGen(NULL) + , mLevelupDialog(NULL) + , mWaitDialog(NULL) + , mSpellCreationDialog(NULL) + , mEnchantingDialog(NULL) + , mTrainingWindow(NULL) + , mMerchantRepair(NULL) + , mRepair(NULL) + , mSoulgemDialog(NULL) + , mCompanionWindow(NULL) + , mPlayerName() + , mPlayerRaceId() + , mPlayerAttributes() + , mPlayerMajorSkills() + , mPlayerMinorSkills() + , mPlayerSkillValues() + , mPlayerHealth() + , mPlayerMagicka() + , mPlayerFatigue() + , mGui(NULL) + , mGarbageDialogs() + , mShown(GW_ALL) + , mAllowed(GW_ALL) + , mRestAllowed(true) + , mShowFPSLevel(fpsLevel) + , mFPS(0.0f) + , mTriangleCount(0) + , mBatchCount(0) + , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) + , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) + , mHudEnabled(true) + , mTranslationDataStorage (translationDataStorage) + , mCursorManager(NULL) + , mUseHardwareCursors(Settings::Manager::getBool("hardware cursors", "GUI")) + { + // Set up the GUI system + mGuiManager = new OEngine::GUI::MyGUIManager(mRendering->getWindow(), mRendering->getScene(), false, logpath); + mGui = mGuiManager->getGui(); + + // Load fonts + FontLoader fontLoader (encoding); + fontLoader.loadAllFonts(); + + //Register own widgets with MyGUI + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + BookPage::registerMyGUIComponents (); + ItemView::registerComponents(); + + MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); + MyGUI::ResourceManager::getInstance().load("core.xml"); + + MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag); + + // Get size info from the Gui object + assert(mGui); + int w = MyGUI::RenderManager::getInstance().getViewSize().width; + int h = MyGUI::RenderManager::getInstance().getViewSize().height; + + MyGUI::Widget* dragAndDropWidget = mGui->createWidgetT("Widget","",0,0,w,h,MyGUI::Align::Default,"DragAndDrop","DragAndDropWidget"); + dragAndDropWidget->setVisible(false); + + mDragAndDrop = new DragAndDrop(); + mDragAndDrop->mIsOnDragAndDrop = false; + mDragAndDrop->mDraggedWidget = 0; + mDragAndDrop->mDragAndDropWidget = dragAndDropWidget; + + mMenu = new MainMenu(w,h); + mMap = new MapWindow(cacheDir); + mStatsWindow = new StatsWindow(); + mConsole = new Console(w,h, consoleOnlyScripts); + mJournal = JournalWindow::create(JournalViewModel::create ()); + mMessageBoxManager = new MessageBoxManager(); + mInventoryWindow = new InventoryWindow(mDragAndDrop); + mTradeWindow = new TradeWindow(); + mSpellBuyingWindow = new SpellBuyingWindow(); + mTravelWindow = new TravelWindow(); + mDialogueWindow = new DialogueWindow(); + mContainerWindow = new ContainerWindow(mDragAndDrop); + mHud = new HUD(w,h, mShowFPSLevel, mDragAndDrop); + mToolTips = new ToolTips(); + mScrollWindow = new ScrollWindow(); + mBookWindow = new BookWindow(); + mCountDialog = new CountDialog(); + mSettingsWindow = new SettingsWindow(); + mConfirmationDialog = new ConfirmationDialog(); + mAlchemyWindow = new AlchemyWindow(); + mSpellWindow = new SpellWindow(); + mQuickKeysMenu = new QuickKeysMenu(); + mLevelupDialog = new LevelupDialog(); + mWaitDialog = new WaitDialog(); + mSpellCreationDialog = new SpellCreationDialog(); + mEnchantingDialog = new EnchantingDialog(); + mTrainingWindow = new TrainingWindow(); + mMerchantRepair = new MerchantRepair(); + mRepair = new Repair(); + mSoulgemDialog = new SoulgemDialog(mMessageBoxManager); + mCompanionWindow = new CompanionWindow(mDragAndDrop, mMessageBoxManager); + + mLoadingScreen = new LoadingScreen(mRendering->getScene (), mRendering->getWindow ()); + mLoadingScreen->onResChange (w,h); + + mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Windows",""); + + mSoftwareCursor = new Cursor(); + + mHud->setVisible(mHudEnabled); + + mCharGen = new CharacterCreation(); + + // Setup player stats + for (int i = 0; i < ESM::Attribute::Length; ++i) + { + mPlayerAttributes.insert(std::make_pair(ESM::Attribute::sAttributeIds[i], MWMechanics::Stat())); + } - // Set up the GUI system - mGuiManager = new OEngine::GUI::MyGUIManager(mOgre->getWindow(), mOgre->getScene(), false, logpath); - mGui = mGuiManager->getGui(); - - //Use our own ResourceImageSetPointer class so we can get the texture and hotspot for a pointer - MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); - MyGUI::ResourceManager::getInstance().load("core.xml"); - - //Register own widgets with MyGUI - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - - MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag); - MyGUI::PointerManager::getInstance().eventChangeMousePointer += MyGUI::newDelegate(this, &WindowManager::onCursorChange); - - // Get size info from the Gui object - assert(mGui); - int w = MyGUI::RenderManager::getInstance().getViewSize().width; - int h = MyGUI::RenderManager::getInstance().getViewSize().height; - - MyGUI::Widget* dragAndDropWidget = mGui->createWidgetT("Widget","",0,0,w,h,MyGUI::Align::Default,"DragAndDrop","DragAndDropWidget"); - dragAndDropWidget->setVisible(false); - - mDragAndDrop = new DragAndDrop(); - mDragAndDrop->mIsOnDragAndDrop = false; - mDragAndDrop->mDraggedWidget = 0; - mDragAndDrop->mDragAndDropWidget = dragAndDropWidget; - - mMenu = new MainMenu(w,h); - mMap = new MapWindow(*this, cacheDir); - mStatsWindow = new StatsWindow(*this); - mConsole = new Console(w,h, consoleOnlyScripts); - mJournal = new JournalWindow(*this); - mMessageBoxManager = new MessageBoxManager(this); - mInventoryWindow = new InventoryWindow(*this,mDragAndDrop); - mTradeWindow = new TradeWindow(*this); - mSpellBuyingWindow = new SpellBuyingWindow(*this); - mTravelWindow = new TravelWindow(*this); - mDialogueWindow = new DialogueWindow(*this); - mContainerWindow = new ContainerWindow(*this,mDragAndDrop); - mHud = new HUD(w,h, mShowFPSLevel, mDragAndDrop); - mToolTips = new ToolTips(this); - mScrollWindow = new ScrollWindow(*this); - mBookWindow = new BookWindow(*this); - mCountDialog = new CountDialog(*this); - mSettingsWindow = new SettingsWindow(*this); - mConfirmationDialog = new ConfirmationDialog(*this); - mAlchemyWindow = new AlchemyWindow(*this); - mSpellWindow = new SpellWindow(*this); - mQuickKeysMenu = new QuickKeysMenu(*this); - mLevelupDialog = new LevelupDialog(*this); - mWaitDialog = new WaitDialog(*this); - mSpellCreationDialog = new SpellCreationDialog(*this); - mEnchantingDialog = new EnchantingDialog(*this); - mTrainingWindow = new TrainingWindow(*this); - - mLoadingScreen = new LoadingScreen(mOgre->getScene (), mOgre->getWindow (), *this); - mLoadingScreen->onResChange (w,h); - - mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Windows",""); - - // The HUD is always on - mHud->setVisible(true); - - mCharGen = new CharacterCreation(this); - - // Setup player stats - for (int i = 0; i < ESM::Attribute::Length; ++i) - { - mPlayerAttributes.insert(std::make_pair(ESM::Attribute::sAttributeIds[i], MWMechanics::Stat())); - } - - for (int i = 0; i < ESM::Skill::Length; ++i) - { - mPlayerSkillValues.insert(std::make_pair(ESM::Skill::sSkillIds[i], MWMechanics::Stat())); - } - - unsetSelectedSpell(); - unsetSelectedWeapon(); - - //set up the hardware cursor manager - mCursorManager = new SFO::SDLCursorManager(Settings::Manager::getBool("debug", "Engine")); - - setUseHardwareCursors(mUseHardwareCursors); - onCursorChange(PointerManager::getInstance().getDefaultPointer()); - mCursorManager->cursorVisibilityChange(false); - - // Set up visibility - updateVisible(); -} + for (int i = 0; i < ESM::Skill::Length; ++i) + { + mPlayerSkillValues.insert(std::make_pair(ESM::Skill::sSkillIds[i], MWMechanics::Stat())); + } -WindowManager::~WindowManager() -{ - delete mConsole; - delete mMessageBoxManager; - delete mHud; - delete mMap; - delete mMenu; - delete mStatsWindow; - delete mJournal; - delete mDialogueWindow; - delete mContainerWindow; - delete mInventoryWindow; - delete mToolTips; - delete mCharGen; - delete mDragAndDrop; - delete mBookWindow; - delete mScrollWindow; - delete mTradeWindow; - delete mSpellBuyingWindow; - delete mTravelWindow; - delete mSettingsWindow; - delete mConfirmationDialog; - delete mAlchemyWindow; - delete mSpellWindow; - delete mLoadingScreen; - delete mLevelupDialog; - delete mWaitDialog; - delete mSpellCreationDialog; - delete mEnchantingDialog; - delete mTrainingWindow; - delete mCountDialog; - delete mQuickKeysMenu; - - cleanupGarbage(); - - delete mGuiManager; - delete mCursorManager; -} + unsetSelectedSpell(); + unsetSelectedWeapon(); -void WindowManager::cleanupGarbage() -{ - // Delete any dialogs which are no longer in use - if (!mGarbageDialogs.empty()) + //set up the hardware cursor manager + mCursorManager = new SFO::SDLCursorManager(Settings::Manager::getBool("debug", "Engine")); + + MyGUI::PointerManager::getInstance().eventChangeMousePointer += MyGUI::newDelegate(this, &WindowManager::onCursorChange); + + setUseHardwareCursors(mUseHardwareCursors); + onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer()); + mCursorManager->cursorVisibilityChange(false); + + // Set up visibility + updateVisible(); + } + + void WindowManager::setNewGame(bool newgame) { - for (std::vector::iterator it = mGarbageDialogs.begin(); it != mGarbageDialogs.end(); ++it) + if (newgame) { - delete *it; + disallowAll(); + delete mCharGen; + mCharGen = new CharacterCreation(); + mGuiModes.clear(); } - mGarbageDialogs.clear(); - } -} + else + allow(GW_ALL); -void WindowManager::update() -{ - cleanupGarbage(); + mRestAllowed = !newgame; + } - mHud->setFPS(mFPS); - mHud->setTriangleCount(mTriangleCount); - mHud->setBatchCount(mBatchCount); -} + WindowManager::~WindowManager() + { + delete mConsole; + delete mMessageBoxManager; + delete mHud; + delete mMap; + delete mMenu; + delete mStatsWindow; + delete mJournal; + delete mDialogueWindow; + delete mContainerWindow; + delete mInventoryWindow; + delete mToolTips; + delete mCharGen; + delete mDragAndDrop; + delete mBookWindow; + delete mScrollWindow; + delete mTradeWindow; + delete mSpellBuyingWindow; + delete mTravelWindow; + delete mSettingsWindow; + delete mConfirmationDialog; + delete mAlchemyWindow; + delete mSpellWindow; + delete mLoadingScreen; + delete mLevelupDialog; + delete mWaitDialog; + delete mSpellCreationDialog; + delete mEnchantingDialog; + delete mTrainingWindow; + delete mCountDialog; + delete mQuickKeysMenu; + delete mMerchantRepair; + delete mRepair; + delete mSoulgemDialog; + delete mSoftwareCursor; + delete mCursorManager; + + cleanupGarbage(); + + delete mGuiManager; + } -void WindowManager::updateVisible() -{ - // Start out by hiding everything except the HUD - mMap->setVisible(false); - mMenu->setVisible(false); - mStatsWindow->setVisible(false); - mConsole->disable(); - mJournal->setVisible(false); - mDialogueWindow->setVisible(false); - mContainerWindow->setVisible(false); - mInventoryWindow->setVisible(false); - mScrollWindow->setVisible(false); - mBookWindow->setVisible(false); - mTradeWindow->setVisible(false); - mSpellBuyingWindow->setVisible(false); - mTravelWindow->setVisible(false); - mSettingsWindow->setVisible(false); - mAlchemyWindow->setVisible(false); - mSpellWindow->setVisible(false); - mQuickKeysMenu->setVisible(false); - mLevelupDialog->setVisible(false); - mWaitDialog->setVisible(false); - mSpellCreationDialog->setVisible(false); - mEnchantingDialog->setVisible(false); - mTrainingWindow->setVisible(false); - - mHud->setVisible(true); - - // Mouse is visible whenever we're not in game mode - setCursorVisible(isGuiMode()); - - bool gameMode = !isGuiMode(); - - mInputBlocker->setVisible (gameMode); - - if (gameMode) - mToolTips->enterGameMode(); - else - mToolTips->enterGuiMode(); - - if (gameMode) - MyGUI::InputManager::getInstance ().setKeyFocusWidget (NULL); - - setMinimapVisibility((mAllowed & GW_Map) && !mMap->pinned()); - setWeaponVisibility((mAllowed & GW_Inventory) && !mInventoryWindow->pinned()); - setSpellVisibility((mAllowed & GW_Magic) && !mSpellWindow->pinned()); - setHMSVisibility((mAllowed & GW_Stats) && !mStatsWindow->pinned()); - - // If in game mode, don't show anything. - if (gameMode) - return; - - GuiMode mode = mGuiModes.back(); - - switch(mode) { - case GM_QuickKeysMenu: - mQuickKeysMenu->setVisible (true); - break; - case GM_MainMenu: - mMenu->setVisible(true); - break; - case GM_Settings: - mSettingsWindow->setVisible(true); - break; - case GM_Console: - mConsole->enable(); - break; - case GM_Scroll: - mScrollWindow->setVisible(true); - break; - case GM_Book: - mBookWindow->setVisible(true); - break; - case GM_Alchemy: - mAlchemyWindow->setVisible(true); - break; - case GM_Rest: - mWaitDialog->setVisible(true); - break; - case GM_RestBed: - mWaitDialog->setVisible(true); - mWaitDialog->bedActivated(); - break; - case GM_Levelup: - mLevelupDialog->setVisible(true); - break; - case GM_Name: - case GM_Race: - case GM_Class: - case GM_ClassPick: - case GM_ClassCreate: - case GM_Birth: - case GM_ClassGenerate: - case GM_Review: - mCharGen->spawnDialog(mode); - break; - case GM_Inventory: + void WindowManager::cleanupGarbage() + { + // Delete any dialogs which are no longer in use + if (!mGarbageDialogs.empty()) { - // First, compute the effective set of windows to show. - // This is controlled both by what windows the - // user has opened/closed (the 'shown' variable) and by what - // windows we are allowed to show (the 'allowed' var.) - int eff = mShown & mAllowed; - - // Show the windows we want - mMap ->setVisible(eff & GW_Map); - mStatsWindow ->setVisible(eff & GW_Stats); - mInventoryWindow->setVisible(eff & GW_Inventory); - mSpellWindow ->setVisible(eff & GW_Magic); - break; + for (std::vector::iterator it = mGarbageDialogs.begin(); it != mGarbageDialogs.end(); ++it) + { + delete *it; + } + mGarbageDialogs.clear(); } - case GM_Container: - mContainerWindow->setVisible(true); - mInventoryWindow->setVisible(true); - break; - case GM_Dialogue: - mDialogueWindow->setVisible(true); - break; - case GM_Barter: - mInventoryWindow->setVisible(true); - mTradeWindow->setVisible(true); - break; - case GM_SpellBuying: - mSpellBuyingWindow->setVisible(true); - break; - case GM_Travel: - mTravelWindow->setVisible(true); - break; - case GM_SpellCreation: - mSpellCreationDialog->setVisible(true); - break; - case GM_Enchanting: - mEnchantingDialog->setVisible(true); - break; - case GM_Training: - mTrainingWindow->setVisible(true); - break; - case GM_InterMessageBox: - break; - case GM_Journal: - mJournal->setVisible(true); - break; - case GM_LoadingWallpaper: - mHud->setVisible(false); - setCursorVisible(false); - break; - case GM_Loading: - setCursorVisible(false); - break; - case GM_Video: - setCursorVisible(false); - mHud->setVisible(false); - break; - default: - // Unsupported mode, switch back to game - break; } -} -void WindowManager::setValue (const std::string& id, const MWMechanics::Stat& value) -{ - mStatsWindow->setValue (id, value); - mCharGen->setValue(id, value); - - static const char *ids[] = - { - "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", - "AttribVal6", "AttribVal7", "AttribVal8" - }; - static ESM::Attribute::AttributeID attributes[] = - { - ESM::Attribute::Strength, - ESM::Attribute::Intelligence, - ESM::Attribute::Willpower, - ESM::Attribute::Agility, - ESM::Attribute::Speed, - ESM::Attribute::Endurance, - ESM::Attribute::Personality, - ESM::Attribute::Luck - }; - for (size_t i = 0; i < sizeof(ids)/sizeof(ids[0]); ++i) - { - if (id != ids[i]) - continue; - mPlayerAttributes[attributes[i]] = value; - break; - } -} + void WindowManager::update() + { + cleanupGarbage(); + mHud->setFPS(mFPS); + mHud->setTriangleCount(mTriangleCount); + mHud->setBatchCount(mBatchCount); -void WindowManager::setValue (int parSkill, const MWMechanics::Stat& value) -{ - /// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we - /// allow custom skills. - mStatsWindow->setValue(static_cast (parSkill), value); - mCharGen->setValue(static_cast (parSkill), value); - mPlayerSkillValues[parSkill] = value; -} + mHud->update(); -void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat& value) -{ - mStatsWindow->setValue (id, value); - mHud->setValue (id, value); - mCharGen->setValue(id, value); - if (id == "HBar") - { - mPlayerHealth = value; - mCharGen->setPlayerHealth (value); + mSoftwareCursor->update(); } - else if (id == "MBar") + + void WindowManager::updateVisible() { - mPlayerMagicka = value; - mCharGen->setPlayerMagicka (value); + // Start out by hiding everything except the HUD + mMap->setVisible(false); + mMenu->setVisible(false); + mStatsWindow->setVisible(false); + mConsole->disable(); + mJournal->setVisible(false); + mDialogueWindow->setVisible(false); + mContainerWindow->setVisible(false); + mInventoryWindow->setVisible(false); + mScrollWindow->setVisible(false); + mBookWindow->setVisible(false); + mTradeWindow->setVisible(false); + mSpellBuyingWindow->setVisible(false); + mTravelWindow->setVisible(false); + mSettingsWindow->setVisible(false); + mAlchemyWindow->setVisible(false); + mSpellWindow->setVisible(false); + mQuickKeysMenu->setVisible(false); + mLevelupDialog->setVisible(false); + mWaitDialog->setVisible(false); + mSpellCreationDialog->setVisible(false); + mEnchantingDialog->setVisible(false); + mTrainingWindow->setVisible(false); + mMerchantRepair->setVisible(false); + mRepair->setVisible(false); + mCompanionWindow->setVisible(false); + mInventoryWindow->setTrading(false); + + mHud->setVisible(mHudEnabled); + + bool gameMode = !isGuiMode(); + + mInputBlocker->setVisible (gameMode); + setCursorVisible(!gameMode); + + if (gameMode) + mToolTips->enterGameMode(); + else + mToolTips->enterGuiMode(); + + if (gameMode) + MyGUI::InputManager::getInstance ().setKeyFocusWidget (NULL); + + setMinimapVisibility((mAllowed & GW_Map) && !mMap->pinned()); + setWeaponVisibility((mAllowed & GW_Inventory) && !mInventoryWindow->pinned()); + setSpellVisibility((mAllowed & GW_Magic) && !mSpellWindow->pinned()); + setHMSVisibility((mAllowed & GW_Stats) && !mStatsWindow->pinned()); + + // If in game mode, show only the pinned windows + if (gameMode) + { + mMap->setVisible(mMap->pinned()); + mStatsWindow->setVisible(mStatsWindow->pinned()); + mInventoryWindow->setVisible(mInventoryWindow->pinned()); + mSpellWindow->setVisible(mSpellWindow->pinned()); + + return; + } + + GuiMode mode = mGuiModes.back(); + + switch(mode) { + case GM_QuickKeysMenu: + mQuickKeysMenu->setVisible (true); + break; + case GM_MainMenu: + mMenu->setVisible(true); + break; + case GM_Settings: + mSettingsWindow->setVisible(true); + break; + case GM_Console: + // Show the pinned windows + mMap->setVisible(mMap->pinned()); + mStatsWindow->setVisible(mStatsWindow->pinned()); + mInventoryWindow->setVisible(mInventoryWindow->pinned()); + mSpellWindow->setVisible(mSpellWindow->pinned()); + + mConsole->enable(); + break; + case GM_Scroll: + mScrollWindow->setVisible(true); + break; + case GM_Book: + mBookWindow->setVisible(true); + break; + case GM_Alchemy: + mAlchemyWindow->setVisible(true); + break; + case GM_Rest: + mWaitDialog->setVisible(true); + break; + case GM_RestBed: + mWaitDialog->setVisible(true); + mWaitDialog->bedActivated(); + break; + case GM_Levelup: + mLevelupDialog->setVisible(true); + break; + case GM_Name: + case GM_Race: + case GM_Class: + case GM_ClassPick: + case GM_ClassCreate: + case GM_Birth: + case GM_ClassGenerate: + case GM_Review: + mCharGen->spawnDialog(mode); + break; + case GM_Inventory: + { + // First, compute the effective set of windows to show. + // This is controlled both by what windows the + // user has opened/closed (the 'shown' variable) and by what + // windows we are allowed to show (the 'allowed' var.) + int eff = mShown & mAllowed; + + // Show the windows we want + mMap ->setVisible(eff & GW_Map); + mStatsWindow ->setVisible(eff & GW_Stats); + mInventoryWindow->setVisible(eff & GW_Inventory); + mSpellWindow ->setVisible(eff & GW_Magic); + break; + } + case GM_Container: + mContainerWindow->setVisible(true); + mInventoryWindow->setVisible(true); + break; + case GM_Companion: + mCompanionWindow->setVisible(true); + mInventoryWindow->setVisible(true); + break; + case GM_Dialogue: + mDialogueWindow->setVisible(true); + break; + case GM_Barter: + mInventoryWindow->setVisible(true); + mInventoryWindow->setTrading(true); + mTradeWindow->setVisible(true); + break; + case GM_SpellBuying: + mSpellBuyingWindow->setVisible(true); + break; + case GM_Travel: + mTravelWindow->setVisible(true); + break; + case GM_SpellCreation: + mSpellCreationDialog->setVisible(true); + break; + case GM_Enchanting: + mEnchantingDialog->setVisible(true); + break; + case GM_Training: + mTrainingWindow->setVisible(true); + break; + case GM_MerchantRepair: + mMerchantRepair->setVisible(true); + break; + case GM_Repair: + mRepair->setVisible(true); + break; + case GM_Journal: + mJournal->setVisible(true); + break; + case GM_LoadingWallpaper: + mHud->setVisible(false); + setCursorVisible(false); + break; + case GM_Loading: + // Show the pinned windows + mMap->setVisible(mMap->pinned()); + mStatsWindow->setVisible(mStatsWindow->pinned()); + mInventoryWindow->setVisible(mInventoryWindow->pinned()); + mSpellWindow->setVisible(mSpellWindow->pinned()); + + setCursorVisible(false); + break; + case GM_Video: + setCursorVisible(false); + mHud->setVisible(false); + break; + default: + // Unsupported mode, switch back to game + break; + } } - else if (id == "FBar") + + void WindowManager::setValue (const std::string& id, const MWMechanics::Stat& value) { - mPlayerFatigue = value; - mCharGen->setPlayerFatigue (value); + mStatsWindow->setValue (id, value); + mCharGen->setValue(id, value); + + static const char *ids[] = + { + "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", + "AttribVal6", "AttribVal7", "AttribVal8" + }; + static ESM::Attribute::AttributeID attributes[] = + { + ESM::Attribute::Strength, + ESM::Attribute::Intelligence, + ESM::Attribute::Willpower, + ESM::Attribute::Agility, + ESM::Attribute::Speed, + ESM::Attribute::Endurance, + ESM::Attribute::Personality, + ESM::Attribute::Luck + }; + for (size_t i = 0; i < sizeof(ids)/sizeof(ids[0]); ++i) + { + if (id != ids[i]) + continue; + mPlayerAttributes[attributes[i]] = value; + break; + } } -} -#if 0 -MWMechanics::DynamicStat WindowManager::getValue(const std::string& id) -{ - if(id == "HBar") - return layerHealth; - 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); - if (id=="name") - mPlayerName = value; - else if (id=="race") - mPlayerRaceId = value; -} + void WindowManager::setValue (int parSkill, const MWMechanics::Stat& value) + { + /// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we + /// allow custom skills. + mStatsWindow->setValue(static_cast (parSkill), value); + mCharGen->setValue(static_cast (parSkill), value); + mPlayerSkillValues[parSkill] = value; + } -void WindowManager::setValue (const std::string& id, int value) -{ - mStatsWindow->setValue (id, value); -} + void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat& value) + { + mStatsWindow->setValue (id, value); + mHud->setValue (id, value); + mCharGen->setValue(id, value); + if (id == "HBar") + { + mPlayerHealth = value; + mCharGen->setPlayerHealth (value); + } + else if (id == "MBar") + { + mPlayerMagicka = value; + mCharGen->setPlayerMagicka (value); + } + else if (id == "FBar") + { + mPlayerFatigue = value; + mCharGen->setPlayerFatigue (value); + } + } -void WindowManager::setPlayerClass (const ESM::Class &class_) -{ - mStatsWindow->setValue("class", class_.mName); -} + #if 0 + MWMechanics::DynamicStat WindowManager::getValue(const std::string& id) + { + if(id == "HBar") + return layerHealth; + else if (id == "MBar") + return mPlayerMagicka; + else if (id == "FBar") + return mPlayerFatigue; + } + #endif -void WindowManager::configureSkills (const SkillList& major, const SkillList& minor) -{ - mStatsWindow->configureSkills (major, minor); - mCharGen->configureSkills(major, minor); - mPlayerMajorSkills = major; - mPlayerMinorSkills = minor; -} + void WindowManager::setValue (const std::string& id, const std::string& value) + { + mStatsWindow->setValue (id, value); + if (id=="name") + mPlayerName = value; + else if (id=="race") + mPlayerRaceId = value; + } -void WindowManager::setReputation (int reputation) -{ - mStatsWindow->setReputation (reputation); -} + void WindowManager::setValue (const std::string& id, int value) + { + mStatsWindow->setValue (id, value); + } -void WindowManager::setBounty (int bounty) -{ - mStatsWindow->setBounty (bounty); -} + void WindowManager::setPlayerClass (const ESM::Class &class_) + { + mStatsWindow->setValue("class", class_.mName); + } -void WindowManager::updateSkillArea() -{ - mStatsWindow->updateSkillArea(); -} + void WindowManager::configureSkills (const SkillList& major, const SkillList& minor) + { + mStatsWindow->configureSkills (major, minor); + mCharGen->configureSkills(major, minor); + mPlayerMajorSkills = major; + mPlayerMinorSkills = minor; + } -void WindowManager::removeDialog(OEngine::GUI::Layout*dialog) -{ - if (!dialog) - return; - dialog->setVisible(false); - mGarbageDialogs.push_back(dialog); -} + void WindowManager::setReputation (int reputation) + { + mStatsWindow->setReputation (reputation); + } -void WindowManager::messageBox (const std::string& message, const std::vector& buttons) -{ - if(buttons.empty()){ - /* If there are no buttons, and there is a dialogue window open, messagebox goes to the dialogue window */ - if(!mGuiModes.empty() && mGuiModes.back() == GM_Dialogue) - mDialogueWindow->addMessageBox(MyGUI::LanguageManager::getInstance().replaceTags(message)); - else - mMessageBoxManager->createMessageBox(message); + void WindowManager::setBounty (int bounty) + { + mStatsWindow->setBounty (bounty); } - - else + + void WindowManager::updateSkillArea() { - mMessageBoxManager->createInteractiveMessageBox(message, buttons); - pushGuiMode(GM_InterMessageBox); + mStatsWindow->updateSkillArea(); } -} -int WindowManager::readPressedButton () -{ - return mMessageBoxManager->readPressedButton(); -} + void WindowManager::removeDialog(OEngine::GUI::Layout*dialog) + { + if (!dialog) + return; + dialog->setVisible(false); + mGarbageDialogs.push_back(dialog); + } -std::string WindowManager::getGameSettingString(const std::string &id, const std::string &default_) -{ - const ESM::GameSetting *setting = - MWBase::Environment::get().getWorld()->getStore().get().search(id); + void WindowManager::messageBox (const std::string& message, const std::vector& buttons, bool showInDialogueModeOnly) + { + 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) { + mDialogueWindow->addMessageBox(MyGUI::LanguageManager::getInstance().replaceTags(message)); + } else { + if (showInDialogueModeOnly) { + if (getMode() == GM_Dialogue) + mMessageBoxManager->createMessageBox(message); + } else { + mMessageBoxManager->createMessageBox(message); + } + } + } else { + mMessageBoxManager->createInteractiveMessageBox(message, buttons); + MWBase::Environment::get().getInputManager()->changeInputMode(isGuiMode()); + } + } - if (setting && setting->mType == ESM::VT_String) - return setting->getString(); - return default_; -} + void WindowManager::staticMessageBox(const std::string& message) + { + mMessageBoxManager->createMessageBox(message, true); + } -void WindowManager::onDialogueWindowBye() -{ - if (mDialogueWindow) + void WindowManager::removeStaticMessageBox() { - //FIXME set some state and stuff? - //removeDialog(dialogueWindow); - mDialogueWindow->setVisible(false); + mMessageBoxManager->removeStaticMessageBox(); } - removeGuiMode(GM_Dialogue); -} -void WindowManager::onFrame (float frameDuration) -{ - mMessageBoxManager->onFrame(frameDuration); - mToolTips->onFrame(frameDuration); + void WindowManager::enterPressed () + { + mMessageBoxManager->enterPressed(); + } - if (mDragAndDrop->mIsOnDragAndDrop) + int WindowManager::readPressedButton () { - assert(mDragAndDrop->mDraggedWidget); - mDragAndDrop->mDraggedWidget->setPosition(MyGUI::InputManager::getInstance().getMousePosition()); + return mMessageBoxManager->readPressedButton(); } - mDialogueWindow->onFrame(); + std::string WindowManager::getGameSettingString(const std::string &id, const std::string &default_) + { + const ESM::GameSetting *setting = + MWBase::Environment::get().getWorld()->getStore().get().search(id); + + if (setting && setting->mValue.getType()==ESM::VT_String) + return setting->mValue.getString(); - mInventoryWindow->onFrame(); + return default_; + } - mStatsWindow->onFrame(); + void WindowManager::onFrame (float frameDuration) + { + mMessageBoxManager->onFrame(frameDuration); - mWaitDialog->onFrame(frameDuration); + mToolTips->onFrame(frameDuration); - mHud->onFrame(frameDuration); + if (mDragAndDrop->mIsOnDragAndDrop) + { + assert(mDragAndDrop->mDraggedWidget); + mDragAndDrop->mDraggedWidget->setPosition(MyGUI::InputManager::getInstance().getMousePosition()); + } - mTrainingWindow->onFrame (frameDuration); + mDialogueWindow->onFrame(); - mTrainingWindow->checkReferenceAvailable(); - mDialogueWindow->checkReferenceAvailable(); - mTradeWindow->checkReferenceAvailable(); - mSpellBuyingWindow->checkReferenceAvailable(); - mSpellCreationDialog->checkReferenceAvailable(); - mEnchantingDialog->checkReferenceAvailable(); - mContainerWindow->checkReferenceAvailable(); - mConsole->checkReferenceAvailable(); -} + mInventoryWindow->onFrame(); -void WindowManager::changeCell(MWWorld::Ptr::CellStore* cell) -{ - if (cell->mCell->isExterior()) + mStatsWindow->onFrame(); + + mWaitDialog->onFrame(frameDuration); + + mHud->onFrame(frameDuration); + + mTrainingWindow->onFrame (frameDuration); + mTradeWindow->onFrame(frameDuration); + + mTrainingWindow->checkReferenceAvailable(); + mDialogueWindow->checkReferenceAvailable(); + mTradeWindow->checkReferenceAvailable(); + mSpellBuyingWindow->checkReferenceAvailable(); + mSpellCreationDialog->checkReferenceAvailable(); + mEnchantingDialog->checkReferenceAvailable(); + mContainerWindow->checkReferenceAvailable(); + mCompanionWindow->checkReferenceAvailable(); + mConsole->checkReferenceAvailable(); + mCompanionWindow->onFrame(); + } + + void WindowManager::changeCell(MWWorld::Ptr::CellStore* cell) { - std::string name; - if (cell->mCell->mName != "") + if (cell->mCell->isExterior()) { - name = cell->mCell->mName; - mMap->addVisitedLocation ("#{sCell=" + name + "}", cell->mCell->getGridX (), cell->mCell->getGridY ()); + std::string name; + if (cell->mCell->mName != "") + { + name = cell->mCell->mName; + 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() ); + mHud->setActiveCell( 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->setCellName( cell->mCell->mName ); + mHud->setCellName( cell->mCell->mName ); + mMap->setCellPrefix( cell->mCell->mName ); + mHud->setCellPrefix( cell->mCell->mName ); } - 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() ); - mHud->setActiveCell( cell->mCell->getGridX(), cell->mCell->getGridY() ); + void WindowManager::setInteriorMapTexture(const int x, const int y) + { + mMap->setActiveCell(x,y, true); + mHud->setActiveCell(x,y, true); } - else + + void WindowManager::setPlayerPos(const float x, const float y) { - mMap->setCellName( cell->mCell->mName ); - mHud->setCellName( cell->mCell->mName ); - mMap->setCellPrefix( cell->mCell->mName ); - mHud->setCellPrefix( cell->mCell->mName ); + mMap->setPlayerPos(x,y); + mHud->setPlayerPos(x,y); } -} + void WindowManager::setPlayerDir(const float x, const float y) + { + mMap->setPlayerDir(x,y); + mHud->setPlayerDir(x,y); + } -void WindowManager::setInteriorMapTexture(const int x, const int y) -{ - mMap->setActiveCell(x,y, true); - mHud->setActiveCell(x,y, true); -} + void WindowManager::setHMSVisibility(bool visible) + { + mHud->setHmsVisible (visible); + } -void WindowManager::setPlayerPos(const float x, const float y) -{ - mMap->setPlayerPos(x,y); - mHud->setPlayerPos(x,y); -} + void WindowManager::setMinimapVisibility(bool visible) + { + mHud->setMinimapVisible (visible); + } -void WindowManager::setPlayerDir(const float x, const float y) -{ - mMap->setPlayerDir(x,y); - mHud->setPlayerDir(x,y); -} + void WindowManager::toggleFogOfWar() + { + mMap->toggleFogOfWar(); + mHud->toggleFogOfWar(); + } -void WindowManager::setHMSVisibility(bool visible) -{ - mHud->setHmsVisible (visible); -} + void WindowManager::setFocusObject(const MWWorld::Ptr& focus) + { + mToolTips->setFocusObject(focus); + } -void WindowManager::setMinimapVisibility(bool visible) -{ - mHud->setMinimapVisible (visible); -} + void WindowManager::setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) + { + mToolTips->setFocusObjectScreenCoords(min_x, min_y, max_x, max_y); + } -void WindowManager::toggleFogOfWar() -{ - mMap->toggleFogOfWar(); - mHud->toggleFogOfWar(); -} + void WindowManager::toggleFullHelp() + { + mToolTips->toggleFullHelp(); + } -void WindowManager::setFocusObject(const MWWorld::Ptr& focus) -{ - mToolTips->setFocusObject(focus); -} + bool WindowManager::getFullHelp() const + { + return mToolTips->getFullHelp(); + } -void WindowManager::setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) -{ - mToolTips->setFocusObjectScreenCoords(min_x, min_y, max_x, max_y); -} + void WindowManager::setWeaponVisibility(bool visible) + { + mHud->setWeapVisible (visible); + } -void WindowManager::toggleFullHelp() -{ - mToolTips->toggleFullHelp(); -} + void WindowManager::setSpellVisibility(bool visible) + { + mHud->setSpellVisible (visible); + mHud->setEffectVisible (visible); + } -bool WindowManager::getFullHelp() const -{ - return mToolTips->getFullHelp(); -} + void WindowManager::setDragDrop(bool dragDrop) + { + mToolTips->setEnabled(!dragDrop); + MWBase::Environment::get().getInputManager()->setDragDrop(dragDrop); + } -void WindowManager::setWeaponVisibility(bool visible) -{ - mHud->setWeapVisible (visible); -} + void WindowManager::setUseHardwareCursors(bool use) + { + mCursorManager->setEnabled(use); + mSoftwareCursor->setVisible(!use && mCursorVisible); + } -void WindowManager::setSpellVisibility(bool visible) -{ - mHud->setSpellVisible (visible); - mHud->setEffectVisible (visible); -} + void WindowManager::setCursorVisible(bool visible) + { + if(mCursorVisible == visible) + return; -void WindowManager::setUseHardwareCursors(bool use) -{ - mCursorManager->setEnabled(use); + mCursorVisible = visible; + mCursorManager->cursorVisibilityChange(visible); - if(use) - { - MyGUI::PointerManager::getInstance().setVisible(false); + mSoftwareCursor->setVisible(!mUseHardwareCursors && visible); } - else + + void WindowManager::onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result) { - MyGUI::PointerManager::getInstance().setVisible(mCursorVisible); - } -} + std::string tag(_tag); -void WindowManager::setCursorVisible(bool visible) -{ - if(mCursorVisible == visible) - return; + std::string tokenToFind = "sCell="; + size_t tokenLength = tokenToFind.length(); - mCursorVisible = visible; - mCursorManager->cursorVisibilityChange(visible); + if (tag.substr(0, tokenLength) == tokenToFind) + { + _result = mTranslationDataStorage.translateCellName(tag.substr(tokenLength)); + } + else + { + const ESM::GameSetting *setting = + MWBase::Environment::get().getWorld()->getStore().get().find(tag); - if(!mUseHardwareCursors) - MyGUI::PointerManager::getInstance().setVisible(visible); -} + if (setting && setting->mValue.getType()==ESM::VT_String) + _result = setting->mValue.getString(); + else + _result = tag; + } + } -void WindowManager::setDragDrop(bool dragDrop) -{ - mToolTips->setEnabled(!dragDrop); - MWBase::Environment::get().getInputManager()->setDragDrop(dragDrop); -} + void WindowManager::processChangedSettings(const Settings::CategorySettingVector& changed) + { + mHud->setFpsLevel(Settings::Manager::getInt("fps", "HUD")); + mToolTips->setDelay(Settings::Manager::getFloat("tooltip delay", "GUI")); -void WindowManager::onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result) -{ - std::string tag(_tag); + setUseHardwareCursors(Settings::Manager::getBool("hardware cursors", "GUI")); - std::string tokenToFind = "sCell="; - size_t tokenLength = tokenToFind.length(); + bool changeRes = false; + bool windowRecreated = false; + for (Settings::CategorySettingVector::const_iterator it = changed.begin(); + it != changed.end(); ++it) + { + if (it->first == "Video" && ( + it->second == "resolution x" + || it->second == "resolution y")) + { + changeRes = true; + } + else if (it->first == "Video" && it->second == "vsync") + windowRecreated = true; + else if (it->first == "HUD" && it->second == "crosshair") + mCrosshairEnabled = Settings::Manager::getBool ("crosshair", "HUD"); + else if (it->first == "GUI" && it->second == "subtitles") + mSubtitlesEnabled = Settings::Manager::getBool ("subtitles", "GUI"); + } - if (tag.substr(0, tokenLength) == tokenToFind) - { - _result = mTranslationDataStorage.translateCellName(tag.substr(tokenLength)); + if (changeRes) + { + int x = Settings::Manager::getInt("resolution x", "Video"); + int y = Settings::Manager::getInt("resolution y", "Video"); + mHud->onResChange(x, y); + mConsole->onResChange(x, y); + mMenu->onResChange(x, y); + mSettingsWindow->center(); + mAlchemyWindow->center(); + mScrollWindow->center(); + mBookWindow->center(); + mQuickKeysMenu->center(); + mSpellBuyingWindow->center(); + mLoadingScreen->onResChange (x,y); + mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y)); + mInputBlocker->setSize(MyGUI::IntSize(x,y)); + } + if (windowRecreated) + { + mGuiManager->updateWindow (mRendering->getWindow ()); + mLoadingScreen->updateWindow (mRendering->getWindow ()); + } } - else + + void WindowManager::pushGuiMode(GuiMode mode) { - const ESM::GameSetting *setting = - MWBase::Environment::get().getWorld()->getStore().get().find(tag); + if (mode==GM_Inventory && mAllowed==GW_None) + return; - if (setting && setting->mType == ESM::VT_String) - _result = setting->getString(); - else - _result = tag; + + // If this mode already exists somewhere in the stack, just bring it to the front. + if (std::find(mGuiModes.begin(), mGuiModes.end(), mode) != mGuiModes.end()) + { + mGuiModes.erase(std::find(mGuiModes.begin(), mGuiModes.end(), mode)); + } + + mGuiModes.push_back(mode); + + bool gameMode = !isGuiMode(); + MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); + + updateVisible(); } -} -void WindowManager::onCursorChange(const std::string &name) -{ - //the cursor manager doesn't want any more info about this cursor - if(!mCursorManager->cursorChanged(name)) - return; - //See if we can get the information we need out of the cursor resource - ResourceImageSetPointerFix* imgSetPtr = dynamic_cast(MyGUI::PointerManager::getInstance().getByName(name)); - if(imgSetPtr != NULL) + void WindowManager::onCursorChange(const std::string &name) { - MyGUI::ResourceImageSet* imgSet = imgSetPtr->getImageSet(); + mSoftwareCursor->onCursorChange(name); - std::string tex_name = imgSet->getIndexInfo(0,0).texture; + if(!mCursorManager->cursorChanged(name)) + return; //the cursor manager doesn't want any more info about this cursor + //See if we can get the information we need out of the cursor resource + ResourceImageSetPointerFix* imgSetPtr = dynamic_cast(MyGUI::PointerManager::getInstance().getByName(name)); + if(imgSetPtr != NULL) + { + MyGUI::ResourceImageSet* imgSet = imgSetPtr->getImageSet(); - Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton().getByName(tex_name); + std::string tex_name = imgSet->getIndexInfo(0,0).texture; - //everything looks good, send it to the cursor manager - if(!tex.isNull()) - { - Uint8 size_x = imgSetPtr->getSize().width; - Uint8 size_y = imgSetPtr->getSize().height; - Uint8 hotspot_x = imgSetPtr->getHotSpot().left; - Uint8 hotspot_y = imgSetPtr->getHotSpot().top; + Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton().getByName(tex_name); - mCursorManager->receiveCursorInfo(name, tex, size_x, size_y, hotspot_x, hotspot_y); + //everything looks good, send it to the cursor manager + if(!tex.isNull()) + { + Uint8 size_x = imgSetPtr->getSize().width; + Uint8 size_y = imgSetPtr->getSize().height; + Uint8 hotspot_x = imgSetPtr->getHotSpot().left; + Uint8 hotspot_y = imgSetPtr->getHotSpot().top; + + mCursorManager->receiveCursorInfo(name, tex, size_x, size_y, hotspot_x, hotspot_y); + } } } -} -void WindowManager::processChangedSettings(const Settings::CategorySettingVector& changed) -{ - mHud->setFpsLevel(Settings::Manager::getInt("fps", "HUD")); - mToolTips->setDelay(Settings::Manager::getFloat("tooltip delay", "GUI")); - setUseHardwareCursors(Settings::Manager::getBool("hardware cursors", "GUI")); + void WindowManager::popGuiMode() + { + if (!mGuiModes.empty()) + mGuiModes.pop_back(); + + bool gameMode = !isGuiMode(); + MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); + + updateVisible(); + } - bool changeRes = false; - for (Settings::CategorySettingVector::const_iterator it = changed.begin(); - it != changed.end(); ++it) + void WindowManager::removeGuiMode(GuiMode mode) { - if (it->first == "Video" && ( - it->second == "resolution x" - || it->second == "resolution y")) + std::vector::iterator it = mGuiModes.begin(); + while (it != mGuiModes.end()) { - changeRes = true; + if (*it == mode) + it = mGuiModes.erase(it); + else + ++it; } - else if (it->first == "HUD" && it->second == "crosshair") - mCrosshairEnabled = Settings::Manager::getBool ("crosshair", "HUD"); - else if (it->first == "GUI" && it->second == "subtitles") - mSubtitlesEnabled = Settings::Manager::getBool ("subtitles", "GUI"); - } - - if (changeRes) - { - int x = Settings::Manager::getInt("resolution x", "Video"); - int y = Settings::Manager::getInt("resolution y", "Video"); - mHud->onResChange(x, y); - mConsole->onResChange(x, y); - mMenu->onResChange(x, y); - mSettingsWindow->center(); - mAlchemyWindow->center(); - mScrollWindow->center(); - mBookWindow->center(); - mQuickKeysMenu->center(); - mSpellBuyingWindow->center(); - mLoadingScreen->onResChange (x,y); - mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y)); - mInputBlocker->setSize(MyGUI::IntSize(x,y)); - } -} -void WindowManager::pushGuiMode(GuiMode mode) -{ - if (mode==GM_Inventory && mAllowed==GW_None) - return; + bool gameMode = !isGuiMode(); + MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); + updateVisible(); + } - // If this mode already exists somewhere in the stack, just bring it to the front. - if (std::find(mGuiModes.begin(), mGuiModes.end(), mode) != mGuiModes.end()) + void WindowManager::setSelectedSpell(const std::string& spellId, int successChancePercent) { - mGuiModes.erase(std::find(mGuiModes.begin(), mGuiModes.end(), mode)); + mHud->setSelectedSpell(spellId, successChancePercent); + + const ESM::Spell* spell = + MWBase::Environment::get().getWorld()->getStore().get().find(spellId); + + mSpellWindow->setTitle(spell->mName); } - mGuiModes.push_back(mode); + void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item) + { + const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get() + .find(MWWorld::Class::get(item).getEnchantment(item)); - bool gameMode = !isGuiMode(); - MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); + int chargePercent = (item.getCellRef().mEnchantmentCharge == -1) ? 100 + : (item.getCellRef().mEnchantmentCharge / static_cast(ench->mData.mCharge) * 100); + mHud->setSelectedEnchantItem(item, chargePercent); + mSpellWindow->setTitle(MWWorld::Class::get(item).getName(item)); + } - updateVisible(); -} + void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item) + { + int durabilityPercent = (item.getCellRef().mCharge == -1) ? 100 + : (item.getCellRef().mCharge / static_cast(MWWorld::Class::get(item).getItemMaxHealth(item)) * 100); + mHud->setSelectedWeapon(item, durabilityPercent); + mInventoryWindow->setTitle(MWWorld::Class::get(item).getName(item)); + } -void WindowManager::popGuiMode() -{ - if (!mGuiModes.empty()) - mGuiModes.pop_back(); + void WindowManager::unsetSelectedSpell() + { + mHud->unsetSelectedSpell(); + mSpellWindow->setTitle("#{sNone}"); + } - bool gameMode = !isGuiMode(); - MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); + void WindowManager::unsetSelectedWeapon() + { + mHud->unsetSelectedWeapon(); + mInventoryWindow->setTitle("#{sSkillHandtohand}"); + } - updateVisible(); -} + void WindowManager::getMousePosition(int &x, int &y) + { + const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition(); + x = pos.left; + y = pos.top; + } -void WindowManager::removeGuiMode(GuiMode mode) -{ - std::vector::iterator it = mGuiModes.begin(); - while (it != mGuiModes.end()) + void WindowManager::getMousePosition(float &x, float &y) { - if (*it == mode) - it = mGuiModes.erase(it); - else - ++it; + const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition(); + x = pos.left; + y = pos.top; + const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + x /= viewSize.width; + y /= viewSize.height; } - bool gameMode = !isGuiMode(); - MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); + bool WindowManager::getWorldMouseOver() + { + return mHud->getWorldMouseOver(); + } - updateVisible(); -} + void WindowManager::executeInConsole (const std::string& path) + { + mConsole->executeFile (path); + } -void WindowManager::setSelectedSpell(const std::string& spellId, int successChancePercent) -{ - mHud->setSelectedSpell(spellId, successChancePercent); + void WindowManager::wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount) + { + mFPS = fps; + mTriangleCount = triangleCount; + mBatchCount = batchCount; + } - const ESM::Spell* spell = - MWBase::Environment::get().getWorld()->getStore().get().find(spellId); + MyGUI::Gui* WindowManager::getGui() const { return mGui; } + + MWGui::DialogueWindow* WindowManager::getDialogueWindow() { return mDialogueWindow; } + MWGui::ContainerWindow* WindowManager::getContainerWindow() { return mContainerWindow; } + MWGui::InventoryWindow* WindowManager::getInventoryWindow() { return mInventoryWindow; } + MWGui::BookWindow* WindowManager::getBookWindow() { return mBookWindow; } + MWGui::ScrollWindow* WindowManager::getScrollWindow() { return mScrollWindow; } + MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; } + MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; } + MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; } + MWGui::SpellBuyingWindow* WindowManager::getSpellBuyingWindow() { return mSpellBuyingWindow; } + MWGui::TravelWindow* WindowManager::getTravelWindow() { return mTravelWindow; } + MWGui::SpellWindow* WindowManager::getSpellWindow() { return mSpellWindow; } + MWGui::Console* WindowManager::getConsole() { return mConsole; } + + bool WindowManager::isAllowed (GuiWindow wnd) const + { + return mAllowed & wnd; + } - mSpellWindow->setTitle(spell->mName); -} + void WindowManager::allow (GuiWindow wnd) + { + mAllowed = (GuiWindow)(mAllowed | wnd); -void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) -{ - mHud->setSelectedEnchantItem(item, chargePercent); - mSpellWindow->setTitle(MWWorld::Class::get(item).getName(item)); -} + if (wnd & GW_Inventory) + { + mBookWindow->setInventoryAllowed (true); + mScrollWindow->setInventoryAllowed (true); + } -void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) -{ - mHud->setSelectedWeapon(item, durabilityPercent); - mInventoryWindow->setTitle(MWWorld::Class::get(item).getName(item)); -} + updateVisible(); + } -void WindowManager::unsetSelectedSpell() -{ - mHud->unsetSelectedSpell(); - mSpellWindow->setTitle("#{sNone}"); -} + void WindowManager::disallowAll() + { + mAllowed = GW_None; -void WindowManager::unsetSelectedWeapon() -{ - mHud->unsetSelectedWeapon(); - mInventoryWindow->setTitle("#{sSkillHandtohand}"); -} + mBookWindow->setInventoryAllowed (false); + mScrollWindow->setInventoryAllowed (false); -void WindowManager::getMousePosition(int &x, int &y) -{ - const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition(); - x = pos.left; - y = pos.top; -} + updateVisible(); + } -void WindowManager::getMousePosition(float &x, float &y) -{ - const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition(); - x = pos.left; - y = pos.top; - const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - x /= viewSize.width; - y /= viewSize.height; -} + void WindowManager::toggleVisible (GuiWindow wnd) + { + mShown = (mShown & wnd) ? (GuiWindow) (mShown & ~wnd) : (GuiWindow) (mShown | wnd); + updateVisible(); + } -bool WindowManager::getWorldMouseOver() -{ - return mHud->getWorldMouseOver(); -} + bool WindowManager::isGuiMode() const + { + return !mGuiModes.empty() || mMessageBoxManager->isInteractiveMessageBox(); + } -void WindowManager::executeInConsole (const std::string& path) -{ - mConsole->executeFile (path); -} + bool WindowManager::isConsoleMode() const + { + if (!mGuiModes.empty() && mGuiModes.back()==GM_Console) + return true; + return false; + } -void WindowManager::wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount) -{ - mFPS = fps; - mTriangleCount = triangleCount; - mBatchCount = batchCount; -} + MWGui::GuiMode WindowManager::getMode() const + { + if (mGuiModes.empty()) + return GM_None; + return mGuiModes.back(); + } -MyGUI::Gui* WindowManager::getGui() const { return mGui; } - -MWGui::DialogueWindow* WindowManager::getDialogueWindow() { return mDialogueWindow; } -MWGui::ContainerWindow* WindowManager::getContainerWindow() { return mContainerWindow; } -MWGui::InventoryWindow* WindowManager::getInventoryWindow() { return mInventoryWindow; } -MWGui::BookWindow* WindowManager::getBookWindow() { return mBookWindow; } -MWGui::ScrollWindow* WindowManager::getScrollWindow() { return mScrollWindow; } -MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; } -MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; } -MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; } -MWGui::SpellBuyingWindow* WindowManager::getSpellBuyingWindow() { return mSpellBuyingWindow; } -MWGui::TravelWindow* WindowManager::getTravelWindow() { return mTravelWindow; } -MWGui::SpellWindow* WindowManager::getSpellWindow() { return mSpellWindow; } -MWGui::Console* WindowManager::getConsole() { return mConsole; } - -bool WindowManager::isAllowed (GuiWindow wnd) const -{ - return mAllowed & wnd; -} + std::map > WindowManager::getPlayerSkillValues() + { + return mPlayerSkillValues; + } -void WindowManager::allow (GuiWindow wnd) -{ - mAllowed = (GuiWindow)(mAllowed | wnd); - updateVisible(); -} + std::map > WindowManager::getPlayerAttributeValues() + { + return mPlayerAttributes; + } -void WindowManager::disallowAll() -{ - mAllowed = GW_None; - updateVisible(); -} + WindowManager::SkillList WindowManager::getPlayerMinorSkills() + { + return mPlayerMinorSkills; + } -void WindowManager::toggleVisible (GuiWindow wnd) -{ - mShown = (mShown & wnd) ? (GuiWindow) (mShown & ~wnd) : (GuiWindow) (mShown | wnd); - updateVisible(); -} + WindowManager::SkillList WindowManager::getPlayerMajorSkills() + { + return mPlayerMajorSkills; + } -bool WindowManager::isGuiMode() const -{ - return !mGuiModes.empty(); -} + void WindowManager::disallowMouse() + { + mInputBlocker->setVisible (true); + } -MWGui::GuiMode WindowManager::getMode() const -{ - if (mGuiModes.empty()) - throw std::runtime_error ("getMode() called, but there is no active mode"); + void WindowManager::allowMouse() + { + mInputBlocker->setVisible (!isGuiMode ()); + } - return mGuiModes.back(); -} + void WindowManager::notifyInputActionBound () + { + mSettingsWindow->updateControlsBox (); + allowMouse(); + } -bool WindowManager::containsMode(GuiMode mode) const -{ - if(mGuiModes.empty()) - return false; + bool WindowManager::containsMode(GuiMode mode) const + { + if(mGuiModes.empty()) + return false; - return std::find(mGuiModes.begin(), mGuiModes.end(), mode) != mGuiModes.end(); -} + return std::find(mGuiModes.begin(), mGuiModes.end(), mode) != mGuiModes.end(); + } -std::map > WindowManager::getPlayerSkillValues() -{ - return mPlayerSkillValues; -} + void WindowManager::showCrosshair (bool show) + { + mHud->setCrosshairVisible (show && mCrosshairEnabled); + } -std::map > WindowManager::getPlayerAttributeValues() -{ - return mPlayerAttributes; -} + void WindowManager::activateQuickKey (int index) + { + mQuickKeysMenu->activateQuickKey(index); + } -WindowManager::SkillList WindowManager::getPlayerMinorSkills() -{ - return mPlayerMinorSkills; -} + bool WindowManager::getSubtitlesEnabled () + { + return mSubtitlesEnabled; + } -WindowManager::SkillList WindowManager::getPlayerMajorSkills() -{ - return mPlayerMajorSkills; -} + void WindowManager::toggleHud () + { + mHudEnabled = !mHudEnabled; + mHud->setVisible (mHudEnabled); + } -void WindowManager::disallowMouse() -{ - mInputBlocker->setVisible (true); -} + void WindowManager::setLoadingProgress (const std::string& stage, int depth, int current, int total) + { + mLoadingScreen->setLoadingProgress (stage, depth, current, total); + } -void WindowManager::allowMouse() -{ - mInputBlocker->setVisible (!isGuiMode ()); -} + void WindowManager::loadingDone () + { + mLoadingScreen->loadingDone (); + } + bool WindowManager::getRestEnabled() + { + //Enable rest dialogue if character creation finished + if(mRestAllowed==false && MWBase::Environment::get().getWorld()->getGlobalVariable ("chargenstate").mFloat==-1) + mRestAllowed=true; + return mRestAllowed; + } -void WindowManager::notifyInputActionBound () -{ - mSettingsWindow->updateControlsBox (); - allowMouse(); -} + bool WindowManager::getPlayerSleeping () + { + return mWaitDialog->getSleeping(); + } + void WindowManager::wakeUpPlayer() + { + mWaitDialog->wakeUp(); + } -void WindowManager::showCrosshair (bool show) -{ - mHud->setCrosshairVisible (show && mCrosshairEnabled); -} + void WindowManager::addVisitedLocation(const std::string& name, int x, int y) + { + mMap->addVisitedLocation (name, x, y); + } -void WindowManager::activateQuickKey (int index) -{ - mQuickKeysMenu->activateQuickKey(index); -} + void WindowManager::startSpellMaking(MWWorld::Ptr actor) + { + mSpellCreationDialog->startSpellMaking (actor); + } -bool WindowManager::getSubtitlesEnabled () -{ - return mSubtitlesEnabled; -} + void WindowManager::startEnchanting (MWWorld::Ptr actor) + { + mEnchantingDialog->startEnchanting (actor); + } -void WindowManager::toggleHud () -{ - mHudEnabled = !mHudEnabled; - mHud->setVisible (mHudEnabled); -} + void WindowManager::startSelfEnchanting(MWWorld::Ptr soulgem) + { + mEnchantingDialog->startSelfEnchanting(soulgem); + } -void WindowManager::setLoadingProgress (const std::string& stage, int depth, int current, int total) -{ - mLoadingScreen->setLoadingProgress (stage, depth, current, total); -} + void WindowManager::startTraining(MWWorld::Ptr actor) + { + mTrainingWindow->startTraining(actor); + } -void WindowManager::loadingDone () -{ - mLoadingScreen->loadingDone (); -} + void WindowManager::startRepair(MWWorld::Ptr actor) + { + mMerchantRepair->startRepair(actor); + } -bool WindowManager::getPlayerSleeping () -{ - return mWaitDialog->getSleeping(); -} + void WindowManager::startRepairItem(MWWorld::Ptr item) + { + mRepair->startRepairItem(item); + } -void WindowManager::wakeUpPlayer() -{ - mWaitDialog->wakeUp(); -} + const Translation::Storage& WindowManager::getTranslationDataStorage() const + { + return mTranslationDataStorage; + } -void WindowManager::addVisitedLocation(const std::string& name, int x, int y) -{ - mMap->addVisitedLocation (name, x, y); -} + void WindowManager::showCompanionWindow(MWWorld::Ptr actor) + { + mCompanionWindow->open(actor); + } -void WindowManager::startSpellMaking(MWWorld::Ptr actor) -{ - mSpellCreationDialog->startSpellMaking (actor); -} + void WindowManager::changePointer(const std::string &name) + { + onCursorChange(name); + } -void WindowManager::startEnchanting (MWWorld::Ptr actor) -{ - mEnchantingDialog->startEnchanting (actor); -} + void WindowManager::showSoulgemDialog(MWWorld::Ptr item) + { + mSoulgemDialog->show(item); + } -void WindowManager::startTraining(MWWorld::Ptr actor) -{ - mTrainingWindow->startTraining(actor); -} + void WindowManager::frameStarted (float dt) + { + mInventoryWindow->doRenderUpdate (); + } + + void WindowManager::updatePlayer() + { + mInventoryWindow->updatePlayer(); + } -const Translation::Storage& WindowManager::getTranslationDataStorage() const -{ - return mTranslationDataStorage; } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 874de8af9..31b829cda 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -10,11 +10,6 @@ this class. **/ -#include -#include - -#include - #include "../mwbase/windowmanager.hpp" namespace MyGUI @@ -77,6 +72,12 @@ namespace MWGui class SpellCreationDialog; class EnchantingDialog; class TrainingWindow; + class Cursor; + class SpellIcons; + class MerchantRepair; + class Repair; + class SoulgemDialog; + class CompanionWindow; class WindowManager : public MWBase::WindowManager { @@ -84,10 +85,10 @@ namespace MWGui typedef std::pair Faction; typedef std::vector FactionList; - WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, + WindowManager(const Compiler::Extensions& extensions, int fpsLevel, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, - Translation::Storage& translationDataStorage); + Translation::Storage& translationDataStorage, ToUTF8::FromType encoding); virtual ~WindowManager(); /** @@ -97,6 +98,8 @@ namespace MWGui */ virtual void update(); + virtual void setNewGame(bool newgame); + virtual void pushGuiMode(GuiMode mode); virtual void popGuiMode(); virtual void removeGuiMode(GuiMode mode); ///< can be anywhere in the stack @@ -106,6 +109,8 @@ namespace MWGui virtual bool isGuiMode() const; + virtual bool isConsoleMode() const; + virtual void toggleVisible(GuiWindow wnd); // Disallow all inventory mode windows @@ -154,7 +159,6 @@ namespace MWGui virtual void setFocusObject(const MWWorld::Ptr& focus); virtual void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y); - virtual void setCursorVisible(bool visible); virtual void getMousePosition(int &x, int &y); virtual void getMousePosition(float &x, float &y); virtual void setDragDrop(bool dragDrop); @@ -177,8 +181,8 @@ namespace MWGui virtual void activateQuickKey (int index); virtual void setSelectedSpell(const std::string& spellId, int successChancePercent); - virtual void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent); - virtual void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent); + virtual void setSelectedEnchantItem(const MWWorld::Ptr& item); + virtual void setSelectedWeapon(const MWWorld::Ptr& item); virtual void unsetSelectedSpell(); virtual void unsetSelectedWeapon(); @@ -194,7 +198,10 @@ 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); + 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 int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) virtual void onFrame (float frameDuration); @@ -222,19 +229,36 @@ namespace MWGui virtual void loadingDone(); virtual void enableRest() { mRestAllowed = true; } - virtual bool getRestEnabled() { return mRestAllowed; } + virtual bool getRestEnabled(); + + virtual bool getJournalAllowed() { return (mAllowed & GW_Magic); } virtual bool getPlayerSleeping(); virtual void wakeUpPlayer(); + virtual void updatePlayer(); + + virtual void showCompanionWindow(MWWorld::Ptr actor); virtual void startSpellMaking(MWWorld::Ptr actor); virtual void startEnchanting(MWWorld::Ptr actor); + virtual void startSelfEnchanting(MWWorld::Ptr soulgem); virtual void startTraining(MWWorld::Ptr actor); + virtual void startRepair(MWWorld::Ptr actor); + virtual void startRepairItem(MWWorld::Ptr item); + + virtual void frameStarted(float dt); + + virtual void showSoulgemDialog (MWWorld::Ptr item); + + virtual void changePointer (const std::string& name); virtual const Translation::Storage& getTranslationDataStorage() const; + void onSoulgemDialogButtonPressed (int button); + private: OEngine::GUI::MyGUIManager *mGuiManager; + OEngine::Render::OgreRenderer *mRendering; HUD *mHud; MapWindow *mMap; MainMenu *mMenu; @@ -264,7 +288,13 @@ namespace MWGui SpellCreationDialog* mSpellCreationDialog; EnchantingDialog* mEnchantingDialog; TrainingWindow* mTrainingWindow; + MerchantRepair* mMerchantRepair; + SoulgemDialog* mSoulgemDialog; + Repair* mRepair; + CompanionWindow* mCompanionWindow; + Translation::Storage& mTranslationDataStorage; + Cursor* mSoftwareCursor; CharacterCreation* mCharGen; @@ -275,6 +305,8 @@ namespace MWGui bool mHudEnabled; bool mCursorVisible; + void setCursorVisible(bool visible); + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. // Various stats about player as needed by window manager std::string mPlayerName; @@ -311,8 +343,6 @@ namespace MWGui unsigned int mTriangleCount; unsigned int mBatchCount; - void onDialogueWindowBye(); - bool mUseHardwareCursors; void setUseHardwareCursors(bool use); diff --git a/apps/openmw/mwgui/windowpinnablebase.cpp b/apps/openmw/mwgui/windowpinnablebase.cpp new file mode 100644 index 000000000..e5a94fc72 --- /dev/null +++ b/apps/openmw/mwgui/windowpinnablebase.cpp @@ -0,0 +1,27 @@ +#include "windowpinnablebase.hpp" + +#include "exposedwindow.hpp" + +namespace MWGui +{ + WindowPinnableBase::WindowPinnableBase(const std::string& parLayout) + : WindowBase(parLayout), mPinned(false), mVisible(false) + { + ExposedWindow* window = static_cast(mMainWidget); + mPinButton = window->getSkinWidget ("Button"); + + mPinButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WindowPinnableBase::onPinButtonClicked); + } + + void WindowPinnableBase::onPinButtonClicked(MyGUI::Widget* _sender) + { + mPinned = !mPinned; + + if (mPinned) + mPinButton->changeWidgetSkin ("PinDown"); + else + mPinButton->changeWidgetSkin ("PinUp"); + + onPinToggled(); + } +} diff --git a/apps/openmw/mwgui/window_pinnable_base.hpp b/apps/openmw/mwgui/windowpinnablebase.hpp similarity index 59% rename from apps/openmw/mwgui/window_pinnable_base.hpp rename to apps/openmw/mwgui/windowpinnablebase.hpp index 250dde1f8..1ab629432 100644 --- a/apps/openmw/mwgui/window_pinnable_base.hpp +++ b/apps/openmw/mwgui/windowpinnablebase.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_WINDOW_PINNABLE_BASE_H #define MWGUI_WINDOW_PINNABLE_BASE_H -#include "window_base.hpp" +#include "windowbase.hpp" namespace MWGui { @@ -10,16 +10,16 @@ namespace MWGui class WindowPinnableBase: public WindowBase { public: - WindowPinnableBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager); - void setVisible(bool b); + WindowPinnableBase(const std::string& parLayout); bool pinned() { return mPinned; } private: - void onWindowButtonPressed(MyGUI::Window* sender, const std::string& eventName); + void onPinButtonClicked(MyGUI::Widget* _sender); protected: virtual void onPinToggled() = 0; + MyGUI::Widget* mPinButton; bool mPinned; bool mVisible; }; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index ab873c686..5d37b0050 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -20,6 +20,7 @@ #include "../engine.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" @@ -29,7 +30,7 @@ using namespace ICS; namespace MWInput { InputManager::InputManager(OEngine::Render::OgreRenderer &ogre, - MWWorld::Player &player, + MWWorld::Player& player, MWBase::WindowManager &windows, OMW::Engine& engine, const std::string& userFile, bool userFileExists) @@ -41,17 +42,19 @@ namespace MWInput , mMouseX(ogre.getWindow()->getWidth ()/2.f) , mMouseY(ogre.getWindow()->getHeight ()/2.f) , mMouseWheel(0) - , mUserFile(userFile) , mDragDrop(false) , mGuiCursorEnabled(false) , mDebug(Settings::Manager::getBool("debug", "Engine")) + , mUserFile(userFile) + , mUserFileExists(userFileExists) , mInvertY (Settings::Manager::getBool("invert y axis", "Input")) , mCameraSensitivity (Settings::Manager::getFloat("camera sensitivity", "Input")) , mUISensitivity (Settings::Manager::getFloat("ui sensitivity", "Input")) , mCameraYMultiplier (Settings::Manager::getFloat("camera y multiplier", "Input")) - , mUIYMultiplier (Settings::Manager::getFloat("ui y multiplier", "Input")) , mPreviewPOVDelay(0.f) , mTimeIdle(0.f) + , mOverencumberedMessageDelay(0.f) + , mAlwaysRunActive(false) { #if defined(__APPLE__) && !defined(__LP64__) // Give the application window focus to receive input events @@ -131,6 +134,10 @@ namespace MWInput break; case A_Activate: resetIdleTime(); + if( MWBase::Environment::get().getWindowManager()->isGuiMode()) { + // Pressing the activation key when a messagebox is prompting for "ok" will activate the ok button + MWBase::Environment::get().getWindowManager()->enterPressed(); + } activate(); break; case A_Journal: @@ -139,10 +146,7 @@ namespace MWInput case A_AutoMove: toggleAutoMove (); break; - case A_ToggleSneak: - /// \todo implement - break; - case A_ToggleWalk: + case A_AlwaysRun: toggleWalking (); break; case A_ToggleWeapon: @@ -190,7 +194,11 @@ namespace MWInput case A_ToggleHUD: mWindows.toggleHud(); break; - } + case A_Use: + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) + mPlayer.use(); + break; + } } } @@ -246,38 +254,65 @@ namespace MWInput // be done in the physics system. if (mControlSwitch["playercontrols"]) { + bool triedToMove = false; if (actionIsActive(A_MoveLeft)) { - mPlayer.setAutoMove (false); - mPlayer.setLeftRight (1); + triedToMove = true; + mPlayer.setLeftRight (-1); } else if (actionIsActive(A_MoveRight)) { - mPlayer.setAutoMove (false); - mPlayer.setLeftRight (-1); + triedToMove = true; + mPlayer.setLeftRight (1); } - else - mPlayer.setLeftRight (0); if (actionIsActive(A_MoveForward)) { + triedToMove = true; mPlayer.setAutoMove (false); mPlayer.setForwardBackward (1); } else if (actionIsActive(A_MoveBackward)) { + triedToMove = true; mPlayer.setAutoMove (false); mPlayer.setForwardBackward (-1); } - else - mPlayer.setForwardBackward (0); + + else if(mPlayer.getAutoMove()) + { + triedToMove = true; + mPlayer.setForwardBackward (1); + } + + mPlayer.setSneak(actionIsActive(A_Sneak)); if (actionIsActive(A_Jump) && mControlSwitch["playerjumping"]) + { mPlayer.setUpDown (1); - else if (actionIsActive(A_Crouch)) - mPlayer.setUpDown (-1); + triedToMove = true; + } + + if (mAlwaysRunActive) + mPlayer.setRunState(!actionIsActive(A_Run)); else - mPlayer.setUpDown (0); + mPlayer.setRunState(actionIsActive(A_Run)); + + // 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 (); + mOverencumberedMessageDelay -= dt; + if (MWWorld::Class::get(player).getEncumbrance(player) >= MWWorld::Class::get(player).getCapacity(player)) + { + mPlayer.setAutoMove (false); + if (mOverencumberedMessageDelay <= 0) + { + MWBase::Environment::get().getWindowManager ()->messageBox("#{sNotifyMessage59}"); + mOverencumberedMessageDelay = 1.0; + } + } + } if (mControlSwitch["playerviewswitch"]) { @@ -305,7 +340,7 @@ namespace MWInput actionIsActive(A_MoveLeft) || actionIsActive(A_MoveRight) || actionIsActive(A_Jump) || - actionIsActive(A_Crouch) || + actionIsActive(A_Sneak) || actionIsActive(A_TogglePOV)) { resetIdleTime(); @@ -321,27 +356,11 @@ namespace MWInput void InputManager::changeInputMode(bool guiMode) { - // Are we in GUI mode now? - if(guiMode) - { - // Disable mouse look - mMouseLookEnabled = false; - - mWindows.showCrosshair (false); - - // Enable GUI events - mGuiCursorEnabled = true; - } - else - { - // Enable mouse look - mMouseLookEnabled = true; - - mWindows.showCrosshair (false); - - // Disable GUI events - mGuiCursorEnabled = false; - } + mGuiCursorEnabled = guiMode; + mMouseLookEnabled = !guiMode; + if (guiMode) + mWindows.showCrosshair(false); + // if not in gui mode, the camera decides whether to show crosshair or not. } void InputManager::processChangedSettings(const Settings::CategorySettingVector& changed) @@ -405,6 +424,13 @@ namespace MWInput mInputBinder->keyPressed (arg); unsigned int text = arg.keysym.unicode; + if(arg.keysym.sym == SDLK_RETURN + && MWBase::Environment::get().getWindowManager()->isGuiMode()) + { + // Pressing enter when a messagebox is prompting for "ok" will activate the ok button + MWBase::Environment::get().getWindowManager()->enterPressed(); + } + //TODO: Check if we need this with SDL /* #ifdef __APPLE__ // filter \016 symbol for F-keys on OS X @@ -491,11 +517,25 @@ namespace MWInput { resetIdleTime(); - float x = arg.xrel * mCameraSensitivity * 0.2; - float y = arg.yrel * mCameraSensitivity * 0.2 * (mInvertY ? -1 : 1) * mUIYMultiplier; + 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; + rot[1] = 0.0f; + rot[2] = x; + + // Only actually turn player when we're not in vanity mode + if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot)) + { + mPlayer.setYaw(x/scale); + mPlayer.setPitch(-y/scale); + } - MWBase::World *world = MWBase::Environment::get().getWorld(); - world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); + if (arg.zrel) + MWBase::Environment::get().getWorld()->changeVanityModeScale(arg.zrel); } return true; @@ -521,8 +561,12 @@ namespace MWInput void InputManager::toggleMainMenu() { - //TODO: should this be here? - if (mWindows.isGuiMode () && mWindows.getMode () == MWGui::GM_Video) + if (MyGUI::InputManager::getInstance ().isModalAny()) + return; + + if (mWindows.isGuiMode () && (mWindows.getMode () == MWGui::GM_MainMenu || mWindows.getMode () == MWGui::GM_Settings)) + mWindows.popGuiMode(); + else if (mWindows.isGuiMode () && mWindows.getMode () == MWGui::GM_Video) MWBase::Environment::get().getWorld ()->stopVideo (); else if (mWindows.containsMode(MWGui::GM_MainMenu)) mWindows.popGuiMode(); @@ -536,15 +580,9 @@ namespace MWInput MWMechanics::DrawState_ state = mPlayer.getDrawState(); if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing) - { mPlayer.setDrawState(MWMechanics::DrawState_Spell); - std::cout << "Player has now readied his hands for spellcasting!\n" << std::endl; - } else - { mPlayer.setDrawState(MWMechanics::DrawState_Nothing); - std::cout << "Player does not have any kind of attack ready now.\n" << std::endl; - } } void InputManager::toggleWeapon() @@ -553,15 +591,9 @@ namespace MWInput MWMechanics::DrawState_ state = mPlayer.getDrawState(); if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) - { mPlayer.setDrawState(MWMechanics::DrawState_Weapon); - std::cout << "Player is now drawing his weapon.\n" << std::endl; - } else - { mPlayer.setDrawState(MWMechanics::DrawState_Nothing); - std::cout << "Player does not have any kind of attack ready now.\n" << std::endl; - } } void InputManager::rest() @@ -583,19 +615,29 @@ namespace MWInput void InputManager::toggleInventory() { + if (MyGUI::InputManager::getInstance ().isModalAny()) + return; + bool gameMode = !mWindows.isGuiMode(); // Toggle between game mode and inventory mode if(gameMode) mWindows.pushGuiMode(MWGui::GM_Inventory); - else if(mWindows.getMode() == MWGui::GM_Inventory) - mWindows.popGuiMode(); + else + { + MWGui::GuiMode mode = mWindows.getMode(); + if(mode == MWGui::GM_Inventory || mode == MWGui::GM_Container) + mWindows.popGuiMode(); + } - // .. but don't touch any other mode. + // .. but don't touch any other mode, except container. } void InputManager::toggleConsole() { + if (MyGUI::InputManager::getInstance ().isModalAny()) + return; + bool gameMode = !mWindows.isGuiMode(); // Switch to console mode no matter what mode we are currently @@ -613,30 +655,43 @@ namespace MWInput void InputManager::toggleJournal() { + if (MyGUI::InputManager::getInstance ().isModalAny()) + return; + // Toggle between game mode and journal mode bool gameMode = !mWindows.isGuiMode(); if(gameMode) + { + MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); mWindows.pushGuiMode(MWGui::GM_Journal); + } else if(mWindows.getMode() == MWGui::GM_Journal) + { + MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); mWindows.popGuiMode(); + } // .. but don't touch any other mode. } void InputManager::quickKey (int index) { - mWindows.activateQuickKey (index); + if (!mWindows.isGuiMode()) + mWindows.activateQuickKey (index); } void InputManager::showQuickKeysMenu() { if (!mWindows.isGuiMode ()) mWindows.pushGuiMode (MWGui::GM_QuickKeysMenu); + else if (mWindows.getMode () == MWGui::GM_QuickKeysMenu) + mWindows.removeGuiMode (MWGui::GM_QuickKeysMenu); } void InputManager::activate() { - mEngine.activate(); + if (mControlSwitch["playercontrols"]) + mEngine.activate(); } void InputManager::toggleAutoMove() @@ -650,7 +705,7 @@ namespace MWInput void InputManager::toggleWalking() { if (mWindows.isGuiMode()) return; - mPlayer.toggleRunning(); + mAlwaysRunActive = !mAlwaysRunActive; } // Exit program now button (which is disabled in GUI mode) @@ -662,19 +717,17 @@ namespace MWInput void InputManager::resetIdleTime() { - if (mTimeIdle < 0) { - MWBase::Environment::get().getWorld()->toggleVanityMode(false, false); - } + if (mTimeIdle < 0) + MWBase::Environment::get().getWorld()->toggleVanityMode(false); mTimeIdle = 0.f; } void InputManager::updateIdleTime(float dt) { - if (mTimeIdle >= 0.f) { + if (mTimeIdle >= 0.f) mTimeIdle += dt; - } if (mTimeIdle > 30.f) { - MWBase::Environment::get().getWorld()->toggleVanityMode(true, false); + MWBase::Environment::get().getWorld()->toggleVanityMode(true); mTimeIdle = -1.f; } } @@ -699,7 +752,8 @@ namespace MWInput defaultKeyBindings[A_ToggleSpell] = SDLK_r; defaultKeyBindings[A_QuickKeysMenu] = SDLK_F1; defaultKeyBindings[A_Console] = SDLK_F2; - defaultKeyBindings[A_Crouch] = SDLK_LCTRL; + defaultKeyBindings[A_Run] = SDLK_LSHIFT; + defaultKeyBindings[A_Sneak] = SDLK_LCTRL; defaultKeyBindings[A_AutoMove] = SDLK_q; defaultKeyBindings[A_Jump] = SDLK_e; defaultKeyBindings[A_Journal] = SDLK_j; @@ -718,6 +772,7 @@ namespace MWInput defaultKeyBindings[A_QuickKey10] = SDLK_0; defaultKeyBindings[A_Screenshot] = SDLK_PRINTSCREEN; defaultKeyBindings[A_ToggleHUD] = SDLK_F12; + defaultKeyBindings[A_AlwaysRun] = SDLK_y; std::map defaultMouseButtonBindings; defaultMouseButtonBindings[A_Inventory] = SDL_BUTTON_RIGHT; @@ -757,6 +812,7 @@ namespace MWInput { std::map descriptions; + descriptions[A_Use] = "sUse"; descriptions[A_Activate] = "sActivate"; descriptions[A_MoveBackward] = "sBack"; descriptions[A_MoveForward] = "sForward"; @@ -765,7 +821,8 @@ namespace MWInput descriptions[A_ToggleWeapon] = "sReady_Weapon"; descriptions[A_ToggleSpell] = "sReady_Magic"; descriptions[A_Console] = "sConsoleTitle"; - descriptions[A_Crouch] = "sCrouch_Sneak"; + descriptions[A_Run] = "sRun"; + descriptions[A_Sneak] = "sCrouch_Sneak"; descriptions[A_AutoMove] = "sAuto_Run"; descriptions[A_Jump] = "sJump"; descriptions[A_Journal] = "sJournal"; @@ -783,6 +840,7 @@ namespace MWInput descriptions[A_QuickKey8] = "sQuick8Cmd"; descriptions[A_QuickKey9] = "sQuick9Cmd"; descriptions[A_QuickKey10] = "sQuick10Cmd"; + descriptions[A_AlwaysRun] = "sAlways_Run"; if (descriptions[action] == "") return ""; // not configurable @@ -813,8 +871,11 @@ namespace MWInput ret.push_back(A_MoveLeft); ret.push_back(A_MoveRight); ret.push_back(A_TogglePOV); - ret.push_back(A_Crouch); + ret.push_back(A_Run); + ret.push_back(A_AlwaysRun); + ret.push_back(A_Sneak); ret.push_back(A_Activate); + ret.push_back(A_Use); ret.push_back(A_ToggleWeapon); ret.push_back(A_ToggleSpell); ret.push_back(A_AutoMove); @@ -855,6 +916,10 @@ namespace MWInput void InputManager::keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control , SDL_Keycode key, ICS::Control::ControlChangingDirection direction) { + //Disallow binding escape key, and unassigned keys + if(key==OIS::KC_ESCAPE || key==OIS::KC_UNASSIGNED) + return + clearAllBindings(control); ICS::DetectingBindingListener::keyBindingDetected (ICS, control, key, direction); MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 9832be9a6..57c367bde 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -123,7 +123,7 @@ namespace MWInput private: OEngine::Render::OgreRenderer &mOgre; - MWWorld::Player &mPlayer; + MWWorld::Player& mPlayer; MWBase::WindowManager &mWindows; OMW::Engine& mEngine; @@ -141,7 +141,6 @@ namespace MWInput float mCameraSensitivity; float mUISensitivity; float mCameraYMultiplier; - float mUIYMultiplier; float mPreviewPOVDelay; float mTimeIdle; @@ -149,9 +148,13 @@ namespace MWInput bool mGuiCursorEnabled; bool mDebug; + float mOverencumberedMessageDelay; + float mMouseX; float mMouseY; int mMouseWheel; + bool mUserFileExists; + bool mAlwaysRunActive; std::map mControlSwitch; @@ -212,14 +215,14 @@ namespace MWInput A_Journal, //Journal A_Weapon, //Draw/Sheath weapon A_Spell, //Ready/Unready Casting - A_AlwaysRun, //Toggle Always Run + A_Run, //Run when held A_CycleSpellLeft, //cycling through spells A_CycleSpellRight, A_CycleWeaponLeft,//Cycling through weapons A_CycleWeaponRight, - A_ToggleSneak, //Toggles Sneak, add Push-Sneak later - A_ToggleWalk, //Toggle Walking/Running - A_Crouch, + A_ToggleSneak, //Toggles Sneak + A_AlwaysRun, //Toggle Walking/Running + A_Sneak, A_QuickSave, A_QuickLoad, diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index ee1e9da36..9aca6b7b7 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -62,7 +62,7 @@ namespace MWMechanics for (TIterator iter (begin()); iter!=end(); ++iter) { - std::pair effects = getEffectList (iter->first); + std::pair > effects = getEffectList (iter->first); const MWWorld::TimeStamp& start = iter->second.first; float magnitude = iter->second.second; @@ -74,7 +74,7 @@ namespace MWMechanics { int duration = iter->mDuration; - if (effects.second) + if (effects.second.first) duration *= magnitude; MWWorld::TimeStamp end = start; @@ -85,7 +85,7 @@ namespace MWMechanics { EffectParam param; - if (effects.second) + if (effects.second.first) { const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find ( @@ -113,15 +113,15 @@ namespace MWMechanics } } - std::pair ActiveSpells::getEffectList (const std::string& id) const + std::pair > ActiveSpells::getEffectList (const std::string& id) const { if (const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get().search (id)) - return std::make_pair (spell->mEffects, false); + return std::make_pair (spell->mEffects, std::make_pair(false, false)); if (const ESM::Potion *potion = MWBase::Environment::get().getWorld()->getStore().get().search (id)) - return std::make_pair (potion->mEffects, false); + return std::make_pair (potion->mEffects, std::make_pair(false, true)); if (const ESM::Ingredient *ingredient = MWBase::Environment::get().getWorld()->getStore().get().search (id)) @@ -140,11 +140,12 @@ namespace MWMechanics effect.mMagnMin = 1; effect.mMagnMax = 1; - std::pair result; - + std::pair > result; + result.second.second = true; + result.second.first = true; + result.first.mList.push_back (effect); - result.second = true; - + return result; } @@ -157,7 +158,8 @@ namespace MWMechanics bool ActiveSpells::addSpell (const std::string& id, const MWWorld::Ptr& actor) { - std::pair effects = getEffectList (id); + std::pair > effects = getEffectList (id); + bool stacks = effects.second.second; bool found = false; @@ -178,7 +180,7 @@ namespace MWMechanics float random = static_cast (std::rand()) / RAND_MAX; - if (effects.second) + if (effects.second.first) { // ingredient -> special treatment required. const CreatureStats& creatureStats = MWWorld::Class::get (actor).getCreatureStats (actor); @@ -194,7 +196,7 @@ namespace MWMechanics random *= 0.25 * x; } - if (iter==mSpells.end()) + if (iter==mSpells.end() || stacks) mSpells.insert (std::make_pair (id, std::make_pair (MWBase::Environment::get().getWorld()->getTimeStamp(), random))); else @@ -236,7 +238,7 @@ namespace MWMechanics double ActiveSpells::timeToExpire (const TIterator& iterator) const { - std::pair effects = getEffectList (iterator->first); + std::pair > effects = getEffectList (iterator->first); int duration = 0; @@ -247,7 +249,7 @@ namespace MWMechanics duration = iter->mDuration; } - if (effects.second) + if (effects.second.first) duration *= iterator->second.second; double scaledDuration = duration * @@ -274,4 +276,9 @@ namespace MWMechanics } return false; } + + const ActiveSpells::TContainer& ActiveSpells::getActiveSpells() const + { + return mSpells; + } } diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 6b832f4cd..8c859b2cb 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -30,7 +30,7 @@ namespace MWMechanics { public: - typedef std::map > TContainer; + typedef std::multimap > TContainer; typedef TContainer::const_iterator TIterator; private: @@ -44,7 +44,8 @@ namespace MWMechanics void rebuildEffects() const; - std::pair getEffectList (const std::string& id) const; + std::pair > getEffectList (const std::string& id) const; + ///< @return (EffectList, (isIngredient, stacks)) public: @@ -63,6 +64,8 @@ namespace MWMechanics const MagicEffects& getMagicEffects() const; + const TContainer& getActiveSpells() const; + TIterator begin() const; TIterator end() const; diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index d541baea9..e85aa9b83 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -17,6 +17,7 @@ #include "../mwbase/windowmanager.hpp" #include "creaturestats.hpp" +#include "movement.hpp" namespace MWMechanics { @@ -28,15 +29,17 @@ namespace MWMechanics calculateCreatureStatModifiers (ptr); // AI - CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); - creatureStats.getAiSequence().execute (ptr); + if(!MWBase::Environment::get().getWindowManager()->isGuiMode()) + { + CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); + creatureStats.getAiSequence().execute (ptr); + } } void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused) { if (!paused && ptr.getRefData().getHandle()!="player") - MWWorld::Class::get (ptr).getInventoryStore (ptr).autoEquip ( - MWWorld::Class::get (ptr).getNpcStats (ptr)); + MWWorld::Class::get (ptr).getInventoryStore (ptr).autoEquip (ptr); } void Actors::adjustMagicEffects (const MWWorld::Ptr& creature) @@ -71,7 +74,7 @@ namespace MWMechanics int endurance = creatureStats.getAttribute(5).getBase(); double magickaFactor = - creatureStats.getMagicEffects().get (EffectKey (84)).mMagnitude * 0.1 + 0.5; + creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyMaximumMagicka)).mMagnitude * 0.1 + 0.5; DynamicStat health = creatureStats.getHealth(); health.setBase (static_cast (0.5 * (strength + endurance)) + creatureStats.getLevelHealthBonus ()); @@ -80,7 +83,7 @@ namespace MWMechanics DynamicStat magicka = creatureStats.getMagicka(); magicka.setBase (static_cast (intelligence + magickaFactor * intelligence)); creatureStats.setMagicka (magicka); - + DynamicStat fatigue = creatureStats.getFatigue(); fatigue.setBase (strength+willpower+agility+endurance); creatureStats.setFatigue (fatigue); @@ -92,11 +95,10 @@ namespace MWMechanics if (duration == 3600) { - // stunted magicka - bool stunted = stats.getMagicEffects ().get(MWMechanics::EffectKey(136)).mMagnitude > 0; + bool stunted = stats.getMagicEffects ().get(MWMechanics::EffectKey(ESM::MagicEffect::StuntedMagicka)).mMagnitude > 0; int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); - + DynamicStat health = stats.getHealth(); health.setCurrent (health.getCurrent() + 0.1 * endurance); stats.setHealth (health); @@ -115,15 +117,15 @@ namespace MWMechanics float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); x *= fEndFatigueMult * endurance; - + DynamicStat fatigue = stats.getFatigue(); fatigue.setCurrent (fatigue.getCurrent() + 3600 * x); stats.setFatigue (fatigue); - + if (!stunted) { float fRestMagicMult = store.get().find("fRestMagicMult")->getFloat (); - + DynamicStat magicka = stats.getMagicka(); magicka.setCurrent (magicka.getCurrent() + fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified()); @@ -140,140 +142,182 @@ namespace MWMechanics for (int i=0; i<8; ++i) { int modifier = - creatureStats.getMagicEffects().get (EffectKey (79, i)).mMagnitude; + creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyAttribute, i)).mMagnitude; - modifier -= creatureStats.getMagicEffects().get (EffectKey (17, i)).mMagnitude; + modifier -= creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::DrainAttribute, i)).mMagnitude; creatureStats.getAttribute(i).setModifier (modifier); } // dynamic stats MagicEffects effects = creatureStats.getMagicEffects(); - + for (int i=0; i<3; ++i) { DynamicStat stat = creatureStats.getDynamic (i); - + stat.setModifier ( effects.get (EffectKey(80+i)).mMagnitude - effects.get (EffectKey(18+i)).mMagnitude); - + creatureStats.setDynamic (i, stat); - } + } } Actors::Actors() : mDuration (0) {} void Actors::addActor (const MWWorld::Ptr& ptr) { - if (!MWWorld::Class::get (ptr).getCreatureStats (ptr).isDead()) - mActors.insert (ptr); + // erase previous death events since we are currently only tracking them while in an active cell + MWWorld::Class::get (ptr).getCreatureStats (ptr).clearHasDied(); + + MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); + if(!MWWorld::Class::get(ptr).getCreatureStats(ptr).isDead()) + mActors.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Idle))); else - MWBase::Environment::get().getWorld()->playAnimationGroup (ptr, "death1", 2); + mActors.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Death1))); } void Actors::removeActor (const MWWorld::Ptr& ptr) { - std::set::iterator iter = mActors.find (ptr); + PtrControllerMap::iterator iter = mActors.find(ptr); + if(iter != mActors.end()) + mActors.erase(iter); + } + + void Actors::updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) + { + PtrControllerMap::iterator iter = mActors.find(old); + if(iter != mActors.end()) + { + CharacterController ctrl = iter->second; + mActors.erase(iter); - if (iter!=mActors.end()) - mActors.erase (iter); + ctrl.updatePtr(ptr); + mActors.insert(std::make_pair(ptr, ctrl)); + } } void Actors::dropActors (const MWWorld::Ptr::CellStore *cellStore) { - std::set::iterator iter = mActors.begin(); - - while (iter!=mActors.end()) - if (iter->getCell()==cellStore) - { - mActors.erase (iter++); - } + PtrControllerMap::iterator iter = mActors.begin(); + while(iter != mActors.end()) + { + if(iter->first.getCell()==cellStore) + mActors.erase(iter++); else ++iter; + } } - void Actors::update (std::vector >& movement, float duration, - bool paused) + void Actors::update (float duration, bool paused) { mDuration += duration; - if (mDuration>=0.25) + //if (mDuration>=0.25) { float totalDuration = mDuration; mDuration = 0; - - std::set::iterator iter (mActors.begin()); - while (iter!=mActors.end()) + for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++) { - if (!MWWorld::Class::get (*iter).getCreatureStats (*iter).isDead()) + if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead()) { - updateActor (*iter, totalDuration); + if(iter->second.getState() >= CharState_Death1) + iter->second.setState(CharState_Idle); + + updateActor(iter->first, totalDuration); + if(iter->first.getTypeName() == typeid(ESM::NPC).name()) + updateNpc(iter->first, totalDuration, paused); - if (iter->getTypeName()==typeid (ESM::NPC).name()) - updateNpc (*iter, totalDuration, paused); + if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead()) + continue; } - if (MWWorld::Class::get (*iter).getCreatureStats (*iter).isDead()) + // workaround: always keep player alive for now + // \todo remove workaround, once player death can be handled + if(iter->first.getRefData().getHandle()=="player") { - // workaround: always keep player alive for now - // \todo remove workaround, once player death can be handled - if (iter->getRefData().getHandle()=="player") + MWMechanics::DynamicStat stat ( + MWWorld::Class::get(iter->first).getCreatureStats(iter->first).getHealth()); + + if (stat.getModified()<1) { - MWMechanics::DynamicStat stat ( - MWWorld::Class::get (*iter).getCreatureStats (*iter).getHealth()); - - if (stat.getModified()<1) - { - stat.setModified (1, 0); - MWWorld::Class::get (*iter).getCreatureStats (*iter).setHealth (stat); - } - - MWWorld::Class::get (*iter).getCreatureStats (*iter).resurrect(); - ++iter; - continue; + stat.setModified (1, 0); + MWWorld::Class::get(iter->first).getCreatureStats(iter->first).setHealth(stat); } - ++mDeathCount[MWWorld::Class::get (*iter).getId (*iter)]; + MWWorld::Class::get(iter->first).getCreatureStats(iter->first).resurrect(); + continue; + } + + if(iter->second.getState() >= CharState_Death1) + continue; - MWBase::Environment::get().getWorld()->playAnimationGroup (*iter, "death1", 0); + iter->second.setState(CharState_Death1); - if (MWWorld::Class::get (*iter).isEssential (*iter)) - MWBase::Environment::get().getWindowManager()->messageBox ( - "#{sKilledEssential}", std::vector()); + ++mDeathCount[MWWorld::Class::get(iter->first).getId(iter->first)]; - mActors.erase (iter++); - } - else - ++iter; + if(MWWorld::Class::get(iter->first).isEssential(iter->first)) + MWBase::Environment::get().getWindowManager()->messageBox( + "#{sKilledEssential}"); } } - for (std::set::iterator iter (mActors.begin()); iter!=mActors.end(); - ++iter) + if(!paused) { - Ogre::Vector3 vector = MWWorld::Class::get (*iter).getMovementVector (*iter); + mMovement.reserve(mActors.size()); + + for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + { + Movement movement; + iter->second.update(duration, movement); + mMovement.push_back(std::make_pair(iter->first, movement)); + } + MWBase::Environment::get().getWorld()->doPhysics(mMovement, duration); - if (vector!=Ogre::Vector3::ZERO) - movement.push_back (std::make_pair (iter->getRefData().getHandle(), vector)); + mMovement.clear(); } } void Actors::restoreDynamicStats() { - for (std::set::iterator iter (mActors.begin()); iter!=mActors.end(); ++iter) - { - calculateRestoration (*iter, 3600); - } + for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + calculateRestoration(iter->first, 3600); } - + int Actors::countDeaths (const std::string& id) const { - std::map::const_iterator iter = mDeathCount.find (id); - - if (iter!=mDeathCount.end()) + std::map::const_iterator iter = mDeathCount.find(id); + if(iter != mDeathCount.end()) return iter->second; - return 0; } + + void Actors::forceStateUpdate(const MWWorld::Ptr & ptr) + { + PtrControllerMap::iterator iter = mActors.find(ptr); + if(iter != mActors.end()) + iter->second.forceStateUpdate(); + } + + void Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) + { + PtrControllerMap::iterator iter = mActors.find(ptr); + if(iter != mActors.end()) + iter->second.playGroup(groupName, mode, number); + } + void Actors::skipAnimation(const MWWorld::Ptr& ptr) + { + PtrControllerMap::iterator iter = mActors.find(ptr); + if(iter != mActors.end()) + iter->second.skipAnim(); + } + + bool Actors::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) + { + PtrControllerMap::iterator iter = mActors.find(ptr); + if(iter != mActors.end()) + return iter->second.isAnimPlaying(groupName); + return false; + } } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 79ae16fc3..386840e3a 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -6,6 +6,10 @@ #include #include +#include "character.hpp" +#include "movement.hpp" +#include "../mwbase/world.hpp" + namespace Ogre { class Vector3; @@ -21,9 +25,14 @@ namespace MWMechanics { class Actors { - std::set mActors; - float mDuration; - std::map mDeathCount; + typedef std::map PtrControllerMap; + PtrControllerMap mActors; + + MWWorld::PtrMovementList mMovement; + + std::map mDeathCount; + + float mDuration; void updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused); @@ -50,11 +59,13 @@ namespace MWMechanics /// /// \note Ignored, if \a ptr is not a registered actor. + void updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr); + ///< Updates an actor with a new Ptr + void dropActors (const MWWorld::CellStore *cellStore); ///< Deregister all actors in the given cell. - void update (std::vector >& movement, - float duration, bool paused); + void update (float duration, bool paused); ///< Update actor stats and store desired velocity vectors in \a movement void updateActor (const MWWorld::Ptr& ptr, float duration); @@ -66,6 +77,12 @@ namespace MWMechanics int countDeaths (const std::string& id) const; ///< Return the number of deaths for actors with the given ID. + + void forceStateUpdate(const MWWorld::Ptr &ptr); + + 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); }; } diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index ebbea55b0..5b94c4938 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -1,30 +1,174 @@ #include "aiescort.hpp" -#include - -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) -{ -} + +#include "movement.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" +#include "../mwworld/timestamp.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" + +namespace +{ + float sgn(float a) + { + if(a > 0) return 1.0; + else return -1.0; + } +} + +/* + TODO: Test vanilla behavior on passing x0, y0, and z0 with duration of anything including 0. + TODO: Different behavior for AIEscort a d x y z and AIEscortCell a c d x y z. + TODO: Take account for actors being in different cells. +*/ + +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) +{ + mMaxDist = 470; + + // The CS Help File states that if a duration is givin, 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; + + else + { + MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); + mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); + } +} 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) { + mMaxDist = 470; + + // The CS Help File states that if a duration is givin, 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; + + else + { + MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); + mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); + } +} + + +MWMechanics::AiEscort *MWMechanics::AiEscort::clone() const +{ + return new AiEscort(*this); +} + +bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor) +{ + // If AiEscort has ran for as long or longer then the duration specified + // and the duration is not infinite, the package is complete. + if(mDuration != 0) + { + MWWorld::TimeStamp current = MWBase::Environment::get().getWorld()->getTimeStamp(); + unsigned int currentSecond = ((current.getHour() - int(current.getHour())) * 100); + if(currentSecond - mStartingSecond >= mDuration) + return true; + } + + ESM::Position pos = actor.getRefData().getPosition(); + bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY; + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + 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) + { + 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. + if(sideX*(pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE / 2. - 200)) + { + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + return true; + } + } + 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. + if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE / 2. - 200)) + { + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + return true; + } + } + + + 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; + } + + ESM::Pathgrid::Point dest; + dest.mX = mX; + dest.mY = mY; + dest.mZ = mZ; + + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; + start.mZ = pos.pos[2]; + + mPathFinder.buildPath(start,dest,pathgrid,xCell,yCell); + } + + if(mPathFinder.checkIfNextPointReached(pos.pos[0],pos.pos[1],pos.pos[2])) + { + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + return true; + } + + const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false); + const float* const leaderPos = actor.getRefData().getPosition().pos; + const float* const followerPos = follower.getRefData().getPosition().pos; + double differenceBetween[3]; + + for (short i = 0; i < 3; ++i) + differenceBetween[i] = (leaderPos[i] - followerPos[i]); + + float distanceBetweenResult = + (differenceBetween[0] * differenceBetween[0]) + (differenceBetween[1] * differenceBetween[1]) + (differenceBetween[2] * differenceBetween[2]); + + if(distanceBetweenResult <= mMaxDist * mMaxDist) + { + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]); + MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + mMaxDist = 470; + } + else + { + // Stop moving if the player is to far away + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + mMaxDist = 330; + } + + return false; +} + +int MWMechanics::AiEscort::getTypeId() const +{ + return 2; } - -MWMechanics::AiEscort *MWMechanics::AiEscort::clone() const -{ - return new AiEscort(*this); -} - -bool MWMechanics::AiEscort::execute (const MWWorld::Ptr& actor) -{ - std::cout << "AiEscort completed. \n"; - return true; -} - -int MWMechanics::AiEscort::getTypeId() const -{ - return 2; -} - diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index d89a9586c..3ae604035 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -4,11 +4,13 @@ #include "aipackage.hpp" #include +#include "pathfinding.hpp" + namespace MWMechanics -{ - class AiEscort : public AiPackage - { - public: +{ + class AiEscort : public AiPackage + { + public: AiEscort(const std::string &actorId,int duration, float x, float y, float z); ///< \implement AiEscort AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z); @@ -27,8 +29,13 @@ namespace MWMechanics float mX; float mY; float mZ; - int mDuration; - - }; -} -#endif + float mMaxDist; + unsigned int mStartingSecond; + unsigned int mDuration; + + PathFinder mPathFinder; + int cellX; + int cellY; + }; +} +#endif diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 897dd1748..fbae5c1d2 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -1,25 +1,100 @@ #include "aitravel.hpp" -#include + +#include "movement.hpp" + +#include "../mwworld/class.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwworld/player.hpp" + +namespace +{ + float sgn(float a) + { + if(a > 0) return 1.; + else return -1.; + } +} MWMechanics::AiTravel::AiTravel(float x, float y, float z) -: mX(x),mY(y),mZ(z) +: mX(x),mY(y),mZ(z),mPathFinder() { } -MWMechanics::AiTravel * MWMechanics::AiTravel::clone() const +MWMechanics::AiTravel *MWMechanics::AiTravel::clone() const { return new AiTravel(*this); } bool MWMechanics::AiTravel::execute (const MWWorld::Ptr& actor) { - std::cout << "AiTravel completed.\n"; - return true; + const ESM::Pathgrid *pathgrid = + MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); + + ESM::Position pos = actor.getRefData().getPosition(); + bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY; + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + 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 aitravel. + if(sideX*(pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX*(ESM::Land::REAL_SIZE/2. - 200)) + { + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + return true; + } + } + 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 aitravel. + if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY*(ESM::Land::REAL_SIZE/2. - 200)) + { + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + return true; + } + } + + 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; + } + + ESM::Pathgrid::Point dest; + dest.mX = mX; + dest.mY = mY; + dest.mZ = mZ; + + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; + start.mZ = pos.pos[2]; + + mPathFinder.buildPath(start,dest,pathgrid,xCell,yCell); + } + + if(mPathFinder.checkIfNextPointReached(pos.pos[0],pos.pos[1],pos.pos[2])) + { + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + return true; + } + + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]); + MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + + return false; } int MWMechanics::AiTravel::getTypeId() const { return 1; } - - diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index 1c6abbf27..6eb9af8ce 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -1,27 +1,33 @@ #ifndef GAME_MWMECHANICS_AITRAVEL_H #define GAME_MWMECHANICS_AITRAVEL_H - + #include "aipackage.hpp" - -namespace MWMechanics -{ - class AiTravel : public AiPackage - { - public: - AiTravel(float x, float y, float z); - virtual AiTravel *clone() const; - - virtual bool execute (const MWWorld::Ptr& actor); - ///< \return Package completed? - - virtual int getTypeId() const; - - private: - float mX; - float mY; - float mZ; - - }; + +#include "pathfinding.hpp" + +namespace MWMechanics +{ + class AiTravel : public AiPackage + { + public: + AiTravel(float x, float y, float z); + virtual AiTravel *clone() const; + + virtual bool execute (const MWWorld::Ptr& actor); + ///< \return Package completed? + + virtual int getTypeId() const; + + private: + float mX; + float mY; + float mZ; + + int cellX; + int cellY; + + PathFinder mPathFinder; + }; } #endif diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index e9db6d212..46c5598cc 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -1,9 +1,52 @@ #include "aiwander.hpp" -#include -MWMechanics::AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle): - mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle) +#include "movement.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" + +#include + +namespace { + float sgn(float a) + { + if(a > 0) return 1.0; + else return -1.0; + } +} + +MWMechanics::AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): + mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) +{ + for(unsigned short counter = 0; counter < mIdle.size(); counter++) + { + if(mIdle[counter] >= 127 || mIdle[counter] < 0) + mIdle[counter] = 0; + } + + if(mDistance < 0) + mDistance = 0; + if(mDuration < 0) + mDuration = 0; + if(mDuration == 0) + mTimeOfDay = 0; + + srand(time(NULL)); + mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp(); + mPlayedIdle = 0; + mPathgrid = NULL; + mIdleChanceMultiplier = + MWBase::Environment::get().getWorld()->getStore().get().find("fIdleChanceMultiplier")->getFloat(); + + mStoredAvailableNodes = false; + mChooseAction = true; + mIdleNow = false; + mMoveNow = false; + mWalking = false; } MWMechanics::AiPackage * MWMechanics::AiWander::clone() const @@ -13,11 +56,268 @@ MWMechanics::AiPackage * MWMechanics::AiWander::clone() const bool MWMechanics::AiWander::execute (const MWWorld::Ptr& actor) { - std::cout << "AiWadner completed.\n"; - return true; + if(mDuration) + { + // End package if duration is complete or mid-night hits: + MWWorld::TimeStamp currentTime = MWBase::Environment::get().getWorld()->getTimeStamp(); + if(currentTime.getHour() >= mStartTime.getHour() + mDuration) + { + if(!mRepeat) + { + stopWalking(actor, mPathFinder); + return true; + } + else + mStartTime = currentTime; + } + else if(int(currentTime.getHour()) == 0 && currentTime.getDay() != mStartTime.getDay()) + { + if(!mRepeat) + { + stopWalking(actor, mPathFinder); + return true; + } + else + mStartTime = currentTime; + } + } + + ESM::Position pos = actor.getRefData().getPosition(); + + if(!mStoredAvailableNodes) + { + mStoredAvailableNodes = true; + mPathgrid = + MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); + + mCellX = actor.getCell()->mCell->mData.mX; + mCellY = actor.getCell()->mCell->mData.mY; + + if(!mPathgrid) + mDistance = 0; + else if(mPathgrid->mPoints.empty()) + mDistance = 0; + + if(mDistance) + { + mXCell = 0; + mYCell = 0; + if(actor.getCell()->mCell->isExterior()) + { + mXCell = mCellX * ESM::Land::REAL_SIZE; + mYCell = mCellY * ESM::Land::REAL_SIZE; + } + + Ogre::Vector3 npcPos(actor.getRefData().getPosition().pos); + npcPos[0] = npcPos[0] - mXCell; + npcPos[1] = npcPos[1] - mYCell; + + for(unsigned int counter = 0; counter < mPathgrid->mPoints.size(); counter++) + { + Ogre::Vector3 nodePos(mPathgrid->mPoints[counter].mX, mPathgrid->mPoints[counter].mY, mPathgrid->mPoints[counter].mZ); + if(npcPos.squaredDistance(nodePos) <= mDistance * mDistance) + mAllowedNodes.push_back(mPathgrid->mPoints[counter]); + } + if(!mAllowedNodes.empty()) + { + Ogre::Vector3 firstNodePos(mAllowedNodes[0].mX, mAllowedNodes[0].mY, mAllowedNodes[0].mZ); + float closestNode = npcPos.squaredDistance(firstNodePos); + unsigned int index = 0; + for(unsigned int counterThree = 1; counterThree < mAllowedNodes.size(); counterThree++) + { + Ogre::Vector3 nodePos(mAllowedNodes[counterThree].mX, mAllowedNodes[counterThree].mY, mAllowedNodes[counterThree].mZ); + float tempDist = npcPos.squaredDistance(nodePos); + if(tempDist < closestNode) + index = counterThree; + } + mCurrentNode = mAllowedNodes[index]; + mAllowedNodes.erase(mAllowedNodes.begin() + index); + } + + if(mAllowedNodes.empty()) + mDistance = 0; + } + } + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + 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) + { + 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 AiWander. + // FIXME: This *should* pause the AiWander package instead of terminating it. + 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; + } + } + + 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 AiWander. + // FIXME: This *should* pause the AiWander package instead of terminating it. + 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; + } + } + + // Don't try to move if you are in a new cell (ie: positioncell command called) but still play idles. + if(mDistance && (cellChange || (mCellX != actor.getCell()->mCell->mData.mX || mCellY != actor.getCell()->mCell->mData.mY))) + mDistance = 0; + + if(mChooseAction) + { + mPlayedIdle = 0; + unsigned short idleRoll = 0; + + for(unsigned int counter = 1; counter < mIdle.size(); counter++) + { + unsigned short idleChance = mIdleChanceMultiplier * mIdle[counter]; + unsigned short randSelect = (int)(rand() / ((double)RAND_MAX + 1) * int(100 / mIdleChanceMultiplier)); + if(randSelect < idleChance && randSelect > idleRoll) + { + mPlayedIdle = counter; + idleRoll = randSelect; + } + } + + if(!mPlayedIdle && mDistance) + { + mChooseAction = false; + mMoveNow = true; + } + else + { + // Play idle animation and recreate vanilla (broken?) behavior of resetting start time of AIWander: + MWWorld::TimeStamp currentTime = MWBase::Environment::get().getWorld()->getTimeStamp(); + mStartTime = currentTime; + playIdle(actor, mPlayedIdle + 1); + mChooseAction = false; + mIdleNow = true; + } + } + + if(mIdleNow) + { + if(!checkIdle(actor, mPlayedIdle + 1)) + { + mPlayedIdle = 0; + mIdleNow = false; + mChooseAction = true; + } + } + + if(mMoveNow && mDistance) + { + if(!mPathFinder.isPathConstructed()) + { + unsigned int randNode = (int)(rand() / ((double)RAND_MAX + 1) * mAllowedNodes.size()); + Ogre::Vector3 destNodePos(mAllowedNodes[randNode].mX, mAllowedNodes[randNode].mY, mAllowedNodes[randNode].mZ); + + // Remove this node as an option and add back the previously used node (stops NPC from picking the same node): + ESM::Pathgrid::Point temp = mAllowedNodes[randNode]; + mAllowedNodes.erase(mAllowedNodes.begin() + randNode); + mAllowedNodes.push_back(mCurrentNode); + mCurrentNode = temp; + + ESM::Pathgrid::Point dest; + dest.mX = destNodePos[0] + mXCell; + dest.mY = destNodePos[1] + mYCell; + dest.mZ = destNodePos[2]; + + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; + start.mZ = pos.pos[2]; + + mPathFinder.buildPath(start,dest,mPathgrid,mXCell,mYCell); + mWalking = true; + } + } + + if(mWalking) + { + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0],pos.pos[1],pos.pos[2]); + MWBase::Environment::get().getWorld()->rotateObject(actor,0,0,zAngle,false); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + + // Unclog path nodes by allowing the NPC to be a small distance away from the center. This way two NPCs can be + // at the same path node at the same time and both will complete instead of endlessly walking into eachother: + Ogre::Vector3 destNodePos(mCurrentNode.mX, mCurrentNode.mY, mCurrentNode.mZ); + Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos); + actorPos[0] = actorPos[0] - mXCell; + actorPos[1] = actorPos[1] - mYCell; + float distance = actorPos.squaredDistance(destNodePos); + + if(distance < 1200 || mPathFinder.checkIfNextPointReached(pos.pos[0],pos.pos[1],pos.pos[2])) + { + stopWalking(actor, mPathFinder); + mMoveNow = false; + mWalking = false; + mChooseAction = true; + } + + } + + return false; } int MWMechanics::AiWander::getTypeId() const { return 0; } + +void MWMechanics::AiWander::stopWalking(const MWWorld::Ptr& actor, PathFinder& path) +{ + PathFinder pathClearer; + path = pathClearer; + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; +} + +void MWMechanics::AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect) +{ + if(idleSelect == 2) + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle2", 0, 1); + else if(idleSelect == 3) + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1); + else if(idleSelect == 4) + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle4", 0, 1); + else if(idleSelect == 5) + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle5", 0, 1); + else if(idleSelect == 6) + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle6", 0, 1); + else if(idleSelect == 7) + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle7", 0, 1); + else if(idleSelect == 8) + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle8", 0, 1); + else if(idleSelect == 9) + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle9", 0, 1); +} + +bool MWMechanics::AiWander::checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect) +{ + if(idleSelect == 2) + return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle2"); + else if(idleSelect == 3) + return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle3"); + else if(idleSelect == 4) + return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle4"); + else if(idleSelect == 5) + return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle5"); + else if(idleSelect == 6) + return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle6"); + else if(idleSelect == 7) + return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle7"); + else if(idleSelect == 8) + return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle8"); + else if(idleSelect == 9) + return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, "idle9"); + else + return false; +} + diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index a71858feb..cf3820527 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -4,26 +4,61 @@ #include "aipackage.hpp" #include +#include "pathfinding.hpp" + +#include "../mwworld/timestamp.hpp" + namespace MWMechanics { - class AiWander : public AiPackage { - public: - - AiWander(int distance, int duration, int timeOfDay, const std::vector& idle); - virtual AiPackage *clone() const; - virtual bool execute (const MWWorld::Ptr& actor); - ///< \return Package completed? - virtual int getTypeId() const; - ///< 0: Wander - - private: - int mDistance; - int mDuration; - int mTimeOfDay; - std::vector mIdle; + public: + + AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat); + virtual AiPackage *clone() const; + virtual bool execute (const MWWorld::Ptr& actor); + ///< \return Package completed? + virtual int getTypeId() const; + ///< 0: Wander + + private: + void stopWalking(const MWWorld::Ptr& actor, PathFinder& path); + void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); + bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); + + int mDistance; + int mDuration; + int mTimeOfDay; + std::vector mIdle; + bool mRepeat; + + float mX; + float mY; + float mZ; + + int mCellX; + int mCellY; + float mXCell; + float mYCell; + + bool mStoredAvailableNodes; + bool mChooseAction; + bool mIdleNow; + bool mMoveNow; + bool mWalking; + + float mIdleChanceMultiplier; + unsigned short mPlayedIdle; + + MWWorld::TimeStamp mStartTime; + + std::vector mAllowedNodes; + ESM::Pathgrid::Point mCurrentNode; + + PathFinder mPathFinder; + const ESM::Pathgrid *mPathgrid; + }; - } +} #endif diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index 8ab81bfdf..718200372 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -144,7 +144,11 @@ void MWMechanics::Alchemy::updateEffects() MWBase::Environment::get().getWorld()->getStore().get().find (iter->mId); if (magicEffect->mData.mBaseCost<=0) - throw std::runtime_error ("invalid base cost for magic effect " + iter->mId); + { + std::ostringstream os; + os << "invalid base cost for magic effect " << iter->mId; + throw std::runtime_error (os.str()); + } float fPotionT1MagMul = MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionT1MagMult")->getFloat(); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp new file mode 100644 index 000000000..aa20e1414 --- /dev/null +++ b/apps/openmw/mwmechanics/character.cpp @@ -0,0 +1,555 @@ +/* + * OpenMW - The completely unofficial reimplementation of Morrowind + * + * This file (character.cpp) is part of the OpenMW package. + * + * OpenMW is distributed as free software: you can redistribute it + * and/or modify it under the terms of the GNU General Public License + * version 3, as published by the Free Software Foundation. + * + * 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 + * version 3 along with this program. If not, see + * http://www.gnu.org/licenses/ . + */ + +#include "character.hpp" + +#include + +#include "movement.hpp" +#include "npcstats.hpp" +#include "creaturestats.hpp" + +#include "../mwrender/animation.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/inventorystore.hpp" + + +namespace MWMechanics +{ + +static const struct StateInfo { + CharacterState state; + const char groupname[32]; + Priority priority; + bool loops; +} sStateList[] = { + { CharState_Idle, "idle", Priority_Default, true }, + { CharState_Idle2, "idle2", Priority_Default, true }, + { CharState_Idle3, "idle3", Priority_Default, true }, + { CharState_Idle4, "idle4", Priority_Default, true }, + { CharState_Idle5, "idle5", Priority_Default, true }, + { CharState_Idle6, "idle6", Priority_Default, true }, + { CharState_Idle7, "idle7", Priority_Default, true }, + { CharState_Idle8, "idle8", Priority_Default, true }, + { CharState_Idle9, "idle9", Priority_Default, true }, + { CharState_IdleSwim, "idleswim", Priority_Default, true }, + { CharState_IdleSneak, "idlesneak", Priority_Default, true }, + + { CharState_WalkForward, "walkforward", Priority_Default, true }, + { CharState_WalkBack, "walkback", Priority_Default, true }, + { CharState_WalkLeft, "walkleft", Priority_Default, true }, + { CharState_WalkRight, "walkright", Priority_Default, true }, + + { CharState_SwimWalkForward, "swimwalkforward", Priority_Default, true }, + { CharState_SwimWalkBack, "swimwalkback", Priority_Default, true }, + { CharState_SwimWalkLeft, "swimwalkleft", Priority_Default, true }, + { CharState_SwimWalkRight, "swimwalkright", Priority_Default, true }, + + { CharState_RunForward, "runforward", Priority_Default, true }, + { CharState_RunBack, "runback", Priority_Default, true }, + { CharState_RunLeft, "runleft", Priority_Default, true }, + { CharState_RunRight, "runright", Priority_Default, true }, + + { CharState_SwimRunForward, "swimrunforward", Priority_Default, true }, + { CharState_SwimRunBack, "swimrunback", Priority_Default, true }, + { CharState_SwimRunLeft, "swimrunleft", Priority_Default, true }, + { CharState_SwimRunRight, "swimrunright", Priority_Default, true }, + + { CharState_SneakForward, "sneakforward", Priority_Default, true }, + { CharState_SneakBack, "sneakback", Priority_Default, true }, + { CharState_SneakLeft, "sneakleft", Priority_Default, true }, + { CharState_SneakRight, "sneakright", Priority_Default, true }, + + { CharState_TurnLeft, "turnleft", Priority_Default, true }, + { CharState_TurnRight, "turnright", Priority_Default, true }, + + { CharState_Jump, "jump", Priority_Default, true }, + + { CharState_Death1, "death1", Priority_Death, false }, + { CharState_Death2, "death2", Priority_Death, false }, + { CharState_Death3, "death3", Priority_Death, false }, + { CharState_Death4, "death4", Priority_Death, false }, + { CharState_Death5, "death5", Priority_Death, false }, +}; +static const StateInfo *sStateListEnd = &sStateList[sizeof(sStateList)/sizeof(sStateList[0])]; + +class FindCharState { + CharacterState state; + +public: + FindCharState(CharacterState _state) : state(_state) { } + + bool operator()(const StateInfo &info) const + { return info.state == state; } +}; + + +static const struct WeaponInfo { + WeaponType type; + const char idlegroup[16]; + const char movementgroup[16]; + const char actiongroup[16]; +} sWeaponTypeList[] = { + { WeapType_HandToHand, "hh", "hh", "handtohand" }, + { WeapType_OneHand, "1h", "1h", "weapononehand" }, + { WeapType_TwoHand, "2c", "2c", "weapontwohand" }, + { WeapType_TwoWide, "2w", "2w", "weapontwowide" }, + { WeapType_BowAndArrow, "1h", "1h", "bowandarrow" }, + { WeapType_Crossbow, "crossbow", "1h", "crossbow" }, + { WeapType_ThowWeapon, "1h", "1h", "throwweapon" }, + { WeapType_PickProbe, "1h", "1h", "pickprobe" }, + { WeapType_Spell, "spell", "", "spellcast" }, +}; +static const WeaponInfo *sWeaponTypeListEnd = &sWeaponTypeList[sizeof(sWeaponTypeList)/sizeof(sWeaponTypeList[0])]; + +class FindWeaponType { + WeaponType type; + +public: + FindWeaponType(WeaponType _type) : type(_type) { } + + bool operator()(const WeaponInfo &weap) const + { return weap.type == type; } +}; + + +void CharacterController::getCurrentGroup(std::string &group, Priority &priority, bool &loops) const +{ + std::string name; + const StateInfo *state = std::find_if(sStateList, sStateListEnd, FindCharState(mCharState)); + if(state == sStateListEnd) + throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mCharState)); + + name = state->groupname; + priority = state->priority; + loops = state->loops; + + if(!(mCharState >= CharState_Death1) && mWeaponType != WeapType_None) + { + const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); + if(weap != sWeaponTypeListEnd) + { + if(mCharState == CharState_Idle) + (group=name) += weap->idlegroup; + else + (group=name) += weap->movementgroup; + } + } + + if(group.empty() || !mAnimation->hasAnimation(group)) + group = (mAnimation->hasAnimation(name) ? name : std::string()); +} + + +void CharacterController::getWeaponGroup(WeaponType weaptype, std::string &group) +{ + const WeaponInfo *info = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(weaptype)); + if(info != sWeaponTypeListEnd) + group = info->actiongroup; +} + + +CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state) + : mPtr(ptr) + , mAnimation(anim) + , mCharState(state) + , mWeaponType(WeapType_None) + , mSkipAnim(false) + , mSecondsOfRunning(0) + , mSecondsOfSwimming(0) +{ + if(!mAnimation) + return; + + if(MWWorld::Class::get(mPtr).isActor()) + { + /* Accumulate along X/Y only for now, until we can figure out how we should + * handle knockout and death which moves the character down. */ + mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f)); + } + else + { + /* Don't accumulate with non-actors. */ + mAnimation->setAccumulation(Ogre::Vector3(0.0f)); + } + + std::string group; + Priority prio; + bool loops; + getCurrentGroup(group, prio, loops); + mAnimation->play(group, prio, MWRender::Animation::Group_All, false, + "start", "stop", 1.0f, loops ? (~(size_t)0) : 0); +} + +CharacterController::~CharacterController() +{ +} + + +void CharacterController::updatePtr(const MWWorld::Ptr &ptr) +{ + mPtr = ptr; +} + + +void CharacterController::update(float duration, Movement &movement) +{ + const MWWorld::Class &cls = MWWorld::Class::get(mPtr); + float speed = 0.0f; + + if(!cls.isActor()) + { + if(mAnimQueue.size() > 1) + { + if(mAnimation->isPlaying(mAnimQueue.front().first) == false) + { + mAnimation->disable(mAnimQueue.front().first); + mAnimQueue.pop_front(); + + mAnimation->play(mAnimQueue.front().first, Priority_Default, + MWRender::Animation::Group_All, false, + "start", "stop", 0.0f, mAnimQueue.front().second); + } + } + } + else if(!cls.getCreatureStats(mPtr).isDead()) + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + + 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); + Ogre::Vector3 vec = cls.getMovementVector(mPtr); + Ogre::Vector3 rot = cls.getRotationVector(mPtr); + speed = cls.getSpeed(mPtr); + + // advance athletics + if (vec.squaredLength() > 0 && mPtr.getRefData().getHandle() == "player") + { + if (inwater) + { + mSecondsOfSwimming += duration; + while(mSecondsOfSwimming > 1) + { + cls.skillUsageSucceeded(mPtr, ESM::Skill::Athletics, 1); + mSecondsOfSwimming -= 1; + } + } + else if (isrunning) + { + mSecondsOfRunning += duration; + while(mSecondsOfRunning > 1) + { + cls.skillUsageSucceeded(mPtr, ESM::Skill::Athletics, 0); + mSecondsOfRunning -= 1; + } + } + } + + /* FIXME: The state should be set to Jump, and X/Y movement should be disallowed except + * for the initial thrust (which would be carried by "physics" until landing). */ + if(!onground) + vec.z = 0.0f; + else if(vec.z > 0.0f) + { + float z = cls.getJump(mPtr); + + if(vec.x == 0 && vec.y == 0) + vec.z *= z; + else + { + /* FIXME: this would be more correct if we were going into a jumping state, + * rather than normal walking/idle states. */ + //Ogre::Vector3 lat = Ogre::Vector3(vec.x, vec.y, 0.0f).normalisedCopy(); + //vec *= Ogre::Vector3(lat.x, lat.y, 1.0f) * z * 0.707f; + vec.z *= z * 0.707f; + } + + //decrease fatigue by fFatigueJumpBase + (1 - normalizedEncumbrance) * fFatigueJumpMult; + } + + if(std::abs(vec.x/2.0f) > std::abs(vec.y) && speed > 0.0f) + { + if(vec.x > 0.0f) + setState(inwater ? (isrunning ? CharState_SwimRunRight : CharState_SwimWalkRight) + : (sneak ? CharState_SneakRight : (isrunning ? CharState_RunRight : CharState_WalkRight))); + else if(vec.x < 0.0f) + setState(inwater ? (isrunning ? CharState_SwimRunLeft : CharState_SwimWalkLeft) + : (sneak ? CharState_SneakLeft : (isrunning ? CharState_RunLeft : CharState_WalkLeft))); + + vec.x *= speed; + vec.y *= speed; + } + else if(vec.y != 0.0f && speed > 0.0f) + { + if(vec.y > 0.0f) + setState(inwater ? (isrunning ? CharState_SwimRunForward : CharState_SwimWalkForward) + : (sneak ? CharState_SneakForward : (isrunning ? CharState_RunForward : CharState_WalkForward))); + else if(vec.y < 0.0f) + setState(inwater ? (isrunning ? CharState_SwimRunBack : CharState_SwimWalkBack) + : (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack))); + + vec.x *= speed; + vec.y *= speed; + } + else if(rot.z != 0.0f && !inwater && !sneak) + { + if(rot.z > 0.0f) + setState(CharState_TurnRight); + else if(rot.z < 0.0f) + setState(CharState_TurnLeft); + } + else if(mAnimQueue.size() > 0) + { + if(mAnimQueue.size() > 1) + { + if(mAnimation->isPlaying(mAnimQueue.front().first) == false) + { + mAnimation->disable(mAnimQueue.front().first); + mAnimQueue.pop_front(); + + mAnimation->play(mAnimQueue.front().first, Priority_Default, + MWRender::Animation::Group_All, false, + "start", "stop", 0.0f, mAnimQueue.front().second); + } + } + } + else + setState((inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle))); + + vec *= duration; + movement.mPosition[0] += vec.x; + movement.mPosition[1] += vec.y; + movement.mPosition[2] += vec.z; + rot *= duration; + movement.mRotation[0] += rot.x; + movement.mRotation[1] += rot.y; + movement.mRotation[2] += rot.z; + + if(mPtr.getTypeName() == typeid(ESM::NPC).name()) + { + NpcStats &stats = cls.getNpcStats(mPtr); + WeaponType weaptype = WeapType_None; + MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); + MWWorld::ContainerStoreIterator weapon = inv.end(); + + if(stats.getDrawState() == DrawState_Spell) + weaptype = WeapType_Spell; + else if(stats.getDrawState() == MWMechanics::DrawState_Weapon) + { + weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if(weapon == inv.end()) + weaptype = WeapType_HandToHand; + else + { + const std::string &type = weapon->getTypeName(); + if(type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name()) + weaptype = WeapType_PickProbe; + else if(type == typeid(ESM::Weapon).name()) + { + MWWorld::LiveCellRef *ref = weapon->get(); + ESM::Weapon::Type type = (ESM::Weapon::Type)ref->mBase->mData.mType; + switch(type) + { + case ESM::Weapon::ShortBladeOneHand: + case ESM::Weapon::LongBladeOneHand: + case ESM::Weapon::BluntOneHand: + case ESM::Weapon::AxeOneHand: + case ESM::Weapon::Arrow: + case ESM::Weapon::Bolt: + weaptype = WeapType_OneHand; + break; + case ESM::Weapon::LongBladeTwoHand: + case ESM::Weapon::BluntTwoClose: + case ESM::Weapon::AxeTwoHand: + weaptype = WeapType_TwoHand; + break; + case ESM::Weapon::BluntTwoWide: + case ESM::Weapon::SpearTwoWide: + weaptype = WeapType_TwoWide; + break; + case ESM::Weapon::MarksmanBow: + weaptype = WeapType_BowAndArrow; + break; + case ESM::Weapon::MarksmanCrossbow: + weaptype = WeapType_Crossbow; + break; + case ESM::Weapon::MarksmanThrown: + weaptype = WeapType_ThowWeapon; + break; + } + } + } + } + else + weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + + if(weaptype != mWeaponType) + { + std::string weapgroup; + if(weaptype == WeapType_None) + { + getWeaponGroup(mWeaponType, weapgroup); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + "unequip start", "unequip stop", 0.0f, 0); + } + else + { + getWeaponGroup(weaptype, weapgroup); + mAnimation->showWeapons(false); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + "equip start", "equip stop", 0.0f, 0); + } + + mWeaponType = weaptype; + forceStateUpdate(); + + if(weapon != inv.end()) + { + std::string soundid = (mWeaponType == WeapType_None) ? + MWWorld::Class::get(*weapon).getDownSoundId(*weapon) : + MWWorld::Class::get(*weapon).getUpSoundId(*weapon); + if(!soundid.empty()) + { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + sndMgr->playSound3D(mPtr, soundid, 1.0f, 1.0f); + } + } + } + + MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) + { + if(!mAnimation->isPlaying("torch")) + mAnimation->play("torch", Priority_Torch, + MWRender::Animation::Group_LeftArm, false, + "start", "stop", 0.0f, (~(size_t)0)); + } + else if(mAnimation->isPlaying("torch")) + mAnimation->disable("torch"); + } + } + + if(mAnimation && !mSkipAnim) + { + mAnimation->setSpeed(speed); + + Ogre::Vector3 moved = mAnimation->runAnimation(duration); + // Ensure we're moving in generally the right direction + if (speed > 0.f) + { + if((movement.mPosition[0] < 0.0f && movement.mPosition[0] < moved.x*2.0f) || + (movement.mPosition[0] > 0.0f && movement.mPosition[0] > moved.x*2.0f)) + moved.x = movement.mPosition[0]; + if((movement.mPosition[1] < 0.0f && movement.mPosition[1] < moved.y*2.0f) || + (movement.mPosition[1] > 0.0f && movement.mPosition[1] > moved.y*2.0f)) + moved.y = movement.mPosition[1]; + if((movement.mPosition[2] < 0.0f && movement.mPosition[2] < moved.z*2.0f) || + (movement.mPosition[2] > 0.0f && movement.mPosition[2] > moved.z*2.0f)) + moved.z = movement.mPosition[2]; + } + + movement.mPosition[0] = moved.x; + movement.mPosition[1] = moved.y; + movement.mPosition[2] = moved.z; + } + mSkipAnim = false; +} + + +void CharacterController::playGroup(const std::string &groupname, int mode, int count) +{ + if(!mAnimation || !mAnimation->hasAnimation(groupname)) + std::cerr<< "Animation "<play(groupname, Priority_Default, + MWRender::Animation::Group_All, false, + ((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1); + } + else if(mode == 0) + { + mAnimQueue.resize(1); + mAnimQueue.push_back(std::make_pair(groupname, count-1)); + } + } +} + +void CharacterController::skipAnim() +{ + mSkipAnim = true; +} + +bool CharacterController::isAnimPlaying(const std::string &groupName) +{ + if(mAnimation == NULL) + return false; + else + return mAnimation->isPlaying(groupName); +} + + +void CharacterController::clearAnimQueue() +{ + if(mAnimQueue.size() > 0) + mAnimation->disable(mAnimQueue.front().first); + mAnimQueue.clear(); +} + + +void CharacterController::setState(CharacterState state) +{ + if(mCharState == state) + return; + mCharState = state; + + forceStateUpdate(); +} + +void CharacterController::forceStateUpdate() +{ + if(!mAnimation) + return; + clearAnimQueue(); + + std::string group; + Priority prio; + bool loops; + getCurrentGroup(group, prio, loops); + mAnimation->play(group, prio, MWRender::Animation::Group_All, false, + "start", "stop", 0.0f, loops ? (~(size_t)0) : 0); +} + +} diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp new file mode 100644 index 000000000..7067176e0 --- /dev/null +++ b/apps/openmw/mwmechanics/character.hpp @@ -0,0 +1,139 @@ +#ifndef GAME_MWMECHANICS_CHARACTER_HPP +#define GAME_MWMECHANICS_CHARACTER_HPP + +#include + +#include "../mwworld/ptr.hpp" + +namespace MWRender +{ + class Animation; +} + +namespace MWMechanics +{ + +class Movement; + +enum Priority { + Priority_Default, + Priority_Weapon, + Priority_Torch, + + Priority_Death, + + Num_Priorities +}; + +enum CharacterState { + CharState_SpecialIdle, + CharState_Idle, + CharState_Idle2, + CharState_Idle3, + CharState_Idle4, + CharState_Idle5, + CharState_Idle6, + CharState_Idle7, + CharState_Idle8, + CharState_Idle9, + CharState_IdleSwim, + CharState_IdleSneak, + + CharState_WalkForward, + CharState_WalkBack, + CharState_WalkLeft, + CharState_WalkRight, + + CharState_SwimWalkForward, + CharState_SwimWalkBack, + CharState_SwimWalkLeft, + CharState_SwimWalkRight, + + CharState_RunForward, + CharState_RunBack, + CharState_RunLeft, + CharState_RunRight, + + CharState_SwimRunForward, + CharState_SwimRunBack, + CharState_SwimRunLeft, + CharState_SwimRunRight, + + CharState_SneakForward, + CharState_SneakBack, + CharState_SneakLeft, + CharState_SneakRight, + + CharState_TurnLeft, + CharState_TurnRight, + + CharState_Jump, + + /* Death states must be last! */ + CharState_Death1, + CharState_Death2, + CharState_Death3, + CharState_Death4, + CharState_Death5 +}; + +enum WeaponType { + WeapType_None, + + WeapType_HandToHand, + WeapType_OneHand, + WeapType_TwoHand, + WeapType_TwoWide, + WeapType_BowAndArrow, + WeapType_Crossbow, + WeapType_ThowWeapon, + WeapType_PickProbe, + + WeapType_Spell +}; + +class CharacterController +{ + MWWorld::Ptr mPtr; + MWRender::Animation *mAnimation; + + typedef std::deque > AnimationQueue; + AnimationQueue mAnimQueue; + + CharacterState mCharState; + WeaponType mWeaponType; + bool mSkipAnim; + + // counted for skill increase + float mSecondsOfSwimming; + float mSecondsOfRunning; + + // Gets an animation group name from the current character state, and whether it should loop. + void getCurrentGroup(std::string &group, Priority &prio, bool &loops) const; + + static void getWeaponGroup(WeaponType weaptype, std::string &group); + + void clearAnimQueue(); + +public: + CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state); + virtual ~CharacterController(); + + void updatePtr(const MWWorld::Ptr &ptr); + + void update(float duration, Movement &movement); + + void playGroup(const std::string &groupname, int mode, int count); + void skipAnim(); + bool isAnimPlaying(const std::string &groupName); + + void setState(CharacterState state); + CharacterState getState() const + { return mCharState; } + + void forceStateUpdate(); +}; + +} + +#endif /* GAME_MWMECHANICS_CHARACTER_HPP */ diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 4be5d55b2..19d208021 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -10,7 +10,8 @@ namespace MWMechanics { CreatureStats::CreatureStats() - : mLevel (0), mLevelHealthBonus(0.f), mDead (false), mFriendlyHits (0), mTalkedTo (false), mAlarmed (false), + : mLevel (0), mLevelHealthBonus(0.f), mDead (false), mDied (false), mFriendlyHits (0), + mTalkedTo (false), mAlarmed (false), mAttacked (false), mHostile (false) { for (int i=0; i<4; ++i) @@ -167,7 +168,12 @@ namespace MWMechanics mDynamic[index] = value; if (index==0 && mDynamic[index].getCurrent()<1) + { + if (!mDead) + mDied = true; + mDead = true; + } } void CreatureStats::setLevel(int level) @@ -196,6 +202,16 @@ namespace MWMechanics return mDead; } + bool CreatureStats::hasDied() const + { + return mDied; + } + + void CreatureStats::clearHasDied() + { + mDied = false; + } + void CreatureStats::resurrect() { if (mDead) diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 3375c1af8..63de13d0d 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -28,6 +28,7 @@ namespace MWMechanics AiSequence mAiSequence; float mLevelHealthBonus; bool mDead; + bool mDied; int mFriendlyHits; bool mTalkedTo; bool mAlarmed; @@ -100,6 +101,10 @@ namespace MWMechanics bool isDead() const; + bool hasDied() const; + + void clearHasDied(); + void resurrect(); bool hasCommonDisease() const; diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp new file mode 100644 index 000000000..faa450df7 --- /dev/null +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -0,0 +1,316 @@ +#include "enchanting.hpp" +#include "../mwworld/player.hpp" +#include "../mwworld/manualref.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/containerstore.hpp" +#include "../mwbase/mechanicsmanager.hpp" + +#include "creaturestats.hpp" +#include "npcstats.hpp" +#include + +namespace MWMechanics +{ + Enchanting::Enchanting(): + mCastStyle(ESM::Enchantment::CastOnce) + {} + + void Enchanting::setOldItem(MWWorld::Ptr oldItem) + { + mOldItemPtr=oldItem; + if(!itemEmpty()) + { + mObjectType = mOldItemPtr.getTypeName(); + mOldItemId = mOldItemPtr.getCellRef().mRefID; + mOldItemCount = mOldItemPtr.getRefData().getCount(); + } + else + { + mObjectType=""; + mOldItemId=""; + } + } + + void Enchanting::setNewItemName(const std::string& s) + { + mNewItemName=s; + } + + void Enchanting::setEffect(ESM::EffectList effectList) + { + mEffectList=effectList; + } + + int Enchanting::getCastStyle() const + { + return mCastStyle; + } + + void Enchanting::setSoulGem(MWWorld::Ptr soulGem) + { + mSoulGemPtr=soulGem; + } + + bool Enchanting::create() + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + ESM::Enchantment enchantment; + enchantment.mData.mCharge = getGemCharge(); + + mSoulGemPtr.getRefData().setCount (mSoulGemPtr.getRefData().getCount()-1); + + //Exception for Azura Star, new one will be added after enchanting + if(boost::iequals(mSoulGemPtr.get()->mBase->mId, "Misc_SoulGem_Azura")) + { + MWWorld::ManualRef azura (MWBase::Environment::get().getWorld()->getStore(), "Misc_SoulGem_Azura"); + MWWorld::Class::get (player).getContainerStore (player).add (azura.getPtr()); + } + + if(mSelfEnchanting) + { + if(getEnchantChance() (RAND_MAX)*100) + return false; + + MWWorld::Class::get (mEnchanter).skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 1); + } + + if(mCastStyle==ESM::Enchantment::ConstantEffect) + { + enchantment.mData.mCharge=0; + } + enchantment.mData.mType = mCastStyle; + enchantment.mData.mCost = getEnchantPoints(); + enchantment.mEffects = mEffectList; + + const ESM::Enchantment *enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment); + + MWWorld::Class::get(mOldItemPtr).applyEnchantment(mOldItemPtr, enchantmentPtr->mId, getGemCharge(), mNewItemName); + + mOldItemPtr.getRefData().setCount(1); + + MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), mOldItemId); + ref.getPtr().getRefData().setCount (mOldItemCount-1); + + MWWorld::Class::get (player).getContainerStore (player).add (ref.getPtr()); + if(!mSelfEnchanting) + payForEnchantment(); + + return true; + } + + void Enchanting::nextCastStyle() + { + if (itemEmpty()) + { + mCastStyle = ESM::Enchantment::WhenUsed; + return; + } + + const bool powerfulSoul = getGemCharge() >= \ + MWBase::Environment::get().getWorld()->getStore().get().find ("iSoulAmountForConstantEffect")->getInt(); + if ((mObjectType == typeid(ESM::Armor).name()) || (mObjectType == typeid(ESM::Clothing).name())) + { // Armor or Clothing + switch(mCastStyle) + { + case ESM::Enchantment::WhenUsed: + if (powerfulSoul) + mCastStyle = ESM::Enchantment::ConstantEffect; + return; + default: // takes care of Constant effect too + mCastStyle = ESM::Enchantment::WhenUsed; + return; + } + } + else if(mObjectType == typeid(ESM::Weapon).name()) + { // Weapon + switch(mCastStyle) + { + case ESM::Enchantment::WhenStrikes: + mCastStyle = ESM::Enchantment::WhenUsed; + return; + case ESM::Enchantment::WhenUsed: + if (powerfulSoul) + mCastStyle = ESM::Enchantment::ConstantEffect; + else + mCastStyle = ESM::Enchantment::WhenStrikes; + return; + default: // takes care of Constant effect too + mCastStyle = ESM::Enchantment::WhenStrikes; + return; + } + } + else if(mObjectType == typeid(ESM::Book).name()) + { // Scroll or Book + mCastStyle = ESM::Enchantment::CastOnce; + return; + } + + // Fail case + mCastStyle = ESM::Enchantment::CastOnce; + } + + /* + * Vanilla enchant cost formula: + * + * Touch/Self: (min + max) * baseCost * 0.025 * duration + area * baseCost * 0.025 + * Target: 1.5 * ((min + max) * baseCost * 0.025 * duration + area * baseCost * 0.025) + * Constant eff: (min + max) * baseCost * 2.5 + area * baseCost * 0.025 + * + * For multiple effects - cost of each effect is multiplied by number of effects that follows +1. + * + * Note: Minimal value inside formula for 'min' and 'max' is 1. So in vanilla: + * (0 + 0) == (1 + 0) == (1 + 1) => 2 or (2 + 0) == (1 + 2) => 3 + * + * Formula on UESPWiki is not entirely correct. + */ + float Enchanting::getEnchantPoints() const + { + if (mEffectList.mList.empty()) + // No effects added, cost = 0 + return 0; + + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + std::vector mEffects = mEffectList.mList; + + float enchantmentCost = 0; + int effectsLeftCnt = mEffects.size(); + float baseCost, magnitudeCost, areaCost; + int magMin, magMax, area; + for (std::vector::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) + { + baseCost = (store.get().find(it->mEffectID))->mData.mBaseCost; + // To reflect vanilla behavior + magMin = (it->mMagnMin == 0) ? 1 : it->mMagnMin; + magMax = (it->mMagnMax == 0) ? 1 : it->mMagnMax; + area = (it->mArea == 0) ? 1 : it->mArea; + + if (mCastStyle == ESM::Enchantment::ConstantEffect) + { + magnitudeCost = (magMin + magMax) * baseCost * 2.5; + } + else + { + magnitudeCost = (magMin + magMax) * it->mDuration * baseCost * 0.025; + if(it->mRange == ESM::RT_Target) + magnitudeCost *= 1.5; + } + + areaCost = area * 0.025 * baseCost; + if (it->mRange == ESM::RT_Target) + areaCost *= 1.5; + + enchantmentCost += (magnitudeCost + areaCost) * effectsLeftCnt; + --effectsLeftCnt; + } + + return enchantmentCost; + } + + + float Enchanting::getCastCost() const + { + if (mCastStyle == ESM::Enchantment::ConstantEffect) + return 0; + + const float enchantCost = getEnchantPoints(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::NpcStats &stats = MWWorld::Class::get(player).getNpcStats(player); + int eSkill = stats.getSkill(ESM::Skill::Enchant).getModified(); + + /* + * Each point of enchant skill above/under 10 subtracts/adds + * one percent of enchantment cost while minimum is 1. + */ + const float castCost = enchantCost - (enchantCost / 100) * (eSkill - 10); + + return (castCost < 1) ? 1 : castCost; + } + + + int Enchanting::getEnchantPrice() const + { + if(mEnchanter.isEmpty()) + return 0; + + float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get().find ("fEnchantmentValueMult")->getFloat(); + int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mEnchanter, (getEnchantPoints() * priceMultipler), true); + return price; + } + + int Enchanting::getGemCharge() const + { + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + if(soulEmpty()) + return 0; + if(mSoulGemPtr.getCellRef().mSoul=="") + return 0; + const ESM::Creature* soul = store.get().find(mSoulGemPtr.getCellRef().mSoul); + return soul->mData.mSoul; + } + + float Enchanting::getMaxEnchantValue() const + { + if (itemEmpty()) + return 0; + return MWWorld::Class::get(mOldItemPtr).getEnchantmentPoints(mOldItemPtr); + } + bool Enchanting::soulEmpty() const + { + return mSoulGemPtr.isEmpty(); + } + + bool Enchanting::itemEmpty() const + { + return mOldItemPtr.isEmpty(); + } + + void Enchanting::setSelfEnchanting(bool selfEnchanting) + { + mSelfEnchanting = selfEnchanting; + } + + void Enchanting::setEnchanter(MWWorld::Ptr enchanter) + { + mEnchanter = enchanter; + } + + 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())); + + 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); + } + + void Enchanting::payForEnchantment() const + { + MWWorld::Ptr gold; + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); + + for (MWWorld::ContainerStoreIterator it = store.begin(); + it != store.end(); ++it) + { + if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, "gold_001")) + { + gold = *it; + } + } + + gold.getRefData().setCount(gold.getRefData().getCount() - getEnchantPrice()); + } +} diff --git a/apps/openmw/mwmechanics/enchanting.hpp b/apps/openmw/mwmechanics/enchanting.hpp new file mode 100644 index 000000000..a25fd43ab --- /dev/null +++ b/apps/openmw/mwmechanics/enchanting.hpp @@ -0,0 +1,49 @@ +#ifndef GAME_MWMECHANICS_ENCHANTING_H +#define GAME_MWMECHANICS_ENCHANTING_H +#include +#include "../mwworld/ptr.hpp" +#include +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +namespace MWMechanics +{ + class Enchanting + { + MWWorld::Ptr mOldItemPtr; + MWWorld::Ptr mSoulGemPtr; + MWWorld::Ptr mEnchanter; + + int mCastStyle; + + bool mSelfEnchanting; + + ESM::EffectList mEffectList; + + std::string mNewItemName; + std::string mObjectType; + std::string mOldItemId; + int mOldItemCount; + + public: + Enchanting(); + void setEnchanter(MWWorld::Ptr enchanter); + void setSelfEnchanting(bool selfEnchanting); + void setOldItem(MWWorld::Ptr oldItem); + void setNewItemName(const std::string& s); + void setEffect(ESM::EffectList effectList); + void setSoulGem(MWWorld::Ptr soulGem); + bool create(); //Return true if created, false if failed. + void nextCastStyle(); //Set enchant type to next possible type (for mOldItemPtr object) + int getCastStyle() const; + float getEnchantPoints() const; + float getCastCost() const; + int getEnchantPrice() const; + float getMaxEnchantValue() const; + int getGemCharge() const; + float getEnchantChance() const; + bool soulEmpty() const; //Return true if empty + bool itemEmpty() const; //Return true if empty + void payForEnchantment() const; + }; +} +#endif diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index dae417d44..29880291d 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -2,6 +2,7 @@ #include "mechanicsmanagerimp.hpp" #include "../mwworld/esmstore.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -52,21 +53,9 @@ namespace MWMechanics for (int i=0; i<8; ++i) { - const ESM::Race::MaleFemale *attribute = 0; - switch (i) - { - case 0: attribute = &race->mData.mStrength; break; - case 1: attribute = &race->mData.mIntelligence; break; - case 2: attribute = &race->mData.mWillpower; break; - case 3: attribute = &race->mData.mAgility; break; - case 4: attribute = &race->mData.mSpeed; break; - case 5: attribute = &race->mData.mEndurance; break; - case 6: attribute = &race->mData.mPersonality; break; - case 7: attribute = &race->mData.mLuck; break; - } + const ESM::Race::MaleFemale& attribute = race->mData.mAttributeValues[i]; - creatureStats.getAttribute(i).setBase ( - static_cast (male ? attribute->mMale : attribute->mFemale)); + creatureStats.getAttribute(i).setBase (male ? attribute.mMale : attribute.mFemale); } for (int i=0; i<27; ++i) @@ -160,12 +149,18 @@ namespace MWMechanics // forced update and current value adjustments mActors.updateActor (ptr, 0); - for (int i=0; i<2; ++i) + for (int i=0; i<3; ++i) { DynamicStat stat = creatureStats.getDynamic (i); stat.setCurrent (stat.getModified()); 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); + for (int i=0; i >& movement, - float duration, bool paused) + void MechanicsManager::update(float duration, bool paused) { if (!mWatched.isEmpty()) { @@ -296,9 +304,16 @@ namespace MWMechanics } winMgr->configureSkills (majorSkills, minorSkills); + + // 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(); + mActors.removeActor(ptr); + mActors.addActor(ptr); } - mActors.update (movement, duration, paused); + mActors.update(duration, paused); + mObjects.update(duration, paused); } void MechanicsManager::restoreDynamicStats() @@ -471,8 +486,10 @@ namespace MWMechanics if(buying) x = buyTerm; else x = std::min(buyTerm, sellTerm); int offerPrice; - if (x < 1) offerPrice = int(x * basePrice); - if (x >= 1) offerPrice = basePrice + int((x - 1) * basePrice); + if (x < 1) + offerPrice = int(x * basePrice); + else + offerPrice = basePrice + int((x - 1) * basePrice); offerPrice = std::max(1, offerPrice); return offerPrice; } @@ -525,7 +542,7 @@ namespace MWMechanics float bribeMod; if (type == PT_Bribe10) bribeMod = gmst.find("fBribe10Mod")->getFloat(); - if (type == PT_Bribe100) bribeMod = gmst.find("fBribe100Mod")->getFloat(); + else if (type == PT_Bribe100) bribeMod = gmst.find("fBribe100Mod")->getFloat(); else bribeMod = gmst.find("fBribe1000Mod")->getFloat(); float target3 = d * (playerRating3 - npcRating3 + 50) + bribeMod; @@ -535,7 +552,8 @@ namespace MWMechanics float fPerDieRollMult = gmst.find("fPerDieRollMult")->getFloat(); float fPerTempMult = gmst.find("fPerTempMult")->getFloat(); - float x,y; + float x = 0; + float y = 0; float roll = static_cast (std::rand()) / RAND_MAX * 100; @@ -629,4 +647,33 @@ namespace MWMechanics permChange = success ? -int(cappedDispositionChange/ fPerTempMult) : y; } } + + void MechanicsManager::forceStateUpdate(const MWWorld::Ptr &ptr) + { + if(MWWorld::Class::get(ptr).isActor()) + mActors.forceStateUpdate(ptr); + } + + void MechanicsManager::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) + { + if(MWWorld::Class::get(ptr).isActor()) + mActors.playAnimationGroup(ptr, groupName, mode, number); + else + mObjects.playAnimationGroup(ptr, groupName, mode, number); + } + void MechanicsManager::skipAnimation(const MWWorld::Ptr& ptr) + { + if(MWWorld::Class::get(ptr).isActor()) + mActors.skipAnimation(ptr); + else + mObjects.skipAnimation(ptr); + } + bool MechanicsManager::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string &groupName) + { + if(MWWorld::Class::get(ptr).isActor()) + return mActors.checkAnimationPlaying(ptr, groupName); + else + return false; + } + } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index d3a97db36..95f760d11 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -7,6 +7,7 @@ #include "creaturestats.hpp" #include "npcstats.hpp" +#include "objects.hpp" #include "actors.hpp" namespace Ogre @@ -29,6 +30,8 @@ namespace MWMechanics bool mUpdatePlayer; bool mClassSelected; bool mRaceSelected; + + Objects mObjects; Actors mActors; void buildPlayer(); @@ -39,24 +42,24 @@ namespace MWMechanics MechanicsManager(); - virtual void addActor (const MWWorld::Ptr& ptr); - ///< Register an actor for stats management - /// - /// \note Dead actors are ignored. + virtual void add (const MWWorld::Ptr& ptr); + ///< Register an object for management + + virtual void remove (const MWWorld::Ptr& ptr); + ///< Deregister an object for management - virtual void removeActor (const MWWorld::Ptr& ptr); - ///< Deregister an actor for stats management + virtual void updateCell(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr); + ///< Moves an object to a new cell - virtual void dropActors (const MWWorld::CellStore *cellStore); - ///< Deregister all actors in the given cell. + virtual void drop(const MWWorld::CellStore *cellStore); + ///< Deregister all objects in the given cell. - virtual void watchActor (const MWWorld::Ptr& ptr); + virtual void watchActor(const MWWorld::Ptr& ptr); ///< On each update look for changes in a previously registered actor and update the /// GUI accordingly. - virtual void update (std::vector >& movement, - float duration, bool paused); - ///< Update actor stats and store desired velocity vectors in \a movement + virtual void update (float duration, bool paused); + ///< Update objects /// /// \param paused In game type does not currently advance (this usually means some GUI /// component is up). @@ -92,6 +95,12 @@ namespace MWMechanics float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange); void toLower(std::string npcFaction); ///< Perform a persuasion action on NPC + + 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); }; } diff --git a/apps/openmw/mwmechanics/movement.hpp b/apps/openmw/mwmechanics/movement.hpp index 11eb83151..6c9a4b758 100644 --- a/apps/openmw/mwmechanics/movement.hpp +++ b/apps/openmw/mwmechanics/movement.hpp @@ -6,11 +6,14 @@ namespace MWMechanics /// Desired movement for an actor struct Movement { - signed char mLeftRight; // 1: wants to move left, -1: wants to move right - signed char mForwardBackward; // 1:wants to move forward, -1: wants to move backward - signed char mUpDown; + float mPosition[3]; + float mRotation[3]; - Movement() : mLeftRight (0), mForwardBackward (0), mUpDown(0) {} + Movement() + { + mPosition[0] = mPosition[1] = mPosition[2] = 0.0f; + mRotation[0] = mRotation[1] = mRotation[2] = 0.0f; + } }; } diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 26c4c8e9a..def91a6c5 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -21,8 +21,16 @@ #include "../mwbase/soundmanager.hpp" MWMechanics::NpcStats::NpcStats() -: mMovementFlags (0), mDrawState (DrawState_Nothing), mBounty (0) -, mLevelProgress(0), mDisposition(0), mVampire (0), mReputation(0), mWerewolf (false), mWerewolfKills (0) +: mMovementFlags (0) +, mDrawState (DrawState_Nothing) +, mBounty (0) +, mLevelProgress(0) +, mDisposition(0) +, mVampire (0) +, mReputation(0) +, mWerewolf (false) +, mWerewolfKills (0) +, mProfit(0) { mSkillIncreases.resize (ESM::Attribute::Length); for (int i=0; igetGameSettingString ("sNotifyMessage39", "")) % std::string("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}") % static_cast (base); - MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), std::vector()); + MWBase::Environment::get().getWindowManager ()->messageBox(message.str()); if (mLevelProgress >= 10) { // levelup is possible now - MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}", std::vector()); + MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}"); } getSkill (skillIndex).setBase (base); @@ -326,7 +333,7 @@ bool MWMechanics::NpcStats::hasSkillsForRank (const std::string& factionId, int std::vector skills; for (int i=0; i<6; ++i) - skills.push_back (static_cast (getSkill (faction.mData.mSkillID[i]).getModified())); + skills.push_back (static_cast (getSkill (faction.mData.mSkills[i]).getModified())); std::sort (skills.begin(), skills.end()); @@ -354,3 +361,13 @@ int MWMechanics::NpcStats::getWerewolfKills() const { return mWerewolfKills; } + +int MWMechanics::NpcStats::getProfit() const +{ + return mProfit; +} + +void MWMechanics::NpcStats::modifyProfit(int diff) +{ + mProfit += diff; +} diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index af32bd294..b0d8db056 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -53,6 +53,7 @@ namespace MWMechanics int mReputation; bool mWerewolf; int mWerewolfKills; + int mProfit; int mLevelProgress; // 0-10 @@ -64,6 +65,10 @@ namespace MWMechanics NpcStats(); + /// for mercenary companions. starts out as 0, and changes when items are added or removed through the UI. + int getProfit() const; + void modifyProfit(int diff); + DrawState_ getDrawState() const; void setDrawState (DrawState_ state); diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp new file mode 100644 index 000000000..24d8a8bf7 --- /dev/null +++ b/apps/openmw/mwmechanics/objects.cpp @@ -0,0 +1,81 @@ +#include "objects.hpp" + +#include + +#include "movement.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +namespace MWMechanics +{ + +Objects::Objects() +{ +} + +void Objects::addObject(const MWWorld::Ptr& ptr) +{ + MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); + if(anim != NULL) + mObjects.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Idle))); +} + +void Objects::removeObject(const MWWorld::Ptr& ptr) +{ + PtrControllerMap::iterator iter = mObjects.find(ptr); + if(iter != mObjects.end()) + mObjects.erase(iter); +} + +void Objects::updateObject(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) +{ + PtrControllerMap::iterator iter = mObjects.find(old); + if(iter != mObjects.end()) + { + CharacterController ctrl = iter->second; + mObjects.erase(iter); + + ctrl.updatePtr(ptr); + mObjects.insert(std::make_pair(ptr, ctrl)); + } +} + +void Objects::dropObjects (const MWWorld::Ptr::CellStore *cellStore) +{ + PtrControllerMap::iterator iter = mObjects.begin(); + while(iter != mObjects.end()) + { + if(iter->first.getCell()==cellStore) + mObjects.erase(iter++); + else + ++iter; + } +} + +void Objects::update(float duration, bool paused) +{ + if(!paused) + { + for(PtrControllerMap::iterator iter(mObjects.begin());iter != mObjects.end();++iter) + { + Movement movement; + iter->second.update(duration, movement); + } + } +} + +void Objects::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) +{ + PtrControllerMap::iterator iter = mObjects.find(ptr); + if(iter != mObjects.end()) + iter->second.playGroup(groupName, mode, number); +} +void Objects::skipAnimation(const MWWorld::Ptr& ptr) +{ + PtrControllerMap::iterator iter = mObjects.find(ptr); + if(iter != mObjects.end()) + iter->second.skipAnim(); +} + +} diff --git a/apps/openmw/mwmechanics/objects.hpp b/apps/openmw/mwmechanics/objects.hpp new file mode 100644 index 000000000..7b1185a29 --- /dev/null +++ b/apps/openmw/mwmechanics/objects.hpp @@ -0,0 +1,45 @@ +#ifndef GAME_MWMECHANICS_ACTIVATORS_H +#define GAME_MWMECHANICS_ACTIVATORS_H + +#include +#include + +#include "character.hpp" + +namespace MWWorld +{ + class Ptr; + class CellStore; +} + +namespace MWMechanics +{ + class Objects + { + typedef std::map PtrControllerMap; + PtrControllerMap mObjects; + + public: + Objects(); + + void addObject (const MWWorld::Ptr& ptr); + ///< Register an animated object + + void removeObject (const MWWorld::Ptr& ptr); + ///< Deregister an object + + void updateObject(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr); + ///< Updates an object with a new Ptr + + void dropObjects(const MWWorld::CellStore *cellStore); + ///< Deregister all objects in the given cell. + + void update(float duration, bool paused); + ///< Update object animations + + void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); + void skipAnimation(const MWWorld::Ptr& ptr); + }; +} + +#endif diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp new file mode 100644 index 000000000..62c825be7 --- /dev/null +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -0,0 +1,233 @@ +#include "pathfinding.hpp" + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + +#include +#include +#include "boost/tuple/tuple.hpp" +#include "OgreMath.h" + +namespace +{ + //helpers functions + float distanceZCorrected(ESM::Pathgrid::Point point,float x,float y,float z) + { + return sqrt((point.mX - x)*(point.mX - x)+(point.mY - y)*(point.mY - y)+0.1*(point.mZ - z)*(point.mZ - z)); + } + + float distance(ESM::Pathgrid::Point point,float x,float y,float z) + { + return sqrt((point.mX - x)*(point.mX - x)+(point.mY - y)*(point.mY - y)+(point.mZ - z)*(point.mZ - z)); + } + + float distance(ESM::Pathgrid::Point a,ESM::Pathgrid::Point b) + { + return sqrt(float(a.mX - b.mX)*(a.mX - b.mX)+(a.mY - b.mY)*(a.mY - b.mY)+(a.mZ - b.mZ)*(a.mZ - b.mZ)); + } + + static float sgn(float a) + { + if(a>0) return 1.; + else return -1.; + } + + int getClosestPoint(const ESM::Pathgrid* grid,float x,float y,float z) + { + if(!grid) return -1; + if(grid->mPoints.empty()) return -1; + + float m = distance(grid->mPoints[0],x,y,z); + int i0 = 0; + + for(unsigned int i=1; imPoints.size();++i) + { + if(distance(grid->mPoints[i],x,y,z)mPoints[i],x,y,z); + i0 = i; + } + } + return i0; + } + + typedef boost::adjacency_list,boost::property > PathGridGraph; + typedef boost::property_map::type WeightMap; + typedef PathGridGraph::vertex_descriptor PointID; + typedef PathGridGraph::edge_descriptor PointConnectionID; + + struct found_path {}; + + /*class goalVisited : public boost::default_astar_visitor + { + public: + goalVisited(PointID goal) : mGoal(goal) {} + + void examine_vertex(PointID u, const PathGridGraph g) + { + if(u == mGoal) + throw found_path(); + } + private: + PointID mGoal; + }; + + class DistanceHeuristic : public boost::atasr_heuristic + { + public: + DistanceHeuristic(const PathGridGraph & l, PointID goal) + : mGraph(l), mGoal(goal) {} + + float operator()(PointID u) + { + const ESM::Pathgrid::Point & U = mGraph[u]; + const ESM::Pathgrid::Point & V = mGraph[mGoal]; + float dx = U.mX - V.mX; + float dy = U.mY - V.mY; + float dz = U.mZ - V.mZ; + return sqrt(dx * dx + dy * dy + dz * dz); + } + private: + const PathGridGraph & mGraph; + PointID mGoal; + };*/ + + 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; + }; + + + PathGridGraph buildGraph(const ESM::Pathgrid* pathgrid,float xCell = 0,float yCell = 0) + { + PathGridGraph graph; + + for(unsigned int i = 0;imPoints.size();++i) + { + PointID pID = boost::add_vertex(graph); + graph[pID].mX = pathgrid->mPoints[i].mX + xCell; + graph[pID].mY = pathgrid->mPoints[i].mY + yCell; + graph[pID].mZ = pathgrid->mPoints[i].mZ; + } + + for(unsigned int i = 0;imEdges.size();++i) + { + PointID u = pathgrid->mEdges[i].mV0; + PointID v = pathgrid->mEdges[i].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,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))//.weight_map(boost::get(&Edge::distance, graph)) + ); + + } 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; + } + + //end of helpers functions + +} + +namespace MWMechanics +{ + PathFinder::PathFinder() + { + mIsPathConstructed = false; + } + + void PathFinder::buildPath(ESM::Pathgrid::Point startPoint,ESM::Pathgrid::Point endPoint, + const ESM::Pathgrid* pathGrid,float xCell,float yCell) + { + //first check if there is an obstacle + if(MWBase::Environment::get().getWorld()->castRay(startPoint.mX,startPoint.mY,startPoint.mZ, + endPoint.mX,endPoint.mY,endPoint.mZ) ) + { + int start = getClosestPoint(pathGrid,startPoint.mX - xCell,startPoint.mY - yCell,startPoint.mZ); + int end = getClosestPoint(pathGrid,endPoint.mX - xCell,endPoint.mY - yCell,endPoint.mZ); + + if(start != -1 && end != -1) + { + PathGridGraph graph = buildGraph(pathGrid,xCell,yCell); + mPath = findPath(start,end,graph); + } + } + mPath.push_back(endPoint); + mIsPathConstructed = true; + } + + float PathFinder::getZAngleToNext(float x,float y,float z) + { + if(mPath.empty()) + { + return 0;/// shouldn't happen! + } + ESM::Pathgrid::Point nextPoint = *mPath.begin(); + float dX = nextPoint.mX - x; + float dY = nextPoint.mY - y; + float h = sqrt(dX*dX+dY*dY); + return Ogre::Radian(acos(dY/h)*sgn(asin(dX/h))).valueDegrees(); + } + + bool PathFinder::checkIfNextPointReached(float x,float y,float z) + { + if(mPath.empty()) + { + return true; + } + ESM::Pathgrid::Point nextPoint = *mPath.begin(); + if(distanceZCorrected(nextPoint,x,y,z) < 20) + { + mPath.pop_front(); + if(mPath.empty()) + { + return true; + } + nextPoint = *mPath.begin(); + } + return false; + } + + std::list PathFinder::getPath() + { + return mPath; + } + bool PathFinder::isPathConstructed() + { + return mIsPathConstructed; + } +} \ No newline at end of file diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp new file mode 100644 index 000000000..b1bbab37a --- /dev/null +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -0,0 +1,29 @@ +#ifndef GAME_MWMECHANICS_PATHFINDING_H +#define GAME_MWMECHANICS_PATHFINDING_H + +#include +#include + +namespace MWMechanics +{ + class PathFinder + { + public: + PathFinder(); + + void buildPath(ESM::Pathgrid::Point startPoint,ESM::Pathgrid::Point endPoint, + const ESM::Pathgrid* pathGrid,float xCell = 0,float yCell = 0); + + bool checkIfNextPointReached(float x,float y,float z);//returns true if the last point of the path has been reached. + float getZAngleToNext(float x,float y,float z); + + std::list getPath(); + bool isPathConstructed(); + + private: + std::list mPath; + bool mIsPathConstructed; + }; +} + +#endif \ No newline at end of file diff --git a/apps/openmw/mwmechanics/repair.cpp b/apps/openmw/mwmechanics/repair.cpp new file mode 100644 index 000000000..8ed449885 --- /dev/null +++ b/apps/openmw/mwmechanics/repair.cpp @@ -0,0 +1,110 @@ +#include "repair.hpp" + +#include + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/soundmanager.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/containerstore.hpp" +#include "../mwworld/class.hpp" + +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/npcstats.hpp" + +namespace MWMechanics +{ + +void Repair::repair(const MWWorld::Ptr &itemToRepair) +{ + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::LiveCellRef *ref = + mTool.get(); + + // 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); + 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); + + float fatigueTerm = stats.getFatigueTerm(); + int pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified(); + int pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + int armorerSkill = npcStats.getSkill(ESM::Skill::Armorer).getModified(); + + float fRepairAmountMult = MWBase::Environment::get().getWorld()->getStore().get() + .find("fRepairAmountMult")->getFloat(); + + float toolQuality = ref->mBase->mData.mQuality; + + float x = (0.1 * pcStrength + 0.1 * pcLuck + armorerSkill) * fatigueTerm; + + int roll = static_cast (std::rand()) / RAND_MAX * 100; + if (roll <= x) + { + int y = fRepairAmountMult * toolQuality * roll; + y = std::max(1, y); + + // repair by 'y' points + itemToRepair.getCellRef().mCharge += y; + itemToRepair.getCellRef().mCharge = std::min(itemToRepair.getCellRef().mCharge, + MWWorld::Class::get(itemToRepair).getItemMaxHealth(itemToRepair)); + + // set the OnPCRepair variable on the item's script + std::string script = MWWorld::Class::get(itemToRepair).getScript(itemToRepair); + if(script != "") + itemToRepair.getRefData().getLocals().setVarByInt(script, "onpcrepair", 1); + + // increase skill + MWWorld::Class::get(player).skillUsageSucceeded(player, ESM::Skill::Armorer, 0); + + MWBase::Environment::get().getSoundManager()->playSound("Repair",1,1); + MWBase::Environment::get().getWindowManager()->messageBox("#{sRepairSuccess}"); + } + else + { + MWBase::Environment::get().getSoundManager()->playSound("Repair Fail",1,1); + MWBase::Environment::get().getWindowManager()->messageBox("#{sRepairFailed}"); + } + + // tool used up? + if (mTool.getCellRef().mCharge == 0) + { + mTool.getRefData().setCount(0); + + std::string message = MWBase::Environment::get().getWorld()->getStore().get() + .find("sNotifyMessage51")->getString(); + + MWBase::Environment::get().getWindowManager()->messageBox((boost::format(message) % MWWorld::Class::get(mTool).getName(mTool)).str()); + + // try to find a new tool of the same ID + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); + for (MWWorld::ContainerStoreIterator iter (store.begin()); + iter!=store.end(); ++iter) + { + if (Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, mTool.getCellRef().mRefID)) + { + mTool = *iter; + break; + } + } + } +} + +} diff --git a/apps/openmw/mwmechanics/repair.hpp b/apps/openmw/mwmechanics/repair.hpp new file mode 100644 index 000000000..6f9a866af --- /dev/null +++ b/apps/openmw/mwmechanics/repair.hpp @@ -0,0 +1,23 @@ +#ifndef OPENMW_MWMECHANICS_REPAIR_H +#define OPENMW_MWMECHANICS_REPAIR_H + +#include "../mwworld/ptr.hpp" + +namespace MWMechanics +{ + + class Repair + { + public: + void setTool (const MWWorld::Ptr& tool) { mTool = tool; } + MWWorld::Ptr getTool() { return mTool; } + + void repair (const MWWorld::Ptr& itemToRepair); + + private: + MWWorld::Ptr mTool; + }; + +} + +#endif diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp new file mode 100644 index 000000000..d19da6e2a --- /dev/null +++ b/apps/openmw/mwmechanics/security.cpp @@ -0,0 +1,109 @@ +#include "security.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" + +#include "npcstats.hpp" +#include "creaturestats.hpp" + +namespace MWMechanics +{ + + Security::Security(const MWWorld::Ptr &actor) + : mActor(actor) + { + CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); + NpcStats& npcStats = MWWorld::Class::get(actor).getNpcStats(actor); + mAgility = creatureStats.getAttribute(ESM::Attribute::Agility).getModified(); + mLuck = creatureStats.getAttribute(ESM::Attribute::Luck).getModified(); + mSecuritySkill = npcStats.getSkill(ESM::Skill::Security).getModified(); + mFatigueTerm = creatureStats.getFatigueTerm(); + } + + void Security::pickLock(const MWWorld::Ptr &lock, const MWWorld::Ptr &lockpick, + std::string& resultMessage, std::string& resultSound) + { + if (lock.getCellRef().mLockLevel <= 0) + return; + + int lockStrength = lock.getCellRef().mLockLevel; + + float pickQuality = lockpick.get()->mBase->mData.mQuality; + + float fPickLockMult = MWBase::Environment::get().getWorld()->getStore().get().find("fPickLockMult")->getFloat(); + + float x = 0.2 * mAgility + 0.1 * mLuck + mSecuritySkill; + x *= pickQuality * mFatigueTerm; + x += fPickLockMult * lockStrength; + + resultSound = "Open Lock Fail"; + if (x <= 0) + resultMessage = "#{sLockImpossible}"; + else + { + int roll = static_cast (std::rand()) / RAND_MAX * 100; + if (roll <= x) + { + MWWorld::Class::get(lock).unlock(lock); + resultMessage = "#{sLockSuccess}"; + resultSound = "Open Lock"; + MWWorld::Class::get(mActor).skillUsageSucceeded(mActor, ESM::Skill::Security, 1); + } + else + resultMessage = "#{sLockFail}"; + } + + if (lockpick.getCellRef().mCharge == -1) + lockpick.getCellRef().mCharge = lockpick.get()->mBase->mData.mUses; + --lockpick.getCellRef().mCharge; + if (!lockpick.getCellRef().mCharge) + lockpick.getRefData().setCount(0); + } + + void Security::probeTrap(const MWWorld::Ptr &trap, const MWWorld::Ptr &probe, + std::string& resultMessage, std::string& resultSound) + { + if (trap.getCellRef().mTrap == "") + return; + + float probeQuality = probe.get()->mBase->mData.mQuality; + + const ESM::Spell* trapSpell = MWBase::Environment::get().getWorld()->getStore().get().find(trap.getCellRef().mTrap); + float trapSpellPoints = trapSpell->mData.mCost; + + float fTrapCostMult = MWBase::Environment::get().getWorld()->getStore().get().find("fTrapCostMult")->getFloat(); + + float x = 0.2 * mAgility + 0.1 * mLuck + mSecuritySkill; + x += fTrapCostMult * trapSpellPoints; + x *= probeQuality * mFatigueTerm; + + resultSound = "Disarm Trap Fail"; + if (x <= 0) + resultMessage = "#{sTrapImpossible}"; + else + { + int roll = static_cast (std::rand()) / RAND_MAX * 100; + if (roll <= x) + { + trap.getCellRef().mTrap = ""; + + resultSound = "Disarm Trap"; + resultMessage = "#{sTrapSuccess}"; + MWWorld::Class::get(mActor).skillUsageSucceeded(mActor, ESM::Skill::Security, 0); + } + else + resultMessage = "#{sTrapFail}"; + } + + if (probe.getCellRef().mCharge == -1) + probe.getCellRef().mCharge = probe.get()->mBase->mData.mUses; + --probe.getCellRef().mCharge; + if (!probe.getCellRef().mCharge) + probe.getRefData().setCount(0); + } + +} diff --git a/apps/openmw/mwmechanics/security.hpp b/apps/openmw/mwmechanics/security.hpp new file mode 100644 index 000000000..f3efb04ed --- /dev/null +++ b/apps/openmw/mwmechanics/security.hpp @@ -0,0 +1,27 @@ +#ifndef MWMECHANICS_SECURITY_H +#define MWMECHANICS_SECURITY_H + +#include "../mwworld/ptr.hpp" + +namespace MWMechanics +{ + + /// @brief implementation of Security skill + class Security + { + public: + Security (const MWWorld::Ptr& actor); + + void pickLock (const MWWorld::Ptr& lock, const MWWorld::Ptr& lockpick, + std::string& resultMessage, std::string& resultSound); + void probeTrap (const MWWorld::Ptr& trap, const MWWorld::Ptr& probe, + std::string& resultMessage, std::string& resultSound); + + private: + float mAgility, mLuck, mSecuritySkill, mFatigueTerm; + MWWorld::Ptr mActor; + }; + +} + +#endif diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index e2da7cdc8..e10dcdc93 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -80,7 +80,7 @@ namespace MWMechanics const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get().find (iter->first); - if (spell->mData.mFlags & ESM::Spell::ST_Disease) + if (spell->mData.mType == ESM::Spell::ST_Disease) return true; } @@ -94,7 +94,7 @@ namespace MWMechanics const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get().find (iter->first); - if (spell->mData.mFlags & ESM::Spell::ST_Blight) + if (spell->mData.mType == ESM::Spell::ST_Blight) return true; } diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index d576020c5..e9b7f4385 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -26,9 +26,9 @@ namespace MWMechanics return mBase; } - const T& getModified() const + T getModified() const { - return mModified; + return std::max(static_cast(0), mModified); } T getModifier() const @@ -108,7 +108,7 @@ namespace MWMechanics return mStatic.getBase(); } - const T& getModified() const + T getModified() const { return mStatic.getModified(); } diff --git a/apps/openmw/mwrender/activatoranimation.cpp b/apps/openmw/mwrender/activatoranimation.cpp new file mode 100644 index 000000000..f3d0ec3ff --- /dev/null +++ b/apps/openmw/mwrender/activatoranimation.cpp @@ -0,0 +1,31 @@ +#include "activatoranimation.hpp" + +#include "renderconst.hpp" + +#include "../mwbase/world.hpp" + +namespace MWRender +{ + +ActivatorAnimation::~ActivatorAnimation() +{ +} + +ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr) + : Animation(ptr) +{ + MWWorld::LiveCellRef *ref = mPtr.get(); + + assert (ref->mBase != NULL); + if(!ref->mBase->mModel.empty()) + { + const std::string name = "meshes\\"+ref->mBase->mModel; + + setObjectRoot(mPtr.getRefData().getBaseNode(), name, false); + setRenderProperties(mObjectRoot, RV_Misc, RQG_Main, RQG_Alpha); + + addAnimSource(name); + } +} + +} diff --git a/apps/openmw/mwrender/activatoranimation.hpp b/apps/openmw/mwrender/activatoranimation.hpp new file mode 100644 index 000000000..f3ea38f44 --- /dev/null +++ b/apps/openmw/mwrender/activatoranimation.hpp @@ -0,0 +1,21 @@ +#ifndef _GAME_RENDER_ACTIVATORANIMATION_H +#define _GAME_RENDER_ACTIVATORANIMATION_H + +#include "animation.hpp" + +namespace MWWorld +{ + class Ptr; +} + +namespace MWRender +{ + class ActivatorAnimation : public Animation + { + public: + ActivatorAnimation(const MWWorld::Ptr& ptr); + virtual ~ActivatorAnimation(); + }; +} + +#endif diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index 05bb030d7..566b6fa81 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -3,44 +3,48 @@ #include #include +#include "../mwworld/ptr.hpp" +#include "../mwworld/class.hpp" + +#include "../mwrender/renderingmanager.hpp" + +#include "animation.hpp" +#include "activatoranimation.hpp" +#include "creatureanimation.hpp" +#include "npcanimation.hpp" + #include "renderconst.hpp" +namespace MWRender +{ using namespace Ogre; -using namespace MWRender; -using namespace NifOgre; -Actors::~Actors(){ - - std::map::iterator it = mAllActors.begin(); - for (; it != mAllActors.end(); ++it) { +Actors::~Actors() +{ + PtrAnimationMap::iterator it = mAllActors.begin(); + for(;it != mAllActors.end();++it) + { delete it->second; it->second = NULL; } } -void Actors::setMwRoot(Ogre::SceneNode* root){ - mMwRoot = root; -} -void Actors::insertNPC(const MWWorld::Ptr& ptr, MWWorld::InventoryStore& inv){ - - insertBegin(ptr, true, true); - NpcAnimation* anim = new MWRender::NpcAnimation(ptr, ptr.getRefData ().getBaseNode (), inv, RV_Actors); +void Actors::setRootNode(Ogre::SceneNode* root) +{ mRootNode = root; } - mAllActors[ptr] = anim; -} -void Actors::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_){ +void Actors::insertBegin(const MWWorld::Ptr &ptr) +{ Ogre::SceneNode* cellnode; - if(mCellSceneNodes.find(ptr.getCell()) == mCellSceneNodes.end()) + CellSceneNodeMap::const_iterator celliter = mCellSceneNodes.find(ptr.getCell()); + if(celliter != mCellSceneNodes.end()) + cellnode = celliter->second; + else { //Create the scenenode and put it in the map - cellnode = mMwRoot->createChildSceneNode(); + cellnode = mRootNode->createChildSceneNode(); mCellSceneNodes[ptr.getCell()] = cellnode; } - else - { - cellnode = mCellSceneNodes[ptr.getCell()]; - } Ogre::SceneNode* insert = cellnode->createChildSceneNode(); const float *f = ptr.getRefData().getPosition().pos; @@ -51,50 +55,67 @@ void Actors::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_){ f = ptr.getCellRef().mPos.rot; // Rotate around X axis - Quaternion xr(Radian(-f[0]), Vector3::UNIT_X); + Ogre::Quaternion xr(Ogre::Radian(-f[0]), Ogre::Vector3::UNIT_X); // Rotate around Y axis - Quaternion yr(Radian(-f[1]), Vector3::UNIT_Y); + Ogre::Quaternion yr(Ogre::Radian(-f[1]), Ogre::Vector3::UNIT_Y); // Rotate around Z axis - Quaternion zr(Radian(-f[2]), Vector3::UNIT_Z); + Ogre::Quaternion zr(Ogre::Radian(-f[2]), Ogre::Vector3::UNIT_Z); // Rotates first around z, then y, then x insert->setOrientation(xr*yr*zr); - if (!enabled) - insert->setVisible (false); ptr.getRefData().setBaseNode(insert); - - } -void Actors::insertCreature (const MWWorld::Ptr& ptr){ - insertBegin(ptr, true, true); - CreatureAnimation* anim = new MWRender::CreatureAnimation(ptr); - //mAllActors.insert(std::pair(ptr,anim)); +void Actors::insertNPC(const MWWorld::Ptr& ptr, MWWorld::InventoryStore& inv) +{ + insertBegin(ptr); + NpcAnimation* anim = new NpcAnimation(ptr, ptr.getRefData().getBaseNode(), inv, RV_Actors); + delete mAllActors[ptr]; + mAllActors[ptr] = anim; + mRendering->addWaterRippleEmitter (ptr); +} +void Actors::insertCreature (const MWWorld::Ptr& ptr) +{ + insertBegin(ptr); + CreatureAnimation* anim = new CreatureAnimation(ptr); + delete mAllActors[ptr]; + mAllActors[ptr] = anim; + mRendering->addWaterRippleEmitter (ptr); +} +void Actors::insertActivator (const MWWorld::Ptr& ptr) +{ + insertBegin(ptr); + ActivatorAnimation* anim = new ActivatorAnimation(ptr); delete mAllActors[ptr]; mAllActors[ptr] = anim; - //mAllActors.push_back(&anim);*/ } bool Actors::deleteObject (const MWWorld::Ptr& ptr) { - delete mAllActors[ptr]; - mAllActors.erase(ptr); - if (Ogre::SceneNode *base = ptr.getRefData().getBaseNode()) - { + if (mAllActors.find(ptr) == mAllActors.end()) + return false; - Ogre::SceneNode *parent = base->getParentSceneNode(); + mRendering->removeWaterRippleEmitter (ptr); + + delete mAllActors[ptr]; + mAllActors.erase(ptr); - for (std::map::const_iterator iter ( - mCellSceneNodes.begin()); iter!=mCellSceneNodes.end(); ++iter) - if (iter->second==parent) + if(Ogre::SceneNode *base=ptr.getRefData().getBaseNode()) + { + Ogre::SceneNode *parent = base->getParentSceneNode(); + CellSceneNodeMap::const_iterator iter(mCellSceneNodes.begin()); + for(;iter != mCellSceneNodes.end();++iter) + { + if(iter->second == parent) { base->removeAndDestroyAllChildren(); mRend.getScene()->destroySceneNode (base); ptr.getRefData().setBaseNode (0); return true; } + } return false; } @@ -102,57 +123,68 @@ bool Actors::deleteObject (const MWWorld::Ptr& ptr) return true; } -void Actors::removeCell(MWWorld::Ptr::CellStore* store){ - if(mCellSceneNodes.find(store) != mCellSceneNodes.end()) - { - Ogre::SceneNode* base = mCellSceneNodes[store]; - base->removeAndDestroyAllChildren(); - mCellSceneNodes.erase(store); - mRend.getScene()->destroySceneNode(base); - base = 0; - } - for(std::map::iterator iter = mAllActors.begin(); iter != mAllActors.end(); ) +void Actors::removeCell(MWWorld::Ptr::CellStore* store) +{ + for(PtrAnimationMap::iterator iter = mAllActors.begin();iter != mAllActors.end();) { - if(iter->first.getCell() == store){ + if(iter->first.getCell() == store) + { + mRendering->removeWaterRippleEmitter (iter->first); delete iter->second; mAllActors.erase(iter++); } else ++iter; } -} + CellSceneNodeMap::iterator celliter = mCellSceneNodes.find(store); + if(celliter != mCellSceneNodes.end()) + { + Ogre::SceneNode *base = celliter->second; + base->removeAndDestroyAllChildren(); + mRend.getScene()->destroySceneNode(base); -void Actors::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number){ - if(mAllActors.find(ptr) != mAllActors.end()) - mAllActors[ptr]->playGroup(groupName, mode, number); + mCellSceneNodes.erase(celliter); + } } -void Actors::skipAnimation (const MWWorld::Ptr& ptr){ - if(mAllActors.find(ptr) != mAllActors.end()) - mAllActors[ptr]->skipAnim(); + +void Actors::update (float duration) +{ + // Nothing to do } -void Actors::update (float duration){ - for(std::map::iterator iter = mAllActors.begin(); iter != mAllActors.end(); iter++) - iter->second->runAnimation(duration); + +Animation* Actors::getAnimation(const MWWorld::Ptr &ptr) +{ + PtrAnimationMap::const_iterator iter = mAllActors.find(ptr); + if(iter != mAllActors.end()) + return iter->second; + return NULL; } -void -Actors::updateObjectCell(const MWWorld::Ptr &ptr) +void Actors::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur) { Ogre::SceneNode *node; - MWWorld::CellStore *newCell = ptr.getCell(); + MWWorld::CellStore *newCell = cur.getCell(); - if(mCellSceneNodes.find(newCell) == mCellSceneNodes.end()) { - node = mMwRoot->createChildSceneNode(); + CellSceneNodeMap::const_iterator celliter = mCellSceneNodes.find(newCell); + if(celliter != mCellSceneNodes.end()) + node = celliter->second; + else + { + node = mRootNode->createChildSceneNode(); mCellSceneNodes[newCell] = node; - } else { - node = mCellSceneNodes[newCell]; } - node->addChild(ptr.getRefData().getBaseNode()); - if (mAllActors.find(ptr) != mAllActors.end()) { - /// \note Update key (Ptr's are compared only with refdata so mCell - /// on key is outdated), maybe redundant - Animation *anim = mAllActors[ptr]; - mAllActors.erase(ptr); - mAllActors[ptr] = anim; + node->addChild(cur.getRefData().getBaseNode()); + + PtrAnimationMap::iterator iter = mAllActors.find(old); + if(iter != mAllActors.end()) + { + Animation *anim = iter->second; + mAllActors.erase(iter); + anim->updatePtr(cur); + mAllActors[cur] = anim; } + + mRendering->updateWaterRippleEmitterPtr (old, cur); +} + } diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index 073c5d51f..bba2d945c 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -1,52 +1,55 @@ #ifndef _GAME_RENDER_ACTORS_H #define _GAME_RENDER_ACTORS_H -#include "npcanimation.hpp" -#include "creatureanimation.hpp" +#include namespace MWWorld { class Ptr; class CellStore; + class InventoryStore; } -namespace MWRender{ - class Actors{ - OEngine::Render::OgreRenderer &mRend; - std::map mCellSceneNodes; - Ogre::SceneNode* mMwRoot; - std::map mAllActors; +namespace MWRender +{ + class Animation; + class RenderingManager; + class Actors + { + typedef std::map CellSceneNodeMap; + typedef std::map PtrAnimationMap; + OEngine::Render::OgreRenderer &mRend; + MWRender::RenderingManager* mRendering; + Ogre::SceneNode* mRootNode; - public: - Actors(OEngine::Render::OgreRenderer& _rend): mRend(_rend) {} + CellSceneNodeMap mCellSceneNodes; + PtrAnimationMap mAllActors; + + public: + Actors(OEngine::Render::OgreRenderer& _rend, MWRender::RenderingManager* rendering) + : mRend(_rend) + , mRendering(rendering) + {} ~Actors(); - void setMwRoot(Ogre::SceneNode* root); - void insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_); - void insertCreature (const MWWorld::Ptr& ptr); + + void setRootNode(Ogre::SceneNode* root); + void insertBegin (const MWWorld::Ptr& ptr); void insertNPC(const MWWorld::Ptr& ptr, MWWorld::InventoryStore& inv); + void insertCreature (const MWWorld::Ptr& ptr); + void insertActivator (const MWWorld::Ptr& ptr); bool deleteObject (const MWWorld::Ptr& ptr); ///< \return found? void removeCell(MWWorld::CellStore* store); - void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, - int number = 1); - ///< Run animation for a MW-reference. Calls to this function for references that are currently not - /// in the rendered scene should be ignored. - /// - /// \param mode: 0 normal, 1 immediate start, 2 immediate loop - /// \param number How offen the animation should be run - - void skipAnimation (const MWWorld::Ptr& ptr); - ///< Skip the animation for the given MW-reference for one frame. Calls to this function for - /// references that are currently not in the rendered scene should be ignored. - void update (float duration); /// Updates containing cell for object rendering data - void updateObjectCell(const MWWorld::Ptr &ptr); + void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur); + + Animation* getAnimation(const MWWorld::Ptr &ptr); }; } #endif diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 2a3b8cf43..853ffc375 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1,148 +1,739 @@ #include "animation.hpp" -#include +#include #include #include +#include +#include #include #include #include +#include "../mwbase/environment.hpp" +#include "../mwbase/soundmanager.hpp" +#include "../mwbase/world.hpp" + +#include "../mwmechanics/character.hpp" + namespace MWRender { -Animation::Animation() - : mInsert(NULL) - , mTime(0.0f) - , mSkipFrame(false) +Ogre::Real Animation::AnimationValue::getValue() const +{ + AnimStateMap::const_iterator iter = mAnimation->mStates.find(mAnimationName); + if(iter != mAnimation->mStates.end()) + return iter->second.mTime; + return 0.0f; +} + +void Animation::AnimationValue::setValue(Ogre::Real) +{ +} + + +void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects) +{ + 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.mParticles.clear(); + objects.mEntities.clear(); + objects.mSkelBase = NULL; +} + +Animation::Animation(const MWWorld::Ptr &ptr) + : mPtr(ptr) + , mInsert(NULL) + , mSkelBase(NULL) + , mAccumRoot(NULL) + , mNonAccumRoot(NULL) + , mNonAccumCtrl(NULL) + , mAccumulate(0.0f) + , mAnimVelocity(0.0f) + , mAnimSpeedMult(1.0f) { + for(size_t i = 0;i < sNumGroups;i++) + mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this)); } Animation::~Animation() { - Ogre::SceneManager *sceneMgr = mInsert->getCreator(); - for(size_t i = 0;i < mEntityList.mEntities.size();i++) - sceneMgr->destroyEntity(mEntityList.mEntities[i]); - mEntityList.mEntities.clear(); + if(mInsert) + { + mAnimSources.clear(); + + Ogre::SceneManager *sceneMgr = mInsert->getCreator(); + destroyObjectList(sceneMgr, mObjectRoot); + } } -struct checklow { - bool operator()(const char &a, const char &b) const +void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, bool baseonly) +{ + OgreAssert(!mInsert, "Object already has a root!"); + mInsert = node->createChildSceneNode(); + + std::string mdlname = Misc::StringUtils::lowerCase(model); + std::string::size_type p = mdlname.rfind('\\'); + if(p == std::string::npos) + p = mdlname.rfind('/'); + if(p != std::string::npos) + mdlname.insert(mdlname.begin()+p+1, 'x'); + else + mdlname.insert(mdlname.begin(), 'x'); + if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(mdlname)) { - return ::tolower(a) == ::tolower(b); + mdlname = model; + Misc::StringUtils::toLower(mdlname); } -}; -bool Animation::findGroupTimes(const std::string &groupname, Animation::GroupTimes *times) + mObjectRoot = (!baseonly ? NifOgre::Loader::createObjects(mInsert, mdlname) : + NifOgre::Loader::createObjectBase(mInsert, mdlname)); + if(mObjectRoot.mSkelBase) + { + mSkelBase = mObjectRoot.mSkelBase; + + Ogre::AnimationStateSet *aset = mObjectRoot.mSkelBase->getAllAnimationStates(); + Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); + while(asiter.hasMoreElements()) + { + Ogre::AnimationState *state = asiter.getNext(); + state->setEnabled(false); + state->setLoop(false); + } + + // Set the bones as manually controlled since we're applying the + // transformations manually + Ogre::SkeletonInstance *skelinst = mObjectRoot.mSkelBase->getSkeleton(); + Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); + while(boneiter.hasMoreElements()) + boneiter.getNext()->setManuallyControlled(true); + } + for(size_t i = 0;i < mObjectRoot.mControllers.size();i++) + { + if(mObjectRoot.mControllers[i].getSource().isNull()) + mObjectRoot.mControllers[i].setSource(mAnimationValuePtr[0]); + } +} + +void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue) { - const std::string &start = groupname+": start"; - const std::string &startloop = groupname+": loop start"; - const std::string &stop = groupname+": stop"; - const std::string &stoploop = groupname+": loop stop"; + for(size_t i = 0;i < objlist.mEntities.size();i++) + { + Ogre::Entity *ent = objlist.mEntities[i]; + if(visflags != 0) + ent->setVisibilityFlags(visflags); - NifOgre::TextKeyMap::const_iterator iter; - for(iter = mTextKeys.begin();iter != mTextKeys.end();iter++) + for(unsigned int j = 0;j < ent->getNumSubEntities();++j) + { + Ogre::SubEntity* subEnt = ent->getSubEntity(j); + subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? transqueue : solidqueue); + } + } + for(size_t i = 0;i < objlist.mParticles.size();i++) + { + Ogre::ParticleSystem *part = objlist.mParticles[i]; + if(visflags != 0) + part->setVisibilityFlags(visflags); + // TODO: Check particle material for actual transparency + part->setRenderQueueGroup(transqueue); + } +} + + +size_t Animation::detectAnimGroup(const Ogre::Node *node) +{ + static const char sGroupRoots[sNumGroups][32] = { + "", /* Lower body / character root */ + "Bip01 Spine1", /* Torso */ + "Bip01 L Clavicle", /* Left arm */ + "Bip01 R Clavicle", /* Right arm */ + }; + + while(node) + { + const Ogre::String &name = node->getName(); + for(size_t i = 1;i < sNumGroups;i++) + { + if(name == sGroupRoots[i]) + return i; + } + + node = node->getParent(); + } + + return 0; +} + + +void Animation::addAnimSource(const std::string &model) +{ + OgreAssert(mInsert, "Object is missing a root!"); + if(!mSkelBase) + return; + + std::string kfname = Misc::StringUtils::lowerCase(model); + std::string::size_type p = kfname.rfind('\\'); + if(p == std::string::npos) + p = kfname.rfind('/'); + if(p != std::string::npos) + kfname.insert(kfname.begin()+p+1, 'x'); + else + kfname.insert(kfname.begin(), 'x'); + + if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) + kfname.replace(kfname.size()-4, 4, ".kf"); + + if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(kfname)) + return; + + std::vector > ctrls; + Ogre::SharedPtr animsrc(OGRE_NEW AnimSource); + NifOgre::Loader::createKfControllers(mSkelBase, kfname, animsrc->mTextKeys, ctrls); + if(animsrc->mTextKeys.size() == 0 || ctrls.size() == 0) + return; + + mAnimSources.push_back(animsrc); + + std::vector > *grpctrls = animsrc->mControllers; + for(size_t i = 0;i < ctrls.size();i++) { - if(times->mStart >= 0.0f && times->mLoopStart >= 0.0f && times->mLoopStop >= 0.0f && times->mStop >= 0.0f) + NifOgre::NodeTargetValue *dstval; + dstval = static_cast*>(ctrls[i].getDestination().getPointer()); + + size_t grp = detectAnimGroup(dstval->getNode()); + + if(!mAccumRoot && grp == 0) + { + mAccumRoot = mInsert; + mNonAccumRoot = dstval->getNode(); + } + + ctrls[i].setSource(mAnimationValuePtr[grp]); + grpctrls[grp].push_back(ctrls[i]); + } +} + +void Animation::clearAnimSources() +{ + mStates.clear(); + + for(size_t i = 0;i < sNumGroups;i++) + mAnimationValuePtr[i]->setAnimName(std::string()); + + mNonAccumCtrl = NULL; + mAnimVelocity = 0.0f; + + mAccumRoot = NULL; + mNonAccumRoot = NULL; + + mAnimSources.clear(); +} + + +Ogre::Node *Animation::getNode(const std::string &name) +{ + if(mSkelBase) + { + Ogre::SkeletonInstance *skel = mSkelBase->getSkeleton(); + if(skel->hasBone(name)) + return skel->getBone(name); + } + return NULL; +} + + +NifOgre::TextKeyMap::const_iterator Animation::findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname) +{ + NifOgre::TextKeyMap::const_iterator iter(keys.begin()); + for(;iter != keys.end();iter++) + { + if(iter->second.compare(0, groupname.size(), groupname) == 0 && + iter->second.compare(groupname.size(), 2, ": ") == 0) + break; + } + return iter; +} + + +bool Animation::hasAnimation(const std::string &anim) +{ + AnimSourceList::const_iterator iter(mAnimSources.begin()); + for(;iter != mAnimSources.end();iter++) + { + const NifOgre::TextKeyMap &keys = (*iter)->mTextKeys; + if(findGroupStart(keys, anim) != keys.end()) return true; + } + + return false; +} + + +void Animation::setAccumulation(const Ogre::Vector3 &accum) +{ + mAccumulate = accum; +} + +void Animation::setSpeed(float speed) +{ + mAnimSpeedMult = 1.0f; + if(speed > 0.0f && mAnimVelocity > 1.0f) + mAnimSpeedMult = speed / mAnimVelocity; +} - std::string::const_iterator strpos = iter->second.begin(); - std::string::const_iterator strend = iter->second.end(); - size_t strlen = strend-strpos; - if(start.size() <= strlen && std::mismatch(strpos, strend, start.begin(), checklow()).first == strend) +void Animation::updatePtr(const MWWorld::Ptr &ptr) +{ + mPtr = ptr; +} + + +float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const Ogre::Vector3 &accum, const std::string &groupname) +{ + const std::string start = groupname+": start"; + const std::string loopstart = groupname+": loop start"; + const std::string loopstop = groupname+": loop stop"; + const std::string stop = groupname+": stop"; + float starttime = std::numeric_limits::max(); + float stoptime = 0.0f; + NifOgre::TextKeyMap::const_iterator keyiter(keys.begin()); + while(keyiter != keys.end()) + { + if(keyiter->second == start || keyiter->second == loopstart) + starttime = keyiter->first; + else if(keyiter->second == loopstop || keyiter->second == stop) { - times->mStart = iter->first; - times->mLoopStart = iter->first; + stoptime = keyiter->first; + break; } - else if(startloop.size() <= strlen && std::mismatch(strpos, strend, startloop.begin(), checklow()).first == strend) + keyiter++; + } + + if(stoptime > starttime) + { + Ogre::Vector3 startpos = nonaccumctrl->getTranslation(starttime) * accum; + Ogre::Vector3 endpos = nonaccumctrl->getTranslation(stoptime) * accum; + + return startpos.distance(endpos) / (stoptime - starttime); + } + + return 0.0f; +} + +static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone) +{ + if(skelsrc->hasBone(bone->getName())) + { + Ogre::Bone *srcbone = skelsrc->getBone(bone->getName()); + if(!srcbone->getParent() || !bone->getParent()) { - times->mLoopStart = iter->first; + bone->setOrientation(srcbone->getOrientation()); + bone->setPosition(srcbone->getPosition()); + bone->setScale(srcbone->getScale()); } - else if(stoploop.size() <= strlen && std::mismatch(strpos, strend, stoploop.begin(), checklow()).first == strend) + else { - times->mLoopStop = iter->first; + bone->_setDerivedOrientation(srcbone->_getDerivedOrientation()); + bone->_setDerivedPosition(srcbone->_getDerivedPosition()); + bone->setScale(Ogre::Vector3::UNIT_SCALE); } - else if(stop.size() <= strlen && std::mismatch(strpos, strend, stop.begin(), checklow()).first == strend) + } + 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()) + updateBoneTree(skelsrc, static_cast(boneiter.getNext())); +} + +void Animation::updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Ogre::SkeletonInstance *skel) +{ + Ogre::Skeleton::BoneIterator boneiter = skel->getRootBoneIterator(); + while(boneiter.hasMoreElements()) + updateBoneTree(skelsrc, boneiter.getNext()); +} + + +void Animation::updatePosition(float oldtime, float newtime, Ogre::Vector3 &position) +{ + /* Get the non-accumulation root's difference from the last update, and move the position + * accordingly. + */ + Ogre::Vector3 off = mNonAccumCtrl->getTranslation(newtime)*mAccumulate; + position += off - mNonAccumCtrl->getTranslation(oldtime)*mAccumulate; + + /* Translate the accumulation root back to compensate for the move. */ + mAccumRoot->setPosition(-off); +} + +bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint) +{ + std::string tag = groupname+": "+start; + NifOgre::TextKeyMap::const_iterator startkey(keys.begin()); + while(startkey != keys.end() && startkey->second != tag) + startkey++; + if(startkey == keys.end() && start == "loop start") + { + tag = groupname+": start"; + startkey = keys.begin(); + while(startkey != keys.end() && startkey->second != tag) + startkey++; + } + if(startkey == keys.end()) + return false; + + tag = groupname+": "+stop; + NifOgre::TextKeyMap::const_iterator stopkey(startkey); + while(stopkey != keys.end() && stopkey->second != tag) + stopkey++; + if(stopkey == keys.end()) + return false; + + if(startkey == stopkey) + return false; + + state.mStartKey = startkey; + state.mLoopStartKey = startkey; + state.mStopKey = stopkey; + state.mNextKey = startkey; + + state.mTime = state.mStartKey->first + ((state.mStopKey->first - state.mStartKey->first) * startpoint); + + tag = groupname+": loop start"; + while(state.mNextKey->first <= state.mTime && state.mNextKey != state.mStopKey) + { + if(state.mNextKey->second == tag) + state.mLoopStartKey = state.mNextKey; + state.mNextKey++; + } + + return true; +} + +bool Animation::doLoop(AnimState &state) +{ + if(state.mLoopCount == 0) + return false; + state.mLoopCount--; + + state.mTime = state.mLoopStartKey->first; + state.mNextKey = state.mLoopStartKey; + state.mNextKey++; + state.mPlaying = true; + + return true; +} + + +bool Animation::handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key) +{ + float time = key->first; + const std::string &evt = key->second; + + if(evt.compare(0, 7, "sound: ") == 0) + { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f); + return true; + } + if(evt.compare(0, 10, "soundgen: ") == 0) + { + // FIXME: Lookup the SoundGen (SNDG) for the specified sound that corresponds + // to this actor type + return true; + } + + if(evt.compare(0, groupname.size(), groupname) != 0 || + evt.compare(groupname.size(), 2, ": ") != 0) + { + // Not ours, skip it + return true; + } + size_t off = groupname.size()+2; + size_t len = evt.size() - off; + + if(evt.compare(off, len, "start") == 0 || evt.compare(off, len, "loop start") == 0) + { + state.mLoopStartKey = key; + return true; + } + + if(evt.compare(off, len, "loop stop") == 0 || evt.compare(off, len, "stop") == 0) + { + if(doLoop(state)) { - times->mStop = iter->first; - if(times->mLoopStop < 0.0f) - times->mLoopStop = iter->first; - break; + if(state.mTime >= time) + return false; } + return true; + } + + if(evt.compare(off, len, "equip attach") == 0) + { + showWeapons(true); + return true; + } + if(evt.compare(off, len, "unequip detach") == 0) + { + showWeapons(false); + return true; } - return (times->mStart >= 0.0f && times->mLoopStart >= 0.0f && times->mLoopStop >= 0.0f && times->mStop >= 0.0f); + /* Nothing to do for these */ + if(evt.compare(off, len, "equip start") == 0 || evt.compare(off, len, "equip stop") == 0 || + evt.compare(off, len, "unequip start") == 0 || evt.compare(off, len, "unequip stop") == 0) + return true; + + std::cerr<< "Unhandled animation textkey: "<first; + priority = std::max(0, priority); + + AnimStateMap::iterator stateiter = mStates.begin(); + while(stateiter != mStates.end()) + { + if(stateiter->second.mPriority == priority) + mStates.erase(stateiter++); + else + stateiter++; } - else if(!findGroupTimes(groupname, ×)) - throw std::runtime_error("Failed to find animation group "+groupname); - if(mode == 0 && mCurGroup.mLoops > 0) - mNextGroup = times; - else + stateiter = mStates.find(groupname); + if(stateiter != mStates.end()) { - mCurGroup = times; - mNextGroup = GroupTimes(); - mTime = ((mode==2) ? mCurGroup.mLoopStart : mCurGroup.mStart); + stateiter->second.mPriority = priority; + resetActiveGroups(); + return; } + + /* Look in reverse; last-inserted source has priority. */ + AnimSourceList::reverse_iterator iter(mAnimSources.rbegin()); + for(;iter != mAnimSources.rend();iter++) + { + AnimState state; + if(reset(state, (*iter)->mTextKeys, groupname, start, stop, startpoint)) + { + state.mSource = *iter; + state.mLoopCount = loops; + state.mPlaying = true; + state.mPriority = priority; + state.mGroups = groups; + state.mAutoDisable = autodisable; + mStates[groupname] = state; + + break; + } + } + if(iter == mAnimSources.rend()) + std::cerr<< "Failed to find animation "<second.mPlaying; + return false; } -void Animation::runAnimation(float timepassed) +void Animation::resetActiveGroups() { - if(mCurGroup.mLoops > 0 && !mSkipFrame) + for(size_t grp = 0;grp < sNumGroups;grp++) { - mTime += timepassed; - if(mTime >= mCurGroup.mLoopStop) + AnimStateMap::const_iterator active = mStates.end(); + + AnimStateMap::const_iterator state = mStates.begin(); + for(;state != mStates.end();state++) { - if(mCurGroup.mLoops > 1) - { - mCurGroup.mLoops--; - mTime = mTime - mCurGroup.mLoopStop + mCurGroup.mLoopStart; - } - else if(mTime >= mCurGroup.mStop) + if(!(state->second.mGroups&(1<second.mPriority < state->second.mPriority) + active = state; + } + + mAnimationValuePtr[grp]->setAnimName((active == mStates.end()) ? + std::string() : active->first); + } + + mNonAccumCtrl = NULL; + mAnimVelocity = 0.0f; + + if(!mNonAccumRoot || mAccumulate == Ogre::Vector3(0.0f)) + return; + + AnimStateMap::const_iterator state = mStates.find(mAnimationValuePtr[0]->getAnimName()); + if(state == mStates.end()) + return; + + const Ogre::SharedPtr &animsrc = state->second.mSource; + const NifOgre::TextKeyMap &keys = animsrc->mTextKeys; + const std::vector >&ctrls = animsrc->mControllers[0]; + for(size_t i = 0;i < ctrls.size();i++) + { + NifOgre::NodeTargetValue *dstval; + dstval = static_cast*>(ctrls[i].getDestination().getPointer()); + if(dstval->getNode() == mNonAccumRoot) + { + mAnimVelocity = calcAnimVelocity(keys, dstval, mAccumulate, state->first); + mNonAccumCtrl = dstval; + break; + } + } + + // If there's no velocity, keep looking + if(!(mAnimVelocity > 1.0f)) + { + AnimSourceList::const_reverse_iterator animiter = mAnimSources.rbegin(); + while(*animiter != animsrc) + ++animiter; + + while(!(mAnimVelocity > 1.0f) && ++animiter != mAnimSources.rend()) + { + const NifOgre::TextKeyMap &keys = (*animiter)->mTextKeys; + const std::vector >&ctrls = (*animiter)->mControllers[0]; + for(size_t i = 0;i < ctrls.size();i++) { - if(mNextGroup.mLoops > 0) - mTime = mTime - mCurGroup.mStop + mNextGroup.mStart; - else - mTime = mCurGroup.mStop; - mCurGroup = mNextGroup; - mNextGroup = GroupTimes(); + NifOgre::NodeTargetValue *dstval; + dstval = static_cast*>(ctrls[i].getDestination().getPointer()); + if(dstval->getNode() == mNonAccumRoot) + { + mAnimVelocity = calcAnimVelocity(keys, dstval, mAccumulate, state->first); + break; + } } } + } +} + + +bool Animation::getInfo(const std::string &groupname, float *complete, std::string *start, std::string *stop) const +{ + AnimStateMap::const_iterator iter = mStates.find(groupname); + if(iter == mStates.end()) + { + if(complete) *complete = 0.0f; + if(start) *start = ""; + if(stop) *stop = ""; + return false; + } + + if(complete) *complete = (iter->second.mTime - iter->second.mStartKey->first) / + (iter->second.mStopKey->first - iter->second.mStartKey->first); + if(start) *start = iter->second.mStartKey->second.substr(groupname.size()+2); + if(stop) *stop = iter->second.mStopKey->second.substr(groupname.size()+2); + return true; +} + - if(mEntityList.mSkelBase) +void Animation::disable(const std::string &groupname) +{ + AnimStateMap::iterator iter = mStates.find(groupname); + if(iter != mStates.end()) + mStates.erase(iter); + resetActiveGroups(); +} + + +Ogre::Vector3 Animation::runAnimation(float duration) +{ + Ogre::Vector3 movement(0.0f); + + duration *= mAnimSpeedMult; + AnimStateMap::iterator stateiter = mStates.begin(); + while(stateiter != mStates.end()) + { + AnimState &state = stateiter->second; + float timepassed = duration; + while(state.mPlaying) { - Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); - Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); - while(as.hasMoreElements()) + float targetTime = state.mTime + timepassed; + if(state.mNextKey->first > targetTime) { - Ogre::AnimationState *state = as.getNext(); - state->setTimePosition(mTime); + if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) + updatePosition(state.mTime, targetTime, movement); + state.mTime = targetTime; + break; } + + NifOgre::TextKeyMap::const_iterator key(state.mNextKey++); + if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) + updatePosition(state.mTime, key->first, movement); + state.mTime = key->first; + + state.mPlaying = (key != state.mStopKey); + timepassed = targetTime - state.mTime; + + if(!handleTextKey(state, stateiter->first, key)) + break; + } + + if(!state.mPlaying && state.mAutoDisable) + { + mStates.erase(stateiter++); + resetActiveGroups(); + } + else + stateiter++; + } + + 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++) + { + const std::string &name = mAnimationValuePtr[grp]->getAnimName(); + if(!name.empty() && (stateiter=mStates.find(name)) != mStates.end()) + { + const Ogre::SharedPtr &src = stateiter->second.mSource; + for(size_t i = 0;i < src->mControllers[grp].size();i++) + src->mControllers[grp][i].update(); } } - mSkipFrame = false; + + if(mSkelBase) + { + // HACK: Dirty the animation state set so that Ogre will apply the + // transformations to entities this skeleton instance is shared with. + mSkelBase->getAllAnimationStates()->_notifyDirty(); + } + + return movement; +} + +void Animation::showWeapons(bool showWeapon) +{ +} + +bool Animation::isPriorityActive(int priority) const +{ + for (AnimStateMap::const_iterator it = mStates.begin(); it != mStates.end(); ++it) + if (it->second.mPriority == priority) + return true; + return false; } } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 07583db78..31be0fb2a 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -1,55 +1,205 @@ #ifndef _GAME_RENDER_ANIMATION_H #define _GAME_RENDER_ANIMATION_H -#include +#include +#include -#include -#include -#include "../mwworld/actiontalk.hpp" -#include -#include +#include +#include "../mwworld/ptr.hpp" +namespace MWRender +{ -namespace MWRender { +class Animation +{ +public: + enum Group { + Group_LowerBody = 1<<0, -class Animation { - struct GroupTimes { - float mStart; - float mStop; - float mLoopStart; - float mLoopStop; + Group_Torso = 1<<1, + Group_LeftArm = 1<<2, + Group_RightArm = 1<<3, - size_t mLoops; + Group_UpperBody = Group_Torso | Group_LeftArm | Group_RightArm, - GroupTimes() - : mStart(-1.0f), mStop(-1.0f), mLoopStart(-1.0f), mLoopStop(-1.0f), - mLoops(0) - { } + Group_All = Group_LowerBody | Group_UpperBody }; protected: - Ogre::SceneNode* mInsert; + /* This is the number of *discrete* groups. */ + static const size_t sNumGroups = 4; + + class AnimationValue : public Ogre::ControllerValue + { + private: + Animation *mAnimation; + std::string mAnimationName; + + public: + AnimationValue(Animation *anim) + : mAnimation(anim) + { } + + void setAnimName(const std::string &name) + { mAnimationName = name; } + const std::string &getAnimName() const + { return mAnimationName; } + + virtual Ogre::Real getValue() const; + virtual void setValue(Ogre::Real value); + }; + + struct AnimSource : public Ogre::AnimationAlloc { + NifOgre::TextKeyMap mTextKeys; + std::vector > mControllers[sNumGroups]; + }; + typedef std::vector< Ogre::SharedPtr > AnimSourceList; + + struct AnimState { + Ogre::SharedPtr mSource; + NifOgre::TextKeyMap::const_iterator mStartKey; + NifOgre::TextKeyMap::const_iterator mLoopStartKey; + NifOgre::TextKeyMap::const_iterator mStopKey; + NifOgre::TextKeyMap::const_iterator mNextKey; + + float mTime; + + bool mPlaying; + size_t mLoopCount; + + int mPriority; + int mGroups; + bool mAutoDisable; + + AnimState() : mTime(0.0f), mPlaying(false), mLoopCount(0), + mPriority(0), mGroups(0), mAutoDisable(true) + { } + }; + typedef std::map AnimStateMap; + + MWWorld::Ptr mPtr; + + Ogre::SceneNode *mInsert; + Ogre::Entity *mSkelBase; + NifOgre::ObjectList mObjectRoot; + AnimSourceList mAnimSources; + Ogre::Node *mAccumRoot; + Ogre::Node *mNonAccumRoot; + NifOgre::NodeTargetValue *mNonAccumCtrl; + Ogre::Vector3 mAccumulate; + + AnimStateMap mStates; + + Ogre::SharedPtr mAnimationValuePtr[sNumGroups]; + + float mAnimVelocity; + float mAnimSpeedMult; + + /* Sets the appropriate animations on the bone groups based on priority. + */ + void resetActiveGroups(); + + static size_t detectAnimGroup(const Ogre::Node *node); + + static float calcAnimVelocity(const NifOgre::TextKeyMap &keys, + NifOgre::NodeTargetValue *nonaccumctrl, + const Ogre::Vector3 &accum, + const std::string &groupname); + + /* Updates a skeleton instance so that all bones matching the source skeleton (based on + * bone names) are positioned identically. */ + void updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Ogre::SkeletonInstance *skel); + + /* Updates the position of the accum root node for the given time, and + * returns the wanted movement vector from the previous time. */ + void updatePosition(float oldtime, float newtime, Ogre::Vector3 &position); + + static NifOgre::TextKeyMap::const_iterator findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname); + + /* Resets the animation to the time of the specified start marker, without + * moving anything, and set the end time to the specified stop marker. If + * the marker is not found, or if the markers are the same, it returns + * false. + */ + bool reset(AnimState &state, const NifOgre::TextKeyMap &keys, + const std::string &groupname, const std::string &start, const std::string &stop, + float startpoint); + + bool doLoop(AnimState &state); + + bool handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key); - float mTime; - GroupTimes mCurGroup; - GroupTimes mNextGroup; + void setObjectRoot(Ogre::SceneNode *node, const std::string &model, bool baseonly); + void addAnimSource(const std::string &model); - bool mSkipFrame; + static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects); - NifOgre::EntityList mEntityList; - NifOgre::TextKeyMap mTextKeys; + static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue); - bool findGroupTimes(const std::string &groupname, GroupTimes *times); + void clearAnimSources(); public: - Animation(); + Animation(const MWWorld::Ptr &ptr); virtual ~Animation(); - void playGroup(std::string groupname, int mode, int loops); - void skipAnim(); - virtual void runAnimation(float timepassed); + void updatePtr(const MWWorld::Ptr &ptr); + + bool hasAnimation(const std::string &anim); + + bool isPriorityActive (int priority) const; + ///< Is there an animation playing with the given priority? + + // Specifies the axis' to accumulate on. Non-accumulated axis will just + // move visually, but not affect the actual movement. Each x/y/z value + // should be on the scale of 0 to 1. + void setAccumulation(const Ogre::Vector3 &accum); + + void setSpeed(float speed); + + /** Plays an animation. + * \param groupname Name of the animation group to play. + * \param priority Priority of the animation. The animation will play on + * bone groups that don't have another animation set of a + * higher priority. + * \param groups Bone groups to play the animation on. + * \param autodisable Automatically disable the animation when it stops + * playing. + * \param start Key marker from which to start. + * \param stop Key marker to stop at. + * \param startpoint How far in between the two markers to start. 0 starts + * at the start marker, 1 starts at the stop marker. + * \param loops How many times to loop the animation. This will use the + * "loop start" and "loop stop" markers if they exist, + * otherwise it will use "start" and "stop". + */ + void play(const std::string &groupname, int priority, int groups, bool autodisable, + const std::string &start, const std::string &stop, + float startpoint, size_t loops); + + /** Returns true if the named animation group is playing. */ + bool isPlaying(const std::string &groupname) 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. + * \param start Stores the start key + * \param stop Stores the stop key + * \return True if the animation is active, false otherwise. + */ + bool getInfo(const std::string &groupname, float *complete=NULL, std::string *start=NULL, std::string *stop=NULL) const; + + /** Disables the specified animation group; + * \param groupname Animation group to disable. + */ + void disable(const std::string &groupname); + + virtual Ogre::Vector3 runAnimation(float duration); + + virtual void showWeapons(bool showWeapon); + + Ogre::Node *getNode(const std::string &name); }; } diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/camera.cpp similarity index 53% rename from apps/openmw/mwrender/player.cpp rename to apps/openmw/mwrender/camera.cpp index 0e4c76b0e..e71e694f9 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -1,7 +1,8 @@ -#include "player.hpp" +#include "camera.hpp" #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -14,10 +15,9 @@ namespace MWRender { - Player::Player (Ogre::Camera *camera, Ogre::SceneNode* node) + Camera::Camera (Ogre::Camera *camera) : mCamera(camera), - mPlayerNode(node), - mCameraNode(mPlayerNode->createChildSceneNode()), + mCameraNode(NULL), mFirstPersonView(true), mPreviewMode(false), mFreeLook(true), @@ -28,51 +28,24 @@ namespace MWRender { mVanity.enabled = false; mVanity.allowed = true; - mVanity.forced = false; - - mCameraNode->attachObject(mCamera); - mCameraNode->setPosition(0.f, 0.f, mHeight); mPreviewCam.yaw = 0.f; mPreviewCam.offset = 400.f; } - Player::~Player() + Camera::~Camera() { - delete mAnimation; } - - bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) - { - if (mVanity.enabled) { - toggleVanityMode(false); - } - - Ogre::Vector3 trueRot = rot; - - /// \note rotate player on forced vanity - if (mVanity.forced) { - if (mFreeLook) { - float diff = (adjust) ? rot.z : mMainCam.yaw - rot.z; - mVanity.enabled = false; - rotateCamera(rot, adjust); - mVanity.enabled = true; - - compensateYaw(diff); - } - trueRot.z = 0.f; - } - - if (mFreeLook || mVanity.enabled || mPreviewMode) { - rotateCamera(trueRot, adjust); - } - - /// \note if vanity mode is forced by TVM then rotate player - return (!mVanity.enabled && !mPreviewMode) || mVanity.forced; + void Camera::reset() + { + togglePreviewMode(false); + toggleVanityMode(false); + if (!mFirstPersonView) + toggleViewMode(); } - void Player::rotateCamera(const Ogre::Vector3 &rot, bool adjust) + void Camera::rotateCamera(const Ogre::Vector3 &rot, bool adjust) { if (adjust) { setYaw(getYaw() + rot.z); @@ -81,75 +54,66 @@ namespace MWRender setYaw(rot.z); setPitch(rot.x); } - Ogre::Quaternion xr( - Ogre::Radian(getPitch() + Ogre::Math::HALF_PI), - Ogre::Vector3::UNIT_X - ); - Ogre::Quaternion zr( - Ogre::Radian(getYaw()), - Ogre::Vector3::NEGATIVE_UNIT_Z - ); + + Ogre::Quaternion xr(Ogre::Radian(getPitch() + Ogre::Math::HALF_PI), Ogre::Vector3::UNIT_X); if (!mVanity.enabled && !mPreviewMode) { - mPlayerNode->setOrientation(zr); mCameraNode->setOrientation(xr); } else { + Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::NEGATIVE_UNIT_Z); mCameraNode->setOrientation(zr * xr); } } - std::string Player::getHandle() const + const std::string &Camera::getHandle() const { - return mPlayerNode->getName(); + return mTrackingPtr.getRefData().getHandle(); } - void Player::attachTo(const MWWorld::Ptr &ptr) + void Camera::attachTo(const MWWorld::Ptr &ptr) { - ptr.getRefData().setBaseNode(mPlayerNode); + mTrackingPtr = ptr; + Ogre::SceneNode *node = mTrackingPtr.getRefData().getBaseNode()->createChildSceneNode(Ogre::Vector3(0.0f, 0.0f, mHeight)); + if(mCameraNode) + { + node->setOrientation(mCameraNode->getOrientation()); + node->setPosition(mCameraNode->getPosition()); + node->setScale(mCameraNode->getScale()); + mCameraNode->getCreator()->destroySceneNode(mCameraNode); + } + mCameraNode = node; + mCameraNode->attachObject(mCamera); } - void Player::updateListener() + void Camera::updateListener() { Ogre::Vector3 pos = mCamera->getRealPosition(); Ogre::Vector3 dir = mCamera->getRealDirection(); Ogre::Vector3 up = mCamera->getRealUp(); - Ogre::Real xch; - xch = pos.y, pos.y = -pos.z, pos.z = xch; - xch = dir.y, dir.y = -dir.z, dir.z = xch; - xch = up.y, up.y = -up.z, up.z = xch; - MWBase::Environment::get().getSoundManager()->setListenerPosDir(pos, dir, up); } - void Player::update(float duration) + void Camera::update(float duration) { updateListener(); // only show the crosshair in game mode and in first person mode. - MWBase::Environment::get().getWindowManager ()->showCrosshair - (!MWBase::Environment::get().getWindowManager ()->isGuiMode () && (mFirstPersonView && !mVanity.enabled && !mPreviewMode)); - - if (mAnimation) { - mAnimation->runAnimation(duration); - } - mPlayerNode->setVisible( - mVanity.enabled || mPreviewMode || !mFirstPersonView, - false - ); + MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager(); + wm->showCrosshair(!wm->isGuiMode() && (mFirstPersonView && !mVanity.enabled && !mPreviewMode)); - if (mFirstPersonView && !mVanity.enabled) { - return; - } - if (mVanity.enabled) { + if(mVanity.enabled) + { Ogre::Vector3 rot(0.f, 0.f, 0.f); rot.z = Ogre::Degree(3.f * duration).valueRadians(); rotateCamera(rot, true); } } - void Player::toggleViewMode() + void Camera::toggleViewMode() { mFirstPersonView = !mFirstPersonView; + mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : + NpcAnimation::VM_Normal); if (mFirstPersonView) { mCamera->setPosition(0.f, 0.f, 0.f); setLowHeight(false); @@ -159,25 +123,24 @@ namespace MWRender } } - void Player::allowVanityMode(bool allow) + void Camera::allowVanityMode(bool allow) { - if (!allow && mVanity.enabled && !mVanity.forced) { + if (!allow && mVanity.enabled) toggleVanityMode(false); - } mVanity.allowed = allow; } - bool Player::toggleVanityMode(bool enable, bool force) + bool Camera::toggleVanityMode(bool enable) { - if ((mVanity.forced && !force) || - (!mVanity.allowed && (force || enable))) - { + if(!mVanity.allowed && enable) return false; - } else if (mVanity.enabled == enable) { + + if(mVanity.enabled == enable) return true; - } mVanity.enabled = enable; - mVanity.forced = force && enable; + + mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : + NpcAnimation::VM_Normal); float offset = mPreviewCam.offset; Ogre::Vector3 rot(0.f, 0.f, 0.f); @@ -193,18 +156,22 @@ namespace MWRender setLowHeight(!mFirstPersonView); } rot.z = getYaw(); + mCamera->setPosition(0.f, 0.f, offset); rotateCamera(rot, false); return true; } - void Player::togglePreviewMode(bool enable) + void Camera::togglePreviewMode(bool enable) { - if (mPreviewMode == enable) { + if(mPreviewMode == enable) return; - } + mPreviewMode = enable; + mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : + NpcAnimation::VM_Normal); + float offset = mCamera->getPosition().z; if (mPreviewMode) { mMainCam.offset = offset; @@ -217,19 +184,19 @@ namespace MWRender setLowHeight(!mFirstPersonView); } + mCamera->setPosition(0.f, 0.f, offset); rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); } - float Player::getYaw() + float Camera::getYaw() { - if (mVanity.enabled || mPreviewMode) { + if(mVanity.enabled || mPreviewMode) return mPreviewCam.yaw; - } return mMainCam.yaw; } - void Player::setYaw(float angle) + void Camera::setYaw(float angle) { if (angle > Ogre::Math::PI) { angle -= Ogre::Math::TWO_PI; @@ -243,7 +210,7 @@ namespace MWRender } } - float Player::getPitch() + float Camera::getPitch() { if (mVanity.enabled || mPreviewMode) { return mPreviewCam.pitch; @@ -251,17 +218,18 @@ namespace MWRender return mMainCam.pitch; } - void Player::setPitch(float angle) + void Camera::setPitch(float angle) { - float limit = Ogre::Math::HALF_PI; - if (mVanity.forced || mPreviewMode) { - limit /= 2; - } - if (angle > limit) { - angle = limit - 0.01; - } else if (angle < -limit) { - angle = -limit + 0.01; - } + const float epsilon = 0.000001f; + float limit = Ogre::Math::HALF_PI - epsilon; + if(mPreviewMode) + limit /= 2; + + if(angle > limit) + angle = limit; + else if(angle < -limit) + angle = -limit; + if (mVanity.enabled || mPreviewMode) { mPreviewCam.pitch = angle; } else { @@ -269,11 +237,11 @@ namespace MWRender } } - void Player::setCameraDistance(float dist, bool adjust, bool override) + void Camera::setCameraDistance(float dist, bool adjust, bool override) { - if (mFirstPersonView && !mPreviewMode && !mVanity.enabled) { + if(mFirstPersonView && !mPreviewMode && !mVanity.enabled) return; - } + Ogre::Vector3 v(0.f, 0.f, dist); if (adjust) { v += mCamera->getPosition(); @@ -282,6 +250,8 @@ namespace MWRender v.z = 800.f; } else if (v.z < 10.f) { v.z = 10.f; + } else if (override && v.z < 50.f) { + v.z = 50.f; } mCamera->setPosition(v); @@ -296,7 +266,7 @@ namespace MWRender } } - void Player::setCameraDistance() + void Camera::setCameraDistance() { if (mDistanceAdjusted) { if (mVanity.enabled || mPreviewMode) { @@ -308,69 +278,54 @@ namespace MWRender mDistanceAdjusted = false; } - void Player::setAnimation(NpcAnimation *anim) + void Camera::setAnimation(NpcAnimation *anim) { - delete mAnimation; + // If we're switching to a new NpcAnimation, ensure the old one is + // using a normal view mode + if(mAnimation && mAnimation != anim) + mAnimation->setViewMode(NpcAnimation::VM_Normal); mAnimation = anim; - - mPlayerNode->setVisible( - mVanity.enabled || mPreviewMode || !mFirstPersonView, - false - ); + mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : + NpcAnimation::VM_Normal); } - void Player::setHeight(float height) + void Camera::setHeight(float height) { mHeight = height; mCameraNode->setPosition(0.f, 0.f, mHeight); } - float Player::getHeight() + float Camera::getHeight() { - return mHeight * mPlayerNode->getScale().z; + return mHeight * mTrackingPtr.getRefData().getBaseNode()->getScale().z; } - bool Player::getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera) + bool Camera::getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera) { - float xch; mCamera->getParentSceneNode ()->needUpdate(true); camera = mCamera->getRealPosition(); - xch = camera.z, camera.z = camera.y, camera.y = -xch; - player = mPlayerNode->getPosition(); + player = mTrackingPtr.getRefData().getBaseNode()->getPosition(); return mFirstPersonView && !mVanity.enabled && !mPreviewMode; } - Ogre::Vector3 Player::getPosition() + Ogre::Vector3 Camera::getPosition() { - return mPlayerNode->getPosition(); + return mTrackingPtr.getRefData().getBaseNode()->getPosition(); } - void Player::getSightAngles(float &pitch, float &yaw) + void Camera::getSightAngles(float &pitch, float &yaw) { pitch = mMainCam.pitch; yaw = mMainCam.yaw; } - void Player::compensateYaw(float diff) - { - mPreviewCam.yaw -= diff; - Ogre::Quaternion zr( - Ogre::Radian(mPreviewCam.yaw), - Ogre::Vector3::NEGATIVE_UNIT_Z - ); - Ogre::Quaternion xr( - Ogre::Radian(mPreviewCam.pitch), - Ogre::Vector3::UNIT_X); - mCameraNode->setOrientation(zr * xr); - } - - void Player::togglePlayerLooking(bool enable) + void Camera::togglePlayerLooking(bool enable) { mFreeLook = enable; } - void Player::setLowHeight(bool low) + void Camera::setLowHeight(bool low) { if (low) { mCameraNode->setPosition(0.f, 0.f, mHeight * 0.85); @@ -378,4 +333,9 @@ namespace MWRender mCameraNode->setPosition(0.f, 0.f, mHeight); } } + + bool Camera::isVanityOrPreviewModeEnabled() + { + return mPreviewMode || mVanity.enabled; + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/camera.hpp similarity index 70% rename from apps/openmw/mwrender/player.hpp rename to apps/openmw/mwrender/camera.hpp index 29a68ca69..ad5e35f93 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -1,8 +1,10 @@ -#ifndef GAME_MWRENDER_PLAYER_H -#define GAME_MWRENDER_PLAYER_H +#ifndef GAME_MWRENDER_CAMERA_H +#define GAME_MWRENDER_CAMERA_H #include +#include "../mwworld/ptr.hpp" + namespace Ogre { class Vector3; @@ -10,24 +12,20 @@ namespace Ogre class SceneNode; } -namespace MWWorld -{ - class Ptr; -} - namespace MWRender { class NpcAnimation; - /// \brief Player character rendering and camera control - class Player + + /// \brief Camera control + class Camera { struct CamData { float pitch, yaw, offset; }; - Ogre::Camera *mCamera; + MWWorld::Ptr mTrackingPtr; - Ogre::SceneNode *mPlayerNode; + Ogre::Camera *mCamera; Ogre::SceneNode *mCameraNode; NpcAnimation *mAnimation; @@ -37,7 +35,7 @@ namespace MWRender bool mFreeLook; struct { - bool enabled, allowed, forced; + bool enabled, allowed; } mVanity; float mHeight, mCameraDistance; @@ -51,15 +49,14 @@ namespace MWRender void setLowHeight(bool low = true); public: + Camera(Ogre::Camera *camera); + ~Camera(); - Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); - ~Player(); + /// Reset to defaults + void reset(); - /// Set where the player is looking at. Uses Morrowind (euler) angles + /// Set where the camera is looking at. Uses Morrowind (euler) angles /// \param rot Rotation angles in radians - /// \return true if player object needs to bo rotated physically - bool rotate(const Ogre::Vector3 &rot, bool adjust); - void rotateCamera(const Ogre::Vector3 &rot, bool adjust); float getYaw(); @@ -68,22 +65,21 @@ namespace MWRender float getPitch(); void setPitch(float angle); - void compensateYaw(float diff); - - std::string getHandle() const; + const std::string &getHandle() const; /// Attach camera to object - /// \note there is no protection from attaching the same camera to - /// several different objects void attachTo(const MWWorld::Ptr &); void toggleViewMode(); - bool toggleVanityMode(bool enable, bool force = false); + bool toggleVanityMode(bool enable); void allowVanityMode(bool allow); void togglePreviewMode(bool enable); + bool isFirstPerson() const + { return !(mVanity.enabled || mPreviewMode || !mFirstPersonView); } + void update(float duration); /// Set camera distance for current mode. Don't work on 1st person view. @@ -95,7 +91,7 @@ namespace MWRender /// Restore default camera distance for current mode. void setCameraDistance(); - void setAnimation(MWRender::NpcAnimation *anim); + void setAnimation(NpcAnimation *anim); void setHeight(float height); float getHeight(); @@ -108,6 +104,8 @@ namespace MWRender void getSightAngles(float &pitch, float &yaw); void togglePlayerLooking(bool enable); + + bool isVanityOrPreviewModeEnabled(); }; } diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index b034e098b..e4bba289f 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -11,6 +11,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/inventorystore.hpp" #include "renderconst.hpp" #include "npcanimation.hpp" @@ -20,13 +22,15 @@ namespace MWRender CharacterPreview::CharacterPreview(MWWorld::Ptr character, int sizeX, int sizeY, const std::string& name, Ogre::Vector3 position, Ogre::Vector3 lookAt) - : mSizeX(sizeX) - , mSizeY(sizeY) - , mName(name) + + : mSceneMgr (0) , mPosition(position) , mLookAt(lookAt) , mCharacter(character) , mAnimation(NULL) + , mName(name) + , mSizeX(sizeX) + , mSizeY(sizeY) { } @@ -39,6 +43,15 @@ namespace MWRender void CharacterPreview::setup () { mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); + + /// \todo Read the fallback values from INIImporter (Inventory:Directional*) + Ogre::Light* l = mSceneMgr->createLight(); + l->setType (Ogre::Light::LT_DIRECTIONAL); + l->setDirection (Ogre::Vector3(0.3, -0.7, 0.3)); + l->setDiffuseColour (Ogre::ColourValue(1,1,1)); + + mSceneMgr->setAmbientLight (Ogre::ColourValue(0.5, 0.5, 0.5)); + mCamera = mSceneMgr->createCamera (mName); mCamera->setAspectRatio (float(mSizeX) / float(mSizeY)); @@ -49,10 +62,8 @@ namespace MWRender mNode = renderRoot->createChildSceneNode(); - mAnimation = new NpcAnimation(mCharacter, mNode, - MWWorld::Class::get(mCharacter).getInventoryStore (mCharacter), RV_PlayerPreview); - - mNode->setVisible (false); + mAnimation = new NpcAnimation(mCharacter, mNode, MWWorld::Class::get(mCharacter).getInventoryStore(mCharacter), + 0, (renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal)); Ogre::Vector3 scale = mNode->getScale(); mCamera->setPosition(mPosition * scale); @@ -72,8 +83,6 @@ namespace MWRender mViewport->setOverlaysEnabled(false); mViewport->setBackgroundColour(Ogre::ColourValue(0, 0, 0, 0)); mViewport->setShadowsEnabled(false); - mViewport->setMaterialScheme("local_map"); - mViewport->setVisibilityMask (RV_PlayerPreview); mRenderTarget->setActive(true); mRenderTarget->setAutoUpdated (false); @@ -82,25 +91,30 @@ namespace MWRender CharacterPreview::~CharacterPreview () { - //Ogre::TextureManager::getSingleton().remove(mName); - mSceneMgr->destroyCamera (mName); - delete mAnimation; - Ogre::Root::getSingleton().destroySceneManager(mSceneMgr); + if (mSceneMgr) + { + //Ogre::TextureManager::getSingleton().remove(mName); + mSceneMgr->destroyAllCameras(); + delete mAnimation; + Ogre::Root::getSingleton().destroySceneManager(mSceneMgr); + } } void CharacterPreview::rebuild() { assert(mAnimation); delete mAnimation; + mAnimation = 0; - mAnimation = new NpcAnimation(mCharacter, mNode, - MWWorld::Class::get(mCharacter).getInventoryStore (mCharacter), RV_PlayerPreview); + mAnimation = new NpcAnimation(mCharacter, mNode, MWWorld::Class::get(mCharacter).getInventoryStore(mCharacter), + 0, (renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal)); - mNode->setVisible (false); + float scale=1.f; + MWWorld::Class::get(mCharacter).adjustScale(mCharacter, scale); + mNode->setScale(Ogre::Vector3(scale)); - Ogre::Vector3 scale = mNode->getScale(); - mCamera->setPosition(mPosition * scale); - mCamera->lookAt(mLookAt * scale); + mCamera->setPosition(mPosition * mNode->getScale()); + mCamera->lookAt(mLookAt * mNode->getScale()); onSetup(); } @@ -110,6 +124,7 @@ namespace MWRender InventoryPreview::InventoryPreview(MWWorld::Ptr character) : CharacterPreview(character, 512, 1024, "CharacterPreview", Ogre::Vector3(0, 65, -180), Ogre::Vector3(0,65,0)) + , mSelectionBuffer(NULL) { } @@ -120,18 +135,66 @@ namespace MWRender void InventoryPreview::update(int sizeX, int sizeY) { - mAnimation->forceUpdate (); + MWWorld::InventoryStore &inv = MWWorld::Class::get(mCharacter).getInventoryStore(mCharacter); + MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + std::string groupname; + if(iter == inv.end()) + groupname = "inventoryhandtohand"; + else + { + const std::string &type = iter->getTypeName(); + if(type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name()) + groupname = "inventoryweapononehand"; + else if(type == typeid(ESM::Weapon).name()) + { + MWWorld::LiveCellRef *ref = iter->get(); + + int type = ref->mBase->mData.mType; + if(type == ESM::Weapon::ShortBladeOneHand || + type == ESM::Weapon::LongBladeOneHand || + type == ESM::Weapon::BluntOneHand || + type == ESM::Weapon::AxeOneHand) + groupname = "inventoryweapononehand"; + else if(type == ESM::Weapon::LongBladeTwoHand || + type == ESM::Weapon::BluntTwoClose || + type == ESM::Weapon::AxeTwoHand) + groupname = "inventoryweapontwohand"; + else if(type == ESM::Weapon::BluntTwoWide || + type == ESM::Weapon::SpearTwoWide) + groupname = "inventoryweapontwowide"; + else + groupname = "inventoryhandtohand"; + } + else + groupname = "inventoryhandtohand"; + } + + if(groupname != mCurrentAnimGroup) + { + mCurrentAnimGroup = groupname; + mAnimation->play(mCurrentAnimGroup, 1, Animation::Group_All, false, "start", "stop", 0.0f, 0); + } + + MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) + { + if(!mAnimation->getInfo("torch")) + mAnimation->play("torch", 2, MWRender::Animation::Group_LeftArm, false, + "start", "stop", 0.0f, (~(size_t)0)); + } + else if(mAnimation->getInfo("torch")) + mAnimation->disable("torch"); + + mAnimation->forceUpdate(); + mAnimation->runAnimation(0.0f); mViewport->setDimensions (0, 0, std::min(1.f, float(sizeX) / float(512)), std::min(1.f, float(sizeY) / float(1024))); mNode->setOrientation (Ogre::Quaternion::IDENTITY); - mNode->setVisible (true); - mRenderTarget->update(); - mSelectionBuffer->update(); - mNode->setVisible (false); + mSelectionBuffer->update(); } int InventoryPreview::getSlotSelected (int posX, int posY) @@ -141,17 +204,20 @@ namespace MWRender void InventoryPreview::onSetup () { - mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, RV_PlayerPreview); + delete mSelectionBuffer; + mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, 0); + + mAnimation->showWeapons(true); - mAnimation->playGroup ("inventoryhandtohand", 0, 1); - mAnimation->runAnimation (0); + mCurrentAnimGroup = "inventoryhandtohand"; + mAnimation->play(mCurrentAnimGroup, 1, Animation::Group_All, false, "start", "stop", 0.0f, 0); } // -------------------------------------------------------------------------------------------------- RaceSelectionPreview::RaceSelectionPreview() : CharacterPreview(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), - 512, 512, "CharacterHeadPreview", Ogre::Vector3(0, 120, -35), Ogre::Vector3(0,125,0)) + 512, 512, "CharacterHeadPreview", Ogre::Vector3(0, 6, -35), Ogre::Vector3(0,125,0)) , mRef(&mBase) { mBase = *mCharacter.get()->mBase; @@ -160,11 +226,12 @@ namespace MWRender void RaceSelectionPreview::update(float angle) { + mAnimation->runAnimation(0.0f); mNode->roll(Ogre::Radian(angle), Ogre::SceneNode::TS_LOCAL); - mNode->setVisible (true); + updateCamera(); + mRenderTarget->update(); - mNode->setVisible (false); } void RaceSelectionPreview::setPrototype(const ESM::NPC &proto) @@ -174,4 +241,21 @@ namespace MWRender rebuild(); update(0); } + + void RaceSelectionPreview::onSetup () + { + mAnimation->play("idle", 1, Animation::Group_All, false, "start", "stop", 0.0f, 0); + + updateCamera(); + } + + void RaceSelectionPreview::updateCamera() + { + Ogre::Vector3 scale = mNode->getScale(); + Ogre::Vector3 headOffset = mAnimation->getNode("Bip01 Head")->_getDerivedPosition(); + headOffset = mNode->convertLocalToWorldPosition(headOffset); + + mCamera->setPosition(headOffset + mPosition * scale); + mCamera->lookAt(headOffset + mPosition*Ogre::Vector3(0,1,0) * scale); + } } diff --git a/apps/openmw/mwrender/characterpreview.hpp b/apps/openmw/mwrender/characterpreview.hpp index d07a03be7..562cb3784 100644 --- a/apps/openmw/mwrender/characterpreview.hpp +++ b/apps/openmw/mwrender/characterpreview.hpp @@ -36,6 +36,8 @@ namespace MWRender virtual void rebuild(); protected: + virtual bool renderHeadOnly() { return false; } + Ogre::TexturePtr mTexture; Ogre::RenderTarget* mRenderTarget; Ogre::Viewport* mViewport; @@ -51,6 +53,7 @@ namespace MWRender MWWorld::Ptr mCharacter; MWRender::NpcAnimation* mAnimation; + std::string mCurrentAnimGroup; std::string mName; @@ -70,8 +73,6 @@ namespace MWRender int getSlotSelected(int posX, int posY); - void setNpcAnimation (NpcAnimation* anim); - private: OEngine::Render::SelectionBuffer* mSelectionBuffer; @@ -82,9 +83,17 @@ namespace MWRender ESM::NPC mBase; MWWorld::LiveCellRef mRef; + protected: + + virtual bool renderHeadOnly() { return true; } + + void updateCamera(); + public: RaceSelectionPreview(); + virtual void onSetup(); + void update(float angle); const ESM::NPC &getPrototype() const { diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 7ee361a6f..7817c23c9 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -1,77 +1,33 @@ #include "creatureanimation.hpp" -#include -#include -#include - #include "renderconst.hpp" #include "../mwbase/world.hpp" -using namespace Ogre; -using namespace NifOgre; -namespace MWRender{ +namespace MWRender +{ CreatureAnimation::~CreatureAnimation() { } -CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr): Animation() +CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) + : Animation(ptr) { - mInsert = ptr.getRefData().getBaseNode(); - MWWorld::LiveCellRef *ref = ptr.get(); + MWWorld::LiveCellRef *ref = mPtr.get(); assert (ref->mBase != NULL); if(!ref->mBase->mModel.empty()) { - std::string mesh = "meshes\\" + ref->mBase->mModel; - - mEntityList = NifOgre::NIFLoader::createEntities(mInsert, &mTextKeys, mesh); - for(size_t i = 0;i < mEntityList.mEntities.size();i++) - { - Ogre::Entity *ent = mEntityList.mEntities[i]; - ent->setVisibilityFlags(RV_Actors); - - bool transparent = false; - for (unsigned int j=0;j < ent->getNumSubEntities() && !transparent; ++j) - { - Ogre::MaterialPtr mat = ent->getSubEntity(j)->getMaterial(); - Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); - while (techIt.hasMoreElements() && !transparent) - { - Ogre::Technique* tech = techIt.getNext(); - Ogre::Technique::PassIterator passIt = tech->getPassIterator(); - while (passIt.hasMoreElements() && !transparent) - { - Ogre::Pass* pass = passIt.getNext(); + std::string model = "meshes\\"+ref->mBase->mModel; - if (pass->getDepthWriteEnabled() == false) - transparent = true; - } - } - } - ent->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); - } + setObjectRoot(mPtr.getRefData().getBaseNode(), model, false); + setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); - if(mEntityList.mSkelBase) - { - Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); - Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); - while(as.hasMoreElements()) - { - Ogre::AnimationState *state = as.getNext(); - state->setEnabled(true); - state->setLoop(false); - } - } + if((ref->mBase->mFlags&ESM::Creature::Biped)) + addAnimSource("meshes\\base_anim.nif"); + addAnimSource(model); } } -void CreatureAnimation::runAnimation(float timepassed) -{ - // Placeholder - - Animation::runAnimation(timepassed); -} - } diff --git a/apps/openmw/mwrender/creatureanimation.hpp b/apps/openmw/mwrender/creatureanimation.hpp index e1a7bbb8f..0c277d198 100644 --- a/apps/openmw/mwrender/creatureanimation.hpp +++ b/apps/openmw/mwrender/creatureanimation.hpp @@ -3,18 +3,19 @@ #include "animation.hpp" -#include "components/nifogre/ogre_nif_loader.hpp" - - -namespace MWRender{ +namespace MWWorld +{ + class Ptr; +} - class CreatureAnimation: public Animation +namespace MWRender +{ + class CreatureAnimation : public Animation { public: - virtual ~CreatureAnimation(); CreatureAnimation(const MWWorld::Ptr& ptr); - virtual void runAnimation(float timepassed); - + virtual ~CreatureAnimation(); }; } + #endif diff --git a/apps/openmw/mwrender/debugging.cpp b/apps/openmw/mwrender/debugging.cpp index 1548cc1b0..b318c2d56 100644 --- a/apps/openmw/mwrender/debugging.cpp +++ b/apps/openmw/mwrender/debugging.cpp @@ -8,6 +8,8 @@ #include #include +#include + #include #include @@ -18,7 +20,6 @@ #include "../mwworld/ptr.hpp" -#include "player.hpp" #include "renderconst.hpp" using namespace Ogre; @@ -148,9 +149,9 @@ ManualObject *Debugging::createPathgridPoints(const ESM::Pathgrid *pathgrid) return result; } -Debugging::Debugging(SceneNode *mwRoot, OEngine::Physic::PhysicEngine *engine) : - mMwRoot(mwRoot), mEngine(engine), - mSceneMgr(mwRoot->getCreator()), +Debugging::Debugging(SceneNode *root, OEngine::Physic::PhysicEngine *engine) : + mRootNode(root), mEngine(engine), + mSceneMgr(root->getCreator()), mPathgridEnabled(false), mInteriorPathgridNode(NULL), mPathGridRoot(NULL), mGridMatsCreated(false) @@ -206,7 +207,7 @@ void Debugging::togglePathgrid() createGridMaterials(); // add path grid meshes to already loaded cells - mPathGridRoot = mMwRoot->createChildSceneNode(); + mPathGridRoot = mRootNode->createChildSceneNode(); for(CellList::iterator it = mActiveCells.begin(); it != mActiveCells.end(); ++it) { enableCellPathgrid(*it); diff --git a/apps/openmw/mwrender/debugging.hpp b/apps/openmw/mwrender/debugging.hpp index d312b6d54..4a574017c 100644 --- a/apps/openmw/mwrender/debugging.hpp +++ b/apps/openmw/mwrender/debugging.hpp @@ -3,7 +3,6 @@ #include #include -#include #include #include @@ -13,6 +12,14 @@ namespace ESM struct Pathgrid; } +namespace OEngine +{ + namespace Physic + { + class PhysicEngine; + } +} + namespace Ogre { class Camera; @@ -32,8 +39,6 @@ namespace MWWorld namespace MWRender { - class Player; - class Debugging { OEngine::Physic::PhysicEngine* mEngine; @@ -47,7 +52,7 @@ namespace MWRender typedef std::vector CellList; CellList mActiveCells; - Ogre::SceneNode *mMwRoot; + Ogre::SceneNode *mRootNode; Ogre::SceneNode *mPathGridRoot; @@ -71,7 +76,7 @@ namespace MWRender Ogre::ManualObject *createPathgridLines(const ESM::Pathgrid *pathgrid); Ogre::ManualObject *createPathgridPoints(const ESM::Pathgrid *pathgrid); public: - Debugging(Ogre::SceneNode* mwRoot, OEngine::Physic::PhysicEngine *engine); + Debugging(Ogre::SceneNode* root, OEngine::Physic::PhysicEngine *engine); ~Debugging(); bool toggleRenderMode (int mode); diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 072015f9a..055faaa1f 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -190,7 +190,7 @@ namespace MWRender { imageX = float(x / 8192.f - mMinX) / (mMaxX - mMinX + 1); - imageY = 1.f-float(-z / 8192.f - mMinY) / (mMaxY - mMinY + 1); + imageY = 1.f-float(z / 8192.f - mMinY) / (mMaxY - mMinY + 1); } void GlobalMap::cellTopLeftCornerToImageSpace(int x, int y, float& imageX, float& imageY) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index d878cb86e..8043f8b12 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -1,6 +1,5 @@ #include "localmap.hpp" -#include #include #include @@ -28,11 +27,14 @@ LocalMap::LocalMap(OEngine::Render::OgreRenderer* rend, MWRender::RenderingManag mCellCamera = mRendering->getScene()->createCamera("CellCamera"); mCellCamera->setProjectionType(PT_ORTHOGRAPHIC); - // look down -y - const float sqrt0pt5 = 0.707106781; - mCellCamera->setOrientation(Quaternion(sqrt0pt5, -sqrt0pt5, 0, 0)); mCameraNode->attachObject(mCellCamera); + + mLight = mRendering->getScene()->createLight(); + mLight->setType (Ogre::Light::LT_DIRECTIONAL); + mLight->setDirection (Ogre::Vector3(0.3, 0.3, -0.7)); + mLight->setVisible (false); + mLight->setDiffuseColour (ColourValue(0.7,0.7,0.7)); } LocalMap::~LocalMap() @@ -82,8 +84,8 @@ void LocalMap::saveFogOfWar(MWWorld::Ptr::CellStore* cell) } else { - Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z); - Vector2 max(mBounds.getMaximum().x, mBounds.getMaximum().z); + Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().y); + Vector2 max(mBounds.getMaximum().x, mBounds.getMaximum().y); Vector2 length = max-min; // divide into segments @@ -107,6 +109,7 @@ void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell) mInterior = false; mCameraRotNode->setOrientation(Quaternion::IDENTITY); + mCellCamera->setOrientation(Quaternion(Ogre::Math::Cos(Ogre::Degree(0)/2.f), 0, 0, -Ogre::Math::Sin(Ogre::Degree(0)/2.f))); int x = cell->mCell->getGridX(); int y = cell->mCell->getGridY(); @@ -115,49 +118,61 @@ void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell) mCameraPosNode->setPosition(Vector3(0,0,0)); - render((x+0.5)*sSize, (-y-0.5)*sSize, -10000, 10000, sSize, sSize, name); + render((x+0.5)*sSize, (y+0.5)*sSize, -10000, 10000, sSize, sSize, name); } void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell, AxisAlignedBox bounds) { + // if we're in an empty cell, don't bother rendering anything + if (bounds.isNull ()) + return; + mInterior = true; mBounds = bounds; - Vector2 z(mBounds.getMaximum().y, mBounds.getMinimum().y); + float zMin = mBounds.getMinimum().z; + float zMax = mBounds.getMaximum().z; const Vector2& north = MWBase::Environment::get().getWorld()->getNorthVector(cell); - Radian angle(std::atan2(-north.x, -north.y)); + Radian angle = Ogre::Math::ATan2 (north.x, north.y); mAngle = angle.valueRadians(); - mCameraRotNode->setOrientation(Quaternion(Math::Cos(angle/2.f), 0, Math::Sin(angle/2.f), 0)); + + mCellCamera->setOrientation(Quaternion::IDENTITY); + mCameraRotNode->setOrientation(Quaternion(Math::Cos(mAngle/2.f), 0, 0, -Math::Sin(mAngle/2.f))); // rotate the cell and merge the rotated corners to the bounding box - Vector2 _center(bounds.getCenter().x, bounds.getCenter().z); - Vector3 _c1 = bounds.getCorner(AxisAlignedBox::NEAR_LEFT_BOTTOM); - Vector3 _c2 = bounds.getCorner(AxisAlignedBox::FAR_LEFT_BOTTOM); - Vector3 _c3 = bounds.getCorner(AxisAlignedBox::NEAR_RIGHT_BOTTOM); - Vector3 _c4 = bounds.getCorner(AxisAlignedBox::FAR_RIGHT_BOTTOM); - Vector2 c1(_c1.x, _c1.z); - Vector2 c2(_c2.x, _c2.z); - Vector2 c3(_c3.x, _c3.z); - Vector2 c4(_c4.x, _c4.z); + Vector2 _center(bounds.getCenter().x, bounds.getCenter().y); + Vector3 _c1 = bounds.getCorner(AxisAlignedBox::FAR_LEFT_BOTTOM); + Vector3 _c2 = bounds.getCorner(AxisAlignedBox::FAR_RIGHT_BOTTOM); + Vector3 _c3 = bounds.getCorner(AxisAlignedBox::FAR_LEFT_TOP); + Vector3 _c4 = bounds.getCorner(AxisAlignedBox::FAR_RIGHT_TOP); + + Vector2 c1(_c1.x, _c1.y); + Vector2 c2(_c2.x, _c2.y); + Vector2 c3(_c3.x, _c3.y); + Vector2 c4(_c4.x, _c4.y); c1 = rotatePoint(c1, _center, mAngle); c2 = rotatePoint(c2, _center, mAngle); c3 = rotatePoint(c3, _center, mAngle); c4 = rotatePoint(c4, _center, mAngle); - mBounds.merge(Vector3(c1.x, 0, c1.y)); - mBounds.merge(Vector3(c2.x, 0, c2.y)); - mBounds.merge(Vector3(c3.x, 0, c3.y)); - mBounds.merge(Vector3(c4.x, 0, c4.y)); + mBounds.merge(Vector3(c1.x, c1.y, 0)); + mBounds.merge(Vector3(c2.x, c2.y, 0)); + mBounds.merge(Vector3(c3.x, c3.y, 0)); + mBounds.merge(Vector3(c4.x, c4.y, 0)); - Vector2 center(mBounds.getCenter().x, mBounds.getCenter().z); + // apply a little padding + mBounds.setMinimum (mBounds.getMinimum() - Vector3(500,500,0)); + mBounds.setMaximum (mBounds.getMaximum() + Vector3(500,500,0)); - Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z); - Vector2 max(mBounds.getMaximum().x, mBounds.getMaximum().z); + Vector2 center(mBounds.getCenter().x, mBounds.getCenter().y); + + Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().y); + Vector2 max(mBounds.getMaximum().x, mBounds.getMaximum().y); Vector2 length = max-min; - mCameraPosNode->setPosition(Vector3(center.x, 0, center.y)); + mCameraPosNode->setPosition(Vector3(center.x, center.y, 0)); // divide into segments const int segsX = std::ceil( length.x / sSize ); @@ -172,7 +187,7 @@ void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell, Vector2 start = min + Vector2(sSize*x,sSize*y); Vector2 newcenter = start + 4096; - render(newcenter.x - center.x, newcenter.y - center.y, z.y, z.x, sSize, sSize, + render(newcenter.x - center.x, newcenter.y - center.y, zMin, zMax, sSize, sSize, cell->mCell->mName + "_" + coordStr(x,y)); } } @@ -182,22 +197,24 @@ void LocalMap::render(const float x, const float y, const float zlow, const float zhigh, const float xw, const float yw, const std::string& texture) { - // disable fog - // changing FOG_MODE is not a solution when using shaders, thus we have to push linear start/end - const float fStart = mRendering->getScene()->getFogStart(); - const float fEnd = mRendering->getScene()->getFogEnd(); - const ColourValue& clr = mRendering->getScene()->getFogColour(); - mRendering->getScene()->setFog(FOG_LINEAR, clr, 0, 1000000, 10000000); + mCellCamera->setFarClipDistance( (zhigh-zlow) + 2000 ); + mCellCamera->setNearClipDistance(50); - // make everything visible - mRendering->getScene()->setAmbientLight(ColourValue(1,1,1)); - mRenderingManager->disableLights(); + mCellCamera->setOrthoWindow(xw, yw); + mCameraNode->setPosition(Vector3(x, y, zhigh+1000)); - mCameraNode->setPosition(Vector3(x, zhigh+100000, y)); - //mCellCamera->setFarClipDistance( (zhigh-zlow) * 1.1 ); - mCellCamera->setFarClipDistance(0); // infinite + // disable fog (only necessary for fixed function, the shader based + // materials already do this through local_map material configuration) + float oldFogStart = mRendering->getScene()->getFogStart(); + float oldFogEnd = mRendering->getScene()->getFogEnd(); + Ogre::ColourValue oldFogColour = mRendering->getScene()->getFogColour(); + mRendering->getScene()->setFog(FOG_NONE); - mCellCamera->setOrthoWindow(xw, yw); + // set up lighting + Ogre::ColourValue oldAmbient = mRendering->getScene()->getAmbientLight(); + mRendering->getScene()->setAmbientLight(Ogre::ColourValue(0.3, 0.3, 0.3)); + mRenderingManager->disableLights(true); + mLight->setVisible(true); TexturePtr tex; // try loading from memory @@ -222,14 +239,13 @@ void LocalMap::render(const float x, const float y, 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); - - // use fallback techniques without shadows and without mrt vp->setMaterialScheme("local_map"); rtt->update(); @@ -263,24 +279,25 @@ void LocalMap::render(const float x, const float y, //rtt->writeContentsToFile("./" + texture + ".jpg"); } } - - mRenderingManager->enableLights(); + mRenderingManager->enableLights(true); + mLight->setVisible(false); // re-enable fog - mRendering->getScene()->setFog(FOG_LINEAR, clr, 0, fStart, fEnd); + mRendering->getScene()->setFog(FOG_LINEAR, oldFogColour, 0, oldFogStart, oldFogEnd); + mRendering->getScene()->setAmbientLight(oldAmbient); } void LocalMap::getInteriorMapPosition (Ogre::Vector2 pos, float& nX, float& nY, int& x, int& y) { - pos = rotatePoint(pos, Vector2(mBounds.getCenter().x, mBounds.getCenter().z), mAngle); + pos = rotatePoint(pos, Vector2(mBounds.getCenter().x, mBounds.getCenter().y), mAngle); - Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z); + Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().y); x = std::ceil((pos.x - min.x)/sSize)-1; y = std::ceil((pos.y - min.y)/sSize)-1; nX = (pos.x - min.x - sSize*x)/sSize; - nY = (pos.y - min.y - sSize*y)/sSize; + nY = 1.0-(pos.y - min.y - sSize*y)/sSize; } bool LocalMap::isPositionExplored (float nX, float nY, int x, int y, bool interior) @@ -311,19 +328,19 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni int x,y; float u,v; - Vector2 pos(position.x, position.z); + Vector2 pos(position.x, position.y); if (mInterior) getInteriorMapPosition(pos, u,v, x,y); - Vector3 playerdirection = mCameraRotNode->convertWorldToLocalOrientation(orientation).zAxis(); + Vector3 playerdirection = mCameraRotNode->convertWorldToLocalOrientation(orientation).yAxis(); - Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z); + Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().y); if (!mInterior) { x = std::ceil(pos.x / sSize)-1; - y = std::ceil(-pos.y / sSize)-1; + y = std::ceil(pos.y / sSize)-1; mCellX = x; mCellY = y; } @@ -337,7 +354,7 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni if (!mInterior) { u = std::abs((pos.x - (sSize*x))/sSize); - v = 1-std::abs((pos.y + (sSize*y))/sSize); + v = 1.0-std::abs((pos.y - (sSize*y))/sSize); texBaseName = "Cell_"; } else @@ -346,15 +363,13 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni } MWBase::Environment::get().getWindowManager()->setPlayerPos(u, v); - MWBase::Environment::get().getWindowManager()->setPlayerDir(playerdirection.x, -playerdirection.z); + MWBase::Environment::get().getWindowManager()->setPlayerDir(playerdirection.x, playerdirection.y); // explore radius (squared) const float sqrExploreRadius = (mInterior ? 0.01 : 0.09) * sFogOfWarResolution*sFogOfWarResolution; const float exploreRadius = (mInterior ? 0.1 : 0.3) * sFogOfWarResolution; // explore radius from 0 to sFogOfWarResolution const float exploreRadiusUV = exploreRadius / sFogOfWarResolution; // explore radius from 0 to 1 (UV space) - int intExtMult = mInterior ? 1 : -1; // interior and exterior have reversed Y coordinates (interior: top to bottom) - // change the affected fog of war textures (in a 3x3 grid around the player) for (int mx = -1; mx<2; ++mx) { @@ -375,7 +390,7 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni if (!affected) continue; - std::string texName = texBaseName + coordStr(x+mx,y+my*intExtMult); + std::string texName = texBaseName + coordStr(x+mx,y+my*-1); TexturePtr tex = TextureManager::getSingleton().getByName(texName+"_fog"); if (!tex.isNull()) diff --git a/apps/openmw/mwrender/localmap.hpp b/apps/openmw/mwrender/localmap.hpp index 1aedf1325..72e637d9a 100644 --- a/apps/openmw/mwrender/localmap.hpp +++ b/apps/openmw/mwrender/localmap.hpp @@ -4,6 +4,7 @@ #include #include +#include namespace MWWorld { @@ -90,6 +91,9 @@ namespace MWRender Ogre::SceneNode* mCameraPosNode; Ogre::SceneNode* mCameraRotNode; + // directional light from a fixed angle + Ogre::Light* mLight; + float mAngle; const Ogre::Vector2 rotatePoint(const Ogre::Vector2& p, const Ogre::Vector2& c, const float angle); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index d33bdda91..b5f2ea031 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -2,74 +2,89 @@ #include #include +#include #include #include "../mwworld/esmstore.hpp" +#include "../mwworld/inventorystore.hpp" +#include "../mwworld/class.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "renderconst.hpp" -using namespace Ogre; -using namespace NifOgre; -namespace MWRender{ +namespace MWRender +{ + +const NpcAnimation::PartInfo NpcAnimation::sPartList[NpcAnimation::sPartListSize] = { + { ESM::PRT_Head, "Head" }, + { ESM::PRT_Hair, "Head" }, + { ESM::PRT_Neck, "Neck" }, + { ESM::PRT_Cuirass, "Chest" }, + { ESM::PRT_Groin, "Groin" }, + { ESM::PRT_Skirt, "Groin" }, + { ESM::PRT_RHand, "Right Hand" }, + { ESM::PRT_LHand, "Left Hand" }, + { ESM::PRT_RWrist, "Right Wrist" }, + { ESM::PRT_LWrist, "Left Wrist" }, + { ESM::PRT_Shield, "Shield Bone" }, + { ESM::PRT_RForearm, "Right Forearm" }, + { ESM::PRT_LForearm, "Left Forearm" }, + { ESM::PRT_RUpperarm, "Right Upper Arm" }, + { ESM::PRT_LUpperarm, "Left Upper Arm" }, + { ESM::PRT_RFoot, "Right Foot" }, + { ESM::PRT_LFoot, "Left Foot" }, + { ESM::PRT_RAnkle, "Right Ankle" }, + { ESM::PRT_LAnkle, "Left Ankle" }, + { ESM::PRT_RKnee, "Right Knee" }, + { ESM::PRT_LKnee, "Left Knee" }, + { ESM::PRT_RLeg, "Right Upper Leg" }, + { ESM::PRT_LLeg, "Left Upper Leg" }, + { ESM::PRT_RPauldron, "Right Clavicle" }, + { ESM::PRT_LPauldron, "Left Clavicle" }, + { ESM::PRT_Weapon, "Weapon Bone" }, + { ESM::PRT_Tail, "Tail" } +}; + NpcAnimation::~NpcAnimation() { - removeEntities(mHead); - removeEntities(mHair); - removeEntities(mNeck); - removeEntities(mChest); - removeEntities(mGroin); - removeEntities(mSkirt); - removeEntities(mHandL); - removeEntities(mHandR); - removeEntities(mWristL); - removeEntities(mWristR); - removeEntities(mForearmL); - removeEntities(mForearmR); - removeEntities(mUpperArmL); - removeEntities(mUpperArmR); - removeEntities(mFootL); - removeEntities(mFootR); - removeEntities(mAnkleL); - removeEntities(mAnkleR); - removeEntities(mKneeL); - removeEntities(mKneeR); - removeEntities(mUpperLegL); - removeEntities(mUpperLegR); - removeEntities(mClavicleL); - removeEntities(mClavicleR); - removeEntities(mTail); + Ogre::SceneManager *sceneMgr = mInsert->getCreator(); + for(size_t i = 0;i < sPartListSize;i++) + destroyObjectList(sceneMgr, mObjectParts[i]); } -NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWorld::InventoryStore& inv, int visibilityFlags) - : Animation(), +NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWorld::InventoryStore& inv, int visibilityFlags, ViewMode viewMode) + : Animation(ptr), mStateID(-1), - mInv(inv), mTimeToChange(0), mVisibilityFlags(visibilityFlags), - mRobe(mInv.end()), - mHelmet(mInv.end()), - mShirt(mInv.end()), - mCuirass(mInv.end()), - mGreaves(mInv.end()), - mPauldronL(mInv.end()), - mPauldronR(mInv.end()), - mBoots(mInv.end()), - mPants(mInv.end()), - mGloveL(mInv.end()), - mGloveR(mInv.end()), - mSkirtIter(mInv.end()) + mRobe(inv.end()), + mHelmet(inv.end()), + mShirt(inv.end()), + mCuirass(inv.end()), + mGreaves(inv.end()), + mPauldronL(inv.end()), + mPauldronR(inv.end()), + mBoots(inv.end()), + mPants(inv.end()), + mGloveL(inv.end()), + mGloveR(inv.end()), + mSkirtIter(inv.end()), + mWeapon(inv.end()), + mShield(inv.end()), + mViewMode(viewMode), + mShowWeapons(false) { - mNpc = ptr.get()->mBase; + mNpc = mPtr.get()->mBase; - for (int init = 0; init < 27; init++) + for(size_t i = 0;i < sPartListSize;i++) { - mPartslots[init] = -1; //each slot is empty - mPartPriorities[init] = 0; + mPartslots[i] = -1; //each slot is empty + mPartPriorities[i] = 0; } const MWWorld::ESMStore &store = @@ -82,316 +97,300 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor mBodyPrefix = "b_n_" + mNpc->mRace; Misc::StringUtils::toLower(mBodyPrefix); - mInsert = node; - assert(mInsert); - bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); - - mEntityList = NifOgre::NIFLoader::createEntities(mInsert, &mTextKeys, smodel); - for(size_t i = 0;i < mEntityList.mEntities.size();i++) + setObjectRoot(node, smodel, true); + + addAnimSource(smodel); + if(mBodyPrefix.find("argonian") != std::string::npos) + addAnimSource("meshes\\argonian_swimkna.nif"); + else if(!mNpc->isMale() && !isBeast) + addAnimSource("meshes\\base_anim_female.nif"); + if(mNpc->mModel.length() > 0) + addAnimSource("meshes\\"+mNpc->mModel); + if(mViewMode == VM_FirstPerson) { - Ogre::Entity *base = mEntityList.mEntities[i]; + /* A bit counter-intuitive, but unlike third-person anims, it seems + * beast races get both base_anim.1st.nif and base_animkna.1st.nif. + */ + addAnimSource("meshes\\base_anim.1st.nif"); + if(isBeast) + addAnimSource("meshes\\base_animkna.1st.nif"); + if(!mNpc->isMale() && !isBeast) + addAnimSource("meshes\\base_anim_female.1st.nif"); + } - base->getUserObjectBindings ().setUserAny (Ogre::Any(-1)); + forceUpdate(); +} - base->setVisibilityFlags(mVisibilityFlags); - bool transparent = false; - for(unsigned int j=0;j < base->getNumSubEntities();++j) - { - Ogre::MaterialPtr mat = base->getSubEntity(j)->getMaterial(); - Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); - while (techIt.hasMoreElements()) - { - Ogre::Technique* tech = techIt.getNext(); - Ogre::Technique::PassIterator passIt = tech->getPassIterator(); - while (passIt.hasMoreElements()) - { - Ogre::Pass* pass = passIt.getNext(); - if (pass->getDepthWriteEnabled() == false) - transparent = true; - } - } - } - base->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); - } +void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) +{ + assert(viewMode != VM_HeadOnly); + mViewMode = viewMode; - if(mEntityList.mSkelBase) - { - Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); - Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); - while(as.hasMoreElements()) - { - Ogre::AnimationState *state = as.getNext(); - state->setEnabled(true); - state->setLoop(false); - } - } + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Race *race = store.get().find(mNpc->mRace); + bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; + std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); - float scale = race->mData.mHeight.mMale; - if (!mNpc->isMale()) { - scale = race->mData.mHeight.mFemale; + clearAnimSources(); + addAnimSource(smodel); + if(mBodyPrefix.find("argonian") != std::string::npos) + addAnimSource("meshes\\argonian_swimkna.nif"); + else if(!mNpc->isMale() && !isBeast) + addAnimSource("meshes\\base_anim_female.nif"); + if(mNpc->mModel.length() > 0) + addAnimSource("meshes\\"+mNpc->mModel); + if(mViewMode == VM_FirstPerson) + { + /* A bit counter-intuitive, but unlike third-person anims, it seems + * beast races get both base_anim.1st.nif and base_animkna.1st.nif. + */ + addAnimSource("meshes\\base_anim.1st.nif"); + if(isBeast) + addAnimSource("meshes\\base_animkna.1st.nif"); + if(!mNpc->isMale() && !isBeast) + addAnimSource("meshes\\base_anim_female.1st.nif"); } - mInsert->scale(scale, scale, scale); + MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr); - updateParts(); + for(size_t i = 0;i < sPartListSize;i++) + removeIndividualPart(i); + forceUpdate(); } -void NpcAnimation::updateParts() +void NpcAnimation::updateParts(bool forceupdate) { - bool apparelChanged = false; - - const struct { - MWWorld::ContainerStoreIterator *iter; - int slot; + static const struct { + MWWorld::ContainerStoreIterator NpcAnimation::*mPart; + int mSlot; + int mBasePriority; } slotlist[] = { - { &mRobe, MWWorld::InventoryStore::Slot_Robe }, - { &mSkirtIter, MWWorld::InventoryStore::Slot_Skirt }, - { &mHelmet, MWWorld::InventoryStore::Slot_Helmet }, - { &mCuirass, MWWorld::InventoryStore::Slot_Cuirass }, - { &mGreaves, MWWorld::InventoryStore::Slot_Greaves }, - { &mPauldronL, MWWorld::InventoryStore::Slot_LeftPauldron }, - { &mPauldronR, MWWorld::InventoryStore::Slot_RightPauldron }, - { &mBoots, MWWorld::InventoryStore::Slot_Boots }, - { &mGloveL, MWWorld::InventoryStore::Slot_LeftGauntlet }, - { &mGloveR, MWWorld::InventoryStore::Slot_RightGauntlet }, - { &mShirt, MWWorld::InventoryStore::Slot_Shirt }, - { &mPants, MWWorld::InventoryStore::Slot_Pants }, + // FIXME: Priority is based on the number of reserved slots. There should be a better way. + { &NpcAnimation::mRobe, MWWorld::InventoryStore::Slot_Robe, 12 }, + { &NpcAnimation::mSkirtIter, MWWorld::InventoryStore::Slot_Skirt, 3 }, + { &NpcAnimation::mHelmet, MWWorld::InventoryStore::Slot_Helmet, 0 }, + { &NpcAnimation::mCuirass, MWWorld::InventoryStore::Slot_Cuirass, 0 }, + { &NpcAnimation::mGreaves, MWWorld::InventoryStore::Slot_Greaves, 0 }, + { &NpcAnimation::mPauldronL, MWWorld::InventoryStore::Slot_LeftPauldron, 0 }, + { &NpcAnimation::mPauldronR, MWWorld::InventoryStore::Slot_RightPauldron, 0 }, + { &NpcAnimation::mBoots, MWWorld::InventoryStore::Slot_Boots, 0 }, + { &NpcAnimation::mGloveL, MWWorld::InventoryStore::Slot_LeftGauntlet, 0 }, + { &NpcAnimation::mGloveR, MWWorld::InventoryStore::Slot_RightGauntlet, 0 }, + { &NpcAnimation::mShirt, MWWorld::InventoryStore::Slot_Shirt, 0 }, + { &NpcAnimation::mPants, MWWorld::InventoryStore::Slot_Pants, 0 }, + { &NpcAnimation::mShield, MWWorld::InventoryStore::Slot_CarriedLeft, 0 }, + { &NpcAnimation::mWeapon, MWWorld::InventoryStore::Slot_CarriedRight, 0 } }; - for(size_t i = 0;i < sizeof(slotlist)/sizeof(slotlist[0]);i++) + static const size_t slotlistsize = sizeof(slotlist)/sizeof(slotlist[0]); + + MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); + for(size_t i = 0;!forceupdate && i < slotlistsize;i++) { - MWWorld::ContainerStoreIterator iter = mInv.getSlot(slotlist[i].slot); - if(*slotlist[i].iter != iter) + MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].mSlot); + if(this->*slotlist[i].mPart != iter) { - *slotlist[i].iter = iter; - removePartGroup(slotlist[i].slot); - apparelChanged = true; + forceupdate = true; + break; } } + if(!forceupdate) + return; - if(apparelChanged) + /* FIXME: Remove this once we figure out how to show what in first-person */ + if(mViewMode == VM_FirstPerson) { - if(mRobe != mInv.end()) - { - MWWorld::Ptr ptr = *mRobe; - - const ESM::Clothing *clothes = (ptr.get())->mBase; - std::vector parts = clothes->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_Robe, 5, parts); - reserveIndividualPart(ESM::PRT_Groin, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_Skirt, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_RLeg, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_LLeg, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_RUpperarm, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_LUpperarm, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_RKnee, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_LKnee, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_RForearm, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_LForearm, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_RPauldron, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_LPauldron, MWWorld::InventoryStore::Slot_Robe, 5); - } - if(mSkirtIter != mInv.end()) - { - MWWorld::Ptr ptr = *mSkirtIter; - - const ESM::Clothing *clothes = (ptr.get())->mBase; - std::vector parts = clothes->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_Skirt, 4, parts); - reserveIndividualPart(ESM::PRT_Groin, MWWorld::InventoryStore::Slot_Skirt, 4); - reserveIndividualPart(ESM::PRT_RLeg, MWWorld::InventoryStore::Slot_Skirt, 4); - reserveIndividualPart(ESM::PRT_LLeg, MWWorld::InventoryStore::Slot_Skirt, 4); - } + for(size_t i = 0;i < slotlistsize;i++) + this->*slotlist[i].mPart = inv.getSlot(slotlist[i].mSlot); + return; + } - if(mHelmet != mInv.end()) - { + for(size_t i = 0;i < slotlistsize && mViewMode != VM_HeadOnly;i++) + { + MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].mSlot); + + this->*slotlist[i].mPart = iter; + removePartGroup(slotlist[i].mSlot); + + if(this->*slotlist[i].mPart == inv.end()) + continue; + + if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Helmet) removeIndividualPart(ESM::PRT_Hair); - const ESM::Armor *armor = (mHelmet->get())->mBase; - std::vector parts = armor->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_Helmet, 3, parts); - } - if(mCuirass != mInv.end()) - { - const ESM::Armor *armor = (mCuirass->get())->mBase; - std::vector parts = armor->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_Cuirass, 3, parts); - } - if(mGreaves != mInv.end()) - { - const ESM::Armor *armor = (mGreaves->get())->mBase; - std::vector parts = armor->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_Greaves, 3, parts); - } - if(mPauldronL != mInv.end()) + int prio = 1; + MWWorld::ContainerStoreIterator &store = this->*slotlist[i].mPart; + if(store->getTypeName() == typeid(ESM::Clothing).name()) { - const ESM::Armor *armor = (mPauldronL->get())->mBase; - std::vector parts = armor->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_LeftPauldron, 3, parts); + prio = ((slotlist[i].mBasePriority+1)<<1) + 0; + const ESM::Clothing *clothes = store->get()->mBase; + addPartGroup(slotlist[i].mSlot, prio, clothes->mParts.mParts); } - if(mPauldronR != mInv.end()) + else if(store->getTypeName() == typeid(ESM::Armor).name()) { - const ESM::Armor *armor = (mPauldronR->get())->mBase; - std::vector parts = armor->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_RightPauldron, 3, parts); + prio = ((slotlist[i].mBasePriority+1)<<1) + 1; + const ESM::Armor *armor = store->get()->mBase; + addPartGroup(slotlist[i].mSlot, prio, armor->mParts.mParts); } - if(mBoots != mInv.end()) + + if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Robe) { - if(mBoots->getTypeName() == typeid(ESM::Clothing).name()) - { - const ESM::Clothing *clothes = (mBoots->get())->mBase; - std::vector parts = clothes->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_Boots, 2, parts); - } - else if(mBoots->getTypeName() == typeid(ESM::Armor).name()) - { - const ESM::Armor *armor = (mBoots->get())->mBase; - std::vector parts = armor->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_Boots, 3, parts); - } + ESM::PartReferenceType parts[] = { + ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg, + ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee, + ESM::PRT_RForearm, ESM::PRT_LForearm, ESM::PRT_RPauldron, ESM::PRT_LPauldron + }; + size_t parts_size = sizeof(parts)/sizeof(parts[0]); + for(size_t p = 0;p < parts_size;++p) + reserveIndividualPart(parts[p], slotlist[i].mSlot, prio); } - if(mGloveL != mInv.end()) + else if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Skirt) { - if(mGloveL->getTypeName() == typeid(ESM::Clothing).name()) - { - const ESM::Clothing *clothes = (mGloveL->get())->mBase; - std::vector parts = clothes->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet, 2, parts); - } - else - { - const ESM::Armor *armor = (mGloveL->get())->mBase; - std::vector parts = armor->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet, 3, parts); - } + reserveIndividualPart(ESM::PRT_Groin, slotlist[i].mSlot, prio); + reserveIndividualPart(ESM::PRT_RLeg, slotlist[i].mSlot, prio); + reserveIndividualPart(ESM::PRT_LLeg, slotlist[i].mSlot, prio); } - if(mGloveR != mInv.end()) - { - if(mGloveR->getTypeName() == typeid(ESM::Clothing).name()) - { - const ESM::Clothing *clothes = (mGloveR->get())->mBase; - std::vector parts = clothes->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_RightGauntlet, 2, parts); - } - else - { - const ESM::Armor *armor = (mGloveR->get())->mBase; - std::vector parts = armor->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_RightGauntlet, 3, parts); - } + } - } + if(mViewMode != VM_FirstPerson) + { + if(mPartPriorities[ESM::PRT_Head] < 1) + addOrReplaceIndividualPart(ESM::PRT_Head, -1,1, mHeadModel); + if(mPartPriorities[ESM::PRT_Hair] < 1 && mPartPriorities[ESM::PRT_Head] <= 1) + addOrReplaceIndividualPart(ESM::PRT_Hair, -1,1, mHairModel); + } + if(mViewMode == VM_HeadOnly) + return; + + showWeapons(mShowWeapons); - if(mShirt != mInv.end()) + const int Flag_Female = 0x01; + const int Flag_FirstPerson = 0x02; + + int flags = 0; + if (!mNpc->isMale()) + flags |= Flag_Female; + if (mViewMode == VM_FirstPerson) + flags |= Flag_FirstPerson; + + // 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; + std::string race = Misc::StringUtils::lowerCase(mNpc->mRace); + std::pair thisCombination = std::make_pair(race, flags); + if (sRaceMapping.find(thisCombination) == sRaceMapping.end()) + { + static std::map bodypartMap; + if(bodypartMap.size() == 0) { - const ESM::Clothing *clothes = (mShirt->get())->mBase; - std::vector parts = clothes->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_Shirt, 2, parts); + bodypartMap[ESM::PRT_Neck] = ESM::BodyPart::MP_Neck; + bodypartMap[ESM::PRT_Cuirass] = ESM::BodyPart::MP_Chest; + bodypartMap[ESM::PRT_Groin] = ESM::BodyPart::MP_Groin; + bodypartMap[ESM::PRT_RHand] = ESM::BodyPart::MP_Hand; + bodypartMap[ESM::PRT_LHand] = ESM::BodyPart::MP_Hand; + bodypartMap[ESM::PRT_RWrist] = ESM::BodyPart::MP_Wrist; + bodypartMap[ESM::PRT_LWrist] = ESM::BodyPart::MP_Wrist; + bodypartMap[ESM::PRT_RForearm] = ESM::BodyPart::MP_Forearm; + bodypartMap[ESM::PRT_LForearm] = ESM::BodyPart::MP_Forearm; + bodypartMap[ESM::PRT_RUpperarm] = ESM::BodyPart::MP_Upperarm; + bodypartMap[ESM::PRT_LUpperarm] = ESM::BodyPart::MP_Upperarm; + bodypartMap[ESM::PRT_RFoot] = ESM::BodyPart::MP_Foot; + bodypartMap[ESM::PRT_LFoot] = ESM::BodyPart::MP_Foot; + bodypartMap[ESM::PRT_RAnkle] = ESM::BodyPart::MP_Ankle; + bodypartMap[ESM::PRT_LAnkle] = ESM::BodyPart::MP_Ankle; + bodypartMap[ESM::PRT_RKnee] = ESM::BodyPart::MP_Knee; + bodypartMap[ESM::PRT_LKnee] = ESM::BodyPart::MP_Knee; + bodypartMap[ESM::PRT_RLeg] = ESM::BodyPart::MP_Upperleg; + bodypartMap[ESM::PRT_LLeg] = ESM::BodyPart::MP_Upperleg; + bodypartMap[ESM::PRT_Tail] = ESM::BodyPart::MP_Tail; } - if(mPants != mInv.end()) + + sRaceMapping[thisCombination].resize(ESM::PRT_Count, NULL); + + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const MWWorld::Store &partStore = store.get(); + for(MWWorld::Store::iterator it = partStore.begin(); it != partStore.end(); ++it) { - const ESM::Clothing *clothes = (mPants->get())->mBase; - std::vector parts = clothes->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_Pants, 2, parts); + const ESM::BodyPart& bodypart = *it; + if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable) + continue; + if (bodypart.mData.mType != ESM::BodyPart::MT_Skin) + continue; + + if (!mNpc->isMale() != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female)) + continue; + if (!Misc::StringUtils::ciEqual(bodypart.mRace, mNpc->mRace)) + continue; + + bool firstPerson = (bodypart.mId.size() >= 3) + && bodypart.mId[bodypart.mId.size()-3] == '1' + && bodypart.mId[bodypart.mId.size()-2] == 's' + && bodypart.mId[bodypart.mId.size()-1] == 't'; + if (firstPerson != (mViewMode == VM_FirstPerson)) + continue; + for (std::map::iterator bIt = bodypartMap.begin(); bIt != bodypartMap.end(); ++bIt ) + if (bIt->second == bodypart.mData.mPart) + sRaceMapping[thisCombination][bIt->first] = &*it; } } - if(mPartPriorities[ESM::PRT_Head] < 1) - addOrReplaceIndividualPart(ESM::PRT_Head, -1,1, mHeadModel); - if(mPartPriorities[ESM::PRT_Hair] < 1 && mPartPriorities[ESM::PRT_Head] <= 1) - addOrReplaceIndividualPart(ESM::PRT_Hair, -1,1, mHairModel); - - static const struct { - ESM::PartReferenceType type; - const char name[2][12]; - } PartTypeList[] = { - { ESM::PRT_Neck, { "neck", "" } }, - { ESM::PRT_Cuirass, { "chest", "" } }, - { ESM::PRT_Groin, { "groin", "" } }, - { ESM::PRT_RHand, { "hand", "hands" } }, - { ESM::PRT_LHand, { "hand", "hands" } }, - { ESM::PRT_RWrist, { "wrist", "" } }, - { ESM::PRT_LWrist, { "wrist", "" } }, - { ESM::PRT_RForearm, { "forearm", "" } }, - { ESM::PRT_LForearm, { "forearm", "" } }, - { ESM::PRT_RUpperarm, { "upper arm", "" } }, - { ESM::PRT_LUpperarm, { "upper arm", "" } }, - { ESM::PRT_RFoot, { "foot", "feet" } }, - { ESM::PRT_LFoot, { "foot", "feet" } }, - { ESM::PRT_RAnkle, { "ankle", "" } }, - { ESM::PRT_LAnkle, { "ankle", "" } }, - { ESM::PRT_RKnee, { "knee", "" } }, - { ESM::PRT_LKnee, { "knee", "" } }, - { ESM::PRT_RLeg, { "upper leg", "" } }, - { ESM::PRT_LLeg, { "upper leg", "" } }, - { ESM::PRT_Tail, { "tail", "" } } - }; - - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - for(size_t i = 0;i < sizeof(PartTypeList)/sizeof(PartTypeList[0]);i++) + for (int part = ESM::PRT_Neck; part < ESM::PRT_Count; ++part) { - if(mPartPriorities[PartTypeList[i].type] < 1) - { - const ESM::BodyPart *part = NULL; - const MWWorld::Store &partStore = - store.get(); - - if (!mNpc->isMale()) { - part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[0]); - if (part == 0) { - part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[1]); - } - } - if (part == 0) { - part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[0]); - } - if (part == 0) { - part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[1]); - } - - if(part) - addOrReplaceIndividualPart(PartTypeList[i].type, -1,1, "meshes\\"+part->mModel); - } + const ESM::BodyPart* bodypart = sRaceMapping[thisCombination][part]; + if (mPartPriorities[part] < 1 && bodypart) + addOrReplaceIndividualPart(part, -1,1, "meshes\\"+bodypart->mModel); } } -NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, int group, const std::string &bonename) +NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename) { - NifOgre::EntityList entities = NIFLoader::createEntities(mEntityList.mSkelBase, bonename, - mInsert, mesh); - std::vector &parts = entities.mEntities; - for(size_t i = 0;i < parts.size();i++) + NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); + setRenderProperties(objects, mVisibilityFlags, RQG_Main, RQG_Alpha); + + for(size_t i = 0;i < objects.mEntities.size();i++) + objects.mEntities[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); + for(size_t i = 0;i < objects.mParticles.size();i++) + objects.mParticles[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); + + if(objects.mSkelBase) { - parts[i]->setVisibilityFlags(mVisibilityFlags); - parts[i]->getUserObjectBindings ().setUserAny (Ogre::Any(group)); + Ogre::AnimationStateSet *aset = objects.mSkelBase->getAllAnimationStates(); + Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); + while(asiter.hasMoreElements()) + { + Ogre::AnimationState *state = asiter.getNext(); + state->setEnabled(false); + state->setLoop(false); + } + Ogre::SkeletonInstance *skelinst = objects.mSkelBase->getSkeleton(); + Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); + while(boneiter.hasMoreElements()) + boneiter.getNext()->setManuallyControlled(true); } - return entities; + + return objects; } -void NpcAnimation::runAnimation(float timepassed) +Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) { - if(mTimeToChange > .2) + if(mTimeToChange <= 0.0f) { - mTimeToChange = 0; + mTimeToChange = 0.2f; updateParts(); } - mTimeToChange += timepassed; + mTimeToChange -= timepassed; - Animation::runAnimation(timepassed); -} - -void NpcAnimation::removeEntities(NifOgre::EntityList &entities) -{ - assert(&entities != &mEntityList); + Ogre::Vector3 ret = Animation::runAnimation(timepassed); - Ogre::SceneManager *sceneMgr = mInsert->getCreator(); - for(size_t i = 0;i < entities.mEntities.size();i++) + Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton(); + for(size_t i = 0;i < sPartListSize;i++) { - entities.mEntities[i]->detachFromParent(); - sceneMgr->destroyEntity(entities.mEntities[i]); + Ogre::Entity *ent = mObjectParts[i].mSkelBase; + if(!ent) continue; + updateSkeletonInstance(baseinst, ent->getSkeleton()); + ent->getAllAnimationStates()->_notifyDirty(); } - entities.mEntities.clear(); - entities.mSkelBase = NULL; + + return ret; } void NpcAnimation::removeIndividualPart(int type) @@ -399,62 +398,14 @@ void NpcAnimation::removeIndividualPart(int type) mPartPriorities[type] = 0; mPartslots[type] = -1; - if(type == ESM::PRT_Head) //0 - removeEntities(mHead); - else if(type == ESM::PRT_Hair) //1 - removeEntities(mHair); - else if(type == ESM::PRT_Neck) //2 - removeEntities(mNeck); - else if(type == ESM::PRT_Cuirass)//3 - removeEntities(mChest); - else if(type == ESM::PRT_Groin)//4 - removeEntities(mGroin); - else if(type == ESM::PRT_Skirt)//5 - removeEntities(mSkirt); - else if(type == ESM::PRT_RHand)//6 - removeEntities(mHandR); - else if(type == ESM::PRT_LHand)//7 - removeEntities(mHandL); - else if(type == ESM::PRT_RWrist)//8 - removeEntities(mWristR); - else if(type == ESM::PRT_LWrist) //9 - removeEntities(mWristL); - else if(type == ESM::PRT_Shield) //10 - { - } - else if(type == ESM::PRT_RForearm) //11 - removeEntities(mForearmR); - else if(type == ESM::PRT_LForearm) //12 - removeEntities(mForearmL); - else if(type == ESM::PRT_RUpperarm) //13 - removeEntities(mUpperArmR); - else if(type == ESM::PRT_LUpperarm) //14 - removeEntities(mUpperArmL); - else if(type == ESM::PRT_RFoot) //15 - removeEntities(mFootR); - else if(type == ESM::PRT_LFoot) //16 - removeEntities(mFootL); - else if(type == ESM::PRT_RAnkle) //17 - removeEntities(mAnkleR); - else if(type == ESM::PRT_LAnkle) //18 - removeEntities(mAnkleL); - else if(type == ESM::PRT_RKnee) //19 - removeEntities(mKneeR); - else if(type == ESM::PRT_LKnee) //20 - removeEntities(mKneeL); - else if(type == ESM::PRT_RLeg) //21 - removeEntities(mUpperLegR); - else if(type == ESM::PRT_LLeg) //22 - removeEntities(mUpperLegL); - else if(type == ESM::PRT_RPauldron) //23 - removeEntities(mClavicleR); - else if(type == ESM::PRT_LPauldron) //24 - removeEntities(mClavicleL); - else if(type == ESM::PRT_Weapon) //25 + for(size_t i = 0;i < sPartListSize;i++) { + if(type == sPartList[i].type) + { + destroyObjectList(mInsert->getCreator(), mObjectParts[i]); + break; + } } - else if(type == ESM::PRT_Tail) //26 - removeEntities(mTail); } void NpcAnimation::reserveIndividualPart(int type, int group, int priority) @@ -484,116 +435,58 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, removeIndividualPart(type); mPartslots[type] = group; mPartPriorities[type] = priority; - switch(type) + + for(size_t i = 0;i < sPartListSize;i++) { - case ESM::PRT_Head: //0 - mHead = insertBoundedPart(mesh, group, "Head"); - break; - case ESM::PRT_Hair: //1 - mHair = insertBoundedPart(mesh, group, "Head"); - break; - case ESM::PRT_Neck: //2 - mNeck = insertBoundedPart(mesh, group, "Neck"); - break; - case ESM::PRT_Cuirass: //3 - mChest = insertBoundedPart(mesh, group, "Chest"); - break; - case ESM::PRT_Groin: //4 - mGroin = insertBoundedPart(mesh, group, "Groin"); - break; - case ESM::PRT_Skirt: //5 - mSkirt = insertBoundedPart(mesh, group, "Groin"); - break; - case ESM::PRT_RHand: //6 - mHandR = insertBoundedPart(mesh, group, "Right Hand"); - break; - case ESM::PRT_LHand: //7 - mHandL = insertBoundedPart(mesh, group, "Left Hand"); - break; - case ESM::PRT_RWrist: //8 - mWristR = insertBoundedPart(mesh, group, "Right Wrist"); - break; - case ESM::PRT_LWrist: //9 - mWristL = insertBoundedPart(mesh, group, "Left Wrist"); - break; - case ESM::PRT_Shield: //10 - break; - case ESM::PRT_RForearm: //11 - mForearmR = insertBoundedPart(mesh, group, "Right Forearm"); - break; - case ESM::PRT_LForearm: //12 - mForearmL = insertBoundedPart(mesh, group, "Left Forearm"); - break; - case ESM::PRT_RUpperarm: //13 - mUpperArmR = insertBoundedPart(mesh, group, "Right Upper Arm"); - break; - case ESM::PRT_LUpperarm: //14 - mUpperArmL = insertBoundedPart(mesh, group, "Left Upper Arm"); - break; - case ESM::PRT_RFoot: //15 - mFootR = insertBoundedPart(mesh, group, "Right Foot"); - break; - case ESM::PRT_LFoot: //16 - mFootL = insertBoundedPart(mesh, group, "Left Foot"); - break; - case ESM::PRT_RAnkle: //17 - mAnkleR = insertBoundedPart(mesh, group, "Right Ankle"); - break; - case ESM::PRT_LAnkle: //18 - mAnkleL = insertBoundedPart(mesh, group, "Left Ankle"); - break; - case ESM::PRT_RKnee: //19 - mKneeR = insertBoundedPart(mesh, group, "Right Knee"); - break; - case ESM::PRT_LKnee: //20 - mKneeL = insertBoundedPart(mesh, group, "Left Knee"); - break; - case ESM::PRT_RLeg: //21 - mUpperLegR = insertBoundedPart(mesh, group, "Right Upper Leg"); - break; - case ESM::PRT_LLeg: //22 - mUpperLegL = insertBoundedPart(mesh, group, "Left Upper Leg"); - break; - case ESM::PRT_RPauldron: //23 - mClavicleR = insertBoundedPart(mesh , group, "Right Clavicle"); - break; - case ESM::PRT_LPauldron: //24 - mClavicleL = insertBoundedPart(mesh, group, "Left Clavicle"); - break; - case ESM::PRT_Weapon: //25 - break; - case ESM::PRT_Tail: //26 - mTail = insertBoundedPart(mesh, group, "Tail"); + if(type == sPartList[i].type) + { + mObjectParts[i] = insertBoundedPart(mesh, group, sPartList[i].name); break; + } } return true; } -void NpcAnimation::addPartGroup(int group, int priority, std::vector &parts) +void NpcAnimation::addPartGroup(int group, int priority, const std::vector &parts) { - for(std::size_t i = 0; i < parts.size(); i++) - { - ESM::PartReference &part = parts[i]; - - const MWWorld::Store &partStore = - MWBase::Environment::get().getWorld()->getStore().get(); + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const MWWorld::Store &partStore = store.get(); + const char *ext = (mViewMode == VM_FirstPerson) ? ".1st" : ""; + std::vector::const_iterator part(parts.begin()); + for(;part != parts.end();part++) + { const ESM::BodyPart *bodypart = 0; - if(!mNpc->isMale()) - bodypart = partStore.search(part.mFemale); - if(!bodypart) - bodypart = partStore.search(part.mMale); + if(!mNpc->isMale() && !part->mFemale.empty()) + bodypart = partStore.search(part->mFemale+ext); + if(!bodypart && !part->mMale.empty()) + bodypart = partStore.search(part->mMale+ext); if(bodypart) - addOrReplaceIndividualPart(part.mPart, group, priority,"meshes\\" + bodypart->mModel); + addOrReplaceIndividualPart(part->mPart, group, priority, "meshes\\"+bodypart->mModel); else - reserveIndividualPart(part.mPart, group, priority); + reserveIndividualPart(part->mPart, group, priority); } } -void NpcAnimation::forceUpdate () +void NpcAnimation::showWeapons(bool showWeapon) { - updateParts(); + mShowWeapons = showWeapon; + if(showWeapon && + mViewMode != VM_FirstPerson/* FIXME: Remove this once first-person bodies work */) + { + MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); + mWeapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if(mWeapon != inv.end()) // special case for weapons + { + std::string mesh = MWWorld::Class::get(*mWeapon).getModel(*mWeapon); + addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, mesh); + } + } + else + { + removeIndividualPart(ESM::PRT_Weapon); + } } } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index ca76dcc22..e72fa56ed 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -3,9 +3,6 @@ #include "animation.hpp" -#include "components/nifogre/ogre_nif_loader.hpp" -#include "../mwworld/inventorystore.hpp" -#include "../mwclass/npc.hpp" #include "../mwworld/containerstore.hpp" namespace ESM @@ -13,48 +10,43 @@ namespace ESM struct NPC; } -namespace MWRender{ +namespace MWWorld +{ + class InventoryStore; +} + +namespace MWRender +{ + +class NpcAnimation : public Animation +{ +public: +struct PartInfo { + ESM::PartReferenceType type; + const char name[32]; +}; + +enum ViewMode { + VM_Normal, + VM_FirstPerson, + VM_HeadOnly +}; -class NpcAnimation: public Animation{ private: - MWWorld::InventoryStore& mInv; + static const size_t sPartListSize = 27; + static const PartInfo sPartList[sPartListSize]; + int mStateID; - int mPartslots[27]; //Each part slot is taken by clothing, armor, or is empty - int mPartPriorities[27]; - - //Bounded Parts - NifOgre::EntityList mClavicleL; - NifOgre::EntityList mClavicleR; - NifOgre::EntityList mUpperArmL; - NifOgre::EntityList mUpperArmR; - NifOgre::EntityList mUpperLegL; - NifOgre::EntityList mUpperLegR; - NifOgre::EntityList mForearmL; - NifOgre::EntityList mForearmR; - NifOgre::EntityList mWristL; - NifOgre::EntityList mWristR; - NifOgre::EntityList mKneeR; - NifOgre::EntityList mKneeL; - NifOgre::EntityList mNeck; - NifOgre::EntityList mAnkleL; - NifOgre::EntityList mAnkleR; - NifOgre::EntityList mGroin; - NifOgre::EntityList mSkirt; - NifOgre::EntityList mFootL; - NifOgre::EntityList mFootR; - NifOgre::EntityList mHair; - NifOgre::EntityList mHandL; - NifOgre::EntityList mHandR; - NifOgre::EntityList mHead; - NifOgre::EntityList mChest; - NifOgre::EntityList mTail; + // Bounded Parts + NifOgre::ObjectList mObjectParts[sPartListSize]; const ESM::NPC *mNpc; std::string mHeadModel; std::string mHairModel; std::string mBodyPrefix; - + ViewMode mViewMode; + bool mShowWeapons; float mTimeToChange; MWWorld::ContainerStoreIterator mRobe; @@ -69,26 +61,41 @@ private: MWWorld::ContainerStoreIterator mGloveL; MWWorld::ContainerStoreIterator mGloveR; MWWorld::ContainerStoreIterator mSkirtIter; + MWWorld::ContainerStoreIterator mWeapon; + MWWorld::ContainerStoreIterator mShield; int mVisibilityFlags; -public: - NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, - MWWorld::InventoryStore& inv, int visibilityFlags); - virtual ~NpcAnimation(); - NifOgre::EntityList insertBoundedPart(const std::string &mesh, int group, const std::string &bonename); - virtual void runAnimation(float timepassed); - void updateParts(); - void removeEntities(NifOgre::EntityList &entities); + int mPartslots[sPartListSize]; //Each part slot is taken by clothing, armor, or is empty + int mPartPriorities[sPartListSize]; + + NifOgre::ObjectList insertBoundedPart(const std::string &model, int group, const std::string &bonename); + + void updateParts(bool forceupdate = false); + void removeIndividualPart(int type); void reserveIndividualPart(int type, int group, int priority); bool addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh); void removePartGroup(int group); - void addPartGroup(int group, int priority, std::vector& parts); + void addPartGroup(int group, int priority, const std::vector &parts); - void forceUpdate(); +public: + NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, + MWWorld::InventoryStore& inv, int visibilityFlags, + ViewMode viewMode=VM_Normal); + virtual ~NpcAnimation(); + + virtual Ogre::Vector3 runAnimation(float timepassed); + + virtual void showWeapons(bool showWeapon); + + void setViewMode(ViewMode viewMode); + + void forceUpdate() + { updateParts(true); } }; } + #endif diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index ee36126f8..3456e1c16 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -1,13 +1,17 @@ #include "objects.hpp" +#include + #include #include #include #include #include +#include +#include #include -#include +#include #include #include "../mwworld/ptr.hpp" @@ -16,16 +20,31 @@ #include "renderconst.hpp" using namespace MWRender; +float Objects::lightLinearValue() +{ + return mFallback->getFallbackFloat("LightAttenuation_LinearValue"); +} +float Objects::lightLinearRadiusMult() +{ + return mFallback->getFallbackFloat("LightAttenuation_LinearRadiusMult"); +} +float Objects::lightQuadraticValue() +{ + return mFallback->getFallbackFloat("LightAttenuation_QuadraticValue"); +} +float Objects::lightQuadraticRadiusMult() +{ + return mFallback->getFallbackFloat("LightAttenuation_QuadraticRadiusMult"); +} -/// \todo Replace these, once fallback values from the ini file are available. -float Objects::lightLinearValue = 3; -float Objects::lightLinearRadiusMult = 1; - -float Objects::lightQuadraticValue = 16; -float Objects::lightQuadraticRadiusMult = 1; - -bool Objects::lightOutQuadInLin = true; -bool Objects::lightQuadratic = false; +bool Objects::lightOutQuadInLin() +{ + return mFallback->getFallbackBool("LightAttenuation_OutQuadInLin"); +} +bool Objects::lightQuadratic() +{ + return mFallback->getFallbackBool("LightAttenuation_UseQuadratic"); +} int Objects::uniqueID = 0; @@ -34,19 +53,37 @@ void Objects::clearSceneNode (Ogre::SceneNode *node) for (int i=node->numAttachedObjects()-1; i>=0; --i) { Ogre::MovableObject *object = node->getAttachedObject (i); + + // for entities, destroy any objects attached to bones + if (object->getTypeFlags () == Ogre::SceneManager::ENTITY_TYPE_MASK) + { + Ogre::Entity* ent = static_cast(object); + Ogre::Entity::ChildObjectListIterator children = ent->getAttachedObjectIterator (); + while (children.hasMoreElements()) + { + mRenderer.getScene ()->destroyMovableObject (children.getNext ()); + } + } + node->detachObject (object); mRenderer.getScene()->destroyMovableObject (object); } + + Ogre::Node::ChildNodeIterator it = node->getChildIterator (); + while (it.hasMoreElements ()) + { + clearSceneNode(static_cast(it.getNext ())); + } } -void Objects::setMwRoot(Ogre::SceneNode* root) +void Objects::setRootNode(Ogre::SceneNode* root) { - mMwRoot = root; + mRootNode = root; } void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_) { - Ogre::SceneNode* root = mMwRoot; + Ogre::SceneNode* root = mRootNode; Ogre::SceneNode* cellnode; if(mCellSceneNodes.find(ptr.getCell()) == mCellSceneNodes.end()) { @@ -87,20 +124,16 @@ void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_) mIsStatic = static_; } -void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) +void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool light) { Ogre::SceneNode* insert = ptr.getRefData().getBaseNode(); assert(insert); Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; - NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(insert, NULL, mesh); - for(size_t i = 0;i < entities.mEntities.size();i++) - { - const Ogre::AxisAlignedBox &tmp = entities.mEntities[i]->getBoundingBox(); - bounds.merge(Ogre::AxisAlignedBox(insert->_getDerivedPosition() + tmp.getMinimum(), - insert->_getDerivedPosition() + tmp.getMaximum()) - ); - } + NifOgre::ObjectList objects = NifOgre::Loader::createObjects(insert, mesh); + for(size_t i = 0;i < objects.mEntities.size();i++) + bounds.merge(objects.mEntities[i]->getWorldBoundingBox(true)); + Ogre::Vector3 extents = bounds.getSize(); extents *= insert->getScale(); float size = std::max(std::max(extents.x, extents.y), extents.z); @@ -115,38 +148,37 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL; mBounds[ptr.getCell()].merge(bounds); - bool transparent = false; - for(size_t i = 0;i < entities.mEntities.size();i++) + bool anyTransparency = false; + for(size_t i = 0;!anyTransparency && i < objects.mEntities.size();i++) { - Ogre::Entity *ent = entities.mEntities[i]; - for (unsigned int i=0; igetNumSubEntities(); ++i) + Ogre::Entity *ent = objects.mEntities[i]; + for(unsigned int i=0;!anyTransparency && i < ent->getNumSubEntities(); ++i) { - Ogre::MaterialPtr mat = ent->getSubEntity(i)->getMaterial(); - Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); - while (techIt.hasMoreElements()) - { - Ogre::Technique* tech = techIt.getNext(); - Ogre::Technique::PassIterator passIt = tech->getPassIterator(); - while (passIt.hasMoreElements()) - { - Ogre::Pass* pass = passIt.getNext(); - - if (pass->getDepthWriteEnabled() == false) - transparent = true; - } - } + anyTransparency = ent->getSubEntity(i)->getMaterial()->isTransparent(); } } - if(!mIsStatic || !Settings::Manager::getBool("use static geometry", "Objects") || transparent) + if(!mIsStatic || !Settings::Manager::getBool("use static geometry", "Objects") || + anyTransparency || objects.mParticles.size() > 0) { - for(size_t i = 0;i < entities.mEntities.size();i++) + for(size_t i = 0;i < objects.mEntities.size();i++) { - Ogre::Entity *ent = entities.mEntities[i]; - + Ogre::Entity *ent = objects.mEntities[i]; + for(unsigned int i=0; i < ent->getNumSubEntities(); ++i) + { + Ogre::SubEntity* subEnt = ent->getSubEntity(i); + subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); + } ent->setRenderingDistance(small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0); ent->setVisibilityFlags(mIsStatic ? (small ? RV_StaticsSmall : RV_Statics) : RV_Misc); - ent->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); + } + for(size_t i = 0;i < objects.mParticles.size();i++) + { + Ogre::ParticleSystem *part = objects.mParticles[i]; + // TODO: Check the particle system's material for actual transparency + part->setRenderQueueGroup(RQG_Alpha); + part->setRenderingDistance(small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0); + part->setVisibilityFlags(mIsStatic ? (small ? RV_StaticsSmall : RV_Statics) : RV_Misc); } } else @@ -191,28 +223,42 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) sg->setCastShadows(true); - sg->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); + sg->setRenderQueueGroup(RQG_Main); - for(size_t i = 0;i < entities.mEntities.size();i++) + std::vector::reverse_iterator iter = objects.mEntities.rbegin(); + while(iter != objects.mEntities.rend()) { - Ogre::Entity *ent = entities.mEntities[i]; - insert->detachObject(ent); - sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale()); + Ogre::Node *node = (*iter)->getParentNode(); + sg->addEntity(*iter, node->_getDerivedPosition(), node->_getDerivedOrientation(), node->_getDerivedScale()); - mRenderer.getScene()->destroyEntity(ent); + (*iter)->detachFromParent(); + mRenderer.getScene()->destroyEntity(*iter); + iter++; } } + + if (light) + { + insertLight(ptr, objects.mSkelBase, bounds.getCenter() - insert->_getDerivedPosition()); + } } -void Objects::insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, float radius) +void Objects::insertLight (const MWWorld::Ptr& ptr, Ogre::Entity* skelBase, Ogre::Vector3 fallbackCenter) { Ogre::SceneNode* insert = mRenderer.getScene()->getSceneNode(ptr.getRefData().getHandle()); assert(insert); - Ogre::Light *light = mRenderer.getScene()->createLight(); - light->setDiffuseColour (r, g, b); MWWorld::LiveCellRef *ref = ptr.get(); + const int color = ref->mBase->mData.mColor; + const float r = ((color >> 0) & 0xFF) / 255.0f; + const float g = ((color >> 8) & 0xFF) / 255.0f; + const float b = ((color >> 16) & 0xFF) / 255.0f; + const float radius = float (ref->mBase->mData.mRadius); + + Ogre::Light *light = mRenderer.getScene()->createLight(); + light->setDiffuseColour (r, g, b); + LightInfo info; info.name = light->getName(); info.radius = radius; @@ -238,32 +284,38 @@ void Objects::insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, f info.time = Ogre::Math::RangeRandom(-500, +500); info.phase = Ogre::Math::RangeRandom(-500, +500); - // adjust the lights depending if we're in an interior or exterior cell - // quadratic means the light intensity falls off quite fast, resulting in a - // dark, atmospheric environment (perfect for exteriors) - // for interiors, we want more "warm" lights, so use linear attenuation. - bool quadratic = false; - if (!lightOutQuadInLin) - quadratic = lightQuadratic; + bool quadratic = lightOutQuadInLin() ? !info.interior : lightQuadratic(); + + // with the standard 1 / (c + d*l + d*d*q) equation the attenuation factor never becomes zero, + // so we ignore lights if their attenuation falls below this factor. + const float threshold = 0.03; + + if (!quadratic) + { + float r = radius * lightLinearRadiusMult(); + float attenuation = lightLinearValue() / r; + float activationRange = 1 / (threshold * attenuation); + light->setAttenuation(activationRange, 0, attenuation, 0); + } else { - quadratic = !info.interior; + float r = radius * lightQuadraticRadiusMult(); + float attenuation = lightQuadraticValue() / std::pow(r, 2); + float activationRange = std::sqrt(1 / (threshold * attenuation)); + light->setAttenuation(activationRange, 0, 0, attenuation); } - if (!quadratic) + // If there's an AttachLight bone, attach the light to that, otherwise attach it to the base scene node + if (skelBase && skelBase->getSkeleton ()->hasBone ("AttachLight")) { - float r = radius * lightLinearRadiusMult; - float attenuation = lightLinearValue / r; - light->setAttenuation(r*10, 0, attenuation, 0); + skelBase->attachObjectToBone ("AttachLight", light); } else { - float r = radius * lightQuadraticRadiusMult; - float attenuation = lightQuadraticValue / pow(r, 2); - light->setAttenuation(r*10, 0, 0, attenuation); + Ogre::SceneNode* childNode = insert->createChildSceneNode (fallbackCenter); + childNode->attachObject(light); } - insert->attachObject(light); mLights.push_back(info); } @@ -348,9 +400,9 @@ void Objects::enableLights() std::vector::iterator it = mLights.begin(); while (it != mLights.end()) { - if (mMwRoot->getCreator()->hasLight(it->name)) + if (mRootNode->getCreator()->hasLight(it->name)) { - mMwRoot->getCreator()->getLight(it->name)->setVisible(true); + mRootNode->getCreator()->getLight(it->name)->setVisible(true); ++it; } else @@ -363,9 +415,9 @@ void Objects::disableLights() std::vector::iterator it = mLights.begin(); while (it != mLights.end()) { - if (mMwRoot->getCreator()->hasLight(it->name)) + if (mRootNode->getCreator()->hasLight(it->name)) { - mMwRoot->getCreator()->getLight(it->name)->setVisible(false); + mRootNode->getCreator()->getLight(it->name)->setVisible(false); ++it; } else @@ -418,9 +470,9 @@ void Objects::update(const float dt) std::vector::iterator it = mLights.begin(); while (it != mLights.end()) { - if (mMwRoot->getCreator()->hasLight(it->name)) + if (mRootNode->getCreator()->hasLight(it->name)) { - Ogre::Light* light = mMwRoot->getCreator()->getLight(it->name); + Ogre::Light* light = mRootNode->getCreator()->getLight(it->name); float brightness; float cycle_time; @@ -502,21 +554,17 @@ void Objects::rebuildStaticGeometry() } } -void Objects::updateObjectCell(const MWWorld::Ptr &ptr) +void Objects::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur) { Ogre::SceneNode *node; - MWWorld::CellStore *newCell = ptr.getCell(); + MWWorld::CellStore *newCell = cur.getCell(); if(mCellSceneNodes.find(newCell) == mCellSceneNodes.end()) { - node = mMwRoot->createChildSceneNode(); + node = mRootNode->createChildSceneNode(); mCellSceneNodes[newCell] = node; } else { node = mCellSceneNodes[newCell]; } - node->addChild(ptr.getRefData().getBaseNode()); - - /// \note Still unaware how to move aabb and static w/o full rebuild, - /// moving static objects may cause problems - insertMesh(ptr, MWWorld::Class::get(ptr).getModel(ptr)); + node->addChild(cur.getRefData().getBaseNode()); } diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 48632dba8..cfe524306 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -2,8 +2,10 @@ #define _GAME_RENDER_OBJECTS_H #include +#include #include +#include "../mwworld/fallback.hpp" namespace MWWorld { @@ -52,28 +54,28 @@ class Objects{ std::map mStaticGeometrySmall; std::map mBounds; std::vector mLights; - Ogre::SceneNode* mMwRoot; + Ogre::SceneNode* mRootNode; bool mIsStatic; static int uniqueID; + MWWorld::Fallback* mFallback; + float lightLinearValue(); + float lightLinearRadiusMult(); - static float lightLinearValue; - static float lightLinearRadiusMult; + bool lightQuadratic(); + float lightQuadraticValue(); + float lightQuadraticRadiusMult(); - static bool lightQuadratic; - static float lightQuadraticValue; - static float lightQuadraticRadiusMult; - - static bool lightOutQuadInLin; + bool lightOutQuadInLin(); void clearSceneNode (Ogre::SceneNode *node); ///< Remove all movable objects from \a node. public: - Objects(OEngine::Render::OgreRenderer& renderer): mRenderer (renderer), mIsStatic(false) {} + Objects(OEngine::Render::OgreRenderer& renderer, MWWorld::Fallback* fallback): mRenderer (renderer), mIsStatic(false), mFallback(fallback) {} ~Objects(){} void insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_); - void insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh); - void insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, float radius); + void insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool light=false); + void insertLight (const MWWorld::Ptr& ptr, Ogre::Entity *skelBase=0, Ogre::Vector3 fallbackCenter=Ogre::Vector3(0.0f)); void enableLights(); void disableLights(); @@ -89,12 +91,12 @@ public: void removeCell(MWWorld::CellStore* store); void buildStaticGeometry(MWWorld::CellStore &cell); - void setMwRoot(Ogre::SceneNode* root); + void setRootNode(Ogre::SceneNode* root); void rebuildStaticGeometry(); /// Updates containing cell for object rendering data - void updateObjectCell(const MWWorld::Ptr &ptr); + void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur); }; } #endif diff --git a/apps/openmw/mwrender/occlusionquery.cpp b/apps/openmw/mwrender/occlusionquery.cpp index 6d3f67de9..477e38215 100644 --- a/apps/openmw/mwrender/occlusionquery.cpp +++ b/apps/openmw/mwrender/occlusionquery.cpp @@ -1,5 +1,4 @@ #include "occlusionquery.hpp" -#include "renderconst.hpp" #include #include @@ -7,16 +6,20 @@ #include #include #include +#include #include +#include "renderconst.hpp" + using namespace MWRender; using namespace Ogre; OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNode* sunNode) : - mSunTotalAreaQuery(0), mSunVisibleAreaQuery(0), mSingleObjectQuery(0), mActiveQuery(0), - mDoQuery(0), mSunVisibility(0), mQuerySingleObjectStarted(false), mTestResult(false), - mQuerySingleObjectRequested(false), mWasVisible(false), mObjectWasVisible(false), mDoQuery2(false), - mBBNode(0) + mSunTotalAreaQuery(0), mSunVisibleAreaQuery(0), mActiveQuery(0), + mDoQuery(0), mSunVisibility(0), + mWasVisible(false), + mActive(false), + mFirstFrame(true) { mRendering = renderer; mSunNode = sunNode; @@ -26,9 +29,8 @@ OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNod mSunTotalAreaQuery = renderSystem->createHardwareOcclusionQuery(); mSunVisibleAreaQuery = renderSystem->createHardwareOcclusionQuery(); - mSingleObjectQuery = renderSystem->createHardwareOcclusionQuery(); - mSupported = (mSunTotalAreaQuery != 0) && (mSunVisibleAreaQuery != 0) && (mSingleObjectQuery != 0); + mSupported = (mSunTotalAreaQuery != 0) && (mSunVisibleAreaQuery != 0); } catch (Ogre::Exception e) { @@ -41,52 +43,26 @@ OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNod return; } - MaterialPtr matBase = MaterialManager::getSingleton().getByName("BaseWhiteNoLighting"); - MaterialPtr matQueryArea = matBase->clone("QueryTotalPixels"); - matQueryArea->setDepthWriteEnabled(false); - matQueryArea->setColourWriteEnabled(false); - matQueryArea->setDepthCheckEnabled(false); // Not occluded by objects - MaterialPtr matQueryVisible = matBase->clone("QueryVisiblePixels"); - matQueryVisible->setDepthWriteEnabled(false); - matQueryVisible->setColourWriteEnabled(false); // Uncomment this to visualize the occlusion query - matQueryVisible->setDepthCheckEnabled(true); // Occluded by objects - matQueryVisible->setCullingMode(CULL_NONE); - matQueryVisible->setManualCullingMode(MANUAL_CULL_NONE); - - if (sunNode) - mBBNode = mSunNode->getParentSceneNode()->createChildSceneNode(); - - mObjectNode = mRendering->getScene()->getRootSceneNode()->createChildSceneNode(); mBBNodeReal = mRendering->getScene()->getRootSceneNode()->createChildSceneNode(); - mBBQueryTotal = mRendering->getScene()->createBillboardSet(1); + static Ogre::Mesh* plane = MeshManager::getSingleton().createPlane("occlusionbillboard", + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::Plane(Ogre::Vector3(0,0,1), 0), 1, 1, 1, 1, true, 1, 1, 1, Vector3::UNIT_Y).get(); + plane->_setBounds(Ogre::AxisAlignedBox::BOX_INFINITE); + + mBBQueryTotal = mRendering->getScene()->createEntity("occlusionbillboard"); mBBQueryTotal->setCastShadows(false); - mBBQueryTotal->setDefaultDimensions(150, 150); - mBBQueryTotal->createBillboard(Vector3::ZERO); - mBBQueryTotal->setMaterialName("QueryTotalPixels"); - mBBQueryTotal->setRenderQueueGroup(RQG_OcclusionQuery+1); mBBQueryTotal->setVisibilityFlags(RV_OcclusionQuery); + mBBQueryTotal->setRenderQueueGroup(RQG_OcclusionQuery+1); + mBBQueryTotal->setMaterialName("QueryTotalPixels"); mBBNodeReal->attachObject(mBBQueryTotal); - mBBQueryVisible = mRendering->getScene()->createBillboardSet(1); + mBBQueryVisible = mRendering->getScene()->createEntity("occlusionbillboard"); mBBQueryVisible->setCastShadows(false); - mBBQueryVisible->setDefaultDimensions(150, 150); - mBBQueryVisible->createBillboard(Vector3::ZERO); - mBBQueryVisible->setMaterialName("QueryVisiblePixels"); - mBBQueryVisible->setRenderQueueGroup(RQG_OcclusionQuery+1); mBBQueryVisible->setVisibilityFlags(RV_OcclusionQuery); + mBBQueryVisible->setRenderQueueGroup(RQG_OcclusionQuery+1); + mBBQueryVisible->setMaterialName("QueryVisiblePixels"); mBBNodeReal->attachObject(mBBQueryVisible); - mBBQuerySingleObject = mRendering->getScene()->createBillboardSet(1); - /// \todo ideally this should occupy exactly 1 pixel on the screen - mBBQuerySingleObject->setCastShadows(false); - mBBQuerySingleObject->setDefaultDimensions(0.003, 0.003); - mBBQuerySingleObject->createBillboard(Vector3::ZERO); - mBBQuerySingleObject->setMaterialName("QueryVisiblePixels"); - mBBQuerySingleObject->setRenderQueueGroup(RQG_OcclusionQuery); - mBBQuerySingleObject->setVisibilityFlags(RV_OcclusionQuery); - mObjectNode->attachObject(mBBQuerySingleObject); - mRendering->getScene()->addRenderObjectListener(this); mRendering->getScene()->addRenderQueueListener(this); mDoQuery = true; @@ -94,10 +70,14 @@ OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNod OcclusionQuery::~OcclusionQuery() { + mRendering->getScene()->removeRenderObjectListener (this); + mRendering->getScene()->removeRenderQueueListener(this); + RenderSystem* renderSystem = Root::getSingleton().getRenderSystem(); - if (mSunTotalAreaQuery) renderSystem->destroyHardwareOcclusionQuery(mSunTotalAreaQuery); - if (mSunVisibleAreaQuery) renderSystem->destroyHardwareOcclusionQuery(mSunVisibleAreaQuery); - if (mSingleObjectQuery) renderSystem->destroyHardwareOcclusionQuery(mSingleObjectQuery); + if (mSunTotalAreaQuery) + renderSystem->destroyHardwareOcclusionQuery(mSunTotalAreaQuery); + if (mSunVisibleAreaQuery) + renderSystem->destroyHardwareOcclusionQuery(mSunVisibleAreaQuery); } bool OcclusionQuery::supported() @@ -108,8 +88,10 @@ bool OcclusionQuery::supported() void OcclusionQuery::notifyRenderSingleObject(Renderable* rend, const Pass* pass, const AutoParamDataSource* source, const LightList* pLightList, bool suppressRenderStateChanges) { + if (!mActive) return; + // The following code activates and deactivates the occlusion queries - // so that the queries only include the rendering of their intended targets + // so that the queries only include the rendering of the intended meshes // Close the last occlusion query // Each occlusion query should only last a single rendering @@ -122,23 +104,16 @@ void OcclusionQuery::notifyRenderSingleObject(Renderable* rend, const Pass* pass // Open a new occlusion query if (mDoQuery == true) { - if (rend == mBBQueryTotal) + if (rend == mBBQueryTotal->getSubEntity(0)) { mActiveQuery = mSunTotalAreaQuery; mWasVisible = true; } - else if (rend == mBBQueryVisible) + else if (rend == mBBQueryVisible->getSubEntity(0)) { mActiveQuery = mSunVisibleAreaQuery; } } - if (mDoQuery == true && rend == mBBQuerySingleObject) - { - mQuerySingleObjectStarted = true; - mQuerySingleObjectRequested = false; - mActiveQuery = mSingleObjectQuery; - mObjectWasVisible = true; - } if (mActiveQuery != NULL) mActiveQuery->beginOcclusionQuery(); @@ -146,6 +121,8 @@ void OcclusionQuery::notifyRenderSingleObject(Renderable* rend, const Pass* pass void OcclusionQuery::renderQueueEnded(uint8 queueGroupId, const String& invocation, bool& repeatThisInvocation) { + if (!mActive) return; + if (mActiveQuery != NULL) { mActiveQuery->endOcclusionQuery(); @@ -166,22 +143,20 @@ void OcclusionQuery::renderQueueEnded(uint8 queueGroupId, const String& invocati mSunVisibleAreaQuery->beginOcclusionQuery(); mSunVisibleAreaQuery->endOcclusionQuery(); } - if (mObjectWasVisible == false && mDoQuery) - { - mSingleObjectQuery->beginOcclusionQuery(); - mSingleObjectQuery->endOcclusionQuery(); - mQuerySingleObjectStarted = true; - mQuerySingleObjectRequested = false; - } } } void OcclusionQuery::update(float duration) { + if (mFirstFrame) + { + // GLHardwareOcclusionQuery::isStillOutstanding doesn't seem to like getting called when nothing has been rendered yet + mFirstFrame = false; + return; + } if (!mSupported) return; mWasVisible = false; - mObjectWasVisible = false; // Adjust the position of the sun billboards according to camera viewing distance // we need to do this to make sure that _everything_ can occlude the sun @@ -189,12 +164,11 @@ void OcclusionQuery::update(float duration) if (dist==0) dist = 10000000; dist -= 1000; // bias dist /= 1000.f; - if (mBBNode) + if (mSunNode) { - mBBNode->setPosition(mSunNode->getPosition() * dist); - mBBNode->setScale(dist, dist, dist); - mBBNodeReal->setPosition(mBBNode->_getDerivedPosition()); - mBBNodeReal->setScale(mBBNode->getScale()); + mBBNodeReal->setPosition(mSunNode->getPosition() * dist); + mBBNodeReal->setOrientation(Ogre::Vector3::UNIT_Z.getRotationTo(-mBBNodeReal->getPosition().normalisedCopy())); + mBBNodeReal->setScale(150.f*dist, 150.f*dist, 150.f*dist); } // Stop occlusion queries until we get their information @@ -202,8 +176,7 @@ void OcclusionQuery::update(float duration) mDoQuery = false; if (!mSunTotalAreaQuery->isStillOutstanding() - && !mSunVisibleAreaQuery->isStillOutstanding() - && !mSingleObjectQuery->isStillOutstanding()) + && !mSunVisibleAreaQuery->isStillOutstanding()) { unsigned int totalPixels; unsigned int visiblePixels; @@ -222,86 +195,11 @@ void OcclusionQuery::update(float duration) if (mSunVisibility > 1) mSunVisibility = 1; } - unsigned int result; - - mSingleObjectQuery->pullOcclusionQuery(&result); - - mTestResult = (result != 0); - - mQuerySingleObjectStarted = false; - mQuerySingleObjectRequested = false; - mDoQuery = true; } } -void OcclusionQuery::occlusionTest(const Ogre::Vector3& position, Ogre::SceneNode* object) -{ - assert( !occlusionTestPending() - && "Occlusion test still pending"); - - mBBQuerySingleObject->setVisible(true); - - mObjectNode->setPosition(position); - // scale proportional to camera distance, in order to always give the billboard the same size in screen-space - mObjectNode->setScale( Vector3(1,1,1)*(position - mRendering->getCamera()->getRealPosition()).length() ); - - mQuerySingleObjectRequested = true; -} - -bool OcclusionQuery::occlusionTestPending() -{ - return (mQuerySingleObjectRequested || mQuerySingleObjectStarted); -} - void OcclusionQuery::setSunNode(Ogre::SceneNode* node) { mSunNode = node; - if (!mBBNode) - mBBNode = node->getParentSceneNode()->createChildSceneNode(); -} - -bool OcclusionQuery::getTestResult() -{ - assert( !occlusionTestPending() - && "Occlusion test still pending"); - - return mTestResult; -} - -bool OcclusionQuery::isPotentialOccluder(Ogre::SceneNode* node) -{ - bool result = false; - for (unsigned int i=0; i < node->numAttachedObjects(); ++i) - { - MovableObject* ob = node->getAttachedObject(i); - std::string type = ob->getMovableType(); - if (type == "Entity") - { - Entity* ent = static_cast(ob); - for (unsigned int j=0; j < ent->getNumSubEntities(); ++j) - { - // if any sub entity has a material with depth write off, - // consider the object as not an occluder - MaterialPtr mat = ent->getSubEntity(j)->getMaterial(); - - Material::TechniqueIterator techIt = mat->getTechniqueIterator(); - while (techIt.hasMoreElements()) - { - Technique* tech = techIt.getNext(); - Technique::PassIterator passIt = tech->getPassIterator(); - while (passIt.hasMoreElements()) - { - Pass* pass = passIt.getNext(); - - if (pass->getDepthWriteEnabled() == false) - return false; - else - result = true; - } - } - } - } - } - return result; } diff --git a/apps/openmw/mwrender/occlusionquery.hpp b/apps/openmw/mwrender/occlusionquery.hpp index c76fcccd0..983361c18 100644 --- a/apps/openmw/mwrender/occlusionquery.hpp +++ b/apps/openmw/mwrender/occlusionquery.hpp @@ -30,34 +30,15 @@ namespace MWRender bool supported(); /** - * per-frame update - */ - void update(float duration); - - /** - * request occlusion test for a billboard at the given position, omitting an entity - * @param position of the billboard in ogre coordinates - * @param object to exclude from the occluders - */ - void occlusionTest(const Ogre::Vector3& position, Ogre::SceneNode* object); - - /** - * @return true if a request is still outstanding + * make sure to disable occlusion queries before updating unrelated render targets + * @param active */ - bool occlusionTestPending(); + void setActive (bool active) { mActive = active; } /** - * Checks if the objects held by this scenenode - * can be considered as potential occluders - * (which might not be the case when transparency is involved) - * @param Scene node - */ - bool isPotentialOccluder(Ogre::SceneNode* node); - - /** - * @return true if the object tested in the last request was occluded + * per-frame update */ - bool getTestResult(); + void update(float duration); float getSunVisibility() const {return mSunVisibility;}; @@ -66,31 +47,22 @@ namespace MWRender private: Ogre::HardwareOcclusionQuery* mSunTotalAreaQuery; Ogre::HardwareOcclusionQuery* mSunVisibleAreaQuery; - Ogre::HardwareOcclusionQuery* mSingleObjectQuery; Ogre::HardwareOcclusionQuery* mActiveQuery; - Ogre::BillboardSet* mBBQueryVisible; - Ogre::BillboardSet* mBBQueryTotal; - Ogre::BillboardSet* mBBQuerySingleObject; + Ogre::Entity* mBBQueryVisible; + Ogre::Entity* mBBQueryTotal; Ogre::SceneNode* mSunNode; - Ogre::SceneNode* mBBNode; Ogre::SceneNode* mBBNodeReal; float mSunVisibility; - Ogre::SceneNode* mObjectNode; - bool mWasVisible; - bool mObjectWasVisible; - bool mTestResult; + bool mActive; + bool mFirstFrame; bool mSupported; bool mDoQuery; - bool mDoQuery2; - - bool mQuerySingleObjectRequested; - bool mQuerySingleObjectStarted; OEngine::Render::OgreRenderer* mRendering; diff --git a/apps/openmw/mwrender/refraction.cpp b/apps/openmw/mwrender/refraction.cpp new file mode 100644 index 000000000..d590dbf4c --- /dev/null +++ b/apps/openmw/mwrender/refraction.cpp @@ -0,0 +1,107 @@ +#include "refraction.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "renderconst.hpp" + +namespace MWRender +{ + + Refraction::Refraction(Ogre::Camera *parentCamera) + : mParentCamera(parentCamera) + , mRenderActive(false) + , mIsUnderwater(false) + { + mCamera = mParentCamera->getSceneManager()->createCamera("RefractionCamera"); + + mParentCamera->getSceneManager()->addRenderQueueListener(this); + + Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().createManual("WaterRefraction", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, 512, 512, 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET); + + mRenderTarget = texture->getBuffer()->getRenderTarget(); + Ogre::Viewport* vp = mRenderTarget->addViewport(mCamera); + vp->setOverlaysEnabled(false); + vp->setShadowsEnabled(false); + vp->setVisibilityMask(RV_Actors + RV_Misc + RV_Statics + RV_StaticsSmall + RV_Terrain + RV_Sky); + vp->setMaterialScheme("water_refraction"); + vp->setBackgroundColour (Ogre::ColourValue(0.18039, 0.23137, 0.25490)); + mRenderTarget->setAutoUpdated(true); + mRenderTarget->addListener(this); + } + + Refraction::~Refraction() + { + mRenderTarget->removeListener(this); + Ogre::TextureManager::getSingleton().remove("WaterRefraction"); + mParentCamera->getSceneManager()->destroyCamera(mCamera); + mParentCamera->getSceneManager()->removeRenderQueueListener(this); + } + + void Refraction::preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt) + { + mParentCamera->getParentSceneNode ()->needUpdate (); + mCamera->setOrientation(mParentCamera->getDerivedOrientation()); + mCamera->setPosition(mParentCamera->getDerivedPosition()); + mCamera->setNearClipDistance(mParentCamera->getNearClipDistance()); + mCamera->setFarClipDistance(mParentCamera->getFarClipDistance()); + mCamera->setAspectRatio(mParentCamera->getAspectRatio()); + mCamera->setFOVy(mParentCamera->getFOVy()); + + // for depth calculation, we want the original viewproj matrix _without_ the custom near clip plane. + // since all we are interested in is depth, we only need the third row of the matrix. + Ogre::Matrix4 projMatrix = mCamera->getProjectionMatrixWithRSDepth () * mCamera->getViewMatrix (); + sh::Vector4* row3 = new sh::Vector4(projMatrix[2][0], projMatrix[2][1], projMatrix[2][2], projMatrix[2][3]); + sh::Factory::getInstance ().setSharedParameter ("vpRow2Fix", sh::makeProperty (row3)); + + // enable clip plane here to take advantage of CPU culling for overwater or underwater objects + mCamera->enableCustomNearClipPlane(mIsUnderwater ? mNearClipPlaneUnderwater : mNearClipPlane); + + mRenderActive = true; + } + + void Refraction::postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt) + { + mCamera->disableCustomNearClipPlane (); + mRenderActive = false; + } + + void Refraction::setHeight(float height) + { + mNearClipPlane = Ogre::Plane( -Ogre::Vector3(0,0,1), -(height + 5)); + mNearClipPlaneUnderwater = Ogre::Plane( Ogre::Vector3(0,0,1), height - 5); + } + + void Refraction::renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &skipThisInvocation) + { + // We don't want the sky to get clipped by custom near clip plane (the water plane) + if (queueGroupId < 20 && mRenderActive) + { + mCamera->disableCustomNearClipPlane(); + Ogre::Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mCamera->getProjectionMatrixRS()); + } + } + + void Refraction::renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &repeatThisInvocation) + { + if (queueGroupId < 20 && mRenderActive) + { + mCamera->enableCustomNearClipPlane(mIsUnderwater ? mNearClipPlaneUnderwater : mNearClipPlane); + Ogre::Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mCamera->getProjectionMatrixRS()); + } + } + + void Refraction::setActive(bool active) + { + mRenderTarget->setActive(active); + } + +} diff --git a/apps/openmw/mwrender/refraction.hpp b/apps/openmw/mwrender/refraction.hpp new file mode 100644 index 000000000..b9ab8deac --- /dev/null +++ b/apps/openmw/mwrender/refraction.hpp @@ -0,0 +1,45 @@ +#ifndef MWRENDER_REFRACTION_H +#define MWRENDER_REFRACTION_H + +#include +#include +#include + +namespace Ogre +{ + class Camera; + class RenderTarget; +} + +namespace MWRender +{ + + class Refraction : public Ogre::RenderTargetListener, public Ogre::RenderQueueListener + { + + public: + Refraction(Ogre::Camera* parentCamera); + ~Refraction(); + + void setHeight (float height); + void preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt); + void postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt); + void setUnderwater(bool underwater) {mIsUnderwater = underwater;} + void setActive (bool active); + + void renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &skipThisInvocation); + void renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &repeatThisInvocation); + + private: + Ogre::Camera* mParentCamera; + Ogre::Camera* mCamera; + Ogre::RenderTarget* mRenderTarget; + Ogre::Plane mNearClipPlane; + Ogre::Plane mNearClipPlaneUnderwater; + bool mRenderActive; + bool mIsUnderwater; + }; + +} + +#endif diff --git a/apps/openmw/mwrender/renderconst.hpp b/apps/openmw/mwrender/renderconst.hpp index 75e243ec7..1d2cdf1ea 100644 --- a/apps/openmw/mwrender/renderconst.hpp +++ b/apps/openmw/mwrender/renderconst.hpp @@ -20,7 +20,7 @@ enum RenderQueueGroups RQG_UnderWater = Ogre::RENDER_QUEUE_4, - RQG_Water = Ogre::RENDER_QUEUE_7+1, + RQG_Water = RQG_Alpha, // Sky late (sun & sun flare) RQG_SkiesLate = Ogre::RENDER_QUEUE_SKIES_LATE @@ -54,9 +54,10 @@ enum VisibilityFlags RV_OcclusionQuery = 256, - RV_PlayerPreview = 512, + RV_Debug = 512, - RV_Debug = 1024, + // overlays, we only want these on the main render target + RV_Overlay = 1024, RV_Map = RV_Terrain + RV_Statics + RV_StaticsSmall + RV_Misc + RV_Water }; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 5d2a5c951..e327c3c89 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -14,19 +14,25 @@ #include #include #include +#include #include #include +#include + #include -#include "../mwworld/esmstore.hpp" #include +#include "../mwworld/esmstore.hpp" +#include "../mwworld/class.hpp" #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone #include "../mwbase/environment.hpp" #include "../mwbase/inputmanager.hpp" // FIXME #include "../mwbase/windowmanager.hpp" // FIXME +#include "../mwmechanics/creaturestats.hpp" + #include "../mwworld/ptr.hpp" #include "../mwworld/player.hpp" @@ -44,24 +50,37 @@ using namespace Ogre; namespace MWRender { -RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, - const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine) - : mRendering(_rend), mObjects(mRendering), mActors(mRendering), mAmbientMode(0), mSunEnabled(0), mPhysicsEngine(engine) +RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, + const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine, + MWWorld::Fallback* fallback) + : mRendering(_rend) + , mFallback(fallback) + , mObjects(mRendering, mFallback) + , mActors(mRendering, this) + , mPlayerAnimation(NULL) + , mAmbientMode(0) + , mSunEnabled(0) + , mPhysicsEngine(engine) { // 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); // glsl is only supported in opengl mode and hlsl only in direct3d mode. - if (Settings::Manager::getString("shader mode", "General") == "" - || (openGL && Settings::Manager::getString("shader mode", "General") == "hlsl") - || (!openGL && Settings::Manager::getString("shader mode", "General") == "glsl")) + std::string currentMode = Settings::Manager::getString("shader mode", "General"); + if (currentMode == "" + || (openGL && currentMode == "hlsl") + || (!openGL && currentMode == "glsl") + || (glES && currentMode != "glsles")) { - Settings::Manager::setString("shader mode", "General", openGL ? "glsl" : "hlsl"); + Settings::Manager::setString("shader mode", "General", openGL ? (glES ? "glsles" : "glsl") : "hlsl"); } mRendering.createScene("PlayerCam", Settings::Manager::getFloat("field of view", "General"), 5); mRendering.setWindowEventListener(this); + mRendering.getWindow()->addListener(this); + mCompositors = new Compositors(mRendering.getViewport()); mWater = 0; @@ -77,6 +96,8 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const std::string l = Settings::Manager::getString("shader mode", "General"); if (l == "glsl") lang = sh::Language_GLSL; + else if (l == "glsles") + lang = sh::Language_GLSLES; else if (l == "hlsl") lang = sh::Language_HLSL; else @@ -90,7 +111,9 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mFactory->loadAllFiles(); // Set default mipmap level (NB some APIs ignore this) - TextureManager::getSingleton().setDefaultNumMipmaps(Settings::Manager::getInt("num mipmaps", "General")); + // Mipmap generation is currently disabled because it causes issues on Intel/AMD + //TextureManager::getSingleton().setDefaultNumMipmaps(Settings::Manager::getInt("num mipmaps", "General")); + TextureManager::getSingleton().setDefaultNumMipmaps(0); // Set default texture filtering options TextureFilterOptions tfo; @@ -103,61 +126,47 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const MaterialManager::getSingleton().setDefaultTextureFiltering(tfo); MaterialManager::getSingleton().setDefaultAnisotropy( (filter == "anisotropic") ? Settings::Manager::getInt("anisotropy", "General") : 1 ); - //ResourceGroupManager::getSingleton ().declareResource ("GlobalMap.png", "Texture", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + Ogre::TextureManager::getSingleton().setMemoryBudget(126*1024*1024); + Ogre::MeshManager::getSingleton().setMemoryBudget(64*1024*1024); ResourceGroupManager::getSingleton().initialiseAllResourceGroups(); - // causes light flicker in opengl when moving.. - //mRendering.getScene()->setCameraRelativeRendering(true); - // disable unsupported effects - //const RenderSystemCapabilities* caps = Root::getSingleton().getRenderSystem()->getCapabilities(); - if (!waterShaderSupported()) - Settings::Manager::setBool("shader", "Water", false); if (!Settings::Manager::getBool("shaders", "Objects")) Settings::Manager::setBool("enabled", "Shadows", false); sh::Factory::getInstance ().setShadersEnabled (Settings::Manager::getBool("shaders", "Objects")); - sh::Factory::getInstance ().setGlobalSetting ("mrt_output", useMRT() ? "true" : "false"); sh::Factory::getInstance ().setGlobalSetting ("fog", "true"); - sh::Factory::getInstance ().setGlobalSetting ("lighting", "true"); sh::Factory::getInstance ().setGlobalSetting ("num_lights", Settings::Manager::getString ("num lights", "Objects")); sh::Factory::getInstance ().setGlobalSetting ("terrain_num_lights", Settings::Manager::getString ("num lights", "Terrain")); - sh::Factory::getInstance ().setGlobalSetting ("underwater_effects", Settings::Manager::getString("underwater effect", "Water")); sh::Factory::getInstance ().setGlobalSetting ("simple_water", Settings::Manager::getBool("shader", "Water") ? "false" : "true"); + sh::Factory::getInstance ().setGlobalSetting ("render_refraction", "false"); - sh::Factory::getInstance ().setSharedParameter ("viewportBackground", sh::makeProperty (new sh::Vector3(0,0,0))); sh::Factory::getInstance ().setSharedParameter ("waterEnabled", sh::makeProperty (new sh::FloatValue(0.0))); sh::Factory::getInstance ().setSharedParameter ("waterLevel", sh::makeProperty(new sh::FloatValue(0))); sh::Factory::getInstance ().setSharedParameter ("waterTimer", sh::makeProperty(new sh::FloatValue(0))); sh::Factory::getInstance ().setSharedParameter ("windDir_windSpeed", sh::makeProperty(new sh::Vector3(0.5, -0.8, 0.2))); sh::Factory::getInstance ().setSharedParameter ("waterSunFade_sunHeight", sh::makeProperty(new sh::Vector2(1, 0.6))); - sh::Factory::getInstance ().setSharedParameter ("gammaCorrection", sh::makeProperty(new sh::FloatValue( - Settings::Manager::getFloat ("gamma", "Video")))); + sh::Factory::getInstance ().setGlobalSetting ("refraction", Settings::Manager::getBool("refraction", "Water") ? "true" : "false"); + sh::Factory::getInstance ().setGlobalSetting ("viewproj_fix", "false"); + sh::Factory::getInstance ().setSharedParameter ("vpRow2Fix", sh::makeProperty (new sh::Vector4(0,0,0,0))); applyCompositors(); - // Turn the entire scene (represented by the 'root' node) -90 - // degrees around the x axis. This makes Z go upwards, and Y go into - // the screen (when x is to the right.) This is the orientation that - // Morrowind uses, and it automagically makes everything work as it - // should. - SceneNode *rt = mRendering.getScene()->getRootSceneNode(); - mMwRoot = rt->createChildSceneNode("mwRoot"); - mMwRoot->pitch(Degree(-90)); + mRootNode = mRendering.getScene()->getRootSceneNode(); + mRootNode->createChildSceneNode("player"); - mObjects.setMwRoot(mMwRoot); - mActors.setMwRoot(mMwRoot); + mObjects.setRootNode(mRootNode); + mActors.setRootNode(mRootNode); - Ogre::SceneNode *playerNode = mMwRoot->createChildSceneNode ("player"); - mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); + mCamera = new MWRender::Camera(mRendering.getCamera()); mShadows = new Shadows(&mRendering); mTerrainManager = new TerrainManager(mRendering.getScene(), this); - mSkyManager = new SkyManager(mMwRoot, mRendering.getCamera()); + mSkyManager = new SkyManager(mRootNode, mRendering.getCamera()); mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode()); @@ -166,17 +175,21 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mSun = 0; - mDebugging = new Debugging(mMwRoot, engine); + mDebugging = new Debugging(mRootNode, engine); mLocalMap = new MWRender::LocalMap(&mRendering, this); + mWater = new MWRender::Water(mRendering.getCamera(), this); + setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI")); } RenderingManager::~RenderingManager () { + mRendering.getWindow()->removeListener(this); mRendering.removeWindowEventListener(this); - delete mPlayer; + delete mPlayerAnimation; + delete mCamera; delete mSkyManager; delete mDebugging; delete mShadows; @@ -217,23 +230,21 @@ void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store) void RenderingManager::removeWater () { - if(mWater){ - mWater->setActive(false); - } + mWater->setActive(false); } void RenderingManager::toggleWater() { - if (mWater) - mWater->toggle(); + mWater->toggle(); } void RenderingManager::cellAdded (MWWorld::Ptr::CellStore *store) { mObjects.buildStaticGeometry (*store); + sh::Factory::getInstance().unloadUnreferencedMaterials(); mDebugging->cellAdded(store); if (store->mCell->isExterior()) - mTerrainManager->cellAdded(store); + mTerrainManager->cellAdded(store); waterAdded(store); } @@ -252,8 +263,7 @@ void RenderingManager::removeObject (const MWWorld::Ptr& ptr) void RenderingManager::moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position) { /// \todo move this to the rendering-subsystems - mRendering.getScene()->getSceneNode (ptr.getRefData().getHandle())-> - setPosition (position); + ptr.getRefData().getBaseNode()->setPosition(position); } void RenderingManager::scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale) @@ -261,94 +271,82 @@ void RenderingManager::scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3 ptr.getRefData().getBaseNode()->setScale(scale); } -bool RenderingManager::rotateObject( const MWWorld::Ptr &ptr, Ogre::Vector3 &rot, bool adjust) +void RenderingManager::rotateObject(const MWWorld::Ptr &ptr) { - bool isActive = ptr.getRefData().getBaseNode() != 0; - bool isPlayer = isActive && ptr.getRefData().getHandle() == "player"; - bool force = true; - - if (isPlayer) - force = mPlayer->rotate(rot, adjust); - - MWWorld::Class::get(ptr).adjustRotation(ptr, rot.x, rot.y, rot.z); + Ogre::Vector3 rot(ptr.getRefData().getPosition().rot); - if (!isPlayer && isActive) - { - Ogre::Quaternion xr(Ogre::Radian(rot.x), Ogre::Vector3::UNIT_X); - Ogre::Quaternion yr(Ogre::Radian(rot.y), Ogre::Vector3::UNIT_Y); - Ogre::Quaternion zr(Ogre::Radian(rot.z), Ogre::Vector3::UNIT_Z); - Ogre::Quaternion newo = adjust ? (xr * yr * zr) * ptr.getRefData().getBaseNode()->getOrientation() : xr * yr * zr; - rot.x = newo.x; - rot.y = newo.y; - rot.z = newo.z; - ptr.getRefData().getBaseNode()->setOrientation(newo); - } - else if(isPlayer) - { - rot.x = mPlayer->getPitch(); - rot.z = mPlayer->getYaw(); - } - else if (adjust) - { - // Stored and passed in radians - float *f = ptr.getRefData().getPosition().rot; - rot.x += f[0]; - rot.y += f[1]; - rot.z += f[2]; - } - return force; + if(ptr.getRefData().getHandle() == mCamera->getHandle() && + !mCamera->isVanityOrPreviewModeEnabled()) + mCamera->rotateCamera(rot, false); + + Ogre::Quaternion newo = Ogre::Quaternion(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Z); + if(!MWWorld::Class::get(ptr).isActor()) + newo = Ogre::Quaternion(Ogre::Radian(-rot.x), Ogre::Vector3::UNIT_X) * + Ogre::Quaternion(Ogre::Radian(-rot.y), Ogre::Vector3::UNIT_Y) * newo; + + ptr.getRefData().getBaseNode()->setOrientation(newo); } void -RenderingManager::moveObjectToCell( - const MWWorld::Ptr& ptr, - const Ogre::Vector3& pos, - MWWorld::CellStore *store) +RenderingManager::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur) { Ogre::SceneNode *child = - mRendering.getScene()->getSceneNode(ptr.getRefData().getHandle()); + mRendering.getScene()->getSceneNode(old.getRefData().getHandle()); Ogre::SceneNode *parent = child->getParentSceneNode(); parent->removeChild(child); - if (MWWorld::Class::get(ptr).isActor()) { - mActors.updateObjectCell(ptr); + if (MWWorld::Class::get(old).isActor()) { + mActors.updateObjectCell(old, cur); } else { - mObjects.updateObjectCell(ptr); + mObjects.updateObjectCell(old, cur); } - child->setPosition(pos); } void RenderingManager::update (float duration, bool paused) { + MWBase::World *world = MWBase::Environment::get().getWorld(); + + MWWorld::Ptr player = world->getPlayer().getPlayer(); + + int blind = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Blind)).mMagnitude; + mRendering.getFader()->setFactor(1.f-(blind / 100.f)); + setAmbientMode(); + + // player position + MWWorld::RefData &data = player.getRefData(); + float *_playerPos = data.getPosition().pos; + Ogre::Vector3 playerPos(_playerPos[0], _playerPos[1], _playerPos[2]); + Ogre::Vector3 orig, dest; - mPlayer->setCameraDistance(); - if (!mPlayer->getPosition(orig, dest)) { - orig.z += mPlayer->getHeight() * mMwRoot->getScale().z; + mCamera->setCameraDistance(); + if(!mCamera->getPosition(orig, dest)) + { + orig.z += mCamera->getHeight() * mRootNode->getScale().z; btVector3 btOrig(orig.x, orig.y, orig.z); btVector3 btDest(dest.x, dest.y, dest.z); - std::pair test = - mPhysicsEngine->rayTest(btOrig, btDest); - if (!test.first.empty()) { - mPlayer->setCameraDistance(test.second * orig.distance(dest), false, false); - } + std::pair test = mPhysicsEngine->sphereCast(mRendering.getCamera()->getNearClipDistance()*2.5, btOrig, btDest); + if (test.first) + mCamera->setCameraDistance(test.second * orig.distance(dest), false, false); } + mOcclusionQuery->update(duration); - + mVideoPlayer->update (); mRendering.update(duration); + Ogre::ControllerManager::getSingleton().setTimeFactor(paused ? 0.f : 1.f); + + Ogre::Vector3 cam = mRendering.getCamera()->getRealPosition(); + + applyFog(world->isUnderwater(player.getCell(), cam)); + if(paused) - { - Ogre::ControllerManager::getSingleton().setTimeFactor(0.f); return; - } - Ogre::ControllerManager::getSingleton().setTimeFactor( - MWBase::Environment::get().getWorld()->getTimeScaleFactor()/30.f); - mPlayer->update(duration); + mCamera->update(duration); mActors.update (duration); mObjects.update (duration); @@ -358,36 +356,26 @@ void RenderingManager::update (float duration, bool paused) mSkyManager->setGlare(mOcclusionQuery->getSunVisibility()); - MWWorld::RefData &data = - MWBase::Environment::get() - .getWorld() - ->getPlayer() - .getPlayer() - .getRefData(); - - float *fpos = data.getPosition().pos; - - // only for LocalMap::updatePlayer() - Ogre::Vector3 pos(fpos[0], -fpos[2], -fpos[1]); - Ogre::SceneNode *node = data.getBaseNode(); - Ogre::Quaternion orient = - node->convertLocalToWorldOrientation(node->_getDerivedOrientation()); + Ogre::Quaternion orient = node->_getDerivedOrientation(); - mLocalMap->updatePlayer(pos, orient); + mLocalMap->updatePlayer(playerPos, orient); - if (mWater) { - Ogre::Vector3 cam = mRendering.getCamera()->getRealPosition(); + mWater->updateUnderwater(world->isUnderwater(player.getCell(), cam)); - MWBase::World *world = MWBase::Environment::get().getWorld(); + mWater->update(duration, playerPos); +} - mWater->updateUnderwater( - world->isUnderwater( - *world->getPlayer().getPlayer().getCell()->mCell, - Ogre::Vector3(cam.x, -cam.z, cam.y)) - ); - mWater->update(duration); - } +void RenderingManager::preRenderTargetUpdate(const RenderTargetEvent &evt) +{ + mOcclusionQuery->setActive(true); +} + +void RenderingManager::postRenderTargetUpdate(const RenderTargetEvent &evt) +{ + // deactivate queries to make sure we aren't getting false results from several misc render targets + // (will be reactivated at the bottom of this method) + mOcclusionQuery->setActive(false); } void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store) @@ -399,10 +387,7 @@ void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store) || ((store->mCell->isExterior()) && !lands.search(store->mCell->getGridX(),store->mCell->getGridY()) )) // always use water, if the cell does not have land. { - if(mWater == 0) - mWater = new MWRender::Water(mRendering.getCamera(), this, store->mCell); - else - mWater->changeCell(store->mCell); + mWater->changeCell(store->mCell); mWater->setActive(true); } else @@ -411,8 +396,7 @@ void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store) void RenderingManager::setWaterHeight(const float height) { - if (mWater) - mWater->setHeight(height); + mWater->setHeight(height); } void RenderingManager::skyEnable () @@ -497,53 +481,59 @@ void RenderingManager::configureFog(MWWorld::Ptr::CellStore &mCell) color.setAsABGR (mCell.mCell->mAmbi.mFog); configureFog(mCell.mCell->mAmbi.mFogDensity, color); - - if (mWater) - mWater->setViewportBackground (Ogre::ColourValue(0.8f, 0.9f, 1.0f)); } void RenderingManager::configureFog(const float density, const Ogre::ColourValue& colour) { + mFogColour = colour; float max = Settings::Manager::getFloat("max viewing distance", "Viewing distance"); - float low = max / (density) * Settings::Manager::getFloat("fog start factor", "Viewing distance"); - float high = max / (density) * Settings::Manager::getFloat("fog end factor", "Viewing distance"); - - mRendering.getScene()->setFog (FOG_LINEAR, colour, 0, low, high); - - mRendering.getCamera()->setFarClipDistance ( max / density ); - mRendering.getViewport()->setBackgroundColour (colour); - - if (mWater) - mWater->setViewportBackground (colour); - - sh::Factory::getInstance ().setSharedParameter ("viewportBackground", - sh::makeProperty (new sh::Vector3(colour.r, colour.g, colour.b))); + mFogStart = max / (density) * Settings::Manager::getFloat("fog start factor", "Viewing distance"); + mFogEnd = max / (density) * Settings::Manager::getFloat("fog end factor", "Viewing distance"); + mRendering.getCamera()->setFarClipDistance ( Settings::Manager::getFloat("max viewing distance", "Viewing distance") / density ); } +void RenderingManager::applyFog (bool underwater) +{ + if (!underwater) + { + mRendering.getScene()->setFog (FOG_LINEAR, mFogColour, 0, mFogStart, mFogEnd); + mRendering.getViewport()->setBackgroundColour (mFogColour); + mWater->setViewportBackground (mFogColour); + } + else + { + mRendering.getScene()->setFog (FOG_LINEAR, Ogre::ColourValue(0.18039, 0.23137, 0.25490), 0, 0, 1000); + mRendering.getViewport()->setBackgroundColour (Ogre::ColourValue(0.18039, 0.23137, 0.25490)); + mWater->setViewportBackground (Ogre::ColourValue(0.18039, 0.23137, 0.25490)); + } +} void RenderingManager::setAmbientMode() { - switch (mAmbientMode) - { + switch (mAmbientMode) + { case 0: - - setAmbientColour(mAmbientColor); - break; + setAmbientColour(mAmbientColor); + break; case 1: - - setAmbientColour(0.7f*mAmbientColor + 0.3f*ColourValue(1,1,1)); - break; + setAmbientColour(0.7f*mAmbientColor + 0.3f*ColourValue(1,1,1)); + break; case 2: + setAmbientColour(ColourValue(1,1,1)); + break; + } +} - setAmbientColour(ColourValue(1,1,1)); - break; - } +float RenderingManager::getTerrainHeightAt(Ogre::Vector3 worldPos) +{ + return mTerrainManager->getTerrainHeightAt(worldPos); } + void RenderingManager::configureAmbient(MWWorld::Ptr::CellStore &mCell) { mAmbientColor.setAsABGR (mCell.mCell->mAmbi.mAmbient); @@ -580,17 +570,6 @@ void RenderingManager::toggleLight() setAmbientMode(); } -void RenderingManager::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, - int mode, int number) -{ - mActors.playAnimationGroup(ptr, groupName, mode, number); -} - -void RenderingManager::skipAnimation (const MWWorld::Ptr& ptr) -{ - mActors.skipAnimation(ptr); -} - void RenderingManager::setSunColour(const Ogre::ColourValue& colour) { if (!mSunEnabled) return; @@ -601,34 +580,46 @@ void RenderingManager::setSunColour(const Ogre::ColourValue& colour) void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour) { - mRendering.getScene()->setAmbientLight(colour); - mTerrainManager->setAmbient(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; + Ogre::ColourValue final = colour; + final += Ogre::ColourValue(0.7,0.7,0.7,0) * std::min(1.f, (nightEye/100.f)); + + mRendering.getScene()->setAmbientLight(final); + mTerrainManager->setAmbient(final); } -void RenderingManager::sunEnable() +void RenderingManager::sunEnable(bool real) { - // Don't disable the light, as the shaders assume the first light to be directional. - //if (mSun) mSun->setVisible(true); - mSunEnabled = true; + if (real && mSun) mSun->setVisible(true); + else + { + // Don't disable the light, as the shaders assume the first light to be directional. + mSunEnabled = true; + } } -void RenderingManager::sunDisable() +void RenderingManager::sunDisable(bool real) { - // Don't disable the light, as the shaders assume the first light to be directional. - //if (mSun) mSun->setVisible(false); - mSunEnabled = false; - if (mSun) + if (real && mSun) mSun->setVisible(false); + else { - mSun->setDiffuseColour(ColourValue(0,0,0)); - mSun->setSpecularColour(ColourValue(0,0,0)); + // Don't disable the light, as the shaders assume the first light to be directional. + mSunEnabled = false; + if (mSun) + { + mSun->setDiffuseColour(ColourValue(0,0,0)); + mSun->setSpecularColour(ColourValue(0,0,0)); + } } } void RenderingManager::setSunDirection(const Ogre::Vector3& direction) { // direction * -1 (because 'direction' is camera to sun vector and not sun to camera), - // then convert from MW to ogre coordinates (swap y,z and make y negative) - if (mSun) mSun->setDirection(Vector3(-direction.x, -direction.z, direction.y)); + if (mSun) mSun->setDirection(Vector3(-direction.x, -direction.y, -direction.z)); mSkyManager->setSunDirection(direction); } @@ -651,21 +642,16 @@ void RenderingManager::preCellChange(MWWorld::Ptr::CellStore* cell) mLocalMap->saveFogOfWar(cell); } -void RenderingManager::disableLights() +void RenderingManager::disableLights(bool sun) { mObjects.disableLights(); - sunDisable(); + sunDisable(sun); } -void RenderingManager::enableLights() +void RenderingManager::enableLights(bool sun) { mObjects.enableLights(); - sunEnable(); -} - -const bool RenderingManager::useMRT() -{ - return Settings::Manager::getBool("shader", "Water"); + sunEnable(sun); } Shadows* RenderingManager::getShadows() @@ -732,6 +718,7 @@ Compositors* RenderingManager::getCompositors() void RenderingManager::processChangedSettings(const Settings::CategorySettingVector& settings) { bool changeRes = false; + bool rebuild = false; // rebuild static geometry (necessary after any material changes) for (Settings::CategorySettingVector::const_iterator it=settings.begin(); it != settings.end(); ++it) { @@ -767,25 +754,19 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec else if (it->second == "shader" && it->first == "Water") { applyCompositors(); - sh::Factory::getInstance ().setGlobalSetting ("mrt_output", useMRT() ? "true" : "false"); sh::Factory::getInstance ().setGlobalSetting ("simple_water", Settings::Manager::getBool("shader", "Water") ? "false" : "true"); - mObjects.rebuildStaticGeometry (); + rebuild = true; mRendering.getViewport ()->setClearEveryFrame (true); } - else if (it->second == "underwater effect" && it->first == "Water") + else if (it->second == "refraction" && it->first == "Water") { - sh::Factory::getInstance ().setGlobalSetting ("underwater_effects", Settings::Manager::getString("underwater effect", "Water")); - mObjects.rebuildStaticGeometry (); + sh::Factory::getInstance ().setGlobalSetting ("refraction", Settings::Manager::getBool("refraction", "Water") ? "true" : "false"); + rebuild = true; } else if (it->second == "shaders" && it->first == "Objects") { sh::Factory::getInstance ().setShadersEnabled (Settings::Manager::getBool("shaders", "Objects")); - mObjects.rebuildStaticGeometry (); - } - else if (it->second == "gamma" && it->first == "Video") - { - sh::Factory::getInstance ().setSharedParameter ("gammaCorrection", sh::makeProperty(new sh::FloatValue( - Settings::Manager::getFloat ("gamma", "Video")))); + rebuild = true; } else if (it->second == "shader mode" && it->first == "General") { @@ -798,13 +779,13 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec else lang = sh::Language_CG; sh::Factory::getInstance ().setCurrentLanguage (lang); - mObjects.rebuildStaticGeometry (); + rebuild = true; } else if (it->first == "Shadows") { mShadows->recreate (); - mObjects.rebuildStaticGeometry (); + rebuild = true; } } @@ -820,8 +801,10 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec mRendering.getWindow()->setFullscreen(Settings::Manager::getBool("fullscreen", "Video"), x, y); } - if (mWater) - mWater->processChangedSettings(settings); + mWater->processChangedSettings(settings); + + if (rebuild) + mObjects.rebuildStaticGeometry(); } void RenderingManager::setMenuTransparency(float val) @@ -841,13 +824,12 @@ void RenderingManager::windowResized(Ogre::RenderWindow* rw) mRendering.adjustViewport(); mCompositors->recreate(); - mWater->assignTextures(); mVideoPlayer->setResolution (rw->getWidth(), rw->getHeight()); const Settings::CategorySettingVector& changed = Settings::Manager::apply(); - MWBase::Environment::get().getInputManager()->processChangedSettings(changed); //FIXME - MWBase::Environment::get().getWindowManager()->processChangedSettings(changed); // FIXME + MWBase::Environment::get().getInputManager()->processChangedSettings(changed); + MWBase::Environment::get().getWindowManager()->processChangedSettings(changed); } void RenderingManager::windowClosed(Ogre::RenderWindow* rw) @@ -855,27 +837,8 @@ void RenderingManager::windowClosed(Ogre::RenderWindow* rw) Ogre::Root::getSingleton ().queueEndRendering (); } -bool RenderingManager::waterShaderSupported() -{ - const RenderSystemCapabilities* caps = Root::getSingleton().getRenderSystem()->getCapabilities(); - if (caps->getNumMultiRenderTargets() < 2 || !Settings::Manager::getBool("shaders", "Objects")) - return false; - return true; -} - void RenderingManager::applyCompositors() { - mCompositors->removeAll(); - if (useMRT()) - { - mCompositors->addCompositor("gbuffer", 0); - mCompositors->setCompositorEnabled("gbuffer", true); - mCompositors->addCompositor("gbufferFinalizer", 2); - mCompositors->setCompositorEnabled("gbufferFinalizer", true); - } - - if (mWater) - mWater->assignTextures(); } void RenderingManager::getTriangleBatchCount(unsigned int &triangles, unsigned int &batches) @@ -891,26 +854,50 @@ void RenderingManager::getTriangleBatchCount(unsigned int &triangles, unsigned i } } -void RenderingManager::attachCameraTo(const MWWorld::Ptr &ptr) +void RenderingManager::setupPlayer(const MWWorld::Ptr &ptr) { - mPlayer->attachTo(ptr); + ptr.getRefData().setBaseNode(mRendering.getScene()->getSceneNode("player")); + mCamera->attachTo(ptr); } void RenderingManager::renderPlayer(const MWWorld::Ptr &ptr) { - MWRender::NpcAnimation *anim = - new MWRender::NpcAnimation( - ptr, ptr.getRefData ().getBaseNode (), - MWWorld::Class::get(ptr).getInventoryStore(ptr), RV_Actors - ); - mPlayer->setAnimation(anim); + if(!mPlayerAnimation) + { + mPlayerAnimation = new NpcAnimation(ptr, ptr.getRefData().getBaseNode(), + MWWorld::Class::get(ptr).getInventoryStore(ptr), + RV_Actors); + } + else + { + // Reconstruct the NpcAnimation in-place + mPlayerAnimation->~NpcAnimation(); + new(mPlayerAnimation) NpcAnimation(ptr, ptr.getRefData().getBaseNode(), + MWWorld::Class::get(ptr).getInventoryStore(ptr), + RV_Actors); + } + mCamera->setAnimation(mPlayerAnimation); + mWater->removeEmitter(ptr); + mWater->addEmitter(ptr); + // apply race height + MWBase::Environment::get().getWorld()->scaleObject(ptr, 1.f); } -void RenderingManager::getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float &yaw) +void RenderingManager::getCameraData(Ogre::Vector3 &eyepos, float &pitch, float &yaw) { - eyepos = mPlayer->getPosition(); - eyepos.z += mPlayer->getHeight(); - mPlayer->getSightAngles(pitch, yaw); + eyepos = mCamera->getPosition(); + eyepos.z += mCamera->getHeight(); + mCamera->getSightAngles(pitch, yaw); +} + +bool RenderingManager::vanityRotateCamera(const float *rot) +{ + if(!mCamera->isVanityOrPreviewModeEnabled()) + return false; + + Ogre::Vector3 vRot(rot); + mCamera->rotateCamera(vRot, true); + return true; } void RenderingManager::getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) @@ -928,6 +915,15 @@ void RenderingManager::setupExternalRendering (MWRender::ExternalRendering& rend rendering.setup (mRendering.getScene()); } +Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr) +{ + Animation *anim = mActors.getAnimation(ptr); + if(!anim && ptr.getRefData().getHandle() == "player") + anim = mPlayerAnimation; + return anim; +} + + void RenderingManager::playVideo(const std::string& name, bool allowSkipping) { mVideoPlayer->playVideo ("video/" + name, allowSkipping); @@ -938,4 +934,29 @@ void RenderingManager::stopVideo() mVideoPlayer->stopVideo (); } +void RenderingManager::addWaterRippleEmitter (const MWWorld::Ptr& ptr, float scale, float force) +{ + mWater->addEmitter (ptr, scale, force); +} + +void RenderingManager::removeWaterRippleEmitter (const MWWorld::Ptr& ptr) +{ + mWater->removeEmitter (ptr); +} + +void RenderingManager::updateWaterRippleEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr) +{ + mWater->updateEmitterPtr(old, ptr); +} + +void RenderingManager::frameStarted(float dt) +{ + mWater->frameStarted(dt); +} + +void RenderingManager::resetCamera() +{ + mCamera->reset(); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 68f2d79c3..b492a0db9 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -11,11 +11,13 @@ #include +#include + #include "renderinginterface.hpp" #include "objects.hpp" #include "actors.hpp" -#include "player.hpp" +#include "camera.hpp" #include "occlusionquery.hpp" namespace Ogre @@ -46,43 +48,48 @@ namespace MWRender class ExternalRendering; class GlobalMap; class VideoPlayer; + class Animation; -class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener { - - private: - - +class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener, public Ogre::RenderTargetListener +{ +private: virtual MWRender::Objects& getObjects(); virtual MWRender::Actors& getActors(); - public: +public: RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, - const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine); + const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine, + MWWorld::Fallback* fallback); virtual ~RenderingManager(); - void togglePOV() { - mPlayer->toggleViewMode(); - } + void togglePOV() + { mCamera->toggleViewMode(); } - void togglePreviewMode(bool enable) { - mPlayer->togglePreviewMode(enable); - } + void togglePreviewMode(bool enable) + { mCamera->togglePreviewMode(enable); } - bool toggleVanityMode(bool enable, bool force) { - return mPlayer->toggleVanityMode(enable, force); - } + bool toggleVanityMode(bool enable) + { return mCamera->toggleVanityMode(enable); } - void allowVanityMode(bool allow) { - mPlayer->allowVanityMode(allow); - } + void allowVanityMode(bool allow) + { mCamera->allowVanityMode(allow); } + + void togglePlayerLooking(bool enable) + { mCamera->togglePlayerLooking(enable); } - void togglePlayerLooking(bool enable) { - mPlayer->togglePlayerLooking(enable); + void changeVanityModeScale(float factor) + { + if(mCamera->isVanityOrPreviewModeEnabled()) + mCamera->setCameraDistance(-factor/120.f*10, true, true); } - void getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float &yaw); + void resetCamera(); - void attachCameraTo(const MWWorld::Ptr &ptr); + bool vanityRotateCamera(const float *rot); + + void getCameraData(Ogre::Vector3 &eyepos, float &pitch, float &yaw); + + void setupPlayer(const MWWorld::Ptr &ptr); void renderPlayer(const MWWorld::Ptr &ptr); SkyManager* getSkyManager(); @@ -102,8 +109,6 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void removeWater(); - static const bool useMRT(); - void preCellChange (MWWorld::CellStore* store); ///< this event is fired immediately before changing cell @@ -113,29 +118,31 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position); void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale); - /// Rotates object accordingly to its type - /// \param rot euler angles in radians - /// \param adjust indicates should rotation be set or adjusted - /// \return true if object needs to be rotated physically - bool rotateObject (const MWWorld::Ptr& ptr, Ogre::Vector3 &rot, bool adjust = false); + /// Updates an object's rotation + void rotateObject (const MWWorld::Ptr& ptr); void setWaterHeight(const float height); void toggleWater(); - /// Moves object rendering part to proper container - /// \param store Cell the object was in previously (\a ptr has already been updated to the new cell). - void moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Vector3& position, MWWorld::CellStore *store); + /// Updates object rendering after cell change + /// \param old Object reference in previous cell + /// \param cur Object reference in new cell + void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur); void update (float duration, bool paused); void setAmbientColour(const Ogre::ColourValue& colour); void setSunColour(const Ogre::ColourValue& colour); void setSunDirection(const Ogre::Vector3& direction); - void sunEnable(); - void sunDisable(); + void sunEnable(bool real); ///< @param real whether or not to really disable the sunlight (otherwise just set diffuse to 0) + void sunDisable(bool real); - void disableLights(); - void enableLights(); + void disableLights(bool sun); ///< @param sun whether or not to really disable the sunlight (otherwise just set diffuse to 0) + void enableLights(bool sun); + + + void preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt); + void postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt); bool occlusionQuerySupported() { return mOcclusionQuery->supported(); } OcclusionQuery* getOcclusionQuery() { return mOcclusionQuery; } @@ -147,6 +154,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void getTriangleBatchCount(unsigned int &triangles, unsigned int &batches); + float getTerrainHeightAt (Ogre::Vector3 worldPos); + void setGlare(bool glare); void skyEnable (); void skyDisable (); @@ -157,6 +166,10 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void skySetMoonColour (bool red); void configureAmbient(MWWorld::CellStore &mCell); + void addWaterRippleEmitter (const MWWorld::Ptr& ptr, float scale = 1.f, float force = 1.f); + void removeWaterRippleEmitter (const MWWorld::Ptr& ptr); + void updateWaterRippleEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr); + void requestMap (MWWorld::CellStore* cell); ///< request the local map for a cell @@ -166,18 +179,6 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList /// configure fog manually void configureFog(const float density, const Ogre::ColourValue& colour); - void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, - int number = 1); - ///< Run animation for a MW-reference. Calls to this function for references that are currently not - /// in the rendered scene should be ignored. - /// - /// \param mode: 0 normal, 1 immediate start, 2 immediate loop - /// \param number How offen the animation should be run - - void skipAnimation (const MWWorld::Ptr& ptr); - ///< Skip the animation for the given MW-reference for one frame. Calls to this function for - /// references that are currently not in the rendered scene should be ignored. - Ogre::Vector4 boundingBoxToScreen(Ogre::AxisAlignedBox bounds); ///< transform the specified bounding box (in world coordinates) into screen coordinates. /// @return packed vector4 (min_x, min_y, max_x, max_y) @@ -186,8 +187,6 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList Ogre::Viewport* getViewport() { return mRendering.getViewport(); } - static bool waterShaderSupported(); - void getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y); ///< see MWRender::LocalMap::getInteriorMapPosition @@ -196,18 +195,21 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void setupExternalRendering (MWRender::ExternalRendering& rendering); + Animation* getAnimation(const MWWorld::Ptr &ptr); + void playVideo(const std::string& name, bool allowSkipping); void stopVideo(); + void frameStarted(float dt); - protected: - virtual void windowResized(Ogre::RenderWindow* rw); +protected: + virtual void windowResized(Ogre::RenderWindow* rw); virtual void windowClosed(Ogre::RenderWindow* rw); - private: - +private: sh::Factory* mFactory; void setAmbientMode(); + void applyFog(bool underwater); void setMenuTransparency(float val); @@ -215,6 +217,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList bool mSunEnabled; + MWWorld::Fallback* mFallback; + SkyManager* mSkyManager; OcclusionQuery* mOcclusionQuery; @@ -230,20 +234,23 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList MWRender::Objects mObjects; MWRender::Actors mActors; + MWRender::NpcAnimation *mPlayerAnimation; + // 0 normal, 1 more bright, 2 max int mAmbientMode; Ogre::ColourValue mAmbientColor; Ogre::Light* mSun; - /// Root node for all objects added to the scene. This is rotated so - /// that the OGRE coordinate system matches that used internally in - /// Morrowind. - Ogre::SceneNode *mMwRoot; + Ogre::SceneNode *mRootNode; + + Ogre::ColourValue mFogColour; + float mFogStart; + float mFogEnd; OEngine::Physic::PhysicEngine* mPhysicsEngine; - MWRender::Player *mPlayer; + MWRender::Camera *mCamera; MWRender::Debugging *mDebugging; diff --git a/apps/openmw/mwrender/ripplesimulation.cpp b/apps/openmw/mwrender/ripplesimulation.cpp new file mode 100644 index 000000000..47fbc8ddf --- /dev/null +++ b/apps/openmw/mwrender/ripplesimulation.cpp @@ -0,0 +1,263 @@ +#include "ripplesimulation.hpp" + +#include +#include +#include +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/player.hpp" + +namespace MWRender +{ + + +RippleSimulation::RippleSimulation(Ogre::SceneManager* mainSceneManager) + : mMainSceneMgr(mainSceneManager), + mTime(0), + mCurrentFrameOffset(0,0), + mPreviousFrameOffset(0,0), + mRippleCenter(0,0), + mTextureSize(512), + mRippleAreaLength(1000), + mImpulseSize(20), + mTexelOffset(0,0), + mFirstUpdate(true) +{ + Ogre::AxisAlignedBox aabInf; + aabInf.setInfinite(); + + mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); + + mCamera = mSceneMgr->createCamera("RippleCamera"); + + mRectangle = new Ogre::Rectangle2D(true); + mRectangle->setBoundingBox(aabInf); + mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0, false); + Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode(); + node->attachObject(mRectangle); + + mImpulse = new Ogre::Rectangle2D(true); + mImpulse->setCorners(-0.1, 0.1, 0.1, -0.1, false); + mImpulse->setBoundingBox(aabInf); + mImpulse->setMaterial("AddImpulse"); + Ogre::SceneNode* impulseNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); + impulseNode->attachObject(mImpulse); + + //float w=0.05; + for (int i=0; i<4; ++i) + { + Ogre::TexturePtr texture; + if (i != 3) + texture = Ogre::TextureManager::getSingleton().createManual("RippleHeight" + Ogre::StringConverter::toString(i), + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mTextureSize, mTextureSize, 1, 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET); + else + texture = Ogre::TextureManager::getSingleton().createManual("RippleNormal", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mTextureSize, mTextureSize, 1, 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET); + + + Ogre::RenderTexture* rt = texture->getBuffer()->getRenderTarget(); + rt->removeAllViewports(); + rt->addViewport(mCamera); + rt->setAutoUpdated(false); + rt->getViewport(0)->setClearEveryFrame(false); + + // debug overlay + /* + Ogre::Rectangle2D* debugOverlay = new Ogre::Rectangle2D(true); + debugOverlay->setCorners(w*2-1, 0.9, (w+0.18)*2-1, 0.4, false); + w += 0.2; + debugOverlay->setBoundingBox(aabInf); + Ogre::SceneNode* debugNode = mMainSceneMgr->getRootSceneNode()->createChildSceneNode(); + debugNode->attachObject(debugOverlay); + + Ogre::MaterialPtr debugMaterial = Ogre::MaterialManager::getSingleton().create("RippleDebug" + Ogre::StringConverter::toString(i), + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + + if (i != 3) + debugMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("RippleHeight" + Ogre::StringConverter::toString(i)); + else + debugMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("RippleNormal"); + debugMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); + + debugOverlay->setMaterial("RippleDebug" + Ogre::StringConverter::toString(i)); + */ + + mRenderTargets[i] = rt; + mTextures[i] = texture; + } + + sh::Factory::getInstance().setSharedParameter("rippleTextureSize", sh::makeProperty( + new sh::Vector4(1.0/512, 1.0/512, 512, 512))); + sh::Factory::getInstance().setSharedParameter("rippleCenter", sh::makeProperty( + new sh::Vector3(0, 0, 0))); + sh::Factory::getInstance().setSharedParameter("rippleAreaLength", sh::makeProperty( + new sh::FloatValue(mRippleAreaLength))); + +} + +RippleSimulation::~RippleSimulation() +{ + delete mRectangle; + + Ogre::Root::getSingleton().destroySceneManager(mSceneMgr); +} + +void RippleSimulation::update(float dt, Ogre::Vector2 position) +{ + // try to keep 20 fps + mTime += dt; + + while (mTime >= 1/20.0 || mFirstUpdate) + { + mPreviousFrameOffset = mCurrentFrameOffset; + + mCurrentFrameOffset = position - mRippleCenter; + // add texel offsets from previous frame. + mCurrentFrameOffset += mTexelOffset; + + mTexelOffset = Ogre::Vector2(std::fmod(mCurrentFrameOffset.x, 1.0f/mTextureSize), + std::fmod(mCurrentFrameOffset.y, 1.0f/mTextureSize)); + + // now subtract new offset in order to snap to texels + mCurrentFrameOffset -= mTexelOffset; + + // texture coordinate space + mCurrentFrameOffset /= mRippleAreaLength; + + mRippleCenter = position; + + addImpulses(); + waterSimulation(); + heightMapToNormalMap(); + + swapHeightMaps(); + if (!mFirstUpdate) + mTime -= 1/20.0; + else + mFirstUpdate = false; + } + + sh::Factory::getInstance().setSharedParameter("rippleCenter", sh::makeProperty( + new sh::Vector3(mRippleCenter.x + mTexelOffset.x, mRippleCenter.y + mTexelOffset.y, 0))); +} + +void RippleSimulation::addImpulses() +{ + mRectangle->setVisible(false); + mImpulse->setVisible(true); + + /// \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 ()) + { + // 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 (); + } + float* _currentPos = it->mPtr.getRefData().getPosition().pos; + Ogre::Vector3 currentPos (_currentPos[0], _currentPos[1], _currentPos[2]); + + if ( (currentPos - it->mLastEmitPosition).length() > 2 + && MWBase::Environment::get().getWorld ()->isUnderwater (it->mPtr.getCell(), currentPos)) + { + it->mLastEmitPosition = currentPos; + + Ogre::Vector2 pos (currentPos.x, currentPos.y); + pos -= mRippleCenter; + pos /= mRippleAreaLength; + float size = mImpulseSize / mRippleAreaLength; + mImpulse->setCorners(pos.x-size, pos.y+size, pos.x+size, pos.y-size, false); + + // don't render if we are offscreen + if (pos.x - size >= 1.0 || pos.y+size <= -1.0 || pos.x+size <= -1.0 || pos.y-size >= 1.0) + continue; + mRenderTargets[1]->update(); + } + } + + mImpulse->setVisible(false); + mRectangle->setVisible(true); +} + +void RippleSimulation::waterSimulation() +{ + mRectangle->setMaterial("HeightmapSimulation"); + + sh::Factory::getInstance().setTextureAlias("Heightmap0", mTextures[0]->getName()); + sh::Factory::getInstance().setTextureAlias("Heightmap1", mTextures[1]->getName()); + + sh::Factory::getInstance().setSharedParameter("currentFrameOffset", sh::makeProperty( + new sh::Vector3(mCurrentFrameOffset.x, mCurrentFrameOffset.y, 0))); + sh::Factory::getInstance().setSharedParameter("previousFrameOffset", sh::makeProperty( + new sh::Vector3(mPreviousFrameOffset.x, mPreviousFrameOffset.y, 0))); + + mRenderTargets[2]->update(); +} + +void RippleSimulation::heightMapToNormalMap() +{ + mRectangle->setMaterial("HeightToNormalMap"); + + sh::Factory::getInstance().setTextureAlias("Heightmap2", mTextures[2]->getName()); + + mRenderTargets[TEX_NORMAL]->update(); +} + +void RippleSimulation::swapHeightMaps() +{ + // 0 -> 1 -> 2 to 2 -> 0 ->1 + Ogre::RenderTexture* tmp = mRenderTargets[0]; + Ogre::TexturePtr tmp2 = mTextures[0]; + + mRenderTargets[0] = mRenderTargets[1]; + mTextures[0] = mTextures[1]; + + mRenderTargets[1] = mRenderTargets[2]; + mTextures[1] = mTextures[2]; + + mRenderTargets[2] = tmp; + mTextures[2] = tmp2; +} + +void RippleSimulation::addEmitter(const MWWorld::Ptr& ptr, float scale, float force) +{ + Emitter newEmitter; + newEmitter.mPtr = ptr; + newEmitter.mScale = scale; + newEmitter.mForce = force; + newEmitter.mLastEmitPosition = Ogre::Vector3(0,0,0); + mEmitters.push_back (newEmitter); +} + +void RippleSimulation::removeEmitter (const MWWorld::Ptr& ptr) +{ + for (std::vector::iterator it = mEmitters.begin(); it != mEmitters.end(); ++it) + { + if (it->mPtr == ptr) + { + mEmitters.erase(it); + return; + } + } +} + +void RippleSimulation::updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr) +{ + for (std::vector::iterator it = mEmitters.begin(); it != mEmitters.end(); ++it) + { + if (it->mPtr == old) + { + it->mPtr = ptr; + return; + } + } +} + + +} diff --git a/apps/openmw/mwrender/ripplesimulation.hpp b/apps/openmw/mwrender/ripplesimulation.hpp new file mode 100644 index 000000000..7e7eebc1c --- /dev/null +++ b/apps/openmw/mwrender/ripplesimulation.hpp @@ -0,0 +1,85 @@ +#ifndef RIPPLE_SIMULATION_H +#define RIPPLE_SIMULATION_H + +#include +#include +#include +#include + +#include "../mwworld/ptr.hpp" + +namespace Ogre +{ + class RenderTexture; + class Camera; + class SceneManager; + class Rectangle2D; +} + +namespace MWRender +{ + +struct Emitter +{ + MWWorld::Ptr mPtr; + Ogre::Vector3 mLastEmitPosition; + float mScale; + float mForce; +}; + +class RippleSimulation +{ +public: + RippleSimulation(Ogre::SceneManager* mainSceneManager); + ~RippleSimulation(); + + void update(float dt, Ogre::Vector2 position); + + /// adds an emitter, position will be tracked automatically + void addEmitter (const MWWorld::Ptr& ptr, float scale = 1.f, float force = 1.f); + void removeEmitter (const MWWorld::Ptr& ptr); + void updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr); + +private: + std::vector mEmitters; + + Ogre::RenderTexture* mRenderTargets[4]; + Ogre::TexturePtr mTextures[4]; + + int mTextureSize; + float mRippleAreaLength; + float mImpulseSize; + + bool mFirstUpdate; + + Ogre::Camera* mCamera; + + // own scenemanager to render our simulation + Ogre::SceneManager* mSceneMgr; + Ogre::Rectangle2D* mRectangle; + + // scenemanager to create the debug overlays on + Ogre::SceneManager* mMainSceneMgr; + + static const int TEX_NORMAL = 3; + + Ogre::Rectangle2D* mImpulse; + + void addImpulses(); + void heightMapToNormalMap(); + void waterSimulation(); + void swapHeightMaps(); + + float mTime; + + Ogre::Vector2 mRippleCenter; + + Ogre::Vector2 mTexelOffset; + + Ogre::Vector2 mCurrentFrameOffset; + Ogre::Vector2 mPreviousFrameOffset; +}; + +} + +#endif diff --git a/apps/openmw/mwrender/shadows.cpp b/apps/openmw/mwrender/shadows.cpp index 3d9f13243..0d066a0ec 100644 --- a/apps/openmw/mwrender/shadows.cpp +++ b/apps/openmw/mwrender/shadows.cpp @@ -9,9 +9,6 @@ #include #include -#include -#include - #include #include "renderconst.hpp" @@ -31,10 +28,7 @@ void Shadows::recreate() { bool enabled = Settings::Manager::getBool("enabled", "Shadows"); - // Split shadow maps are currently disabled because the terrain cannot cope with them - // (Too many texture units) Solution would be a multi-pass terrain material bool split = Settings::Manager::getBool("split", "Shadows"); - //const bool split = false; sh::Factory::getInstance ().setGlobalSetting ("shadows", enabled && !split ? "true" : "false"); sh::Factory::getInstance ().setGlobalSetting ("shadows_pssm", enabled && split ? "true" : "false"); @@ -125,6 +119,7 @@ void Shadows::recreate() // -------------------------------------------------------------------------------------------------------------------- // --------------------------- Debug overlays to display the content of shadow maps ----------------------------------- // -------------------------------------------------------------------------------------------------------------------- + /* if (Settings::Manager::getBool("debug", "Shadows")) { OverlayManager& mgr = OverlayManager::getSingleton(); @@ -181,6 +176,7 @@ void Shadows::recreate() if ((overlay = mgr.getByName("DebugOverlay"))) mgr.destroy(overlay); } + */ } PSSMShadowCameraSetup* Shadows::getPSSMSetup() diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 60ecd4303..03e14dc07 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -12,15 +12,19 @@ #include #include +#include + #include -#include +#include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwworld/fallback.hpp" + #include "renderconst.hpp" #include "renderingmanager.hpp" @@ -39,25 +43,25 @@ BillboardObject::BillboardObject( const String& textureName, static unsigned int bodyCount=0; - /// \todo These billboards are not 100% correct, might want to revisit them later - mBBSet = sceneMgr->createBillboardSet("SkyBillboardSet"+StringConverter::toString(bodyCount), 1); - mBBSet->setDefaultDimensions(550.f*initialSize, 550.f*initialSize); - mBBSet->setBillboardType(BBT_PERPENDICULAR_COMMON); - mBBSet->setCommonDirection( -position.normalisedCopy() ); - mBBSet->setVisibilityFlags(RV_Sky); + mMaterial = sh::Factory::getInstance().createMaterialInstance ("BillboardMaterial"+StringConverter::toString(bodyCount), material); + mMaterial->setProperty("texture", sh::makeProperty(new sh::StringValue(textureName))); + + static Ogre::Mesh* plane = MeshManager::getSingleton().createPlane("billboard", + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::Plane(Ogre::Vector3(0,0,1), 0), 1, 1, 1, 1, true, 1, 1, 1, Vector3::UNIT_Y).get(); + plane->_setBounds(Ogre::AxisAlignedBox::BOX_INFINITE); + mEntity = sceneMgr->createEntity("billboard"); + mEntity->setMaterialName("BillboardMaterial"+StringConverter::toString(bodyCount)); + mEntity->setVisibilityFlags(RV_Sky); + mEntity->setCastShadows(false); + mNode = rootNode->createChildSceneNode(); mNode->setPosition(finalPosition); - mNode->attachObject(mBBSet); - mBBSet->createBillboard(0,0,0); - mBBSet->setCastShadows(false); - - mMaterial = sh::Factory::getInstance().createMaterialInstance ("BillboardMaterial"+StringConverter::toString(bodyCount), material); - mMaterial->setProperty("texture", sh::makeProperty(new sh::StringValue(textureName))); + mNode->attachObject(mEntity); + mNode->setScale(Ogre::Vector3(450.f*initialSize)); + mNode->setOrientation(Ogre::Vector3::UNIT_Z.getRotationTo(-position.normalisedCopy())); sh::Factory::getInstance().getMaterialInstance ("BillboardMaterial"+StringConverter::toString(bodyCount))->setListener(this); - mBBSet->setMaterialName("BillboardMaterial"+StringConverter::toString(bodyCount)); - bodyCount++; } @@ -77,12 +81,12 @@ void BillboardObject::createdConfiguration (sh::MaterialInstance* m, const std:: void BillboardObject::setVisible(const bool visible) { - mBBSet->setVisible(visible); + mEntity->setVisible(visible); } void BillboardObject::setSize(const float size) { - mNode->setScale(size, size, size); + mNode->setScale(450.f*size, 450.f*size, 450.f*size); } void BillboardObject::setVisibility(const float visibility) @@ -101,21 +105,18 @@ void BillboardObject::setPosition(const Vector3& pPosition) { Vector3 normalised = pPosition.normalisedCopy(); Vector3 finalPosition = normalised * 1000.f; - - mBBSet->setCommonDirection( -normalised ); - + mNode->setOrientation(Ogre::Vector3::UNIT_Z.getRotationTo(-normalised)); mNode->setPosition(finalPosition); } Vector3 BillboardObject::getPosition() const { - Vector3 p = mNode->_getDerivedPosition() - mNode->getParentSceneNode()->_getDerivedPosition(); - return Vector3(p.x, -p.z, p.y); + return mNode->getPosition(); } void BillboardObject::setVisibilityFlags(int flags) { - mBBSet->setVisibilityFlags(flags); + mEntity->setVisibilityFlags(flags); } void BillboardObject::setColour(const ColourValue& pColour) @@ -132,7 +133,7 @@ void BillboardObject::setColour(const ColourValue& pColour) void BillboardObject::setRenderQueue(unsigned int id) { - mBBSet->setRenderQueueGroup(id); + mEntity->setRenderQueueGroup(id); } SceneNode* BillboardObject::getNode() @@ -203,49 +204,7 @@ unsigned int Moon::getPhaseInt() const return 0; } -void SkyManager::ModVertexAlpha(Entity* ent, unsigned int meshType) -{ - // Get the vertex colour buffer of this mesh - const Ogre::VertexElement* ves_diffuse = ent->getMesh()->getSubMesh(0)->vertexData->vertexDeclaration->findElementBySemantic( Ogre::VES_DIFFUSE ); - HardwareVertexBufferSharedPtr colourBuffer = ent->getMesh()->getSubMesh(0)->vertexData->vertexBufferBinding->getBuffer(ves_diffuse->getSource()); - - // Lock - void* pData = colourBuffer->lock(HardwareBuffer::HBL_NORMAL); - - // Iterate over all vertices - int vertex_size = colourBuffer->getVertexSize(); - float * currentVertex = NULL; - for (unsigned int i=0; igetNumVertices(); ++i) - { - // Get a pointer to the vertex colour - ves_diffuse->baseVertexPointerToElement( pData, ¤tVertex ); - - unsigned char alpha=0; - if (meshType == 0) alpha = i%2 ? 0 : 255; // this is a cylinder, so every second vertex belongs to the bottom-most row - else if (meshType == 1) - { - if (i>= 49 && i <= 64) alpha = 0; // bottom-most row - else if (i>= 33 && i <= 48) alpha = 64; // second bottom-most row - else alpha = 255; - } - // NB we would have to swap R and B depending on rendersystem specific VertexElementType, but doesn't matter since they are both 1 - uint8 tmpR = static_cast(255); - uint8 tmpG = static_cast(255); - uint8 tmpB = static_cast(255); - uint8 tmpA = static_cast(alpha); - - // Modify - *((uint32*)currentVertex) = tmpR | (tmpG << 8) | (tmpB << 16) | (tmpA << 24); - - // Move to the next vertex - pData = static_cast (pData) + vertex_size; - } - - // Unlock - ent->getMesh()->getSubMesh(0)->vertexData->vertexBufferBinding->getBuffer(ves_diffuse->getSource())->unlock(); -} - -SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) +SkyManager::SkyManager(Ogre::SceneNode *root, Ogre::Camera *pCamera) : mHour(0.0f) , mDay(0) , mMonth(0) @@ -258,7 +217,6 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) , mSceneMgr(NULL) , mAtmosphereDay(NULL) , mAtmosphereNight(NULL) - , mCloudFragmentShader() , mClouds() , mNextClouds() , mCloudBlendFactor(0.0f) @@ -276,9 +234,8 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) , mCloudAnimationTimer(0.f) , mMoonRed(false) { - mSceneMgr = pMwRoot->getCreator(); + mSceneMgr = root->getCreator(); mRootNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); - mRootNode->pitch(Degree(-90)); // convert MW to ogre coordinates mRootNode->setInheritOrientation(false); } @@ -297,6 +254,7 @@ void SkyManager::create() sh::Factory::getInstance().setSharedParameter ("nightFade", sh::makeProperty(new sh::FloatValue(0))); sh::Factory::getInstance().setSharedParameter ("atmosphereColour", sh::makeProperty(new sh::Vector4(0,0,0,1))); + sh::Factory::getInstance().setSharedParameter ("horizonColour", sh::makeProperty(new sh::Vector4(0,0,0,1))); sh::Factory::getInstance().setTextureAlias ("cloud_texture_1", ""); sh::Factory::getInstance().setTextureAlias ("cloud_texture_2", ""); @@ -308,11 +266,12 @@ void SkyManager::create() mLightning->setVisible (false); mLightning->setDiffuseColour (ColourValue(3,3,3)); - mSecunda = new Moon("secunda_texture", 0.5, Vector3(-0.4, 0.4, 0.5), mRootNode, "openmw_moon"); + const MWWorld::Fallback* fallback=MWBase::Environment::get().getWorld()->getFallback(); + mSecunda = new Moon("secunda_texture", fallback->getFallbackFloat("Moons_Secunda_Size")/100, Vector3(-0.4, 0.4, 0.5), mRootNode, "openmw_moon"); mSecunda->setType(Moon::Type_Secunda); mSecunda->setRenderQueue(RQG_SkiesEarly+4); - mMasser = new Moon("masser_texture", 0.75, Vector3(-0.4, 0.4, 0.5), mRootNode, "openmw_moon"); + mMasser = new Moon("masser_texture", fallback->getFallbackFloat("Moons_Masser_Size")/100, Vector3(-0.4, 0.4, 0.5), mRootNode, "openmw_moon"); mMasser->setRenderQueue(RQG_SkiesEarly+3); mMasser->setType(Moon::Type_Masser); @@ -322,15 +281,18 @@ void SkyManager::create() mSunGlare->setRenderQueue(RQG_SkiesLate); mSunGlare->setVisibilityFlags(RV_NoReflection); + Ogre::AxisAlignedBox aabInf = Ogre::AxisAlignedBox::BOX_INFINITE; + // Stars mAtmosphereNight = mRootNode->createChildSceneNode(); - NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(mAtmosphereNight, NULL, "meshes\\sky_night_01.nif"); - for(size_t i = 0, matidx = 0;i < entities.mEntities.size();i++) + NifOgre::ObjectList 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 = entities.mEntities[i]; + Entity* night1_ent = objects.mEntities[i]; night1_ent->setRenderQueueGroup(RQG_SkiesEarly+1); night1_ent->setVisibilityFlags(RV_Sky); night1_ent->setCastShadows(false); + night1_ent->getMesh()->_setBounds (aabInf); for (unsigned int j=0; jgetNumSubEntities(); ++j) { @@ -349,30 +311,35 @@ void SkyManager::create() // Atmosphere (day) mAtmosphereDay = mRootNode->createChildSceneNode(); - entities = NifOgre::NIFLoader::createEntities(mAtmosphereDay, NULL, "meshes\\sky_atmosphere.nif"); - for(size_t i = 0;i < entities.mEntities.size();i++) + objects = NifOgre::Loader::createObjects(mAtmosphereDay, "meshes\\sky_atmosphere.nif"); + for(size_t i = 0;i < objects.mEntities.size();i++) { - Entity* atmosphere_ent = entities.mEntities[i]; + Entity* atmosphere_ent = objects.mEntities[i]; atmosphere_ent->setCastShadows(false); atmosphere_ent->setRenderQueueGroup(RQG_SkiesEarly); atmosphere_ent->setVisibilityFlags(RV_Sky); - atmosphere_ent->getSubEntity (0)->setMaterialName ("openmw_atmosphere"); - ModVertexAlpha(atmosphere_ent, 0); + + for(unsigned int j = 0;j < atmosphere_ent->getNumSubEntities();j++) + atmosphere_ent->getSubEntity (j)->setMaterialName("openmw_atmosphere"); + + // Using infinite AAB here to prevent being clipped by the custom near clip plane used for reflections/refractions + atmosphere_ent->getMesh()->_setBounds (aabInf); } // Clouds SceneNode* clouds_node = mRootNode->createChildSceneNode(); - entities = NifOgre::NIFLoader::createEntities(clouds_node, NULL, "meshes\\sky_clouds_01.nif"); - for(size_t i = 0;i < entities.mEntities.size();i++) + objects = NifOgre::Loader::createObjects(clouds_node, "meshes\\sky_clouds_01.nif"); + for(size_t i = 0;i < objects.mEntities.size();i++) { - Entity* clouds_ent = entities.mEntities[i]; + Entity* clouds_ent = objects.mEntities[i]; clouds_ent->setVisibilityFlags(RV_Sky); clouds_ent->setRenderQueueGroup(RQG_SkiesEarly+5); - clouds_ent->getSubEntity(0)->setMaterialName ("openmw_clouds"); + for(unsigned int j = 0;j < clouds_ent->getNumSubEntities();j++) + clouds_ent->getSubEntity(j)->setMaterialName("openmw_clouds"); clouds_ent->setCastShadows(false); - - ModVertexAlpha(clouds_ent, 1); + // Using infinite AAB here to prevent being clipped by the custom near clip plane used for reflections/refractions + clouds_ent->getMesh()->_setBounds (aabInf); } mCreated = true; @@ -401,12 +368,10 @@ int SkyManager::getSecundaPhase() const void SkyManager::update(float duration) { if (!mEnabled) return; - - mCamera->getParentSceneNode ()->needUpdate (); - mRootNode->setPosition(mCamera->getDerivedPosition()); + const MWWorld::Fallback* fallback=MWBase::Environment::get().getWorld()->getFallback(); // UV Scroll the clouds - mCloudAnimationTimer += duration * mCloudSpeed * (MWBase::Environment::get().getWorld()->getTimeScaleFactor()/30.f); + mCloudAnimationTimer += duration * mCloudSpeed; sh::Factory::getInstance().setSharedParameter ("cloudAnimationTimer", sh::makeProperty(new sh::FloatValue(mCloudAnimationTimer))); @@ -414,7 +379,7 @@ void SkyManager::update(float duration) mMasser->setPhase( static_cast( (int) ((mDay % 32)/4.f)) ); mSecunda->setPhase ( static_cast( (int) ((mDay % 32)/4.f)) ); - mSecunda->setColour ( mMoonRed ? ColourValue(1.0, 0.0784, 0.0784) : ColourValue(1,1,1,1)); + mSecunda->setColour ( mMoonRed ? fallback->getFallbackColour("Moons_Script_Color") : ColourValue(1,1,1,1)); mMasser->setColour (ColourValue(1,1,1,1)); if (mSunEnabled) @@ -434,12 +399,10 @@ void SkyManager::update(float duration) // increase the strength of the sun glare effect depending // on how directly the player is looking at the sun Vector3 sun = mSunGlare->getPosition(); - sun = Vector3(sun.x, sun.z, -sun.y); Vector3 cam = mCamera->getRealDirection(); const Degree angle = sun.angleBetween( cam ); float val = 1- (angle.valueDegrees() / 180.f); - val = (val*val*val*val)*2; - + val = (val*val*val*val)*6; mSunGlare->setSize(val * mGlareFade); } @@ -521,6 +484,13 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) weather.mSkyColor.r, weather.mSkyColor.g, weather.mSkyColor.b, weather.mSkyColor.a))); } + if (mFogColour != weather.mFogColor) + { + mFogColour = weather.mFogColor; + sh::Factory::getInstance().setSharedParameter ("horizonColour", sh::makeProperty(new sh::Vector4( + weather.mFogColor.r, weather.mFogColor.g, weather.mFogColor.b, weather.mFogColor.a))); + } + mCloudSpeed = weather.mCloudSpeed; if (weather.mNight && mStarsOpacity != weather.mNightFade) @@ -561,7 +531,7 @@ void SkyManager::setGlare(const float glare) Vector3 SkyManager::getRealSunPos() { if (!mCreated) return Vector3(0,0,0); - return mSun->getNode()->_getDerivedPosition(); + return mSun->getNode()->getPosition() + mCamera->getRealPosition(); } void SkyManager::sunEnable() @@ -628,6 +598,10 @@ void SkyManager::setLightningStrength(const float factor) else mLightning->setVisible(false); } +void SkyManager::setLightningEnabled(bool enabled) +{ + /// \todo +} void SkyManager::setLightningDirection(const Ogre::Vector3& dir) { @@ -664,25 +638,9 @@ Ogre::SceneNode* SkyManager::getSunNode() return mSun->getNode(); } -void SkyManager::setSkyPosition(const Ogre::Vector3& position) -{ - mRootNode->setPosition(position); -} - -void SkyManager::resetSkyPosition() -{ - mCamera->getParentSceneNode ()->needUpdate (); - mRootNode->setPosition(mCamera->getDerivedPosition()); -} - -void SkyManager::scaleSky(float scale) -{ - mRootNode->setScale(scale, scale, scale); -} - void SkyManager::setGlareEnabled (bool enabled) { - if (!mCreated) + if (!mCreated || !mEnabled) return; mSunGlare->setVisible (mSunEnabled && enabled); } diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index ee1360853..3df8846cd 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDER_SKY_H -#define _GAME_RENDER_SKY_H +#ifndef GAME_RENDER_SKY_H +#define GAME_RENDER_SKY_H #include @@ -61,7 +61,7 @@ namespace MWRender Ogre::ColourValue mColour; Ogre::SceneNode* mNode; sh::MaterialInstance* mMaterial; - Ogre::BillboardSet* mBBSet; + Ogre::Entity* mEntity; }; @@ -112,14 +112,11 @@ namespace MWRender class SkyManager { public: - SkyManager(Ogre::SceneNode* pMwRoot, Ogre::Camera* pCamera); + SkyManager(Ogre::SceneNode* root, Ogre::Camera* pCamera); ~SkyManager(); void update(float duration); - void create(); - ///< no need to call this, automatically done on first enable() - void enable(); void disable(); @@ -167,16 +164,16 @@ namespace MWRender void setLightningStrength(const float factor); void setLightningDirection(const Ogre::Vector3& dir); + void setLightningEnabled(bool enabled); ///< disable prior to map render void setGlare(const float glare); void setGlareEnabled(bool enabled); Ogre::Vector3 getRealSunPos(); - void setSkyPosition(const Ogre::Vector3& position); - void resetSkyPosition(); - void scaleSky(float scale); - private: + void create(); + ///< no need to call this, automatically done on first enable() + bool mCreated; bool mMoonRed; @@ -199,8 +196,6 @@ namespace MWRender Ogre::SceneNode* mAtmosphereDay; Ogre::SceneNode* mAtmosphereNight; - Ogre::HighLevelGpuProgramPtr mCloudFragmentShader; - // remember some settings so we don't have to apply them again if they didnt change Ogre::String mClouds; Ogre::String mNextClouds; @@ -210,6 +205,7 @@ namespace MWRender float mStarsOpacity; Ogre::ColourValue mCloudColour; Ogre::ColourValue mSkyColour; + Ogre::ColourValue mFogColour; Ogre::Light* mLightning; @@ -218,8 +214,6 @@ namespace MWRender float mGlare; // target float mGlareFade; // actual - void ModVertexAlpha(Ogre::Entity* ent, unsigned int meshType); - bool mEnabled; bool mSunEnabled; bool mMasserEnabled; @@ -227,4 +221,4 @@ namespace MWRender }; } -#endif // _GAME_RENDER_SKY_H +#endif // GAME_RENDER_SKY_H diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index e5a1362d7..c27dce6ca 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "../mwworld/esmstore.hpp" @@ -25,7 +26,7 @@ namespace MWRender //---------------------------------------------------------------------------------------------- TerrainManager::TerrainManager(Ogre::SceneManager* mgr, RenderingManager* rend) : - mTerrainGroup(TerrainGroup(mgr, Terrain::ALIGN_X_Z, mLandSize, mWorldSize)), mRendering(rend) + mTerrainGroup(TerrainGroup(mgr, Terrain::ALIGN_X_Y, mLandSize, mWorldSize)), mRendering(rend) { mTerrainGlobals = OGRE_NEW TerrainGlobalOptions(); @@ -39,9 +40,10 @@ namespace MWRender ->getActiveProfile(); mActiveProfile = static_cast(activeProfile); - //The pixel error should be as high as possible without it being noticed - //as it governs how fast mesh quality decreases. - mTerrainGlobals->setMaxPixelError(8); + // We don't want any pixel error at all. Really, LOD makes no sense here - morrowind uses 65x65 verts in one cell, + // so applying LOD is most certainly slower than doing no LOD at all. + // Setting this to 0 seems to cause glitches though. :/ + mTerrainGlobals->setMaxPixelError(1); mTerrainGlobals->setLayerBlendMapSize(32); @@ -53,8 +55,8 @@ namespace MWRender mTerrainGlobals->setCompositeMapDistance(mWorldSize*2); mTerrainGroup.setOrigin(Vector3(mWorldSize/2, - 0, - -mWorldSize/2)); + mWorldSize/2, + 0)); Terrain::ImportData& importSettings = mTerrainGroup.getDefaultImportSettings(); @@ -69,6 +71,17 @@ namespace MWRender //---------------------------------------------------------------------------------------------- + float TerrainManager::getTerrainHeightAt(Vector3 worldPos) + { + Ogre::Terrain* terrain = NULL; + float height = mTerrainGroup.getHeightAtWorldPosition(worldPos, &terrain); + if (terrain == NULL) + return std::numeric_limits().min(); + return height; + } + + //---------------------------------------------------------------------------------------------- + TerrainManager::~TerrainManager() { OGRE_DELETE mTerrainGlobals; @@ -143,7 +156,7 @@ namespace MWRender std::map indexes; initTerrainTextures(&terrainData, cellX, cellY, x * numTextures, y * numTextures, - numTextures, indexes); + numTextures, indexes, land->mPlugin); if (mTerrainGroup.getTerrain(terrainX, terrainY) == NULL) { @@ -178,6 +191,16 @@ namespace MWRender } } + // when loading from a heightmap, Ogre::Terrain does not update the derived data (normal map, LOD) + // synchronously, even if we supply synchronous = true parameter to loadTerrain. + // the following to be the only way to make sure derived data is ready when rendering the next frame. + while (mTerrainGroup.isDerivedDataUpdateInProgress()) + { + // we need to wait for this to finish + OGRE_THREAD_SLEEP(5); + Root::getSingleton().getWorkQueue()->processResponses(); + } + mTerrainGroup.freeTemporaryResources(); } @@ -202,8 +225,13 @@ namespace MWRender void TerrainManager::initTerrainTextures(Terrain::ImportData* terrainData, int cellX, int cellY, int fromX, int fromY, int size, - std::map& indexes) + std::map& indexes, size_t plugin) { + // FIXME: In a multiple esm configuration, we have multiple palettes. Since this code + // crosses cell boundaries, we no longer have a unique terrain palette. Instead, we need + // to adopt the following code for a dynamic palette. And this is evil - the current design + // does not work well for this task... + assert(terrainData != NULL && "Must have valid terrain data"); assert(fromX >= 0 && fromY >= 0 && "Can't get a terrain texture on terrain outside the current cell"); @@ -216,12 +244,20 @@ namespace MWRender // //If we don't sort the ltex indexes, the splatting order may differ between //cells which may lead to inconsistent results when shading between cells + int num = MWBase::Environment::get().getWorld()->getStore().get().getSize(plugin); std::set ltexIndexes; for ( int y = fromY - 1; y < fromY + size + 1; y++ ) { for ( int x = fromX - 1; x < fromX + size + 1; x++ ) { - ltexIndexes.insert(getLtexIndexAt(cellX, cellY, x, y)); + int idx = getLtexIndexAt(cellX, cellY, x, y); + // This is a quick hack to prevent the program from trying to fetch textures + // from a neighboring cell, which might originate from a different plugin, + // and use a separate texture palette. Right now, we simply cast it to the + // default texture (i.e. 0). + if (idx > num) + idx = 0; + ltexIndexes.insert(idx); } } @@ -233,7 +269,7 @@ namespace MWRender iter != ltexIndexes.end(); ++iter ) { - const uint16_t ltexIndex = *iter; + uint16_t ltexIndex = *iter; //this is the base texture, so we can ignore this at present if ( ltexIndex == baseTexture ) { @@ -249,8 +285,11 @@ namespace MWRender const MWWorld::Store <exStore = MWBase::Environment::get().getWorld()->getStore().get(); - assert( (int)ltexStore.getSize() >= (int)ltexIndex - 1 && - "LAND.VTEX must be within the bounds of the LTEX array"); + // NOTE: using the quick hack above, we should no longer end up with textures indices + // that are out of bounds. However, I haven't updated the test to a multi-palette + // system yet. We probably need more work here, so we skip it for now. + //assert( (int)ltexStore.getSize() >= (int)ltexIndex - 1 && + //"LAND.VTEX must be within the bounds of the LTEX array"); std::string texture; if ( ltexIndex == 0 ) @@ -259,7 +298,7 @@ namespace MWRender } else { - texture = ltexStore.search(ltexIndex-1)->mTexture; + texture = ltexStore.search(ltexIndex-1, plugin)->mTexture; //TODO this is needed due to MWs messed up texture handling texture = texture.substr(0, texture.rfind(".")) + ".dds"; } diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index c83d96cf4..45c56390e 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -40,6 +40,9 @@ namespace MWRender{ void cellAdded(MWWorld::CellStore* store); void cellRemoved(MWWorld::CellStore* store); + + float getTerrainHeightAt (Ogre::Vector3 worldPos); + private: Ogre::TerrainGlobalOptions* mTerrainGlobals; Ogre::TerrainGroup mTerrainGroup; @@ -75,7 +78,7 @@ namespace MWRender{ void initTerrainTextures(Ogre::Terrain::ImportData* terrainData, int cellX, int cellY, int fromX, int fromY, int size, - std::map& indexes); + std::map& indexes, size_t plugin); /** * Creates the blend (splatting maps) for the given terrain from the ltex data. diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index 5ef9fe58f..892dab7cf 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -49,14 +49,16 @@ namespace MWRender TerrainMaterial::Profile::Profile(Ogre::TerrainMaterialGenerator* parent, const Ogre::String& name, const Ogre::String& desc) : Ogre::TerrainMaterialGenerator::Profile(parent, name, desc) , mGlobalColourMap(false) + , mMaterial(0) { } TerrainMaterial::Profile::~Profile() { + if (mMaterial) + sh::Factory::getInstance().destroyMaterialInstance(mMaterial->getName()); } - Ogre::MaterialPtr TerrainMaterial::Profile::generate(const Ogre::Terrain* terrain) { const Ogre::String& matName = terrain->getMaterialName(); @@ -68,64 +70,114 @@ namespace MWRender Ogre::MaterialManager::getSingleton().remove(matName); mMaterial = sh::Factory::getInstance().createMaterialInstance (matName); - mMaterial->setProperty ("allow_fixed_function", sh::makeProperty(new sh::BooleanValue(false))); - sh::MaterialInstancePass* p = mMaterial->createPass (); - - p->setProperty ("vertex_program", sh::makeProperty(new sh::StringValue("terrain_vertex"))); - p->setProperty ("fragment_program", sh::makeProperty(new sh::StringValue("terrain_fragment"))); - - p->mShaderProperties.setProperty ("colour_map", sh::makeProperty(new sh::BooleanValue(mGlobalColourMap))); - - // global colour map - sh::MaterialInstanceTextureUnit* colourMap = p->createTextureUnit ("colourMap"); - colourMap->setProperty ("texture_alias", sh::makeProperty (new sh::StringValue(mMaterial->getName() + "_colourMap"))); - colourMap->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); - - // global normal map - sh::MaterialInstanceTextureUnit* normalMap = p->createTextureUnit ("normalMap"); - normalMap->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getTerrainNormalMap ()->getName()))); - normalMap->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); - - Ogre::uint maxLayers = getMaxLayers(terrain); - Ogre::uint numBlendTextures = std::min(terrain->getBlendTextureCount(maxLayers), terrain->getBlendTextureCount()); - Ogre::uint numLayers = std::min(maxLayers, static_cast(terrain->getLayerCount())); - - p->mShaderProperties.setProperty ("num_layers", sh::makeProperty(new sh::StringValue(Ogre::StringConverter::toString(numLayers)))); - p->mShaderProperties.setProperty ("num_blendmaps", sh::makeProperty(new sh::StringValue(Ogre::StringConverter::toString(numBlendTextures)))); - - // blend maps - for (Ogre::uint i = 0; i < numBlendTextures; ++i) - { - sh::MaterialInstanceTextureUnit* blendTex = p->createTextureUnit ("blendMap" + Ogre::StringConverter::toString(i)); - blendTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getBlendTextureName(i)))); - blendTex->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); - } - - // layer maps - for (Ogre::uint i = 0; i < numLayers; ++i) - { - sh::MaterialInstanceTextureUnit* diffuseTex = p->createTextureUnit ("diffuseMap" + Ogre::StringConverter::toString(i)); - diffuseTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getLayerTextureName(i, 0)))); - p->mShaderProperties.setProperty ("blendmap_component_" + Ogre::StringConverter::toString(i), - sh::makeProperty(new sh::StringValue(Ogre::StringConverter::toString(int((i-1) / 4)) + "." + getComponent(int((i-1) % 4))))); - } + int numPasses = getRequiredPasses(terrain); + int maxLayersInOnePass = getMaxLayersPerPass(terrain); - // shadow - for (Ogre::uint i = 0; i < 3; ++i) + for (int pass=0; passcreateTextureUnit ("shadowMap" + Ogre::StringConverter::toString(i)); - shadowTex->setProperty ("content_type", sh::makeProperty (new sh::StringValue("shadow"))); + int layerOffset = maxLayersInOnePass * pass; + int blendmapOffset = (pass == 0) ? 1 : 0; // the first layer of the first pass is the base layer and does not need a blend map + + sh::MaterialInstancePass* p = mMaterial->createPass (); + + p->setProperty ("vertex_program", sh::makeProperty(new sh::StringValue("terrain_vertex"))); + p->setProperty ("fragment_program", sh::makeProperty(new sh::StringValue("terrain_fragment"))); + if (pass != 0) + { + p->setProperty ("scene_blend", sh::makeProperty(new sh::StringValue("alpha_blend"))); + // Only write if depth is equal to the depth value written by the previous pass. + p->setProperty ("depth_func", sh::makeProperty(new sh::StringValue("equal"))); + } + + p->mShaderProperties.setProperty ("colour_map", sh::makeProperty(new sh::BooleanValue(mGlobalColourMap))); + p->mShaderProperties.setProperty ("is_first_pass", sh::makeProperty(new sh::BooleanValue(pass == 0))); + + // global colour map + sh::MaterialInstanceTextureUnit* colourMap = p->createTextureUnit ("colourMap"); + colourMap->setProperty ("texture_alias", sh::makeProperty (new sh::StringValue(mMaterial->getName() + "_colourMap"))); + colourMap->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); + + // global normal map + sh::MaterialInstanceTextureUnit* normalMap = p->createTextureUnit ("normalMap"); + normalMap->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getTerrainNormalMap ()->getName()))); + normalMap->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); + + Ogre::uint numLayersInThisPass = std::min(maxLayersInOnePass, terrain->getLayerCount()-layerOffset); + + // HACK: Terrain::getLayerBlendTextureIndex should be const, but it is not. + // Remove this once ogre got fixed. + Ogre::Terrain* nonconstTerrain = const_cast(terrain); + + // a blend map might be shared between two passes + // so we can't just use terrain->getBlendTextureCount() + Ogre::uint numBlendTextures=0; + std::vector blendTextures; + for (unsigned int layer=blendmapOffset; layergetBlendTextureName(nonconstTerrain->getLayerBlendTextureIndex( + static_cast(layerOffset+layer)).first); + if (std::find(blendTextures.begin(), blendTextures.end(), blendTextureName) == blendTextures.end()) + { + blendTextures.push_back(blendTextureName); + ++numBlendTextures; + } + } + + p->mShaderProperties.setProperty ("num_layers", sh::makeProperty (new sh::StringValue(Ogre::StringConverter::toString(numLayersInThisPass)))); + p->mShaderProperties.setProperty ("num_blendmaps", sh::makeProperty (new sh::StringValue(Ogre::StringConverter::toString(numBlendTextures)))); + + // blend maps + // the index of the first blend map used in this pass + int blendmapStart; + if (terrain->getLayerCount() == 1) // special case. if there's only one layer, we don't need blend maps at all + blendmapStart = 0; + else + blendmapStart = nonconstTerrain->getLayerBlendTextureIndex(static_cast(layerOffset+blendmapOffset)).first; + for (Ogre::uint i = 0; i < numBlendTextures; ++i) + { + sh::MaterialInstanceTextureUnit* blendTex = p->createTextureUnit ("blendMap" + Ogre::StringConverter::toString(i)); + blendTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getBlendTextureName(blendmapStart+i)))); + blendTex->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); + } + + // layer maps + for (Ogre::uint i = 0; i < numLayersInThisPass; ++i) + { + sh::MaterialInstanceTextureUnit* diffuseTex = p->createTextureUnit ("diffuseMap" + Ogre::StringConverter::toString(i)); + diffuseTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getLayerTextureName(layerOffset+i, 0)))); + + if (i+layerOffset > 0) + { + int blendTextureIndex = nonconstTerrain->getLayerBlendTextureIndex(static_cast(layerOffset+i)).first; + int blendTextureComponent = nonconstTerrain->getLayerBlendTextureIndex(static_cast(layerOffset+i)).second; + p->mShaderProperties.setProperty ("blendmap_component_" + Ogre::StringConverter::toString(i), + sh::makeProperty (new sh::StringValue(Ogre::StringConverter::toString(blendTextureIndex-blendmapStart) + "." + getComponent(blendTextureComponent)))); + } + else + { + // just to make it shut up about blendmap_component_0 not existing in the first pass. + // it might be retrieved, but will never survive the preprocessing step. + p->mShaderProperties.setProperty ("blendmap_component_" + Ogre::StringConverter::toString(i), + sh::makeProperty (new sh::StringValue(""))); + } + } + + // shadow + for (Ogre::uint i = 0; i < 3; ++i) + { + sh::MaterialInstanceTextureUnit* shadowTex = p->createTextureUnit ("shadowMap" + Ogre::StringConverter::toString(i)); + shadowTex->setProperty ("content_type", sh::makeProperty (new sh::StringValue("shadow"))); + } + + p->mShaderProperties.setProperty ("shadowtexture_offset", sh::makeProperty (new sh::StringValue( + Ogre::StringConverter::toString(numBlendTextures + numLayersInThisPass + 2)))); + + // 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(pass))); } - // caustics - sh::MaterialInstanceTextureUnit* caustics = p->createTextureUnit ("causticMap"); - caustics->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue("water_nm.png"))); - - p->mShaderProperties.setProperty ("shadowtexture_offset", sh::makeProperty(new sh::StringValue( - Ogre::StringConverter::toString(numBlendTextures + numLayers + 2)))); - return Ogre::MaterialManager::getSingleton().getByName(matName); } @@ -146,6 +198,11 @@ namespace MWRender } Ogre::uint8 TerrainMaterial::Profile::getMaxLayers(const Ogre::Terrain* terrain) const + { + return 255; + } + + int TerrainMaterial::Profile::getMaxLayersPerPass (const Ogre::Terrain* terrain) { // count the texture units free Ogre::uint8 freeTextureUnits = 16; @@ -153,14 +210,23 @@ namespace MWRender --freeTextureUnits; // colourmap --freeTextureUnits; - freeTextureUnits -= 3; // shadow PSSM - - --freeTextureUnits; // caustics + // shadow + --freeTextureUnits; + --freeTextureUnits; + --freeTextureUnits; // each layer needs 1.25 units (1xdiffusespec, 0.25xblend) return static_cast(freeTextureUnits / (1.25f)); } + int TerrainMaterial::Profile::getRequiredPasses (const Ogre::Terrain* terrain) + { + int maxLayersPerPass = getMaxLayersPerPass(terrain); + assert(terrain->getLayerCount()); + assert(maxLayersPerPass); + return std::ceil(static_cast(terrain->getLayerCount()) / maxLayersPerPass); + } + void TerrainMaterial::Profile::updateParams(const Ogre::MaterialPtr& mat, const Ogre::Terrain* terrain) { } diff --git a/apps/openmw/mwrender/terrainmaterial.hpp b/apps/openmw/mwrender/terrainmaterial.hpp index 3e31b2a58..c90499bae 100644 --- a/apps/openmw/mwrender/terrainmaterial.hpp +++ b/apps/openmw/mwrender/terrainmaterial.hpp @@ -67,10 +67,14 @@ namespace MWRender void setGlobalColourMapEnabled(bool enabled); void setGlobalColourMap (Ogre::Terrain* terrain, const std::string& name); + virtual void setLightmapEnabled(bool) {} private: sh::MaterialInstance* mMaterial; + int getRequiredPasses (const Ogre::Terrain* terrain); + int getMaxLayersPerPass (const Ogre::Terrain* terrain); + bool mGlobalColourMap; }; diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index baec2f740..87ae8175d 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -17,6 +17,13 @@ #include "../mwsound/sound_decoder.hpp" #include "../mwsound/sound.hpp" +#include "renderconst.hpp" + +#ifdef _WIN32 +#include + +typedef SSIZE_T ssize_t; +#endif namespace MWRender { @@ -361,7 +368,11 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder } void open(const std::string&) +#ifdef _WIN32 + { fail(std::string("Invalid call to ")+__FUNCSIG__); } +#else { fail(std::string("Invalid call to ")+__PRETTY_FUNCTION__); } +#endif void close() { } @@ -395,6 +406,8 @@ public: *type = MWSound::SampleType_UInt8; else if(mAVStream->codec->sample_fmt == AV_SAMPLE_FMT_S16) *type = MWSound::SampleType_Int16; + else if(mAVStream->codec->sample_fmt == AV_SAMPLE_FMT_FLT) + *type = MWSound::SampleType_Float32; else fail(std::string("Unsupported sample format: ")+ av_get_sample_fmt_name(mAVStream->codec->sample_fmt)); @@ -775,8 +788,8 @@ void VideoState::decode_thread_loop(VideoState *self) // main decode loop while(!self->quit) { - if((self->audio_st >= 0 && self->audioq.size > MAX_AUDIOQ_SIZE) || - (self->video_st >= 0 && self->videoq.size > MAX_VIDEOQ_SIZE)) + if((self->audio_st && self->audioq.size > MAX_AUDIOQ_SIZE) || + (self->video_st && self->videoq.size > MAX_VIDEOQ_SIZE)) { boost::this_thread::sleep(boost::posix_time::milliseconds(10)); continue; @@ -1056,9 +1069,9 @@ VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) mBackgroundNode->attachObject(mBackgroundRectangle); mRectangle->setVisible(false); - mRectangle->setVisibilityFlags(0x1); + mRectangle->setVisibilityFlags(RV_Overlay); mBackgroundRectangle->setVisible(false); - mBackgroundRectangle->setVisibilityFlags(0x1); + mBackgroundRectangle->setVisibilityFlags(RV_Overlay); } VideoPlayer::~VideoPlayer() diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index e8f099640..772eaf623 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -4,17 +4,12 @@ #include #include #include -#include -#include -#include #include -#include -#include -#include #include "sky.hpp" #include "renderingmanager.hpp" -#include "compositors.hpp" +#include "ripplesimulation.hpp" +#include "refraction.hpp" #include #include @@ -27,38 +22,195 @@ using namespace Ogre; namespace MWRender { -Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cell) : - mCamera (camera), mSceneManager (camera->getSceneManager()), +CubeReflection::CubeReflection(Ogre::SceneManager* sceneManager) + : Reflection(sceneManager) +{ + Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton ().createManual("CubeReflection", + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_CUBE_MAP, + 512,512, 0, PF_R8G8B8, TU_RENDERTARGET); + + mCamera = mSceneMgr->createCamera ("CubeCamera"); + mCamera->setNearClipDistance (5); + mCamera->setFarClipDistance (1000); + + for (int face = 0; face < 6; ++face) + { + mRenderTargets[face] = texture->getBuffer (face)->getRenderTarget(); + mRenderTargets[face]->removeAllViewports (); + Viewport* vp = mRenderTargets[face]->addViewport (mCamera); + vp->setOverlaysEnabled(false); + vp->setShadowsEnabled(false); + vp->setMaterialScheme ("water_reflection"); + mRenderTargets[face]->setAutoUpdated(false); + + /* + Vector3 lookAt(0,0,0), up(0,0,0), right(0,0,0); + switch(face) + { + case 0: lookAt.x =-1; up.y = 1; right.z = 1; break; // +X + case 1: lookAt.x = 1; up.y = 1; right.z =-1; break; // -X + case 2: lookAt.y =-1; up.z = 1; right.x = 1; break; // +Y + case 3: lookAt.y = 1; up.z =-1; right.x = 1; break; // -Y + case 4: lookAt.z = 1; up.y = 1; right.x =-1; break; // +Z + case 5: lookAt.z =-1; up.y = 1; right.x =-1; break; // -Z + } + Quaternion orient(right, up, lookAt); + mCamera->setOrientation(orient); + */ + } +} + +CubeReflection::~CubeReflection () +{ + Ogre::TextureManager::getSingleton ().remove("CubeReflection"); + mSceneMgr->destroyCamera (mCamera); +} + +void CubeReflection::update () +{ + mParentCamera->getParentSceneNode ()->needUpdate (); + mCamera->setPosition(mParentCamera->getDerivedPosition()); +} + +// -------------------------------------------------------------------------------------------------------------------------------- + +PlaneReflection::PlaneReflection(Ogre::SceneManager* sceneManager, SkyManager* sky) + : Reflection(sceneManager) + , mSky(sky) + , mRenderActive(false) +{ + mCamera = mSceneMgr->createCamera ("PlaneReflectionCamera"); + mSceneMgr->addRenderQueueListener(this); + + mTexture = TextureManager::getSingleton().createManual("WaterReflection", + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, 512, 512, 0, PF_R8G8B8, TU_RENDERTARGET); + + mRenderTarget = mTexture->getBuffer()->getRenderTarget(); + Viewport* vp = mRenderTarget->addViewport(mCamera); + vp->setOverlaysEnabled(false); + vp->setBackgroundColour(ColourValue(0.8f, 0.9f, 1.0f)); + vp->setShadowsEnabled(false); + vp->setMaterialScheme("water_reflection"); + mRenderTarget->addListener(this); + mRenderTarget->setActive(true); + mRenderTarget->setAutoUpdated(true); + + sh::Factory::getInstance ().setTextureAlias ("WaterReflection", mTexture->getName()); +} + +PlaneReflection::~PlaneReflection () +{ + mRenderTarget->removeListener (this); + mSceneMgr->destroyCamera (mCamera); + mSceneMgr->removeRenderQueueListener(this); + TextureManager::getSingleton ().remove("WaterReflection"); +} + +void PlaneReflection::renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &skipThisInvocation) +{ + // We don't want the sky to get clipped by custom near clip plane (the water plane) + if (queueGroupId < 20 && mRenderActive) + { + mCamera->disableCustomNearClipPlane(); + Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mCamera->getProjectionMatrixRS()); + } +} + +void PlaneReflection::renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &repeatThisInvocation) +{ + if (queueGroupId < 20 && mRenderActive) + { + mCamera->enableCustomNearClipPlane(mIsUnderwater ? mErrorPlaneUnderwater : mErrorPlane); + Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mCamera->getProjectionMatrixRS()); + } +} + +void PlaneReflection::preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt) +{ + mParentCamera->getParentSceneNode ()->needUpdate (); + mCamera->setOrientation(mParentCamera->getDerivedOrientation()); + mCamera->setPosition(mParentCamera->getDerivedPosition()); + mCamera->setNearClipDistance(mParentCamera->getNearClipDistance()); + mCamera->setFarClipDistance(mParentCamera->getFarClipDistance()); + mCamera->setAspectRatio(mParentCamera->getAspectRatio()); + mCamera->setFOVy(mParentCamera->getFOVy()); + mRenderActive = true; + + mCamera->enableReflection(mWaterPlane); + + // for depth calculation, we want the original viewproj matrix _without_ the custom near clip plane. + // since all we are interested in is depth, we only need the third row of the matrix. + Ogre::Matrix4 projMatrix = mCamera->getProjectionMatrixWithRSDepth () * mCamera->getViewMatrix (); + sh::Vector4* row3 = new sh::Vector4(projMatrix[2][0], projMatrix[2][1], projMatrix[2][2], projMatrix[2][3]); + sh::Factory::getInstance ().setSharedParameter ("vpRow2Fix", sh::makeProperty (row3)); + + // enable clip plane here to take advantage of CPU culling for overwater or underwater objects + mCamera->enableCustomNearClipPlane(mIsUnderwater ? mErrorPlaneUnderwater : mErrorPlane); +} + +void PlaneReflection::postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt) +{ + mCamera->disableReflection(); + mCamera->disableCustomNearClipPlane(); + mRenderActive = false; +} + +void PlaneReflection::setHeight (float height) +{ + mWaterPlane = Plane(Ogre::Vector3(0,0,1), height); + mErrorPlane = Plane(Ogre::Vector3(0,0,1), height - 5); + mErrorPlaneUnderwater = Plane(Ogre::Vector3(0,0,-1), -height - 5); +} + +void PlaneReflection::setActive (bool active) +{ + mRenderTarget->setActive(active); +} + +void PlaneReflection::setViewportBackground(Ogre::ColourValue colour) +{ + mRenderTarget->getViewport (0)->setBackgroundColour (colour); +} + +void PlaneReflection::setVisibilityMask (int flags) +{ + mRenderTarget->getViewport (0)->setVisibilityMask (flags); +} + +// -------------------------------------------------------------------------------------------------------------------------------- + +Water::Water (Ogre::Camera *camera, RenderingManager* rend) : + mCamera (camera), mSceneMgr (camera->getSceneManager()), mIsUnderwater(false), mVisibilityFlags(0), - mReflectionTarget(0), mActive(1), mToggled(1), - mReflectionRenderActive(false), mRendering(rend), - mWaterTimer(0.f) + mActive(1), mToggled(1), + mRendering(rend), + mWaterTimer(0.f), + mReflection(NULL), + mRefraction(NULL), + mSimulation(NULL), + mPlayer(0,0) { + mSimulation = new RippleSimulation(mSceneMgr); + mSky = rend->getSkyManager(); mMaterial = MaterialManager::getSingleton().getByName("Water"); - mTop = cell->mWater; + mTop = 0; mIsUnderwater = false; - mWaterPlane = Plane(Vector3::UNIT_Y, 0); + mWaterPlane = Plane(Vector3::UNIT_Z, 0); - MeshManager::getSingleton().createPlane("water", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, mWaterPlane, CELL_SIZE*5, CELL_SIZE * 5, 10, 10, true, 1, 3,3, Vector3::UNIT_Z); + MeshManager::getSingleton().createPlane("water", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, mWaterPlane, CELL_SIZE*5, CELL_SIZE * 5, 10, 10, true, 1, 3,3, Vector3::UNIT_Y); - mWater = mSceneManager->createEntity("water"); + mWater = mSceneMgr->createEntity("water"); mWater->setVisibilityFlags(RV_Water); - mWater->setRenderQueueGroup(RQG_Water); mWater->setCastShadows(false); + mWater->setRenderQueueGroup(RQG_Alpha); - mWaterNode = mSceneManager->getRootSceneNode()->createChildSceneNode(); - - mReflectionCamera = mSceneManager->createCamera("ReflectionCamera"); + mWaterNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); - if(!(cell->mData.mFlags & cell->Interior)) - { - mWaterNode->setPosition(getSceneNodeCoordinates(cell->mData.mX, cell->mData.mY)); - } mWaterNode->attachObject(mWater); applyRTT(); @@ -66,26 +218,11 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel mWater->setMaterial(mMaterial); - /* - Ogre::Entity* underwaterDome = mSceneManager->createEntity ("underwater_dome.mesh"); - underwaterDome->setRenderQueueGroup (RQG_UnderWater); - mUnderwaterDome = mSceneManager->getRootSceneNode ()->createChildSceneNode (); - mUnderwaterDome->attachObject (underwaterDome); - mUnderwaterDome->setScale(10000,10000,10000); - mUnderwaterDome->setVisible(false); - underwaterDome->setMaterialName("Underwater_Dome"); - */ - - mSceneManager->addRenderQueueListener(this); - - assignTextures(); - setHeight(mTop); sh::MaterialInstance* m = sh::Factory::getInstance ().getMaterialInstance ("Water"); m->setListener (this); - // ---------------------------------------------------------------------------------------------- // ---------------------------------- reflection debug overlay ---------------------------------- // ---------------------------------------------------------------------------------------------- @@ -146,12 +283,12 @@ Water::~Water() { MeshManager::getSingleton().remove("water"); - if (mReflectionTarget) - mReflectionTexture->getBuffer()->getRenderTarget()->removeListener(this); - mWaterNode->detachObject(mWater); - mSceneManager->destroyEntity(mWater); - mSceneManager->destroySceneNode(mWaterNode); + mSceneMgr->destroyEntity(mWater); + mSceneMgr->destroySceneNode(mWaterNode); + + delete mReflection; + delete mRefraction; } void Water::changeCell(const ESM::Cell* cell) @@ -168,12 +305,14 @@ void Water::setHeight(const float height) { mTop = height; - mWaterPlane = Plane(Vector3::UNIT_Y, height); + mWaterPlane = Plane(Vector3::UNIT_Z, -height); - // small error due to reflection texture size & reflection distortion - mErrorPlane = Plane(Vector3::UNIT_Y, height - 5); + if (mReflection) + mReflection->setHeight(height); + if (mRefraction) + mRefraction->setHeight(height); - mWaterNode->setPosition(0, height, 0); + mWaterNode->setPosition(0, 0, height); sh::Factory::getInstance ().setSharedParameter ("waterLevel", sh::makeProperty(new sh::FloatValue(height))); } @@ -194,137 +333,76 @@ Water::updateUnderwater(bool underwater) mWater->isVisible() && mCamera->getPolygonMode() == Ogre::PM_SOLID; + if (mReflection) + mReflection->setUnderwater (mIsUnderwater); + if (mRefraction) + mRefraction->setUnderwater (mIsUnderwater); + updateVisible(); } Vector3 Water::getSceneNodeCoordinates(int gridX, int gridY) { - return Vector3(gridX * CELL_SIZE + (CELL_SIZE / 2), mTop, -gridY * CELL_SIZE - (CELL_SIZE / 2)); -} - -void Water::preRenderTargetUpdate(const RenderTargetEvent& evt) -{ - if (evt.source == mReflectionTarget) - { - mCamera->getParentSceneNode ()->needUpdate (); - mReflectionCamera->setOrientation(mCamera->getDerivedOrientation()); - mReflectionCamera->setPosition(mCamera->getDerivedPosition()); - mReflectionCamera->setNearClipDistance(mCamera->getNearClipDistance()); - mReflectionCamera->setFarClipDistance(mCamera->getFarClipDistance()); - mReflectionCamera->setAspectRatio(mCamera->getAspectRatio()); - mReflectionCamera->setFOVy(mCamera->getFOVy()); - mReflectionRenderActive = true; - - Vector3 pos = mCamera->getRealPosition(); - pos.y = mTop*2 - pos.y; - mSky->setSkyPosition(pos); - mReflectionCamera->enableReflection(mWaterPlane); - } -} - -void Water::postRenderTargetUpdate(const RenderTargetEvent& evt) -{ - if (evt.source == mReflectionTarget) - { - mSky->resetSkyPosition(); - mReflectionCamera->disableReflection(); - mReflectionCamera->disableCustomNearClipPlane(); - mReflectionRenderActive = false; - } -} - -void Water::assignTextures() -{ - if (Settings::Manager::getBool("shader", "Water")) - { - - CompositorInstance* compositor = CompositorManager::getSingleton().getCompositorChain(mRendering->getViewport())->getCompositor("gbuffer"); - - TexturePtr colorTexture = compositor->getTextureInstance("mrt_output", 0); - sh::Factory::getInstance ().setTextureAlias ("WaterRefraction", colorTexture->getName()); - - TexturePtr depthTexture = compositor->getTextureInstance("mrt_output", 1); - sh::Factory::getInstance ().setTextureAlias ("SceneDepth", depthTexture->getName()); - } + return Vector3(gridX * CELL_SIZE + (CELL_SIZE / 2), gridY * CELL_SIZE + (CELL_SIZE / 2), mTop); } void Water::setViewportBackground(const ColourValue& bg) { - if (mReflectionTarget) - mReflectionTarget->getViewport(0)->setBackgroundColour(bg); + if (mReflection) + mReflection->setViewportBackground(bg); } void Water::updateVisible() { mWater->setVisible(mToggled && mActive); - if (mReflectionTarget) - mReflectionTarget->setActive(mToggled && mActive); + if (mReflection) + mReflection->setActive(mToggled && mActive); + if (mRefraction) + mRefraction->setActive(mToggled && mActive); } -void Water::renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &skipThisInvocation) +void Water::update(float dt, Ogre::Vector3 player) { - // We don't want the sky to get clipped by custom near clip plane (the water plane) - if (queueGroupId < 20 && mReflectionRenderActive) - { - mReflectionCamera->disableCustomNearClipPlane(); - Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mReflectionCamera->getProjectionMatrixRS()); - } -} + mWaterTimer += dt; + sh::Factory::getInstance ().setSharedParameter ("waterTimer", sh::makeProperty(new sh::FloatValue(mWaterTimer))); -void Water::renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &repeatThisInvocation) -{ - if (queueGroupId < 20 && mReflectionRenderActive) - { - if (!mIsUnderwater) - mReflectionCamera->enableCustomNearClipPlane(mErrorPlane); - Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mReflectionCamera->getProjectionMatrixRS()); - } + mRendering->getSkyManager ()->setGlareEnabled (!mIsUnderwater); + + mPlayer = Ogre::Vector2(player.x, player.y); } -void Water::update(float dt) +void Water::frameStarted(float dt) { - /* - Ogre::Vector3 pos = mCamera->getDerivedPosition (); - pos.y = -mWaterPlane.d; - mUnderwaterDome->setPosition (pos); - */ - - mWaterTimer += dt / 30.0 * MWBase::Environment::get().getWorld()->getTimeScaleFactor(); - sh::Factory::getInstance ().setSharedParameter ("waterTimer", sh::makeProperty(new sh::FloatValue(mWaterTimer))); + mSimulation->update(dt, mPlayer); - mRendering->getSkyManager ()->setGlareEnabled (!mIsUnderwater); + if (mReflection) + mReflection->update(); } void Water::applyRTT() { - if (mReflectionTarget) - { - TextureManager::getSingleton().remove("WaterReflection"); - mReflectionTarget = 0; - } + delete mReflection; + mReflection = NULL; + delete mRefraction; + mRefraction = NULL; // Create rendertarget for reflection - int rttsize = Settings::Manager::getInt("rtt size", "Water"); + //int rttsize = Settings::Manager::getInt("rtt size", "Water"); if (Settings::Manager::getBool("shader", "Water")) { - mReflectionTexture = TextureManager::getSingleton().createManual("WaterReflection", - ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, rttsize, rttsize, 0, PF_A8R8G8B8, TU_RENDERTARGET); - - RenderTarget* rtt = mReflectionTexture->getBuffer()->getRenderTarget(); - Viewport* vp = rtt->addViewport(mReflectionCamera); - vp->setOverlaysEnabled(false); - vp->setBackgroundColour(ColourValue(0.8f, 0.9f, 1.0f)); - vp->setShadowsEnabled(false); - // use fallback techniques without shadows and without mrt (currently not implemented for sky and terrain) - vp->setMaterialScheme("water_reflection"); - rtt->addListener(this); - rtt->setActive(true); - - mReflectionTarget = rtt; + mReflection = new PlaneReflection(mSceneMgr, mSky); + mReflection->setParentCamera (mCamera); + mReflection->setHeight(mTop); - sh::Factory::getInstance ().setTextureAlias ("WaterReflection", mReflectionTexture->getName()); + if (Settings::Manager::getBool("refraction", "Water")) + { + mRefraction = new Refraction(mCamera); + mRefraction->setHeight(mTop); + } } + + updateVisible(); } void Water::applyVisibilityMask() @@ -336,10 +414,8 @@ void Water::applyVisibilityMask() + RV_Misc * Settings::Manager::getBool("reflect misc", "Water") + RV_Sky; - if (mReflectionTarget) - { - mReflectionTexture->getBuffer()->getRenderTarget()->getViewport(0)->setVisibilityMask(mVisibilityFlags); - } + if (mReflection) + mReflection->setVisibilityMask(mVisibilityFlags); } void Water::processChangedSettings(const Settings::CategorySettingVector& settings) @@ -350,7 +426,8 @@ void Water::processChangedSettings(const Settings::CategorySettingVector& settin it != settings.end(); ++it) { if ( it->first == "Water" && ( - it->second == "shader" + it->second == "shader" + || it->second == "refraction" || it->second == "rtt size")) applyRT = true; @@ -368,7 +445,6 @@ void Water::processChangedSettings(const Settings::CategorySettingVector& settin applyRTT(); applyVisibilityMask(); mWater->setMaterial(mMaterial); - assignTextures(); } if (applyVisMask) applyVisibilityMask(); @@ -399,4 +475,19 @@ void Water::createdConfiguration (sh::MaterialInstance* m, const std::string& co } } +void Water::addEmitter (const MWWorld::Ptr& ptr, float scale, float force) +{ + mSimulation->addEmitter (ptr, scale, force); +} + +void Water::removeEmitter (const MWWorld::Ptr& ptr) +{ + mSimulation->removeEmitter (ptr); +} + +void Water::updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr) +{ + mSimulation->updateEmitterPtr(old, ptr); +} + } // namespace diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index f1e3d7a3b..a21d03ad6 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -7,13 +7,17 @@ #include #include #include +#include #include #include +#include + + #include "renderconst.hpp" -#include +#include "../mwworld/ptr.hpp" namespace Ogre { @@ -22,6 +26,7 @@ namespace Ogre class SceneNode; class Entity; class Vector3; + class Rectangle2D; struct RenderTargetEvent; } @@ -29,22 +34,84 @@ namespace MWRender { class SkyManager; class RenderingManager; + class RippleSimulation; + class Refraction; + + class Reflection + { + public: + Reflection(Ogre::SceneManager* sceneManager) + : mSceneMgr(sceneManager) + , mIsUnderwater(false) + {} + virtual ~Reflection() {} + + virtual void setHeight (float height) {} + virtual void setParentCamera (Ogre::Camera* parent) { mParentCamera = parent; } + void setUnderwater(bool underwater) { mIsUnderwater = underwater; } + virtual void setActive (bool active) {} + virtual void setViewportBackground(Ogre::ColourValue colour) {} + virtual void update() {} + virtual void setVisibilityMask (int flags) {} + + protected: + Ogre::Camera* mCamera; + Ogre::Camera* mParentCamera; + Ogre::TexturePtr mTexture; + Ogre::SceneManager* mSceneMgr; + bool mIsUnderwater; + }; + + class CubeReflection : public Reflection + { + public: + CubeReflection(Ogre::SceneManager* sceneManager); + virtual ~CubeReflection(); + + virtual void update(); + protected: + Ogre::RenderTarget* mRenderTargets[6]; + }; + + class PlaneReflection : public Reflection, public Ogre::RenderQueueListener, public Ogre::RenderTargetListener + { + public: + PlaneReflection(Ogre::SceneManager* sceneManager, SkyManager* sky); + virtual ~PlaneReflection(); + + virtual void setHeight (float height); + virtual void setActive (bool active); + virtual void setVisibilityMask (int flags); + + void preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt); + void postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt); + + void renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &skipThisInvocation); + void renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &repeatThisInvocation); + + virtual void setViewportBackground(Ogre::ColourValue colour); + + protected: + Ogre::RenderTarget* mRenderTarget; + SkyManager* mSky; + Ogre::Plane mWaterPlane; + Ogre::Plane mErrorPlane; + Ogre::Plane mErrorPlaneUnderwater; + bool mRenderActive; + }; /// Water rendering - class Water : public Ogre::RenderTargetListener, public Ogre::RenderQueueListener, public sh::MaterialInstanceListener + class Water : public sh::MaterialInstanceListener { static const int CELL_SIZE = 8192; Ogre::Camera *mCamera; - Ogre::SceneManager *mSceneManager; + Ogre::SceneManager *mSceneMgr; Ogre::Plane mWaterPlane; - Ogre::Plane mErrorPlane; Ogre::SceneNode *mWaterNode; Ogre::Entity *mWater; - //Ogre::SceneNode* mUnderwaterDome; - bool mIsUnderwater; bool mActive; bool mToggled; @@ -52,17 +119,10 @@ namespace MWRender { float mWaterTimer; - bool mReflectionRenderActive; Ogre::Vector3 getSceneNodeCoordinates(int gridX, int gridY); protected: - void preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt); - void postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt); - - void renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &skipThisInvocation); - void renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &repeatThisInvocation); - void applyRTT(); void applyVisibilityMask(); @@ -75,24 +135,29 @@ namespace MWRender { Ogre::MaterialPtr mMaterial; - Ogre::Camera* mReflectionCamera; - - Ogre::TexturePtr mReflectionTexture; - Ogre::RenderTarget* mReflectionTarget; - bool mUnderwaterEffect; int mVisibilityFlags; + Reflection* mReflection; + Refraction* mRefraction; + RippleSimulation* mSimulation; + + Ogre::Vector2 mPlayer; + public: - Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cell); + Water (Ogre::Camera *camera, RenderingManager* rend); ~Water(); void setActive(bool active); void toggle(); - void update(float dt); + void update(float dt, Ogre::Vector3 player); + void frameStarted(float dt); - void assignTextures(); + /// adds an emitter, position will be tracked automatically using its scene node + void addEmitter (const MWWorld::Ptr& ptr, float scale = 1.f, float force = 1.f); + void removeEmitter (const MWWorld::Ptr& ptr); + void updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr); void setViewportBackground(const Ogre::ColourValue& bg); diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 35243533c..9f29163af 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -181,19 +181,30 @@ namespace MWScript runtime.pop(); std::vector idleList; - for (unsigned int i=0; i #include -#include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -27,7 +27,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - MWBase::Environment::get().getWorld()->skipAnimation (ptr); + MWBase::Environment::get().getMechanicsManager()->skipAnimation (ptr); } }; @@ -54,7 +54,7 @@ namespace MWScript throw std::runtime_error ("animation mode out of range"); } - MWBase::Environment::get().getWorld()->playAnimationGroup (ptr, group, mode, 1); + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, 1); } }; @@ -87,7 +87,7 @@ namespace MWScript throw std::runtime_error ("animation mode out of range"); } - MWBase::Environment::get().getWorld()->playAnimationGroup (ptr, group, mode, loops); + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, loops); } }; diff --git a/apps/openmw/mwscript/compilercontext.cpp b/apps/openmw/mwscript/compilercontext.cpp index 57d93512f..7e63a33b2 100644 --- a/apps/openmw/mwscript/compilercontext.cpp +++ b/apps/openmw/mwscript/compilercontext.cpp @@ -59,7 +59,7 @@ namespace MWScript store.get().search (name) || store.get().search (name) || store.get().search (name) || - store.get().search (name) || + store.get().search (name) || store.get().search (name) || store.get().search (name) || store.get().search (name) || diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 1fa69d1fd..3a46f62d6 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -21,6 +21,7 @@ #include "../mwworld/containerstore.hpp" #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwworld/player.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -47,11 +48,43 @@ namespace MWScript if (count<0) throw std::runtime_error ("second argument for AddItem must be non-negative"); + // no-op + if (count == 0) + return; + MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), item); ref.getPtr().getRefData().setCount (count); + + // Configure item's script variables + std::string script = MWWorld::Class::get(ref.getPtr()).getScript(ref.getPtr()); + if (script != "") + { + const ESM::Script *esmscript = MWBase::Environment::get().getWorld()->getStore().get().find (script); + ref.getPtr().getRefData().setLocals(*esmscript); + } MWWorld::Class::get (ptr).getContainerStore (ptr).add (ref.getPtr()); + + // 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() ) + { + // 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; + std::string itemName = MWWorld::Class::get(ref.getPtr()).getName(ref.getPtr()); + if (count == 1) + { + msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage60}"); + msgBox = boost::str(boost::format(msgBox) % itemName); + } + else + { + msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage61}"); + msgBox = boost::str(boost::format(msgBox) % count % itemName); + } + std::vector noButtons; + MWBase::Environment::get().getWindowManager()->messageBox(msgBox, noButtons, /*showInDialogueModeOnly*/ true); + } } }; @@ -93,13 +126,19 @@ namespace MWScript Interpreter::Type_Integer count = runtime[0].mInteger; runtime.pop(); - + if (count<0) throw std::runtime_error ("second argument for RemoveItem must be non-negative"); + // no-op + if (count == 0) + return; + MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); std::string itemName = ""; + + // originalCount holds the total number of items to remove, count holds the remaining number of items to remove Interpreter::Type_Integer originalCount = count; for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end() && count; @@ -122,28 +161,28 @@ namespace MWScript } } - /* 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; - if(originalCount - count > 1) + // Spawn a messagebox (only for items removed from player's inventory) + if (ptr == MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer()) { - msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage63}"); - std::stringstream temp; - temp << boost::format(msgBox) % (originalCount - count) % itemName; - msgBox = temp.str(); - } - else - { - msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage62}"); - std::stringstream temp; - temp << boost::format(msgBox) % itemName; - msgBox = temp.str(); + // 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; + int numRemoved = (originalCount - count); + if (numRemoved == 0) + return; + + if(numRemoved > 1) + { + msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage63}"); + msgBox = boost::str (boost::format(msgBox) % numRemoved % itemName); + } + else + { + msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage62}"); + msgBox = boost::str (boost::format(msgBox) % itemName); + } + std::vector noButtons; + MWBase::Environment::get().getWindowManager()->messageBox(msgBox, noButtons, /*showInDialogueModeOnly*/ true); } - - if(originalCount - count > 0) - MWBase::Environment::get().getWindowManager()->messageBox(msgBox, std::vector()); - - // To be fully compatible with original Morrowind, we would need to check if - // count is >= 0 here and throw an exception. But let's be tollerant instead. } }; diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp index 8d65dfdd5..ac53b8ee2 100644 --- a/apps/openmw/mwscript/controlextensions.cpp +++ b/apps/openmw/mwscript/controlextensions.cpp @@ -12,6 +12,7 @@ #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/ptr.hpp" #include "../mwmechanics/npcstats.hpp" diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 283f149de..85d8deaec 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -315,5 +315,28 @@ op 0x20001f8: Drop op 0x20001f9: Drop, explicit reference op 0x20001fa: DropSoulGem op 0x20001fb: DropSoulGem, explicit reference +op 0x20001fc: OnDeath +op 0x20001fd: IsWerewolf +op 0x20001fe: IsWerewolf, explicit reference +op 0x20001ff: Rotate +op 0x2000200: Rotate, explicit reference +op 0x2000201: RotateWorld +op 0x2000202: RotateWorld, explicit reference +op 0x2000203: SetAtStart +op 0x2000204: SetAtStart, explicit +op 0x2000205: OnDeath, explicit +op 0x2000206: Move +op 0x2000207: Move, explicit +op 0x2000208: MoveWorld +op 0x2000209: MoveWorld, explicit +op 0x200020a: Fall +op 0x200020b: Fall, explicit +op 0x200020c: GetStandingPC +op 0x200020d: GetStandingPC, explicit +op 0x200020e: GetStandingActor +op 0x200020f: GetStandingActor, explicit +op 0x2000210: GetStartingAngle +op 0x2000211: GetStartingAngle, explicit +op 0x2000212: GetWindSpeed -opcodes 0x20001fa-0x3ffffff unused +opcodes 0x2000213-0x3ffffff unused diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 3ef138432..608725ae6 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -15,12 +15,18 @@ namespace MWScript GlobalScripts::GlobalScripts (const MWWorld::ESMStore& store) : mStore (store) { + reset(); + } + + void GlobalScripts::reset() + { + mScripts.clear(); addScript ("Main"); MWWorld::Store::iterator iter = - store.get().begin(); + mStore.get().begin(); - for (; iter != store.get().end(); ++iter) { + for (; iter != mStore.get().end(); ++iter) { addScript (iter->mScript); } } diff --git a/apps/openmw/mwscript/globalscripts.hpp b/apps/openmw/mwscript/globalscripts.hpp index f9f9b7ae3..628919d1d 100644 --- a/apps/openmw/mwscript/globalscripts.hpp +++ b/apps/openmw/mwscript/globalscripts.hpp @@ -22,6 +22,8 @@ namespace MWScript GlobalScripts (const MWWorld::ESMStore& store); + void reset(); + void addScript (const std::string& name); void removeScript (const std::string& name); diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index c74e3f163..bba5cb4ad 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -215,19 +215,22 @@ namespace MWScript std::string InterpreterContext::getNPCRace() const { ESM::NPC npc = *mReference.get()->mBase; - return npc.mRace; + const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npc.mRace); + return race->mName; } std::string InterpreterContext::getNPCClass() const { ESM::NPC npc = *mReference.get()->mBase; - return npc.mClass; + const ESM::Class* class_ = MWBase::Environment::get().getWorld()->getStore().get().find(npc.mClass); + return class_->mName; } std::string InterpreterContext::getNPCFaction() const { ESM::NPC npc = *mReference.get()->mBase; - return npc.mFaction; + const ESM::Faction* faction = MWBase::Environment::get().getWorld()->getStore().get().find(npc.mFaction); + return faction->mName; } std::string InterpreterContext::getNPCRank() const @@ -289,19 +292,25 @@ namespace MWScript std::string factionId = MWWorld::Class::get (mReference).getNpcStats (mReference).getFactionRanks().begin()->first; - std::map ranks = MWWorld::Class::get (player).getNpcStats (player).getFactionRanks(); - std::map::const_iterator it = ranks.begin(); - const MWWorld::ESMStore &store = world->getStore(); const ESM::Faction *faction = store.get().find(factionId); - if(it->second < 0 || it->second > 9) - return ""; + std::map ranks = MWWorld::Class::get (player).getNpcStats (player).getFactionRanks(); - if(it->second <= 8) // If player is at max rank, there is no next rank - return faction->mRanks[it->second + 1]; + if (ranks.size()) + { + std::map::const_iterator it = ranks.begin(); + + if(it->second < -1 || it->second > 9) + return ""; + + if(it->second <= 8) // If player is at max rank, there is no next rank + return faction->mRanks[it->second + 1]; + else + return faction->mRanks[it->second]; + } else - return faction->mRanks[it->second]; + return faction->mRanks[0]; } int InterpreterContext::getPCBounty() const diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index f0b2758d9..9c7b443ae 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -89,7 +89,7 @@ namespace MWScript virtual std::string getNPCClass() const; virtual std::string getNPCFaction() const; - + virtual std::string getNPCRank() const; virtual std::string getPCName() const; diff --git a/apps/openmw/mwscript/locals.cpp b/apps/openmw/mwscript/locals.cpp new file mode 100644 index 000000000..180a2791b --- /dev/null +++ b/apps/openmw/mwscript/locals.cpp @@ -0,0 +1,68 @@ +#include "locals.hpp" + +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/scriptmanager.hpp" + +namespace MWScript +{ + void Locals::configure (const ESM::Script& script) + { + mShorts.clear(); + mShorts.resize (script.mData.mNumShorts, 0); + mLongs.clear(); + mLongs.resize (script.mData.mNumLongs, 0); + mFloats.clear(); + mFloats.resize (script.mData.mNumFloats, 0); + } + + int Locals::getIntVar(const std::string &script, const std::string &var) + { + Compiler::Locals locals = MWBase::Environment::get().getScriptManager()->getLocals(script); + int index = locals.getIndex(var); + char type = locals.getType(var); + if(index != -1) + { + switch(type) + { + case 's': + return mShorts.at (index); + + case 'l': + return mLongs.at (index); + + case 'f': + return mFloats.at (index); + default: + return 0; + } + } + return 0; + } + + bool Locals::setVarByInt(const std::string& script, const std::string& var, int val) + { + Compiler::Locals locals = MWBase::Environment::get().getScriptManager()->getLocals(script); + int index = locals.getIndex(var); + char type = locals.getType(var); + if(index != -1) + { + switch(type) + { + case 's': + mShorts.at (index) = val; break; + + case 'l': + mLongs.at (index) = val; break; + + case 'f': + mFloats.at (index) = val; break; + } + return true; + } + return false; + } +} diff --git a/apps/openmw/mwscript/locals.hpp b/apps/openmw/mwscript/locals.hpp index ec02e2f12..deae0d44e 100644 --- a/apps/openmw/mwscript/locals.hpp +++ b/apps/openmw/mwscript/locals.hpp @@ -3,26 +3,26 @@ #include -#include #include +namespace ESM +{ + struct Script; +} + namespace MWScript { - struct Locals + class Locals { - std::vector mShorts; - std::vector mLongs; - std::vector mFloats; - - void configure (const ESM::Script& script) - { - mShorts.clear(); - mShorts.resize (script.mData.mNumShorts, 0); - mLongs.clear(); - mLongs.resize (script.mData.mNumLongs, 0); - mFloats.clear(); - mFloats.resize (script.mData.mNumFloats, 0); - } + public: + std::vector mShorts; + std::vector mLongs; + std::vector mFloats; + + void configure (const ESM::Script& script); + 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 + }; } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index f329e30d9..5ed26119c 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -282,10 +282,8 @@ namespace MWScript MWBase::World *world = MWBase::Environment::get().getWorld(); - if (world->toggleVanityMode(sActivate, true)) { - context.report( - (sActivate) ? "Vanity Mode -> On" : "Vanity Mode -> Off" - ); + if (world->toggleVanityMode(sActivate)) { + context.report(sActivate ? "Vanity Mode -> On" : "Vanity Mode -> Off"); sActivate = !sActivate; } else { context.report("Vanity Mode -> No"); @@ -399,6 +397,13 @@ namespace MWScript Interpreter::Type_Integer amount = runtime[0].mInteger; runtime.pop(); + if (amount<0) + throw std::runtime_error ("amount must be non-negative"); + + // no-op + if (amount == 0) + return; + MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); @@ -550,6 +555,50 @@ namespace MWScript } }; + template + class OpFall : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + } + }; + + template + class OpGetStandingPc : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + runtime.push (MWBase::Environment::get().getWorld()->getPlayerStandingOn(ptr)); + } + }; + + template + class OpGetStandingActor : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + runtime.push (MWBase::Environment::get().getWorld()->getActorStandingOn(ptr)); + } + }; + + class OpGetWindSpeed : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + runtime.push(MWBase::Environment::get().getWorld()->getWindSpeed()); + } + }; + const int opcodeXBox = 0x200000c; const int opcodeOnActivate = 0x200000d; const int opcodeActivate = 0x2000075; @@ -591,6 +640,13 @@ namespace MWScript const int opcodeSetDelete = 0x20001e5; const int opcodeSetDeleteExplicit = 0x20001e6; const int opcodeGetSquareRoot = 0x20001e7; + const int opcodeFall = 0x200020a; + const int opcodeFallExplicit = 0x200020b; + const int opcodeGetStandingPc = 0x200020c; + const int opcodeGetStandingPcExplicit = 0x200020d; + const int opcodeGetStandingActor = 0x200020e; + const int opcodeGetStandingActorExplicit = 0x200020f; + const int opcodeGetWindSpeed = 0x2000212; const int opcodePlayBink = 0x20001f7; @@ -632,6 +688,10 @@ namespace MWScript extensions.registerFunction ("getcurrenttime", 'f', "", opcodeGetCurrentTime); extensions.registerInstruction ("setdelete", "l", opcodeSetDelete, opcodeSetDeleteExplicit); extensions.registerFunction ("getsquareroot", 'f', "f", opcodeGetSquareRoot); + extensions.registerInstruction ("fall", "", opcodeFall, opcodeFallExplicit); + extensions.registerFunction ("getstandingpc", 'l', "", opcodeGetStandingPc, opcodeGetStandingPcExplicit); + extensions.registerFunction ("getstandingactor", 'l', "", opcodeGetStandingActor, opcodeGetStandingActorExplicit); + extensions.registerFunction ("getwindspeed", 'f', "", opcodeGetWindSpeed); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -678,6 +738,13 @@ namespace MWScript interpreter.installSegment5 (opcodeSetDelete, new OpSetDelete); interpreter.installSegment5 (opcodeSetDeleteExplicit, new OpSetDelete); interpreter.installSegment5 (opcodeGetSquareRoot, new OpGetSquareRoot); + interpreter.installSegment5 (opcodeFall, new OpFall); + interpreter.installSegment5 (opcodeFallExplicit, new OpFall); + interpreter.installSegment5 (opcodeGetStandingPc, new OpGetStandingPc); + interpreter.installSegment5 (opcodeGetStandingPcExplicit, new OpGetStandingPc); + interpreter.installSegment5 (opcodeGetStandingActor, new OpGetStandingActor); + interpreter.installSegment5 (opcodeGetStandingActorExplicit, new OpGetStandingActor); + interpreter.installSegment5 (opcodeGetWindSpeed, new OpGetWindSpeed); } } } diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index 1f338edbd..14fe5b7fd 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -114,7 +114,7 @@ namespace MWScript } catch (const std::exception& e) { - std::cerr << "exeution of script " << name << " failed." << std::endl; + std::cerr << "execution of script " << name << " failed." << std::endl; if (mVerbose) std::cerr << "(" << e.what() << ")" << std::endl; @@ -140,25 +140,42 @@ namespace MWScript Compiler::Locals& ScriptManager::getLocals (const std::string& name) { - ScriptCollection::iterator iter = mScripts.find (name); + { + ScriptCollection::iterator iter = mScripts.find (name); + + if (iter!=mScripts.end()) + return iter->second.second; + } - if (iter==mScripts.end()) { - if (!compile (name)) - { - /// \todo Handle case of cyclic member variable access. Currently this could look up - /// the whole application in an endless recursion. + std::map::iterator iter = mOtherLocals.find (name); - // failed -> ignore script from now on. - std::vector empty; - mScripts.insert (std::make_pair (name, std::make_pair (empty, Compiler::Locals()))); - throw std::runtime_error ("failed to compile script " + name); - } + if (iter!=mOtherLocals.end()) + return iter->second; + } - iter = mScripts.find (name); + Compiler::Locals locals; + + if (const ESM::Script *script = mStore.get().find (name)) + { + int index = 0; + + 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::map::iterator iter = + mOtherLocals.insert (std::make_pair (name, locals)).first; + + return iter->second; } - return iter->second.second; + throw std::logic_error ("script " + name + " does not exist"); } GlobalScripts& ScriptManager::getGlobalScripts() @@ -192,6 +209,7 @@ namespace MWScript offset = script->mData.mNumShorts+script->mData.mNumLongs; size = script->mData.mNumFloats; + break; default: @@ -204,4 +222,9 @@ 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 c4a016eb7..7bb98ffbd 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.hpp +++ b/apps/openmw/mwscript/scriptmanagerimp.hpp @@ -47,6 +47,7 @@ namespace MWScript ScriptCollection mScripts; GlobalScripts mGlobalScripts; + std::map mOtherLocals; public: @@ -60,6 +61,8 @@ 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/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 530d44c9d..04e89edc6 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -1025,6 +1025,37 @@ namespace MWScript } }; + template + class OpOnDeath : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + Interpreter::Type_Integer value = + MWWorld::Class::get (ptr).getCreatureStats (ptr).hasDied(); + + if (value) + MWWorld::Class::get (ptr).getCreatureStats (ptr).clearHasDied(); + + runtime.push (value); + } + }; + + template + class OpIsWerewolf : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + runtime.push(MWWorld::Class::get(ptr).getNpcStats(ptr).isWerewolf()); + } + }; + const int numberOfAttributes = 8; const int opcodeGetAttribute = 0x2000027; @@ -1113,6 +1144,10 @@ namespace MWScript const int opcodeRaiseRankExplicit = 0x20001e9; const int opcodeLowerRank = 0x20001ea; const int opcodeLowerRankExplicit = 0x20001eb; + const int opcodeOnDeath = 0x20001fc; + const int opcodeOnDeathExplicit = 0x2000205; + const int opcodeIsWerewolf = 0x20001fd; + const int opcodeIsWerewolfExplicit = 0x20001fe; void registerExtensions (Compiler::Extensions& extensions) { @@ -1227,6 +1262,10 @@ namespace MWScript extensions.registerInstruction ("pcclearexpelled", "/S", opcodePcClearExpelled, opcodePcClearExpelledExplicit); extensions.registerInstruction ("raiserank", "", opcodeRaiseRank, opcodeRaiseRankExplicit); extensions.registerInstruction ("lowerrank", "", opcodeLowerRank, opcodeLowerRankExplicit); + + extensions.registerFunction ("ondeath", 'l', "", opcodeOnDeath, opcodeOnDeathExplicit); + + extensions.registerFunction ("iswerewolf", 'l', "", opcodeIsWerewolf, opcodeIsWerewolfExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -1341,6 +1380,12 @@ namespace MWScript interpreter.installSegment5 (opcodeRaiseRankExplicit, new OpRaiseRank); interpreter.installSegment5 (opcodeLowerRank, new OpLowerRank); interpreter.installSegment5 (opcodeLowerRankExplicit, new OpLowerRank); + + interpreter.installSegment5 (opcodeOnDeath, new OpOnDeath); + interpreter.installSegment5 (opcodeOnDeathExplicit, new OpOnDeath); + + interpreter.installSegment5 (opcodeIsWerewolf, new OpIsWerewolf); + interpreter.installSegment5 (opcodeIsWerewolfExplicit, new OpIsWerewolf); } } } diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 2dd8f3e16..9b7b51a5a 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -84,21 +84,27 @@ namespace MWScript Interpreter::Type_Float angle = runtime[0].mFloat; runtime.pop(); - float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); - float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); - float az = Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees(); + float ax = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees(); + float ay = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees(); + float az = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees(); + + float *objRot = ptr.getRefData().getPosition().rot; + + float lx = Ogre::Radian(objRot[0]).valueDegrees(); + float ly = Ogre::Radian(objRot[1]).valueDegrees(); + float lz = Ogre::Radian(objRot[2]).valueDegrees(); if (axis == "x") { - MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az); + MWBase::Environment::get().getWorld()->localRotateObject(ptr,angle-lx,ay,az); } else if (axis == "y") { - MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az); + MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,angle-ly,az); } else if (axis == "z") { - MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle); + MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,ay,angle-lz); } else throw std::runtime_error ("invalid ration axis: " + axis); @@ -119,15 +125,15 @@ namespace MWScript if (axis == "x") { - runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees()); + runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[0]).valueDegrees()); } else if (axis == "y") { - runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees()); + runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[1]).valueDegrees()); } else if (axis == "z") { - runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees()); + runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[2]).valueDegrees()); } else throw std::runtime_error ("invalid ration axis: " + axis); @@ -148,15 +154,15 @@ namespace MWScript if (axis=="x") { - runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[0]).valueDegrees()); + runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[0]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees()); } else if (axis=="y") { - runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[1]).valueDegrees()); + runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[1]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees()); } else if (axis=="z") { - runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[2]).valueDegrees()); + runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[2]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees()); } else throw std::runtime_error ("invalid ration axis: " + axis); @@ -303,6 +309,8 @@ namespace MWScript zRot = zRot/60.; } MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot); + + MWWorld::Class::get(ptr).adjustPosition(ptr); } else { @@ -341,6 +349,7 @@ namespace MWScript zRot = zRot/60.; } MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot); + MWWorld::Class::get(ptr).adjustPosition(ptr); } }; @@ -457,6 +466,13 @@ namespace MWScript Interpreter::Type_Integer direction = runtime[0].mInteger; runtime.pop(); + if (count<0) + throw std::runtime_error ("count must be non-negative"); + + // no-op + if (count == 0) + return; + ESM::Position ipos = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getRefData().getPosition(); Ogre::Vector3 pos(ipos.pos[0],ipos.pos[1],ipos.pos[2]); Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z); @@ -500,6 +516,13 @@ namespace MWScript Interpreter::Type_Integer direction = runtime[0].mInteger; runtime.pop(); + if (count<0) + throw std::runtime_error ("count must be non-negative"); + + // no-op + if (count == 0) + return; + ESM::Position ipos = me.getRefData().getPosition(); Ogre::Vector3 pos(ipos.pos[0],ipos.pos[1],ipos.pos[2]); Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z); @@ -525,6 +548,165 @@ namespace MWScript } }; + template + class OpRotate : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + const MWWorld::Ptr& ptr = R()(runtime); + + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + Interpreter::Type_Float rotation = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); + runtime.pop(); + + float ax = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees(); + float ay = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees(); + float az = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees(); + + if (axis == "x") + { + MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax+rotation,ay,az); + } + else if (axis == "y") + { + MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,ay+rotation,az); + } + else if (axis == "z") + { + MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,ay,az+rotation); + } + else + throw std::runtime_error ("invalid rotation axis: " + axis); + } + }; + + template + class OpRotateWorld : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + Interpreter::Type_Float rotation = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); + runtime.pop(); + + float *objRot = ptr.getRefData().getPosition().rot; + + float ax = Ogre::Radian(objRot[0]).valueDegrees(); + float ay = Ogre::Radian(objRot[1]).valueDegrees(); + float az = Ogre::Radian(objRot[2]).valueDegrees(); + + if (axis == "x") + { + MWBase::Environment::get().getWorld()->rotateObject(ptr,ax+rotation,ay,az); + } + else if (axis == "y") + { + MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay+rotation,az); + } + else if (axis == "z") + { + MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,az+rotation); + } + else + throw std::runtime_error ("invalid rotation axis: " + axis); + } + }; + + template + class OpSetAtStart : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + ptr.getRefData().getLocalRotation().rot[0] = 0; + ptr.getRefData().getLocalRotation().rot[1] = 0; + ptr.getRefData().getLocalRotation().rot[2] = 0; + MWBase::Environment::get().getWorld()->rotateObject(ptr, 0,0,0,true); + MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().mPos.pos[0], + ptr.getCellRef().mPos.pos[1], ptr.getCellRef().mPos.pos[2]); + + } + }; + + template + class OpMove : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + const MWWorld::Ptr& ptr = R()(runtime); + + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); + runtime.pop(); + + Ogre::Vector3 posChange; + if (axis == "x") + { + posChange=Ogre::Vector3(movement, 0, 0); + } + else if (axis == "y") + { + posChange=Ogre::Vector3(0, movement, 0); + } + else if (axis == "z") + { + posChange=Ogre::Vector3(0, 0, movement); + } + else + throw std::runtime_error ("invalid movement axis: " + axis); + + Ogre::Vector3 worldPos = ptr.getRefData().getBaseNode()->convertLocalToWorldPosition(posChange); + MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x, worldPos.y, worldPos.z); + } + }; + + template + class OpMoveWorld : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); + runtime.pop(); + + float *objPos = ptr.getRefData().getPosition().pos; + + if (axis == "x") + { + MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+movement, objPos[1], objPos[2]); + } + else if (axis == "y") + { + MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1]+movement, objPos[2]); + } + else if (axis == "z") + { + MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1], objPos[2]+movement); + } + else + throw std::runtime_error ("invalid movement axis: " + axis); + } + }; + + const int opcodeSetScale = 0x2000164; const int opcodeSetScaleExplicit = 0x2000165; const int opcodeSetAngle = 0x2000166; @@ -539,6 +721,8 @@ namespace MWScript const int opcodeSetPosExplicit = 0x2000193; const int opcodeGetStartingPos = 0x2000194; const int opcodeGetStartingPosExplicit = 0x2000195; + const int opcodeGetStartingAngle = 0x2000210; + const int opcodeGetStartingAngleExplicit = 0x2000211; const int opcodePosition = 0x2000196; const int opcodePositionExplicit = 0x2000197; const int opcodePositionCell = 0x2000198; @@ -551,6 +735,16 @@ namespace MWScript const int opcodePlaceAtMeExplicit = 0x200019e; const int opcodeModScale = 0x20001e3; const int opcodeModScaleExplicit = 0x20001e4; + const int opcodeRotate = 0x20001ff; + const int opcodeRotateExplicit = 0x2000200; + const int opcodeRotateWorld = 0x2000201; + const int opcodeRotateWorldExplicit = 0x2000202; + const int opcodeSetAtStart = 0x2000203; + const int opcodeSetAtStartExplicit = 0x2000204; + const int opcodeMove = 0x2000206; + const int opcodeMoveExplicit = 0x2000207; + const int opcodeMoveWorld = 0x2000208; + const int opcodeMoveWorldExplicit = 0x2000209; void registerExtensions (Compiler::Extensions& extensions) { @@ -568,6 +762,12 @@ namespace MWScript extensions.registerInstruction("placeatpc","clfl",opcodePlaceAtPc); extensions.registerInstruction("placeatme","clfl",opcodePlaceAtMe,opcodePlaceAtMeExplicit); extensions.registerInstruction("modscale","f",opcodeModScale,opcodeModScaleExplicit); + extensions.registerInstruction("rotate","cf",opcodeRotate,opcodeRotateExplicit); + extensions.registerInstruction("rotateworld","cf",opcodeRotateWorld,opcodeRotateWorldExplicit); + extensions.registerInstruction("setatstart","",opcodeSetAtStart,opcodeSetAtStartExplicit); + extensions.registerInstruction("move","cf",opcodeMove,opcodeMoveExplicit); + extensions.registerInstruction("moveworld","cf",opcodeMoveWorld,opcodeMoveWorldExplicit); + extensions.registerFunction("getstartingangle",'f',"c",opcodeGetStartingAngle,opcodeGetStartingAngleExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -597,6 +797,18 @@ namespace MWScript interpreter.installSegment5(opcodePlaceAtMeExplicit,new OpPlaceAtMe); interpreter.installSegment5(opcodeModScale,new OpModScale); interpreter.installSegment5(opcodeModScaleExplicit,new OpModScale); + interpreter.installSegment5(opcodeRotate,new OpRotate); + interpreter.installSegment5(opcodeRotateExplicit,new OpRotate); + interpreter.installSegment5(opcodeRotateWorld,new OpRotateWorld); + interpreter.installSegment5(opcodeRotateWorldExplicit,new OpRotateWorld); + interpreter.installSegment5(opcodeSetAtStart,new OpSetAtStart); + interpreter.installSegment5(opcodeSetAtStartExplicit,new OpSetAtStart); + interpreter.installSegment5(opcodeMove,new OpMove); + interpreter.installSegment5(opcodeMoveExplicit,new OpMove); + interpreter.installSegment5(opcodeMoveWorld,new OpMoveWorld); + interpreter.installSegment5(opcodeMoveWorldExplicit,new OpMoveWorld); + interpreter.installSegment5(opcodeGetStartingAngle, new OpGetStartingAngle); + interpreter.installSegment5(opcodeGetStartingAngleExplicit, new OpGetStartingAngle); } } } diff --git a/apps/openmw/mwscript/userextensions.hpp b/apps/openmw/mwscript/userextensions.hpp index 3642eb5f4..4bc3b46ec 100644 --- a/apps/openmw/mwscript/userextensions.hpp +++ b/apps/openmw/mwscript/userextensions.hpp @@ -13,7 +13,7 @@ namespace Interpreter namespace MWScript { - /// \brief Temporaty script functionality limited to the console + /// \brief Temporary script functionality limited to the console namespace User { void registerExtensions (Compiler::Extensions& extensions); diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 261a86ca6..d5c382a41 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -134,6 +134,18 @@ size_t FFmpeg_Decoder::readAVAudioData(void *data, size_t length) return dec; } +static AVSampleFormat ffmpegNonPlanarSampleFormat (AVSampleFormat format) +{ + switch (format) + { + case AV_SAMPLE_FMT_U8P: return AV_SAMPLE_FMT_U8; + case AV_SAMPLE_FMT_S16P: return AV_SAMPLE_FMT_S16; + case AV_SAMPLE_FMT_S32P: return AV_SAMPLE_FMT_S32; + case AV_SAMPLE_FMT_FLTP: return AV_SAMPLE_FMT_FLT; + case AV_SAMPLE_FMT_DBLP: return AV_SAMPLE_FMT_DBL; + default:return format; + } +} void FFmpeg_Decoder::open(const std::string &fname) { @@ -167,6 +179,8 @@ void FFmpeg_Decoder::open(const std::string &fname) if(!mStream) fail("No audio streams in "+fname); + (*mStream)->codec->request_sample_fmt = ffmpegNonPlanarSampleFormat ((*mStream)->codec->sample_fmt); + AVCodec *codec = avcodec_find_decoder((*mStream)->codec->codec_id); if(!codec) { @@ -219,6 +233,8 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType * *type = SampleType_UInt8; else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_S16) *type = SampleType_Int16; + else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLT) + *type = SampleType_Float32; else fail(std::string("Unsupported sample format: ")+ av_get_sample_fmt_name((*mStream)->codec->sample_fmt)); diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index 32b2797ed..a5e5b5083 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -1,8 +1,6 @@ #ifndef GAME_SOUND_FFMPEG_DECODER_H #define GAME_SOUND_FFMPEG_DECODER_H -#include - // FIXME: This can't be right? The headers refuse to build without UINT64_C, // which only gets defined in stdint.h in either C99 mode or with this macro // defined... @@ -14,6 +12,8 @@ extern "C" #include } +#include + #include "sound_decoder.hpp" diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 67008e2bc..690b07331 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -88,6 +88,51 @@ static ALenum getALFormat(ChannelConfig chans, SampleType type) } } } + if(alIsExtensionPresent("AL_EXT_FLOAT32")) + { + static const struct { + char name[32]; + ChannelConfig chans; + SampleType type; + } fltfmtlist[] = { + { "AL_FORMAT_MONO_FLOAT32", ChannelConfig_Mono, SampleType_Float32 }, + { "AL_FORMAT_STEREO_FLOAT32", ChannelConfig_Stereo, SampleType_Float32 }, + }; + static const size_t fltfmtlistsize = sizeof(fltfmtlist)/sizeof(fltfmtlist[0]); + + for(size_t i = 0;i < fltfmtlistsize;i++) + { + if(fltfmtlist[i].chans == chans && fltfmtlist[i].type == type) + { + ALenum format = alGetEnumValue(fltfmtlist[i].name); + if(format != 0 && format != -1) + return format; + } + } + if(alIsExtensionPresent("AL_EXT_MCFORMATS")) + { + static const struct { + char name[32]; + ChannelConfig chans; + SampleType type; + } fltmcfmtlist[] = { + { "AL_FORMAT_QUAD32", ChannelConfig_Quad, SampleType_Float32 }, + { "AL_FORMAT_51CHN32", ChannelConfig_5point1, SampleType_Float32 }, + { "AL_FORMAT_71CHN32", ChannelConfig_7point1, SampleType_Float32 }, + }; + static const size_t fltmcfmtlistsize = sizeof(fltmcfmtlist)/sizeof(fltmcfmtlist[0]); + + for(size_t i = 0;i < fltmcfmtlistsize;i++) + { + if(fltmcfmtlist[i].chans == chans && fltmcfmtlist[i].type == type) + { + ALenum format = alGetEnumValue(fltmcfmtlist[i].name); + if(format != 0 && format != -1) + return format; + } + } + } + } fail(std::string("Unsupported sound format (")+getChannelConfigName(chans)+", "+getSampleTypeName(type)+")"); return AL_NONE; @@ -541,7 +586,7 @@ void OpenAL_Sound::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(); @@ -561,7 +606,7 @@ void OpenAL_Sound3D::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(); @@ -878,10 +923,10 @@ void OpenAL_Output::updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 if(mContext) { ALfloat orient[6] = { - atdir.x, atdir.z, -atdir.y, - updir.x, updir.z, -updir.y + atdir.x, atdir.y, atdir.z, + updir.x, updir.y, updir.z }; - alListener3f(AL_POSITION, mPos.x, mPos.z, -mPos.y); + alListener3f(AL_POSITION, mPos.x, mPos.y, mPos.z); alListenerfv(AL_ORIENTATION, orient); throwALerror(); } diff --git a/apps/openmw/mwsound/sound_decoder.hpp b/apps/openmw/mwsound/sound_decoder.hpp index 29d99e8fa..151b58036 100644 --- a/apps/openmw/mwsound/sound_decoder.hpp +++ b/apps/openmw/mwsound/sound_decoder.hpp @@ -9,7 +9,8 @@ namespace MWSound { enum SampleType { SampleType_UInt8, - SampleType_Int16 + SampleType_Int16, + SampleType_Float32 }; const char *getSampleTypeName(SampleType type); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 8a69fc96b..69e301676 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -160,7 +160,7 @@ namespace MWSound return volume; } - bool SoundManager::isPlaying(MWWorld::Ptr ptr, const std::string &id) const + bool SoundManager::isPlaying(const MWWorld::Ptr &ptr, const std::string &id) const { SoundMap::const_iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) @@ -208,14 +208,21 @@ namespace MWSound void SoundManager::startRandomTitle() { - Ogre::StringVectorPtr filelist; - filelist = mResourceMgr.findResourceNames(Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - "Music/"+mCurrentPlaylist+"/*"); - if(!filelist->size()) + Ogre::StringVector filelist; + + Ogre::StringVector groups = Ogre::ResourceGroupManager::getSingleton().getResourceGroups (); + for (Ogre::StringVector::iterator it = groups.begin(); it != groups.end(); ++it) + { + Ogre::StringVectorPtr resourcesInThisGroup = mResourceMgr.findResourceNames(*it, + "Music/"+mCurrentPlaylist+"/*"); + filelist.insert(filelist.end(), resourcesInThisGroup->begin(), resourcesInThisGroup->end()); + } + + if(!filelist.size()) return; - int i = rand()%filelist->size(); - streamMusicFull((*filelist)[i]); + int i = rand()%filelist.size(); + streamMusicFull(filelist[i]); } bool SoundManager::isMusicPlaying() @@ -229,7 +236,7 @@ namespace MWSound startRandomTitle(); } - void SoundManager::say(MWWorld::Ptr ptr, const std::string& filename) + void SoundManager::say(const MWWorld::Ptr &ptr, const std::string& filename) { if(!mOutput->isInitialized()) return; @@ -269,12 +276,12 @@ namespace MWSound } } - bool SoundManager::sayDone(MWWorld::Ptr ptr) const + bool SoundManager::sayDone(const MWWorld::Ptr &ptr) const { return !isPlaying(ptr, "_say_sound"); } - void SoundManager::stopSay(MWWorld::Ptr ptr) + void SoundManager::stopSay(const MWWorld::Ptr &ptr) { SoundMap::iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) @@ -328,7 +335,7 @@ namespace MWSound return sound; } - MWBase::SoundPtr SoundManager::playSound3D(MWWorld::Ptr ptr, const std::string& soundId, + MWBase::SoundPtr SoundManager::playSound3D(const MWWorld::Ptr &ptr, const std::string& soundId, float volume, float pitch, PlayMode mode) { MWBase::SoundPtr sound; @@ -356,7 +363,7 @@ namespace MWSound return sound; } - void SoundManager::stopSound3D(MWWorld::Ptr ptr, const std::string& soundId) + void SoundManager::stopSound3D(const MWWorld::Ptr &ptr, const std::string& soundId) { SoundMap::iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) @@ -371,7 +378,7 @@ namespace MWSound } } - void SoundManager::stopSound3D(MWWorld::Ptr ptr) + void SoundManager::stopSound3D(const MWWorld::Ptr &ptr) { SoundMap::iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) @@ -418,7 +425,7 @@ namespace MWSound } } - bool SoundManager::getSoundPlaying(MWWorld::Ptr ptr, const std::string& soundId) const + bool SoundManager::getSoundPlaying(const MWWorld::Ptr &ptr, const std::string& soundId) const { return isPlaying(ptr, soundId); } @@ -607,6 +614,7 @@ namespace MWSound { case SampleType_UInt8: return "U8"; case SampleType_Int16: return "S16"; + case SampleType_Float32: return "Float32"; } return "(unknown sample type)"; } @@ -638,6 +646,7 @@ namespace MWSound { case SampleType_UInt8: frames *= 1; break; case SampleType_Int16: frames *= 2; break; + case SampleType_Float32: frames *= 4; break; } return frames; } diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index b475449d9..2af26d3db 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -14,8 +14,6 @@ #include "../mwbase/soundmanager.hpp" -#include "../mwworld/ptr.hpp" - namespace MWSound { class Sound_Output; @@ -57,7 +55,7 @@ namespace MWSound std::string lookup(const std::string &soundId, float &volume, float &min, float &max); void streamMusicFull(const std::string& filename); - bool isPlaying(MWWorld::Ptr ptr, const std::string &id) const; + bool isPlaying(const MWWorld::Ptr &ptr, const std::string &id) const; void updateSounds(float duration); void updateRegionSound(float duration); @@ -93,7 +91,7 @@ namespace MWSound ///< Start playing music from the selected folder /// \param name of the folder that contains the playlist - virtual void say(MWWorld::Ptr reference, const std::string& filename); + virtual void say(const MWWorld::Ptr &reference, const std::string& filename); ///< Make an actor say some text. /// \param filename name of a sound file in "Sound/" in the data directory. @@ -101,10 +99,10 @@ namespace MWSound ///< Say some text, without an actor ref /// \param filename name of a sound file in "Sound/" in the data directory. - virtual bool sayDone(MWWorld::Ptr reference=MWWorld::Ptr()) const; + virtual bool sayDone(const MWWorld::Ptr &reference=MWWorld::Ptr()) const; ///< Is actor not speaking? - virtual void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()); + virtual void stopSay(const MWWorld::Ptr &reference=MWWorld::Ptr()); ///< Stop an actor speaking virtual MWBase::SoundPtr playTrack(const DecoderPtr& decoder, PlayType type); @@ -113,14 +111,14 @@ namespace MWSound virtual MWBase::SoundPtr playSound(const std::string& soundId, float volume, float pitch, PlayMode mode=Play_Normal); ///< Play a sound, independently of 3D-position - virtual MWBase::SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId, + virtual MWBase::SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId, float volume, float pitch, PlayMode mode=Play_Normal); ///< Play a sound from an object - virtual void stopSound3D(MWWorld::Ptr reference, const std::string& soundId); + virtual void stopSound3D(const MWWorld::Ptr &reference, const std::string& soundId); ///< Stop the given object from playing the given sound, - virtual void stopSound3D(MWWorld::Ptr reference); + virtual void stopSound3D(const MWWorld::Ptr &reference); ///< Stop the given object from playing all sounds. virtual void stopSound(const MWWorld::CellStore *cell); @@ -129,7 +127,7 @@ namespace MWSound virtual void stopSound(const std::string& soundId); ///< Stop a non-3d looping sound - virtual bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const; + virtual bool getSoundPlaying(const MWWorld::Ptr &reference, const std::string& soundId) const; ///< Is the given sound currently playing on the given object? virtual void pauseSounds(int types=Play_TypeMask); diff --git a/apps/openmw/mwworld/actiondoor.cpp b/apps/openmw/mwworld/actiondoor.cpp new file mode 100644 index 000000000..6e3441e22 --- /dev/null +++ b/apps/openmw/mwworld/actiondoor.cpp @@ -0,0 +1,16 @@ +#include "actiondoor.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +namespace MWWorld +{ + ActionDoor::ActionDoor (const MWWorld::Ptr& object) : Action (false, object) + { + } + + void ActionDoor::executeImp (const MWWorld::Ptr& actor) + { + MWBase::Environment::get().getWorld()->activateDoor(getTarget()); + } +} diff --git a/apps/openmw/mwworld/actiondoor.hpp b/apps/openmw/mwworld/actiondoor.hpp new file mode 100644 index 000000000..2dc5ad8c1 --- /dev/null +++ b/apps/openmw/mwworld/actiondoor.hpp @@ -0,0 +1,18 @@ +#ifndef GAME_MWWORLD_ACTIONDOOR_H +#define GAME_MWWORLD_ACTIONDOOR_H + +#include "action.hpp" +#include "ptr.hpp" + +namespace MWWorld +{ + class ActionDoor : public Action + { + virtual void executeImp (const MWWorld::Ptr& actor); + + public: + ActionDoor (const Ptr& object); + }; +} + +#endif diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index 60260a812..85e73a056 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -4,6 +4,8 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include + #include "inventorystore.hpp" #include "player.hpp" #include "class.hpp" @@ -16,8 +18,27 @@ namespace MWWorld void ActionEquip::executeImp (const Ptr& actor) { + MWWorld::Ptr object = getTarget(); MWWorld::InventoryStore& invStore = MWWorld::Class::get(actor).getInventoryStore(actor); + 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()) + MWBase::Environment::get().getWindowManager()->messageBox(result.second); + + switch(result.first) + { + case 0: + return; + case 2: + invStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, invStore.end()); + break; + case 3: + invStore.equip(MWWorld::InventoryStore::Slot_CarriedRight, invStore.end()); + break; + } + // slots that this item can be equipped in std::pair, bool> slots = MWWorld::Class::get(getTarget()).getEquipmentSlots(getTarget()); @@ -25,72 +46,26 @@ namespace MWWorld MWWorld::ContainerStoreIterator it = invStore.begin(); for (; it != invStore.end(); ++it) { - if (*it == getTarget()) + if (*it == object) { break; } } assert(it != invStore.end()); - - std::string npcRace = actor.get()->mBase->mRace; + + 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) { - // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) - if(npcRace == "argonian" || npcRace == "khajiit") - { - if(*slot == MWWorld::InventoryStore::Slot_Helmet){ - std::vector parts; - - if(it.getType() == MWWorld::ContainerStore::Type_Clothing) - parts = it->get()->mBase->mParts.mParts; - else - parts = it->get()->mBase->mParts.mParts; - - bool allow = true; - for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) - { - if((*itr).mPart == ESM::PartReferenceType::PRT_Head) - { - if(actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage13}", std::vector()); - - allow = false; - break; - } - } - - if(!allow) - break; - } - - if (*slot == MWWorld::InventoryStore::Slot_Boots) - { - // Only notify the player, not npcs - if(actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) - { - if(it.getType() == MWWorld::ContainerStore::Type_Clothing){ // It's shoes - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage15}", std::vector()); - } - - else // It's boots - { - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage14}", std::vector()); - } - } - break; - } - - } - // if all slots are occupied, replace the last slot if (slot == --slots.first.end()) { invStore.equip(*slot, it); + equipped = true; break; } @@ -98,8 +73,15 @@ namespace MWWorld { // slot is not occupied invStore.equip(*slot, it); + equipped = true; 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()->getPlayer().getPlayer() && script != "") + (object).mRefData->getLocals().setVarByInt(script, "onpcequip", 1); } } diff --git a/apps/openmw/mwworld/actionopen.cpp b/apps/openmw/mwworld/actionopen.cpp index 040a3856e..728b6e32b 100644 --- a/apps/openmw/mwworld/actionopen.cpp +++ b/apps/openmw/mwworld/actionopen.cpp @@ -10,7 +10,9 @@ namespace MWWorld { - ActionOpen::ActionOpen (const MWWorld::Ptr& container) : Action (false, container) + ActionOpen::ActionOpen (const MWWorld::Ptr& container, bool loot) + : Action (false, container) + , mLoot(loot) { } @@ -20,6 +22,6 @@ namespace MWWorld return; MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Container); - MWBase::Environment::get().getWindowManager()->getContainerWindow()->open(getTarget()); + MWBase::Environment::get().getWindowManager()->getContainerWindow()->open(getTarget(), mLoot); } } diff --git a/apps/openmw/mwworld/actionopen.hpp b/apps/openmw/mwworld/actionopen.hpp index c49ebefa5..8578995ae 100644 --- a/apps/openmw/mwworld/actionopen.hpp +++ b/apps/openmw/mwworld/actionopen.hpp @@ -13,8 +13,12 @@ namespace MWWorld virtual void executeImp (const MWWorld::Ptr& actor); public: - ActionOpen (const Ptr& container); - ///< \param The Container the Player has activated. + ActionOpen (const Ptr& container, bool loot=false); + ///< \param container The Container the Player has activated. + /// \param loot If true, display the "dispose of corpse" button + + private: + bool mLoot; }; } diff --git a/apps/openmw/mwworld/actionrepair.cpp b/apps/openmw/mwworld/actionrepair.cpp new file mode 100644 index 000000000..bd5642116 --- /dev/null +++ b/apps/openmw/mwworld/actionrepair.cpp @@ -0,0 +1,18 @@ +#include "actionrepair.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" + +namespace MWWorld +{ + ActionRepair::ActionRepair(const Ptr &item) + : Action(false, item) + { + } + + void ActionRepair::executeImp (const Ptr& actor) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Repair); + MWBase::Environment::get().getWindowManager()->startRepairItem(getTarget()); + } +} diff --git a/apps/openmw/mwworld/actionrepair.hpp b/apps/openmw/mwworld/actionrepair.hpp new file mode 100644 index 000000000..1d3ef2bc1 --- /dev/null +++ b/apps/openmw/mwworld/actionrepair.hpp @@ -0,0 +1,17 @@ +#ifndef GAME_MWWORLD_ACTIONREPAIR_H +#define GAME_MWWORLD_ACTIONREPAIR_H + +#include "action.hpp" + +namespace MWWorld +{ + class ActionRepair : public Action + { + virtual void executeImp (const Ptr& actor); + + public: + ActionRepair(const MWWorld::Ptr& item); + }; +} + +#endif diff --git a/apps/openmw/mwworld/actionsoulgem.cpp b/apps/openmw/mwworld/actionsoulgem.cpp new file mode 100644 index 000000000..6746f692f --- /dev/null +++ b/apps/openmw/mwworld/actionsoulgem.cpp @@ -0,0 +1,21 @@ +#include "actionsoulgem.hpp" + +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/environment.hpp" + +namespace MWWorld +{ + +ActionSoulgem::ActionSoulgem(const Ptr &object) + : Action(false, object) +{ + +} + +void ActionSoulgem::executeImp(const Ptr &actor) +{ + MWBase::Environment::get().getWindowManager()->showSoulgemDialog(getTarget()); +} + + +} diff --git a/apps/openmw/mwworld/actionsoulgem.hpp b/apps/openmw/mwworld/actionsoulgem.hpp new file mode 100644 index 000000000..0dd526657 --- /dev/null +++ b/apps/openmw/mwworld/actionsoulgem.hpp @@ -0,0 +1,19 @@ +#ifndef GAME_MWWORLD_ACTIONSOULGEM_H +#define GAME_MWWORLD_ACTIONSOULGEM_H + +#include "action.hpp" +#include "ptr.hpp" + +namespace MWWorld +{ + class ActionSoulgem : public Action + { + virtual void executeImp (const MWWorld::Ptr& actor); + + public: + /// @param soulgem to use + ActionSoulgem (const Ptr& object); + }; +} + +#endif diff --git a/apps/openmw/mwworld/actiontake.cpp b/apps/openmw/mwworld/actiontake.cpp index fd28dd52e..cdd19b46e 100644 --- a/apps/openmw/mwworld/actiontake.cpp +++ b/apps/openmw/mwworld/actiontake.cpp @@ -14,9 +14,6 @@ namespace MWWorld void ActionTake::executeImp (const Ptr& actor) { - if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) - return; - // insert into player's inventory MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPtr ("player", true); diff --git a/apps/openmw/mwworld/cellfunctors.hpp b/apps/openmw/mwworld/cellfunctors.hpp index 8bba898ce..4b1f70096 100644 --- a/apps/openmw/mwworld/cellfunctors.hpp +++ b/apps/openmw/mwworld/cellfunctors.hpp @@ -13,8 +13,8 @@ namespace ESM namespace MWWorld { - /// List all (Ogre-)handles. - struct ListHandles + /// List all (Ogre-)handles, then reset RefData::mBaseNode to 0. + struct ListAndResetHandles { std::vector mHandles; @@ -23,6 +23,8 @@ namespace MWWorld Ogre::SceneNode* handle = data.getBaseNode(); if (handle) mHandles.push_back (handle); + + data.setBaseNode(0); return true; } }; diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 622c8a10a..cb2e49ca0 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -11,11 +11,11 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) { if (cell->mData.mFlags & ESM::Cell::Interior) { - std::map::iterator result = mInteriors.find (cell->mName); + std::map::iterator result = mInteriors.find (Misc::StringUtils::lowerCase(cell->mName)); if (result==mInteriors.end()) { - result = mInteriors.insert (std::make_pair (cell->mName, Ptr::CellStore (cell))).first; + result = mInteriors.insert (std::make_pair (Misc::StringUtils::lowerCase(cell->mName), Ptr::CellStore (cell))).first; } return &result->second; @@ -36,6 +36,14 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) } } +void MWWorld::Cells::clear() +{ + mInteriors.clear(); + mExteriors.clear(); + std::fill(mIdCache.begin(), mIdCache.end(), std::make_pair("", (MWWorld::Ptr::CellStore*)0)); + mIdCacheIndex = 0; +} + void MWWorld::Cells::fillContainers (Ptr::CellStore& cellStore) { for (CellRefList::List::iterator iter ( @@ -45,7 +53,7 @@ void MWWorld::Cells::fillContainers (Ptr::CellStore& cellStore) Ptr container (&*iter, &cellStore); Class::get (container).getContainerStore (container).fill ( - iter->mBase->mInventory, mStore); + iter->mBase->mInventory, container.getCellRef().mOwner, mStore); } for (CellRefList::List::iterator iter ( @@ -55,7 +63,7 @@ void MWWorld::Cells::fillContainers (Ptr::CellStore& cellStore) Ptr container (&*iter, &cellStore); Class::get (container).getContainerStore (container).fill ( - iter->mBase->mInventory, mStore); + iter->mBase->mInventory, Class::get(container).getId(container), mStore); } for (CellRefList::List::iterator iter ( @@ -65,7 +73,7 @@ void MWWorld::Cells::fillContainers (Ptr::CellStore& cellStore) Ptr container (&*iter, &cellStore); Class::get (container).getContainerStore (container).fill ( - iter->mBase->mInventory, mStore); + iter->mBase->mInventory, Class::get(container).getId(container), mStore); } } @@ -84,9 +92,9 @@ MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, Ptr::CellS return ptr; } -MWWorld::Cells::Cells (const MWWorld::ESMStore& store, ESM::ESMReader& reader) +MWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector& reader) : mStore (store), mReader (reader), - mIdCache (20, std::pair ("", (Ptr::CellStore*)0)), /// \todo make cache size configurable + mIdCache (40, std::pair ("", (Ptr::CellStore*)0)), /// \todo make cache size configurable mIdCacheIndex (0) {} @@ -119,6 +127,7 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y) if (result->second.mState!=Ptr::CellStore::State_Loaded) { + // Multiple plugin support for landscape data is much easier than for references. The last plugin wins. result->second.load (mStore, mReader); fillContainers (result->second); } @@ -128,13 +137,14 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y) MWWorld::Ptr::CellStore *MWWorld::Cells::getInterior (const std::string& name) { - std::map::iterator result = mInteriors.find (name); + std::string lowerName = Misc::StringUtils::lowerCase(name); + std::map::iterator result = mInteriors.find (lowerName); if (result==mInteriors.end()) { - const ESM::Cell *cell = mStore.get().find(name); + const ESM::Cell *cell = mStore.get().find(lowerName); - result = mInteriors.insert (std::make_pair (name, Ptr::CellStore (cell))).first; + result = mInteriors.insert (std::make_pair (lowerName, Ptr::CellStore (cell))).first; } if (result->second.mState!=Ptr::CellStore::State_Loaded) @@ -163,6 +173,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& ce else return Ptr(); } + MWWorld::Ptr ptr; if (MWWorld::LiveCellRef *ref = cell.mActivators.find (name)) @@ -204,7 +215,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& ce if (MWWorld::LiveCellRef *ref = cell.mLights.find (name)) ptr = Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.mLockpicks.find (name)) + if (MWWorld::LiveCellRef *ref = cell.mLockpicks.find (name)) ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.mMiscItems.find (name)) @@ -244,16 +255,16 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) } // Then check cells that are already listed - for (std::map::iterator iter = mInteriors.begin(); - iter!=mInteriors.end(); ++iter) + for (std::map, Ptr::CellStore>::iterator iter = mExteriors.begin(); + iter!=mExteriors.end(); ++iter) { Ptr ptr = getPtrAndCache (name, iter->second); if (!ptr.isEmpty()) - return ptr; + return ptr; } - for (std::map, Ptr::CellStore>::iterator iter = mExteriors.begin(); - iter!=mExteriors.end(); ++iter) + for (std::map::iterator iter = mInteriors.begin(); + iter!=mInteriors.end(); ++iter) { Ptr ptr = getPtrAndCache (name, iter->second); if (!ptr.isEmpty()) @@ -264,7 +275,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) const MWWorld::Store &cells = mStore.get(); MWWorld::Store::iterator iter; - for (iter = cells.intBegin(); iter != cells.intEnd(); ++iter) + for (iter = cells.extBegin(); iter != cells.extEnd(); ++iter) { Ptr::CellStore *cellStore = getCellStore (&(*iter)); @@ -274,7 +285,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) return ptr; } - for (iter = cells.extBegin(); iter != cells.extEnd(); ++iter) + for (iter = cells.intBegin(); iter != cells.intEnd(); ++iter) { Ptr::CellStore *cellStore = getCellStore (&(*iter)); diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index edaf70055..53c72bb75 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -2,6 +2,7 @@ #define GAME_MWWORLD_CELLS_H #include +#include #include #include "ptr.hpp" @@ -19,7 +20,7 @@ namespace MWWorld class Cells { const MWWorld::ESMStore& mStore; - ESM::ESMReader& mReader; + std::vector& mReader; std::map mInteriors; std::map, CellStore> mExteriors; std::vector > mIdCache; @@ -36,7 +37,9 @@ namespace MWWorld public: - Cells (const MWWorld::ESMStore& store, ESM::ESMReader& reader); + 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 diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index fb5e45556..baf4fea32 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -10,13 +10,48 @@ namespace MWWorld { + + template + 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); + + // Skip this when reference was deleted. + // TODO: Support respawning references, in this case, we need to track it somehow. + if (ref.mDeleted) { + 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)); + } + } + + template bool operator==(const LiveCellRef& ref, int pRefnum) + { + return (ref.mRef.mRefnum == pRefnum); + } + CellStore::CellStore (const ESM::Cell *cell) : mCell (cell), mState (State_Unloaded) { mWaterLevel = cell->mWater; } - void CellStore::load (const MWWorld::ESMStore &store, ESM::ESMReader &esm) + void CellStore::load (const MWWorld::ESMStore &store, std::vector &esm) { if (mState!=State_Loaded) { @@ -31,7 +66,7 @@ namespace MWWorld } } - void CellStore::preload (const MWWorld::ESMStore &store, ESM::ESMReader &esm) + void CellStore::preload (const MWWorld::ESMStore &store, std::vector &esm) { if (mState==State_Unloaded) { @@ -41,57 +76,126 @@ namespace MWWorld } } - void CellStore::listRefs(const MWWorld::ESMStore &store, ESM::ESMReader &esm) + void CellStore::listRefs(const MWWorld::ESMStore &store, std::vector &esm) { assert (mCell); - if (mCell->mContext.filename.empty()) + if (mCell->mContextList.size() == 0) return; // this is a dynamically generated cell -> skipping. - // Reopen the ESM reader and seek to the right position. - mCell->restore (esm); - - ESM::CellRef ref; - - // Get each reference in turn - while (mCell->getNextRef (esm, ref)) + // Load references from all plugins that do something with this cell. + for (size_t i = 0; i < mCell->mContextList.size(); i++) { - std::string lowerCase = Misc::StringUtils::lowerCase (ref.mRefID); + // Reopen the ESM reader and seek to the right position. + int index = mCell->mContextList.at(i).index; + mCell->restore (esm[index], i); + + ESM::CellRef ref; - mIds.push_back (lowerCase); + // Get each reference in turn + while (mCell->getNextRef (esm[index], ref)) + { + 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... + continue; + } + + mIds.push_back (lowerCase); + } } std::sort (mIds.begin(), mIds.end()); } - void CellStore::loadRefs(const MWWorld::ESMStore &store, ESM::ESMReader &esm) + void CellStore::loadRefs(const MWWorld::ESMStore &store, std::vector &esm) { assert (mCell); - if (mCell->mContext.filename.empty()) + if (mCell->mContextList.size() == 0) return; // this is a dynamically generated cell -> skipping. - // Reopen the ESM reader and seek to the right position. - mCell->restore(esm); + // Load references from all plugins that do something with this cell. + for (size_t i = 0; i < mCell->mContextList.size(); i++) + { + // Reopen the ESM reader and seek to the right position. + int index = mCell->mContextList.at(i).index; + mCell->restore (esm[index], i); + + ESM::CellRef ref; + + // 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); + 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; - ESM::CellRef ref; + case 0: std::cout << "Cell reference " + ref.mRefID + " not found!\n"; break; + default: + std::cout << "WARNING: Ignoring reference '" << ref.mRefID << "' of unhandled type\n"; + } + } + } - // Get each reference in turn - while(mCell->getNextRef(esm, ref)) + // Load moved references, from separately tracked list. + for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); it++) { - std::string lowerCase = Misc::StringUtils::lowerCase(ref.mRefID); + // 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); - int rec = store.find(ref.mRefID); + 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) - { + /* 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; @@ -113,10 +217,11 @@ namespace MWWorld 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"; - } + case 0: std::cout << "Cell reference " + ref.mRefID + " not found!\n"; break; + default: + std::cout << "WARNING: Ignoring reference '" << ref.mRefID << "' of unhandled type\n"; + } + } } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index ba3d24d7e..f8467c84f 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -1,47 +1,14 @@ #ifndef GAME_MWWORLD_CELLSTORE_H #define GAME_MWWORLD_CELLSTORE_H -#include - -#include +#include #include -#include "refdata.hpp" +#include "livecellref.hpp" #include "esmstore.hpp" namespace MWWorld { - class Ptr; - class ESMStore; - - /// A reference to one object (of any type) in a cell. - /// - /// Constructing this with a CellRef instance in the constructor means that - /// in practice (where D is RefData) the possibly mutable data is copied - /// across to mData. If later adding data (such as position) to CellRef - /// this would have to be manually copied across. - template - struct LiveCellRef - { - LiveCellRef(const ESM::CellRef& cref, const X* b = NULL) - : mBase(b), mRef(cref), mData(mRef) - {} - - LiveCellRef(const X* b = NULL) - : mBase(b), mData(mRef) - {} - - // The object that this instance is based on. - const X* mBase; - - /* Information about this instance, such as 3D location and - rotation and individual type-dependent data. - */ - ESM::CellRef mRef; - - /// runtime-data - RefData mData; - }; /// A list of cell references template @@ -51,21 +18,14 @@ namespace MWWorld typedef std::list List; List mList; - /// Searches for reference of appropriate type in given ESMStore. - /// If reference exists, loads it into container, throws an exception - /// on miss - void load(ESM::CellRef &ref, const MWWorld::ESMStore &esmStore) - { - // for throwing exception on unhandled record type - const MWWorld::Store &store = esmStore.get(); - const X *ptr = store.find(ref.mRefID); - - /// \note redundant because Store::find() throws exception on miss - if (ptr == NULL) { - throw std::runtime_error("Error resolving cell reference " + ref.mRefID); - } - mList.push_back(LiveRef(ref, ptr)); - } + // Search for the given reference in the given reclist from + // ESMStore. Insert the reference into the list if a match is + // found. If not, throw an exception. + // Moved to cpp file, as we require a custom compare operator for it, + // 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); LiveRef *find (const std::string& name) { @@ -116,7 +76,7 @@ namespace MWWorld CellRefList mCreatureLists; CellRefList mItemLists; CellRefList mLights; - CellRefList mLockpicks; + CellRefList mLockpicks; CellRefList mMiscItems; CellRefList mNpcs; CellRefList mProbes; @@ -124,9 +84,9 @@ namespace MWWorld CellRefList mStatics; CellRefList mWeapons; - void load (const MWWorld::ESMStore &store, ESM::ESMReader &esm); + void load (const MWWorld::ESMStore &store, std::vector &esm); - void preload (const MWWorld::ESMStore &store, ESM::ESMReader &esm); + void preload (const MWWorld::ESMStore &store, std::vector &esm); /// Call functor (ref) for each reference. functor must return a bool. Returning /// false will abort the iteration. @@ -185,9 +145,9 @@ namespace MWWorld } /// Run through references and store IDs - void listRefs(const MWWorld::ESMStore &store, ESM::ESMReader &esm); + void listRefs(const MWWorld::ESMStore &store, std::vector &esm); - void loadRefs(const MWWorld::ESMStore &store, ESM::ESMReader &esm); + void loadRefs(const MWWorld::ESMStore &store, std::vector &esm); }; } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index c76019149..5690531e7 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -47,6 +47,16 @@ namespace MWWorld throw std::runtime_error ("class does not represent an actor"); } + bool Class::canSell (const MWWorld::Ptr& item, int npcServices) const + { + return false; + } + + int Class::getServices(const Ptr &actor) const + { + throw std::runtime_error ("class does not have services"); + } + MWMechanics::CreatureStats& Class::getCreatureStats (const Ptr& ptr) const { throw std::runtime_error ("class does not have creature stats"); @@ -122,6 +132,16 @@ namespace MWWorld return 0; } + float Class::getJump (const Ptr& ptr) const + { + return 0; + } + + float Class::getEnchantmentPoints (const MWWorld::Ptr& ptr) const + { + throw std::runtime_error ("class does not support enchanting"); + } + MWMechanics::Movement& Class::getMovementSettings (const Ptr& ptr) const { throw std::runtime_error ("movement settings not supported by class"); @@ -132,6 +152,11 @@ namespace MWWorld return Ogre::Vector3 (0, 0, 0); } + Ogre::Vector3 Class::getRotationVector (const Ptr& ptr) const + { + return Ogre::Vector3 (0, 0, 0); + } + std::pair, bool> Class::getEquipmentSlots (const Ptr& ptr) const { return std::make_pair (std::vector(), false); @@ -152,6 +177,11 @@ namespace MWWorld throw std::runtime_error ("capacity not supported by this class"); } + float Class::getWeight(const Ptr &ptr) const + { + throw std::runtime_error ("weight not supported by this class"); + } + float Class::getEncumbrance (const MWWorld::Ptr& ptr) const { throw std::runtime_error ("encumbrance not supported by class"); @@ -162,17 +192,25 @@ namespace MWWorld return false; } - bool Class::hasDetected (const MWWorld::Ptr& ptr, const MWWorld::Ptr& ptr2) const + bool Class::hasDetected (const MWWorld::Ptr& ptr, const MWWorld::Ptr& ptr2) const { - return false; + return true; + } + + float Class::getArmorRating (const MWWorld::Ptr& ptr) const + { + throw std::runtime_error("Class does not support armor rating"); } const Class& Class::get (const std::string& key) { + if (key.empty()) + throw std::logic_error ("Class::get(): attempting to get an empty key"); + std::map >::const_iterator iter = sClasses.find (key); if (iter==sClasses.end()) - throw std::logic_error ("unknown class key: " + key); + throw std::logic_error ("Class::get(): unknown class key: " + key); return *iter->second; } @@ -182,6 +220,11 @@ namespace MWWorld return get (ptr.getTypeName()); } + bool Class::isPersistent(const Ptr &ptr) const + { + throw std::runtime_error ("class does not support persistence"); + } + void Class::registerClass (const std::string& key, boost::shared_ptr instance) { sClasses.insert (std::make_pair (key, instance)); @@ -231,6 +274,20 @@ namespace MWWorld return ""; } + void Class::applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const + { + throw std::runtime_error ("class can't be enchanted"); + } + + std::pair Class::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const + { + return std::make_pair (1, ""); + } + + void Class::adjustPosition(const MWWorld::Ptr& ptr) const + { + } + MWWorld::Ptr Class::copyToCellImpl(const Ptr &ptr, CellStore &cell) const { diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 07dcb8fe0..b4fd84a6b 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -81,6 +81,9 @@ namespace MWWorld ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. + virtual void adjustPosition(const MWWorld::Ptr& ptr) const; + ///< Adjust position to stand on ground. Must be called post model load + virtual MWMechanics::CreatureStats& getCreatureStats (const Ptr& ptr) const; ///< Return creature stats or throw an exception, if class does not have creature stats /// (default implementation: throw an exceoption) @@ -140,6 +143,9 @@ namespace MWWorld virtual float getSpeed (const Ptr& ptr) const; ///< Return movement speed. + virtual float getJump(const MWWorld::Ptr &ptr) const; + ///< Return jump velocity (not accounting for movement) + virtual MWMechanics::Movement& getMovementSettings (const Ptr& ptr) const; ///< Return desired movement. @@ -147,6 +153,9 @@ namespace MWWorld ///< Return desired movement vector (determined based on movement settings, /// stance and stats). + virtual Ogre::Vector3 getRotationVector (const Ptr& ptr) const; + ///< Return desired rotations, as euler angles. + virtual std::pair, bool> getEquipmentSlots (const Ptr& ptr) const; ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? @@ -212,6 +221,9 @@ namespace MWWorld ///< Return the down sound ID of \a ptr or throw an exception, if class does not support ID retrieval /// (default implementation: throw an exception) + virtual float getArmorRating (const MWWorld::Ptr& ptr) const; + ///< @return combined armor rating of this actor + virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; ///< Return name of inventory icon. @@ -219,12 +231,30 @@ 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; + ///< @return the number of enchantment points available for possible enchanting + virtual void adjustScale(const MWWorld::Ptr& ptr,float& scale) const; virtual void adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const; + virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + ///< Determine whether or not \a item can be sold to an npc with the given \a npcServices + + virtual int getServices (const MWWorld::Ptr& actor) const; + virtual std::string getModel(const MWWorld::Ptr &ptr) const; + virtual void applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + + virtual std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; + ///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that. + /// Second item in the pair specifies the error message + + virtual float getWeight (const MWWorld::Ptr& ptr) const; + + virtual bool isPersistent (const MWWorld::Ptr& ptr) const; + virtual Ptr copyToCell(const Ptr &ptr, CellStore &cell) const; diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index db4537daf..6682f072a 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -8,13 +8,19 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/scriptmanager.hpp" + +#include "../mwmechanics/creaturestats.hpp" #include "manualref.hpp" #include "refdata.hpp" #include "class.hpp" +#include "localscripts.hpp" +#include "player.hpp" namespace { @@ -34,12 +40,6 @@ namespace return sum; } - - bool compare_string_ci(std::string str1, std::string str2) - { - Misc::StringUtils::toLower(str1); - return str1 == str2; - } } MWWorld::ContainerStore::ContainerStore() : mStateId (0), mCachedWeight (0), mWeightUpToDate (false) {} @@ -58,19 +58,53 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::end() bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& ptr2) { - /// \todo add current weapon/armor health, remaining lockpick/repair uses, current enchantment charge here as soon as they are implemented + /// \todo add current enchantment charge here when it is implemented if ( ptr1.mCellRef->mRefID == ptr2.mCellRef->mRefID - && MWWorld::Class::get(ptr1).getScript(ptr1) == "" // item with a script never stacks - && MWWorld::Class::get(ptr1).getEnchantment(ptr1) == "" // item with enchantment never stacks (we could revisit this later, but for now it makes selecting items in the spell window much easier) + && MWWorld::Class::get(ptr1).getScript(ptr1) == "" // item with a script never stacks + && MWWorld::Class::get(ptr1).getEnchantment(ptr1) == "" // item with enchantment never stacks (we could revisit this later, but for now it makes selecting items in the spell window much easier) && ptr1.mCellRef->mOwner == ptr2.mCellRef->mOwner && ptr1.mCellRef->mSoul == ptr2.mCellRef->mSoul - && ptr1.mCellRef->mCharge == ptr2.mCellRef->mCharge) + // item that is already partly used up never stacks + && (!MWWorld::Class::get(ptr1).hasItemHealth(ptr1) || ptr1.mCellRef->mCharge == -1 + || MWWorld::Class::get(ptr1).getItemMaxHealth(ptr1) == ptr1.mCellRef->mCharge) + && (!MWWorld::Class::get(ptr2).hasItemHealth(ptr2) || ptr2.mCellRef->mCharge == -1 + || MWWorld::Class::get(ptr2).getItemMaxHealth(ptr2) == ptr2.mCellRef->mCharge)) return true; return false; } MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& ptr) +{ + MWWorld::ContainerStoreIterator it = addImp(ptr); + MWWorld::Ptr item = *it; + + std::string script = MWWorld::Class::get(item).getScript(item); + if(script != "") + { + CellStore *cell; + + Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + + if(&(MWWorld::Class::get (player).getContainerStore (player)) == this) + { + cell = 0; // Items in player's inventory have cell set to 0, so their scripts will never be removed + + // Set OnPCAdd special variable, if it is declared + item.mRefData->getLocals().setVarByInt(script, "onpcadd", 1); + } + else + cell = player.getCell(); + + item.mCell = cell; + item.mContainerStore = 0; + MWBase::Environment::get().getWorld()->getLocalScripts().add(script, item); + } + + return it; +} + +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr) { int type = getType(ptr); @@ -79,33 +113,28 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& ptr) // gold needs special handling: when it is inserted into a container, the base object automatically becomes Gold_001 // this ensures that gold piles of different sizes stack with each other (also, several scripts rely on Gold_001 for detecting player gold) - if (MWWorld::Class::get(ptr).getName(ptr) == esmStore.get().find("sGold")->getString()) + if (Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_001") + || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_005") + || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_010") + || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_025") + || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_100")) { - MWWorld::LiveCellRef *gold = - ptr.get(); - - if (compare_string_ci(gold->mRef.mRefID, "gold_001") - || compare_string_ci(gold->mRef.mRefID, "gold_005") - || compare_string_ci(gold->mRef.mRefID, "gold_010") - || compare_string_ci(gold->mRef.mRefID, "gold_025") - || compare_string_ci(gold->mRef.mRefID, "gold_100")) - { - MWWorld::ManualRef ref(esmStore, "Gold_001"); + MWWorld::ManualRef ref(esmStore, "Gold_001"); + + int count = MWWorld::Class::get(ptr).getValue(ptr) * ptr.getRefData().getCount(); - int count = (ptr.getRefData().getCount() == 1) ? gold->mBase->mData.mValue : ptr.getRefData().getCount(); - ref.getPtr().getRefData().setCount(count); - for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) + ref.getPtr().getRefData().setCount(count); + for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) + { + if (Misc::StringUtils::ciEqual((*iter).get()->mRef.mRefID, "gold_001")) { - if (compare_string_ci((*iter).get()->mRef.mRefID, "gold_001")) - { - (*iter).getRefData().setCount( (*iter).getRefData().getCount() + count); - flagAsModified(); - return iter; - } + (*iter).getRefData().setCount( (*iter).getRefData().getCount() + count); + flagAsModified(); + return iter; } - - return addImpl(ref.getPtr()); } + + return addImpl(ref.getPtr()); } // determine whether to stack or not @@ -137,7 +166,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImpl (const Ptr& ptr case Type_Clothing: clothes.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --clothes.mList.end()); break; case Type_Ingredient: ingreds.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --ingreds.mList.end()); break; case Type_Light: lights.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --lights.mList.end()); break; - case Type_Lockpick: lockpicks.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --lockpicks.mList.end()); break; + case Type_Lockpick: lockpicks.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --lockpicks.mList.end()); break; case Type_Miscellaneous: miscItems.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --miscItems.mList.end()); break; case Type_Probe: probes.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --probes.mList.end()); break; case Type_Repair: repairs.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --repairs.mList.end()); break; @@ -148,24 +177,85 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImpl (const Ptr& ptr return it; } -void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const MWWorld::ESMStore& store) +void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::string& owner, const MWWorld::ESMStore& store) { for (std::vector::const_iterator iter (items.mList.begin()); iter!=items.mList.end(); ++iter) { - ManualRef ref (store, iter->mItem.toString()); + std::string id = iter->mItem.toString(); + addInitialItem(id, owner, iter->mCount); + } + + flagAsModified(); +} + +void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, int count, unsigned char failChance, bool topLevel) +{ + count = std::abs(count); /// \todo implement item restocking (indicated by negative count) + + try + { + ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id); if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) { - /// \todo implement leveled item lists - continue; - } + const ESM::ItemLevList* levItem = ref.getPtr().get()->mBase; + const std::vector& items = levItem->mList; - ref.getPtr().getRefData().setCount (std::abs(iter->mCount)); /// \todo implement item restocking (indicated by negative count) - add (ref.getPtr()); - } + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + int playerLevel = MWWorld::Class::get(player).getCreatureStats(player).getLevel(); - flagAsModified(); + 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.size()) + return; + std::string item = candidates[std::rand()%candidates.size()]; + addInitialItem(item, owner, count, failChance, false); + } + } + else + { + ref.getPtr().getRefData().setCount (count); + ref.getPtr().getCellRef().mOwner = owner; + addImp (ref.getPtr()); + } + } + catch (std::logic_error& e) + { + // Vanilla doesn't fail on nonexistent items in levelled lists + std::cerr << "Warning: ignoring nonexistent item '" << id << "'" << std::endl; + return; + } } void MWWorld::ContainerStore::clear() @@ -238,7 +328,7 @@ int MWWorld::ContainerStore::getType (const Ptr& ptr) if (ptr.getTypeName()==typeid (ESM::Light).name()) return Type_Light; - if (ptr.getTypeName()==typeid (ESM::Tool).name()) + if (ptr.getTypeName()==typeid (ESM::Lockpick).name()) return Type_Lockpick; if (ptr.getTypeName()==typeid (ESM::Miscellaneous).name()) @@ -287,7 +377,7 @@ MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *contain : mType(MWWorld::ContainerStore::Type_Ingredient), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mIngredient(iterator){} MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Light), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mLight(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Lockpick), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mLockpick(iterator){} MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Miscellaneous), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mMiscellaneous(iterator){} @@ -514,7 +604,8 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStoreIterator::operator++ (int bool MWWorld::ContainerStoreIterator::isEqual (const ContainerStoreIterator& iter) const { - assert (mContainer==iter.mContainer); + if (mContainer!=iter.mContainer) + return false; if (mType!=iter.mType) return false; diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 1297fc53c..559463d46 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -44,7 +44,7 @@ namespace MWWorld MWWorld::CellRefList clothes; MWWorld::CellRefList ingreds; MWWorld::CellRefList lights; - MWWorld::CellRefList lockpicks; + MWWorld::CellRefList lockpicks; MWWorld::CellRefList miscItems; MWWorld::CellRefList probes; MWWorld::CellRefList repairs; @@ -52,6 +52,8 @@ namespace MWWorld int mStateId; mutable float mCachedWeight; mutable bool mWeightUpToDate; + ContainerStoreIterator addImp (const Ptr& ptr); + void addInitialItem (const std::string& id, const std::string& owner, int count, unsigned char failChance=0, bool topLevel=true); public: @@ -77,13 +79,12 @@ namespace MWWorld ContainerStoreIterator addImpl (const Ptr& ptr); ///< Add the item to this container (no stacking) + public: + virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2); ///< @return true if the two specified objects can stack with each other - /// @note ptr1 is the item that is already in this container - - public: - void fill (const ESM::InventoryList& items, const MWWorld::ESMStore& store); + void fill (const ESM::InventoryList& items, const std::string& owner, const MWWorld::ESMStore& store); ///< Insert items into *this. void clear(); @@ -126,7 +127,7 @@ namespace MWWorld MWWorld::CellRefList::List::iterator mClothing; MWWorld::CellRefList::List::iterator mIngredient; MWWorld::CellRefList::List::iterator mLight; - MWWorld::CellRefList::List::iterator mLockpick; + MWWorld::CellRefList::List::iterator mLockpick; MWWorld::CellRefList::List::iterator mMiscellaneous; MWWorld::CellRefList::List::iterator mProbe; MWWorld::CellRefList::List::iterator mRepair; @@ -148,7 +149,7 @@ namespace MWWorld ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 73f5185c9..5a4f1db54 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -3,6 +3,8 @@ #include #include +#include + namespace MWWorld { @@ -25,6 +27,34 @@ void ESMStore::load(ESM::ESMReader &esm) ESM::Dialogue *dialogue = 0; + /// \todo Move this to somewhere else. ESMReader? + // Cache parent esX files by tracking their indices in the global list of + // all files/readers used by the engine. This will greaty accelerate + // refnumber mangling, as required for handling moved references. + int index = ~0; + const std::vector &masters = esm.getMasters(); + std::vector *allPlugins = esm.getGlobalReaderList(); + for (size_t j = 0; j < masters.size(); j++) { + ESM::Header::MasterData &mast = const_cast(masters[j]); + std::string fname = mast.name; + for (int i = 0; i < esm.getIndex(); i++) { + const std::string &candidate = allPlugins->at(i).getContext().filename; + std::string fnamecandidate = boost::filesystem::path(candidate).filename().string(); + if (fname == fnamecandidate) { + index = i; + break; + } + } + 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 + + ", but it has not been loaded yet. Please check your load order."; + esm.fail(fstring); + } + mast.index = index; + } + // Loop through all records while(esm.hasMoreRecs()) { @@ -55,12 +85,21 @@ void ESMStore::load(ESM::ESMReader &esm) } else { // Load it std::string id = esm.getHNOString("NAME"); + // ... unless it got deleted! This means that the following record + // has been deleted, and trying to load it using standard assumptions + // on the structure will (probably) fail. + if (esm.isNextSub("DELE")) { + esm.skipRecord(); + it->second->eraseStatic(id); + continue; + } it->second->load(esm, id); if (n.val==ESM::REC_DIAL) { // dirty hack, but it is better than non-const search() // or friends - dialogue = &mDialogs.mStatic.back(); + //dialogue = &mDialogs.mStatic.back(); + dialogue = const_cast(mDialogs.find(id)); assert (dialogue->mId == id); } else { dialogue = 0; @@ -84,7 +123,6 @@ void ESMStore::load(ESM::ESMReader &esm) cout << *it << " "; cout << endl; */ - setUp(); } void ESMStore::setUp() @@ -96,16 +134,6 @@ void ESMStore::setUp() mSkills.setUp(); mMagicEffects.setUp(); mAttributes.setUp(); - - ESM::NPC item; - item.mId = "player"; - - std::vector::iterator pIt = - std::lower_bound(mNpcs.mStatic.begin(), mNpcs.mStatic.end(), item, RecordCmp()); - assert(pIt != mNpcs.mStatic.end() && pIt->mId == "player"); - - mNpcs.insert(*pIt); - mNpcs.mStatic.erase(pIt); } } // end namespace diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index e14219aae..d86faf766 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -32,7 +32,7 @@ namespace MWWorld Store mCreatureLists; Store mItemLists; Store mLights; - Store mLockpicks; + Store mLockpicks; Store mMiscItems; Store mNpcs; Store mNpcChange; @@ -67,6 +67,8 @@ namespace MWWorld std::map mIds; std::map mStores; + ESM::NPC mPlayerTemplate; + unsigned int mDynamicCount; public: @@ -94,6 +96,9 @@ namespace MWWorld ESMStore() : mDynamicCount(0) { + // 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; @@ -138,6 +143,21 @@ namespace MWWorld mStores[ESM::REC_WEAP] = &mWeapons; } + void clearDynamic () + { + for (std::map::iterator it = mStores.begin(); it != mStores.end(); ++it) + it->second->clearDynamic(); + + mNpcs.insert(mPlayerTemplate); + } + + void movePlayerRecord () + { + mPlayerTemplate = *mNpcs.find("player"); + mNpcs.eraseStatic(mPlayerTemplate.mId); + mNpcs.insert(mPlayerTemplate); + } + void load(ESM::ESMReader &esm); template @@ -168,7 +188,27 @@ namespace MWWorld return ptr; } - private: + template + const T *insertStatic(const T &x) { + Store &store = const_cast &>(get()); + if (store.search(x.mId) != 0) { + std::ostringstream msg; + msg << "Try to override existing record '" << x.mId << "'"; + throw std::runtime_error(msg.str()); + } + T record = x; + + T *ptr = store.insertStatic(record); + for (iterator it = mStores.begin(); it != mStores.end(); ++it) { + if (it->second == &store) { + mIds[ptr->mId] = it->first; + } + } + return ptr; + } + + // 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(); }; @@ -308,7 +348,7 @@ namespace MWWorld } template <> - inline const Store &ESMStore::get() const { + inline const Store &ESMStore::get() const { return mLockpicks; } diff --git a/apps/openmw/mwworld/failedaction.cpp b/apps/openmw/mwworld/failedaction.cpp index ec763dba0..1db00ad06 100644 --- a/apps/openmw/mwworld/failedaction.cpp +++ b/apps/openmw/mwworld/failedaction.cpp @@ -15,7 +15,7 @@ namespace MWWorld { if ( actor.getRefData().getHandle()=="player" && !(message.empty())) { - MWBase::Environment::get().getWindowManager() ->messageBox(message, std::vector()); + MWBase::Environment::get().getWindowManager() ->messageBox(message); } } } diff --git a/apps/openmw/mwworld/fallback.cpp b/apps/openmw/mwworld/fallback.cpp new file mode 100644 index 000000000..569a6b50c --- /dev/null +++ b/apps/openmw/mwworld/fallback.cpp @@ -0,0 +1,49 @@ +#include "fallback.hpp" +#include "boost/lexical_cast.hpp" +namespace MWWorld +{ + Fallback::Fallback(const std::map& fallback):mFallbackMap(fallback) + {} + + std::string Fallback::getFallbackString(const std::string& fall) const + { + std::map::const_iterator it; + if((it = mFallbackMap.find(fall)) == mFallbackMap.end()) + { + return ""; + } + return it->second; + } + float Fallback::getFallbackFloat(const std::string& fall) const + { + std::string fallback=getFallbackString(fall); + if(fallback.empty()) + return 0; + else + return boost::lexical_cast(fallback); + } + bool Fallback::getFallbackBool(const std::string& fall) const + { + std::string fallback=getFallbackString(fall); + if(fallback.empty()) + return false; + else + return boost::lexical_cast(fallback); + } + Ogre::ColourValue Fallback::getFallbackColour(const std::string& fall) const + { + std::string sum=getFallbackString(fall); + if(sum.empty()) + return Ogre::ColourValue(0,0,0); + else + { + std::string ret[3]; + unsigned int j=0; + for(unsigned int i=0;i(ret[0])/255.f,boost::lexical_cast(ret[1])/255.f,boost::lexical_cast(ret[2])/255.f); + } + } +} diff --git a/apps/openmw/mwworld/fallback.hpp b/apps/openmw/mwworld/fallback.hpp new file mode 100644 index 000000000..6c5802e07 --- /dev/null +++ b/apps/openmw/mwworld/fallback.hpp @@ -0,0 +1,22 @@ +#ifndef GAME_MWWORLD_FALLBACK_H +#define GAME_MWWORLD_FALLBACK_H + +#include +#include + +#include + +namespace MWWorld +{ + class Fallback + { + const std::map mFallbackMap; + public: + Fallback(const std::map& fallback); + std::string getFallbackString(const std::string& fall) const; + float getFallbackFloat(const std::string& fall) const; + bool getFallbackBool(const std::string& fall) const; + Ogre::ColourValue getFallbackColour(const std::string& fall) const; + }; +} +#endif diff --git a/apps/openmw/mwworld/globals.cpp b/apps/openmw/mwworld/globals.cpp index f010661b9..a905f8aae 100644 --- a/apps/openmw/mwworld/globals.cpp +++ b/apps/openmw/mwworld/globals.cpp @@ -21,21 +21,21 @@ namespace MWWorld Globals::Collection::const_iterator Globals::find (const std::string& name) const { Collection::const_iterator iter = mVariables.find (name); - + if (iter==mVariables.end()) throw std::runtime_error ("unknown global variable: " + name); - - return iter; + + return iter; } Globals::Collection::iterator Globals::find (const std::string& name) { Collection::iterator iter = mVariables.find (name); - + if (iter==mVariables.end()) throw std::runtime_error ("unknown global variable: " + name); - - return iter; + + return iter; } Globals::Globals (const MWWorld::ESMStore& store) @@ -46,76 +46,64 @@ namespace MWWorld { char type = ' '; Data value; - - switch (iter->mType) + + switch (iter->mValue.getType()) { case ESM::VT_Short: - + type = 's'; - value.mShort = *reinterpret_cast ( - &iter->mValue); + value.mShort = iter->mValue.getInteger(); break; - - case ESM::VT_Int: - + + case ESM::VT_Long: + type = 'l'; - value.mLong = *reinterpret_cast ( - &iter->mValue); + value.mLong = iter->mValue.getInteger(); break; - + case ESM::VT_Float: - + type = 'f'; - value.mFloat = *reinterpret_cast ( - &iter->mValue); + 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))); } - - if (mVariables.find ("dayspassed")==mVariables.end()) - { - // vanilla Morrowind does not define dayspassed. - Data value; - value.mLong = 0; - - mVariables.insert (std::make_pair ("dayspassed", std::make_pair ('l', value))); - } } const Globals::Data& Globals::operator[] (const std::string& name) const { Collection::const_iterator iter = find (name); - + return iter->second.second; } Globals::Data& 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); @@ -127,9 +115,9 @@ namespace MWWorld 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); @@ -141,13 +129,13 @@ namespace MWWorld 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; @@ -155,16 +143,16 @@ namespace MWWorld case 'f': return iter->second.second.mFloat; default: throw std::runtime_error ("unsupported global variable type"); - } + } } - + char Globals::getType (const std::string& name) const { Collection::const_iterator iter = mVariables.find (name); - + if (iter==mVariables.end()) return ' '; - + return iter->second.first; } } diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index dd518ff6a..9d07ecb76 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -128,8 +128,11 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot) return mSlots[slot]; } -void MWWorld::InventoryStore::autoEquip (const MWMechanics::NpcStats& stats) +void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& npc) { + const MWMechanics::NpcStats& stats = MWWorld::Class::get(npc).getNpcStats(npc); + MWWorld::InventoryStore& invStore = MWWorld::Class::get(npc).getInventoryStore(npc); + TSlots slots; initSlots (slots); @@ -183,6 +186,18 @@ void MWWorld::InventoryStore::autoEquip (const MWMechanics::NpcStats& stats) } } + switch(MWWorld::Class::get (test).canBeEquipped (test, npc).first) + { + case 0: + continue; + case 2: + invStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, invStore.end()); + break; + case 3: + invStore.equip(MWWorld::InventoryStore::Slot_CarriedRight, invStore.end()); + break; + } + if (!itemsSlots.second) // if itemsSlots.second is true, item can stay stacked when equipped { // unstack item pointed to by iterator if required @@ -261,6 +276,8 @@ bool MWWorld::InventoryStore::stacks(const Ptr& ptr1, const Ptr& ptr2) { if (*iter != end() && ptr1 == **iter) return false; + if (*iter != end() && ptr2 == **iter) + return false; } return true; diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index e55eca0ae..0801e04bc 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -78,7 +78,7 @@ namespace MWWorld ContainerStoreIterator getSlot (int slot); - void autoEquip (const MWMechanics::NpcStats& stats); + void autoEquip (const MWWorld::Ptr& npc); ///< Auto equip items according to stats and item value. const MWMechanics::MagicEffects& getMagicEffects(); @@ -90,8 +90,6 @@ namespace MWWorld ///< \attention This function is internal to the world model and should not be called from /// outside. - protected: - virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2); ///< @return true if the two specified objects can stack with each other /// @note ptr1 is the item that is already in this container diff --git a/apps/openmw/mwworld/livecellref.hpp b/apps/openmw/mwworld/livecellref.hpp new file mode 100644 index 000000000..28c1bb5c2 --- /dev/null +++ b/apps/openmw/mwworld/livecellref.hpp @@ -0,0 +1,45 @@ +#ifndef GAME_MWWORLD_LIVECELLREF_H +#define GAME_MWWORLD_LIVECELLREF_H + +#include + +#include "refdata.hpp" + +namespace MWWorld +{ + class Ptr; + class ESMStore; + + /// A reference to one object (of any type) in a cell. + /// + /// Constructing this with a CellRef instance in the constructor means that + /// in practice (where D is RefData) the possibly mutable data is copied + /// across to mData. If later adding data (such as position) to CellRef + /// this would have to be manually copied across. + template + struct LiveCellRef + { + LiveCellRef(const ESM::CellRef& cref, const X* b = NULL) + : mBase(b), mRef(cref), mData(mRef) + {} + + LiveCellRef(const X* b = NULL) + : mBase(b), mData(mRef) + {} + + // The object that this instance is based on. + const X* mBase; + + /* Information about this instance, such as 3D location and + rotation and individual type-dependent data. + */ + ESM::CellRef mRef; + + /// runtime-data + RefData mData; + }; + +// template bool operator==(const LiveCellRef& ref, int pRefnum); +} + +#endif diff --git a/apps/openmw/mwworld/localscripts.cpp b/apps/openmw/mwworld/localscripts.cpp index a821ad486..5ec5ca9b5 100644 --- a/apps/openmw/mwworld/localscripts.cpp +++ b/apps/openmw/mwworld/localscripts.cpp @@ -3,6 +3,10 @@ #include "esmstore.hpp" #include "cellstore.hpp" +#include "class.hpp" +#include "containerstore.hpp" + + namespace { template @@ -19,6 +23,32 @@ namespace } } } + + // Adds scripts for items in containers (containers/npcs/creatures) + template + void listCellScriptsCont (MWWorld::LocalScripts& localScripts, + MWWorld::CellRefList& cellRefList, MWWorld::Ptr::CellStore *cell) + { + for (typename MWWorld::CellRefList::List::iterator iter ( + cellRefList.mList.begin()); + iter!=cellRefList.mList.end(); ++iter) + { + + MWWorld::Ptr containerPtr (&*iter, cell); + + MWWorld::ContainerStore& container = MWWorld::Class::get(containerPtr).getContainerStore(containerPtr); + for(MWWorld::ContainerStoreIterator it3 = container.begin(); it3 != container.end(); ++it3) + { + std::string script = MWWorld::Class::get(*it3).getScript(*it3); + if(script != "") + { + MWWorld::Ptr item = *it3; + item.mCell = cell; + localScripts.add (script, item); + } + } + } + } } MWWorld::LocalScripts::LocalScripts (const MWWorld::ESMStore& store) : mStore (store) {} @@ -78,13 +108,16 @@ void MWWorld::LocalScripts::addCell (Ptr::CellStore *cell) listCellScripts (*this, cell->mBooks, cell); listCellScripts (*this, cell->mClothes, cell); listCellScripts (*this, cell->mContainers, cell); + listCellScriptsCont (*this, cell->mContainers, cell); listCellScripts (*this, cell->mCreatures, cell); + listCellScriptsCont (*this, cell->mCreatures, cell); listCellScripts (*this, cell->mDoors, cell); listCellScripts (*this, cell->mIngreds, cell); listCellScripts (*this, cell->mLights, cell); listCellScripts (*this, cell->mLockpicks, cell); listCellScripts (*this, cell->mMiscItems, cell); listCellScripts (*this, cell->mNpcs, cell); + listCellScriptsCont (*this, cell->mNpcs, cell); listCellScripts (*this, cell->mProbes, cell); listCellScripts (*this, cell->mRepairs, cell); listCellScripts (*this, cell->mWeapons, cell); @@ -101,7 +134,7 @@ void MWWorld::LocalScripts::clearCell (Ptr::CellStore *cell) while (iter!=mScripts.end()) { - if (iter->second.getCell()==cell) + if (iter->second.mCell==cell) { if (iter==mIter) ++mIter; @@ -113,6 +146,20 @@ void MWWorld::LocalScripts::clearCell (Ptr::CellStore *cell) } } +void MWWorld::LocalScripts::remove (RefData *ref) +{ + for (std::list >::iterator iter = mScripts.begin(); + iter!=mScripts.end(); ++iter) + if (&(iter->second.getRefData()) == ref) + { + if (iter==mIter) + ++mIter; + + mScripts.erase (iter); + break; + } +} + void MWWorld::LocalScripts::remove (const Ptr& ptr) { for (std::list >::iterator iter = mScripts.begin(); diff --git a/apps/openmw/mwworld/localscripts.hpp b/apps/openmw/mwworld/localscripts.hpp index 028dcdeda..840243fff 100644 --- a/apps/openmw/mwworld/localscripts.hpp +++ b/apps/openmw/mwworld/localscripts.hpp @@ -10,6 +10,7 @@ namespace MWWorld { struct ESMStore; class CellStore; + class RefData; /// \brief List of active local scripts class LocalScripts @@ -47,6 +48,8 @@ namespace MWWorld void clearCell (CellStore *cell); ///< Remove all scripts belonging to \a cell. + + void remove (RefData *ref); void remove (const Ptr& ptr); ///< Remove script for given reference (ignored if reference does not have a scirpt listed). diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index 91b8cf8cd..6616165fa 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -53,7 +53,7 @@ namespace MWWorld !create (store.get(), name) && !create (store.get(), name) && !create (store.get(), name) && - !create (store.get(), name) && + !create (store.get(), name) && !create (store.get(), name) && !create (store.get(), name) && !create (store.get(), name) && @@ -68,12 +68,12 @@ namespace MWWorld cellRef.mRefnum = -1; cellRef.mScale = 1; cellRef.mFactIndex = 0; - cellRef.mCharge = 0; - cellRef.mIntv = 0; - cellRef.mNam9 = 0; + cellRef.mCharge = -1; + cellRef.mGoldValue = 1; + cellRef.mEnchantmentCharge = -1; cellRef.mTeleport = false; cellRef.mLockLevel = 0; - cellRef.mUnam = 0; + cellRef.mReferenceBlocked = 0; } const Ptr& getPtr() const diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 9e2e94143..7d63a2362 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -9,7 +9,11 @@ #include #include -#include +#include +#include +#include + +#include //#include "../mwbase/world.hpp" // FIXME #include "../mwbase/environment.hpp" @@ -21,23 +25,228 @@ using namespace Ogre; namespace MWWorld { - PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend) : - mRender(_rend), mEngine(0), mFreeFly (true) + static const float sMaxSlope = 60.0f; + static const float sStepSize = 30.0f; + // Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared. + static const int sMaxIterations = 50; + + class MovementSolver { + private: + static bool stepMove(Ogre::Vector3& position, const Ogre::Quaternion& orient, const Ogre::Vector3 &velocity, float remainingTime, + const Ogre::Vector3 &halfExtents, bool isInterior, + OEngine::Physic::PhysicEngine *engine) + { + traceResults trace; // no initialization needed + + newtrace(&trace, orient, position, position+Ogre::Vector3(0.0f,0.0f,sStepSize), + halfExtents, isInterior, engine); + if(trace.fraction == 0.0f) + return false; + + newtrace(&trace, orient, trace.endpos, trace.endpos + velocity*remainingTime, + halfExtents, isInterior, engine); + if(trace.fraction == 0.0f || (trace.fraction != 1.0f && getSlope(trace.planenormal) > sMaxSlope)) + return false; + + newtrace(&trace, orient, trace.endpos, trace.endpos-Ogre::Vector3(0.0f,0.0f,sStepSize), halfExtents, isInterior, engine); + if(getSlope(trace.planenormal) <= sMaxSlope) + { + // only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall. + position = trace.endpos; + return true; + } + + return false; + } + + static void clipVelocity(Ogre::Vector3& inout, const Ogre::Vector3& normal, float overbounce=1.0f) + { + //Math stuff. Basically just project the velocity vector onto the plane represented by the normal. + //More specifically, it projects velocity onto the normal, takes that result, multiplies it by overbounce and then subtracts it from velocity. + float backoff = inout.dotProduct(normal); + if(backoff < 0.0f) + backoff *= overbounce; + else + backoff /= overbounce; + + inout -= normal*backoff; + } + + static void projectVelocity(Ogre::Vector3& velocity, const Ogre::Vector3& direction) + { + Ogre::Vector3 normalizedDirection(direction); + normalizedDirection.normalise(); + + // no divide by normalizedDirection.length necessary because it's normalized + velocity = normalizedDirection * velocity.dotProduct(normalizedDirection); + } + + static float getSlope(const Ogre::Vector3 &normal) + { + return normal.angleBetween(Ogre::Vector3(0.0f,0.0f,1.0f)).valueDegrees(); + } + + public: + static Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr, OEngine::Physic::PhysicEngine *engine) + { + const ESM::Position &refpos = ptr.getRefData().getPosition(); + Ogre::Vector3 position(refpos.pos); + + bool hit=false; + bool isInterior = !ptr.getCell()->isExterior(); + + OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle()); + if (!physicActor) + return position; + + bool wasCollisionMode = physicActor->getCollisionMode(); + + physicActor->enableCollisions(false); + + Ogre::Vector3 halfExtents = physicActor->getHalfExtents();// + Vector3(1,1,1); + halfExtents.z = 1; // we trace the feet only, so we use a very thin box + + Ogre::Vector3 newPosition = position; + + traceResults trace; //no initialization needed + + int maxHeight = 200.f; + newtrace(&trace, Ogre::Quaternion::IDENTITY, newPosition, newPosition-Ogre::Vector3(0,0,maxHeight), halfExtents, isInterior, engine); + if(trace.fraction < 1.0f) + hit = true; + newPosition = trace.endpos; + + physicActor->setOnGround(hit && getSlope(trace.planenormal) <= sMaxSlope); + physicActor->enableCollisions(wasCollisionMode); + + if (hit) + return newPosition+Ogre::Vector3(0,0,4); + else + return position; + } + + static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, + bool gravity, OEngine::Physic::PhysicEngine *engine) + { + const ESM::Position &refpos = ptr.getRefData().getPosition(); + Ogre::Vector3 position(refpos.pos); + + /* Anything to collide with? */ + OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle()); + if(!physicActor || !physicActor->getCollisionMode()) + { + // FIXME: This works, but it's inconcsistent with how the rotations are applied elsewhere. Why? + return position + (Ogre::Quaternion(Ogre::Radian( -refpos.rot[2]), Ogre::Vector3::UNIT_Z)* + Ogre::Quaternion(Ogre::Radian( -refpos.rot[1]), Ogre::Vector3::UNIT_Y)* + Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) * + movement; + } + + traceResults trace; //no initialization needed + bool onground = false; + float remainingTime = time; + bool isInterior = !ptr.getCell()->isExterior(); + Ogre::Vector3 halfExtents = physicActor->getHalfExtents();// + Vector3(1,1,1); + physicActor->enableCollisions(false); + Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[2]), Ogre::Vector3::UNIT_Z); + Ogre::Vector3 velocity; + if(!gravity) + { + velocity = (Ogre::Quaternion(Ogre::Radian( -refpos.rot[2]), Ogre::Vector3::UNIT_Z)* + Ogre::Quaternion(Ogre::Radian( -refpos.rot[1]), Ogre::Vector3::UNIT_Y)* + Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) * + movement / time; + } + else + { + if(!(movement.z > 0.0f)) + { + newtrace(&trace, orient, position, position-Ogre::Vector3(0,0,4), halfExtents, isInterior, engine); + if(trace.fraction < 1.0f && getSlope(trace.planenormal) <= sMaxSlope) + onground = true; + } + velocity = Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z)*movement / time; + velocity.z += physicActor->getVerticalForce(); + } + + Ogre::Vector3 clippedVelocity(velocity); + if(onground) + { + // if we're on the ground, force velocity to track it + clippedVelocity.z = velocity.z = std::max(0.0f, velocity.z); + clipVelocity(clippedVelocity, trace.planenormal); + } + + const Ogre::Vector3 up(0.0f, 0.0f, 1.0f); + Ogre::Vector3 newPosition = position; + int iterations = 0; + do { + // trace to where character would go if there were no obstructions + newtrace(&trace, orient, newPosition, newPosition+clippedVelocity*remainingTime, halfExtents, isInterior, engine); + newPosition = trace.endpos; + remainingTime = remainingTime * (1.0f-trace.fraction); + + // check for obstructions + if(trace.fraction < 1.0f) + { + //std::cout<<"angle: "< 0.0f); + + if(onground) + { + newtrace(&trace, orient, newPosition, newPosition-Ogre::Vector3(0,0,sStepSize+4.0f), halfExtents, isInterior, engine); + if(trace.fraction < 1.0f && getSlope(trace.planenormal) <= sMaxSlope) + newPosition.z = trace.endpos.z + 2.0f; + else + onground = false; + } + physicActor->setOnGround(onground); + physicActor->setVerticalForce(!onground ? clippedVelocity.z - time*627.2f : 0.0f); + physicActor->enableCollisions(true); + return newPosition; + } + }; - playerphysics = new playerMove; + + PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend) : + mRender(_rend), mEngine(0) + { // Create physics. shapeLoader is deleted by the physic engine NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader(); mEngine = new OEngine::Physic::PhysicEngine(shapeLoader); - playerphysics->mEngine = mEngine; } PhysicsSystem::~PhysicsSystem() { delete mEngine; - delete playerphysics; - } + OEngine::Physic::PhysicEngine* PhysicsSystem::getEngine() { return mEngine; @@ -46,14 +255,13 @@ namespace MWWorld std::pair PhysicsSystem::getFacedHandle (MWWorld::World& world, float queryDistance) { btVector3 dir(0, 1, 0); - dir = dir.rotate(btVector3(1, 0, 0), mPlayerData.pitch); - dir = dir.rotate(btVector3(0, 0, 1), mPlayerData.yaw); + dir = dir.rotate(btVector3(1, 0, 0), mCameraData.pitch); + dir = dir.rotate(btVector3(0, 0, 1), mCameraData.yaw); dir.setX(-dir.x()); - btVector3 origin( - mPlayerData.eyepos.x, - mPlayerData.eyepos.y, - mPlayerData.eyepos.z); + btVector3 origin(mCameraData.eyepos.x, + mCameraData.eyepos.y, + mCameraData.eyepos.z); origin += dir * 5; btVector3 dest = origin + dir * queryDistance; @@ -66,14 +274,13 @@ namespace MWWorld std::vector < std::pair > PhysicsSystem::getFacedHandles (float queryDistance) { btVector3 dir(0, 1, 0); - dir = dir.rotate(btVector3(1, 0, 0), mPlayerData.pitch); - dir = dir.rotate(btVector3(0, 0, 1), mPlayerData.yaw); + dir = dir.rotate(btVector3(1, 0, 0), mCameraData.pitch); + dir = dir.rotate(btVector3(0, 0, 1), mCameraData.yaw); dir.setX(-dir.x()); - btVector3 origin( - mPlayerData.eyepos.x, - mPlayerData.eyepos.y, - mPlayerData.eyepos.z); + btVector3 origin(mCameraData.eyepos.x, + mCameraData.eyepos.y, + mCameraData.eyepos.z); origin += dir * 5; btVector3 dest = origin + dir * queryDistance; @@ -92,9 +299,8 @@ namespace MWWorld Ogre::Vector3 to = ray.getPoint(queryDistance); btVector3 _from, _to; - // OGRE to MW coordinates - _from = btVector3(from.x, -from.z, from.y); - _to = btVector3(to.x, -to.z, to.y); + _from = btVector3(from.x, from.y, from.z); + _to = btVector3(to.x, to.y, to.z); std::vector < std::pair > results; /* auto */ results = mEngine->rayTest2(_from,_to); @@ -106,15 +312,7 @@ namespace MWWorld void PhysicsSystem::setCurrentWater(bool hasWater, int waterHeight) { - playerphysics->hasWater = hasWater; - if(hasWater){ - playerphysics->waterHeight = waterHeight; - } - for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) - { - it->second->setCurrentWater(hasWater, waterHeight); - } - + // TODO: store and use } btVector3 PhysicsSystem::getRayPoint(float extent) @@ -123,7 +321,7 @@ namespace MWWorld Ray centerRay = mRender.getCamera()->getCameraToViewportRay( mRender.getViewport()->getWidth()/2, mRender.getViewport()->getHeight()/2); - btVector3 result(centerRay.getPoint(extent).x,-centerRay.getPoint(extent).z,centerRay.getPoint(extent).y); + btVector3 result(centerRay.getPoint(extent).x,centerRay.getPoint(extent).y,centerRay.getPoint(extent).z); return result; } @@ -131,18 +329,17 @@ namespace MWWorld { //get a ray pointing to the center of the viewport Ray centerRay = mRender.getCamera()->getCameraToViewportRay(mouseX, mouseY); - btVector3 result(centerRay.getPoint(extent).x,-centerRay.getPoint(extent).z,centerRay.getPoint(extent).y); + btVector3 result(centerRay.getPoint(extent).x,centerRay.getPoint(extent).y,centerRay.getPoint(extent).z); return result; } - bool PhysicsSystem::castRay(const Vector3& from, const Vector3& to) + bool PhysicsSystem::castRay(const Vector3& from, const Vector3& to, bool raycastingObjectOnly,bool ignoreHeightMap) { btVector3 _from, _to; _from = btVector3(from.x, from.y, from.z); _to = btVector3(to.x, to.y, to.z); - std::pair result = mEngine->rayTest(_from, _to); - + std::pair result = mEngine->rayTest(_from, _to, raycastingObjectOnly,ignoreHeightMap); return !(result.first == ""); } @@ -156,7 +353,7 @@ namespace MWWorld btVector3 btTo = btVector3(to.x, to.y, to.z); std::pair test = mEngine->rayTest(btFrom, btTo); - if (test.first == "") { + if (test.second == -1) { return std::make_pair(false, Ogre::Vector3()); } return std::make_pair(true, ray.getPoint(len * test.second)); @@ -171,9 +368,8 @@ namespace MWWorld Ogre::Vector3 to = ray.getPoint(200); /// \todo make this distance (ray length) configurable btVector3 _from, _to; - // OGRE to MW coordinates - _from = btVector3(from.x, -from.z, from.y); - _to = btVector3(to.x, -to.z, to.y); + _from = btVector3(from.x, from.y, from.z); + _to = btVector3(to.x, to.y, to.z); std::pair result = mEngine->rayTest(_from, _to); @@ -185,70 +381,19 @@ namespace MWWorld } } - void PhysicsSystem::doPhysics(float dt, const std::vector >& actors) + std::vector PhysicsSystem::getCollisions(const Ptr &ptr) { - //set the DebugRenderingMode. To disable it,set it to 0 - //eng->setDebugRenderingMode(1); - - //set the movement keys to 0 (no movement) for every actor) - for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) - { - OEngine::Physic::PhysicActor* act = it->second; - act->setMovement(0,0,0); - } - - playerMove::playercmd& pm_ref = playerphysics->cmd; - - - pm_ref.rightmove = 0; - pm_ref.forwardmove = 0; - pm_ref.upmove = 0; - - - //playerphysics->ps.move_type = PM_NOCLIP; - for (std::vector >::const_iterator iter (actors.begin()); - iter!=actors.end(); ++iter) - { - //dirty stuff to get the camera orientation. Must be changed! - if (iter->first == "player") { - playerphysics->ps.viewangles.x = - Ogre::Radian(mPlayerData.pitch).valueDegrees(); - - - - playerphysics->ps.viewangles.y = - Ogre::Radian(mPlayerData.yaw).valueDegrees() + 90; - - pm_ref.rightmove = iter->second.x; - pm_ref.forwardmove = -iter->second.y; - pm_ref.upmove = iter->second.z; - } - } - mEngine->stepSimulation(dt); + return mEngine->getCollisions(ptr.getRefData().getBaseNode()->getName()); } - std::vector< std::pair > PhysicsSystem::doPhysicsFixed ( - const std::vector >& actors) + Ogre::Vector3 PhysicsSystem::move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity) { - Pmove(playerphysics); - - - std::vector< std::pair > response; - for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) - { - - Ogre::Vector3 coord = it->second->getPosition(); - if(it->first == "player"){ - - coord = playerphysics->ps.origin ; - - } - - - response.push_back(std::pair(it->first, coord)); - } + return MovementSolver::move(ptr, movement, time, gravity, mEngine); + } - return response; + Ogre::Vector3 PhysicsSystem::traceDown(const MWWorld::Ptr &ptr) + { + return MovementSolver::traceDown(ptr, mEngine); } void PhysicsSystem::addHeightField (float* heights, @@ -263,13 +408,16 @@ namespace MWWorld mEngine->removeHeightField(x, y); } - void PhysicsSystem::addObject (const Ptr& ptr) + void PhysicsSystem::addObject (const Ptr& ptr, bool placeable) { std::string mesh = MWWorld::Class::get(ptr).getModel(ptr); Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); handleToMesh[node->getName()] = mesh; - OEngine::Physic::RigidBody* body = mEngine->createAndAdjustRigidBody(mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation()); - mEngine->addRigidBody(body); + OEngine::Physic::RigidBody* body = mEngine->createAndAdjustRigidBody( + mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation(), 0, 0, false, placeable); + OEngine::Physic::RigidBody* raycastingBody = mEngine->createAndAdjustRigidBody( + mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation(), 0, 0, true, placeable); + mEngine->addRigidBody(body, true, raycastingBody); } void PhysicsSystem::addActor (const Ptr& ptr) @@ -291,46 +439,25 @@ namespace MWWorld void PhysicsSystem::moveObject (const Ptr& ptr) { - Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); - std::string handle = node->getName(); - Ogre::Vector3 position = node->getPosition(); - if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle)) - { - // TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow - // start positions others than 0, 0, 0 - - - if(dynamic_cast(body->getCollisionShape()) == NULL){ - btTransform tr = body->getWorldTransform(); - tr.setOrigin(btVector3(position.x,position.y,position.z)); - body->setWorldTransform(tr); - } - else{ - //For objects that contain a box shape. - //Do any such objects exist? Perhaps animated objects? - mEngine->boxAdjustExternal(handleToMesh[handle], body, node->getScale().x, position, node->getOrientation()); - } - } - if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) - { - // TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow - // start positions others than 0, 0, 0 - if (handle == "player") - { - playerphysics->ps.origin = position; - } - else - { - act->setPosition(position); - } - } + Ogre::SceneNode *node = ptr.getRefData().getBaseNode(); + const std::string &handle = node->getName(); + const Ogre::Vector3 &position = node->getPosition(); + + if(OEngine::Physic::RigidBody *body = mEngine->getRigidBody(handle)) + body->getWorldTransform().setOrigin(btVector3(position.x,position.y,position.z)); + + if(OEngine::Physic::RigidBody *body = mEngine->getRigidBody(handle, true)) + body->getWorldTransform().setOrigin(btVector3(position.x,position.y,position.z)); + + if(OEngine::Physic::PhysicActor *physact = mEngine->getCharacter(handle)) + physact->setPosition(position); } void PhysicsSystem::rotateObject (const Ptr& ptr) { Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); - std::string handle = node->getName(); - Ogre::Quaternion rotation = node->getOrientation(); + const std::string &handle = node->getName(); + const Ogre::Quaternion &rotation = node->getOrientation(); if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) { //Needs to be changed @@ -343,16 +470,28 @@ namespace MWWorld else mEngine->boxAdjustExternal(handleToMesh[handle], body, node->getScale().x, node->getPosition(), rotation); } + if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle, true)) + { + if(dynamic_cast(body->getCollisionShape()) == NULL) + body->getWorldTransform().setRotation(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w)); + else + mEngine->boxAdjustExternal(handleToMesh[handle], body, node->getScale().x, node->getPosition(), rotation); + } } void PhysicsSystem::scaleObject (const Ptr& ptr) { Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); - std::string handle = node->getName(); + const std::string &handle = node->getName(); if(handleToMesh.find(handle) != handleToMesh.end()) { + bool placeable = false; + if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle,true)) + placeable = body->mPlaceable; + else if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle,false)) + placeable = body->mPlaceable; removeObject(handle); - addObject(ptr); + addObject(ptr, placeable); } if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) @@ -361,8 +500,7 @@ namespace MWWorld bool PhysicsSystem::toggleCollisionMode() { - playerphysics->ps.move_type = (playerphysics->ps.move_type == PM_NOCLIP ? PM_NORMAL : PM_NOCLIP); - for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) + for(std::map::iterator it = mEngine->mActorMap.begin(); it != mEngine->mActorMap.end();it++) { if (it->first=="player") { @@ -372,12 +510,10 @@ namespace MWWorld if(cmode) { act->enableCollisions(false); - mFreeFly = true; return false; } else { - mFreeFly = false; act->enableCollisions(true); return true; } @@ -408,10 +544,10 @@ namespace MWWorld return true; } - void PhysicsSystem::updatePlayerData(Ogre::Vector3 &eyepos, float pitch, float yaw) + void PhysicsSystem::updateCameraData(const Ogre::Vector3 &eyepos, float pitch, float yaw) { - mPlayerData.eyepos = eyepos; - mPlayerData.pitch = pitch; - mPlayerData.yaw = yaw; + mCameraData.eyepos = eyepos; + mCameraData.pitch = pitch; + mCameraData.yaw = yaw; } } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index 81715c31e..7b48f880e 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -1,12 +1,27 @@ #ifndef GAME_MWWORLD_PHYSICSSYSTEM_H #define GAME_MWWORLD_PHYSICSSYSTEM_H -#include -#include "ptr.hpp" -#include +#include + +#include + + +namespace OEngine +{ + namespace Render + { + class OgreRenderer; + } + namespace Physic + { + class PhysicEngine; + } +} namespace MWWorld { + class World; + class Ptr; class PhysicsSystem { @@ -14,13 +29,7 @@ namespace MWWorld PhysicsSystem (OEngine::Render::OgreRenderer &_rend); ~PhysicsSystem (); - void doPhysics(float duration, const std::vector >& actors); - ///< do physics with dt - Usage: first call doPhysics with frame dt, then call doPhysicsFixed as often as time steps have passed - - std::vector< std::pair > doPhysicsFixed (const std::vector >& actors); - ///< do physics with fixed timestep - Usage: first call doPhysics with frame dt, then call doPhysicsFixed as often as time steps have passed - - void addObject (const MWWorld::Ptr& ptr); + void addObject (const MWWorld::Ptr& ptr, bool placeable=false); void addActor (const MWWorld::Ptr& ptr); @@ -41,6 +50,10 @@ namespace MWWorld bool toggleCollisionMode(); + Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity); + std::vector getCollisions(const MWWorld::Ptr &ptr); ///< get handles this object collides with + Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr); + std::pair getFacedHandle (MWWorld::World& world, float queryDistance); std::vector < std::pair > getFacedHandles (float queryDistance); std::vector < std::pair > getFacedHandles (float mouseX, float mouseY, float queryDistance); @@ -49,8 +62,8 @@ namespace MWWorld btVector3 getRayPoint(float extent, float mouseX, float mouseY); - // cast ray, return true if it hit something - bool castRay(const Ogre::Vector3& from, const Ogre::Vector3& to); + // cast ray, return true if it hit something. if raycasringObjectOnlt is set to false, it ignores NPCs and objects with no collisions. + bool castRay(const Ogre::Vector3& from, const Ogre::Vector3& to, bool raycastingObjectOnly = true,bool ignoreHeightMap = false); std::pair castRay(const Ogre::Vector3 &orig, const Ogre::Vector3 &dir, float len); @@ -64,18 +77,16 @@ namespace MWWorld bool getObjectAABB(const MWWorld::Ptr &ptr, Ogre::Vector3 &min, Ogre::Vector3 &max); - void updatePlayerData(Ogre::Vector3 &eyepos, float pitch, float yaw); + void updateCameraData(const Ogre::Vector3 &eyepos, float pitch, float yaw); private: struct { Ogre::Vector3 eyepos; float pitch, yaw; - } mPlayerData; + } mCameraData; OEngine::Render::OgreRenderer &mRender; OEngine::Physic::PhysicEngine* mEngine; - bool mFreeFly; - playerMove* playerphysics; std::map handleToMesh; PhysicsSystem (const PhysicsSystem&); diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 3414ba448..e352b4c82 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -1,14 +1,21 @@ #include "player.hpp" - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/soundmanager.hpp" + +#include "../mwworld/ptr.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwmechanics/movement.hpp" #include "../mwmechanics/npcstats.hpp" +#include "../mwmechanics/character.hpp" +#include "../mwmechanics/security.hpp" + +#include "../mwrender/animation.hpp" -#include "esmstore.hpp" #include "class.hpp" namespace MWWorld @@ -25,12 +32,46 @@ namespace MWWorld playerPos[0] = playerPos[1] = playerPos[2] = 0; } + 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) + { + mCellStore = cellStore; + } + + MWWorld::Ptr Player::getPlayer() + { + MWWorld::Ptr ptr (&mPlayer, mCellStore); + return ptr; + } + + void Player::setBirthSign (const std::string &sign) + { + mSign = sign; + } + + const std::string& Player::getBirthSign() const + { + return mSign; + } + void Player::setDrawState (MWMechanics::DrawState_ state) { MWWorld::Ptr ptr = getPlayer(); MWWorld::Class::get(ptr).getNpcStats(ptr).setDrawState (state); } + bool Player::getAutoMove() const + { + return mAutoMove; + } + void Player::setAutoMove (bool enable) { MWWorld::Ptr ptr = getPlayer(); @@ -42,14 +83,14 @@ namespace MWWorld if (mAutoMove) value = 1; - MWWorld::Class::get (ptr).getMovementSettings (ptr).mForwardBackward = value; + MWWorld::Class::get (ptr).getMovementSettings (ptr).mPosition[1] = value; } void Player::setLeftRight (int value) { MWWorld::Ptr ptr = getPlayer(); - MWWorld::Class::get (ptr).getMovementSettings (ptr).mLeftRight = value; + MWWorld::Class::get (ptr).getMovementSettings (ptr).mPosition[0] = value; } void Player::setForwardBackward (int value) @@ -61,23 +102,88 @@ namespace MWWorld if (mAutoMove) value = 1; - MWWorld::Class::get (ptr).getMovementSettings (ptr).mForwardBackward = value; + MWWorld::Class::get (ptr).getMovementSettings (ptr).mPosition[1] = value; } void Player::setUpDown(int value) { MWWorld::Ptr ptr = getPlayer(); - MWWorld::Class::get (ptr).getMovementSettings (ptr).mUpDown = value; + MWWorld::Class::get (ptr).getMovementSettings (ptr).mPosition[2] = value; + } + + void Player::setRunState(bool run) + { + MWWorld::Ptr ptr = getPlayer(); + MWWorld::Class::get(ptr).setStance(ptr, MWWorld::Class::Run, run); } - void Player::toggleRunning() + void Player::setSneak(bool sneak) { MWWorld::Ptr ptr = getPlayer(); - bool running = MWWorld::Class::get (ptr).getStance (ptr, MWWorld::Class::Run, true); + MWWorld::Class::get (ptr).setStance (ptr, MWWorld::Class::Sneak, sneak); + } + + void Player::setYaw(float yaw) + { + MWWorld::Ptr ptr = getPlayer(); + MWWorld::Class::get(ptr).getMovementSettings(ptr).mRotation[2] = yaw; + } + void Player::setPitch(float pitch) + { + MWWorld::Ptr ptr = getPlayer(); + MWWorld::Class::get(ptr).getMovementSettings(ptr).mRotation[0] = pitch; + } + void Player::setRoll(float roll) + { + MWWorld::Ptr ptr = getPlayer(); + MWWorld::Class::get(ptr).getMovementSettings(ptr).mRotation[1] = roll; + } - MWWorld::Class::get (ptr).setStance (ptr, MWWorld::Class::Run, !running); + void Player::use() + { + MWWorld::InventoryStore& store = MWWorld::Class::get(getPlayer()).getInventoryStore(getPlayer()); + MWWorld::ContainerStoreIterator equipped = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + + if (getDrawState() == MWMechanics::DrawState_Weapon) + { + if (equipped != store.end()) + { + MWWorld::Ptr item = *equipped; + MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(getPlayer()); + MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getFacedObject(); + + if (anim->isPriorityActive(MWMechanics::Priority_Weapon)) + return; + + std::string resultMessage, resultSound; + + if (item.getTypeName() == typeid(ESM::Lockpick).name()) + { + if (!target.isEmpty()) + MWMechanics::Security(getPlayer()).pickLock(target, item, resultMessage, resultSound); + anim->play("pickprobe", MWMechanics::Priority_Weapon, MWRender::Animation::Group_UpperBody, true, "start", "stop", 0.0, 0); + } + else if (item.getTypeName() == typeid(ESM::Probe).name()) + { + if (!target.isEmpty()) + MWMechanics::Security(getPlayer()).probeTrap(target, item, resultMessage, resultSound); + anim->play("pickprobe", MWMechanics::Priority_Weapon, MWRender::Animation::Group_UpperBody, true, "start", "stop", 0.0, 0); + } + + if (!resultMessage.empty()) + MWBase::Environment::get().getWindowManager()->messageBox(resultMessage); + if (!resultSound.empty()) + MWBase::Environment::get().getSoundManager()->playSound(resultSound,1,1); + + // tool used up? + if (!item.getRefData().getCount()) + MWBase::Environment::get().getWindowManager()->unsetSelectedWeapon(); + else + MWBase::Environment::get().getWindowManager()->setSelectedWeapon(item); + } + } } MWMechanics::DrawState_ Player::getDrawState() diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 1c1ef76cf..fd25f749f 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -1,15 +1,20 @@ #ifndef GAME_MWWORLD_PLAYER_H #define GAME_MWWORLD_PLAYER_H -#include "../mwworld/cellstore.hpp" #include "../mwworld/refdata.hpp" -#include "../mwworld/ptr.hpp" +#include "../mwworld/livecellref.hpp" #include "../mwmechanics/drawstate.hpp" +namespace ESM +{ + struct NPC; +} + namespace MWBase { class World; + class Ptr; } namespace MWWorld @@ -30,31 +35,19 @@ namespace MWWorld Player(const ESM::NPC *player, const MWBase::World& world); - void setCell (MWWorld::CellStore *cellStore) - { - mCellStore = cellStore; - } + void set (const ESM::NPC *player); - MWWorld::Ptr getPlayer() - { - MWWorld::Ptr ptr (&mPlayer, mCellStore); - return ptr; - } + void setCell (MWWorld::CellStore *cellStore); - void setBirthSign(const std::string &sign) { - mSign = sign; - } + MWWorld::Ptr getPlayer(); - const std::string &getBirthSign() const { - return mSign; - } + void setBirthSign(const std::string &sign); + + const std::string &getBirthSign() const; void setDrawState (MWMechanics::DrawState_ state); - bool getAutoMove() const - { - return mAutoMove; - } + bool getAutoMove() const; MWMechanics::DrawState_ getDrawState(); /// \todo constness @@ -65,7 +58,15 @@ namespace MWWorld void setForwardBackward (int value); void setUpDown(int value); - void toggleRunning(); + void use (); + ///< Use item equipped on right hand, or fists + + void setRunState(bool run); + void setSneak(bool sneak); + + void setYaw(float yaw); + void setPitch(float pitch); + void setRoll(float roll); }; } #endif diff --git a/apps/openmw/mwworld/ptr.hpp b/apps/openmw/mwworld/ptr.hpp index 594ddef2d..14e2c76a4 100644 --- a/apps/openmw/mwworld/ptr.hpp +++ b/apps/openmw/mwworld/ptr.hpp @@ -68,13 +68,13 @@ namespace MWWorld Ptr::CellStore *getCell() const { - assert (mCell); + assert(mCell); return mCell; } bool isInCell() const { - return (mCell != 0); + return (mContainerStore == 0); } void setContainerStore (ContainerStore *store); diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index 5630bfd5c..c1a3ae785 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -6,6 +6,9 @@ #include "customdata.hpp" #include "cellstore.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + namespace MWWorld { void RefData::copy (const RefData& refData) @@ -16,6 +19,7 @@ namespace MWWorld mEnabled = refData.mEnabled; mCount = refData.mCount; mPosition = refData.mPosition; + mLocalRotation = refData.mLocalRotation; mCustomData = refData.mCustomData ? refData.mCustomData->clone() : 0; } @@ -31,7 +35,11 @@ namespace MWWorld RefData::RefData (const ESM::CellRef& cellRef) : mBaseNode(0), mHasLocals (false), mEnabled (true), mCount (1), mPosition (cellRef.mPos), mCustomData (0) - {} + { + mLocalRotation.rot[0]=0; + mLocalRotation.rot[1]=0; + mLocalRotation.rot[2]=0; + } RefData::RefData (const RefData& refData) : mBaseNode(0), mCustomData (0) @@ -73,10 +81,13 @@ namespace MWWorld {} } - std::string RefData::getHandle() + const std::string &RefData::getHandle() { - if (!mBaseNode) - return ""; + if(!mBaseNode) + { + static const std::string empty; + return empty; + } return mBaseNode->getName(); } @@ -107,6 +118,9 @@ namespace MWWorld void RefData::setCount (int count) { + if(count == 0) + MWBase::Environment::get().getWorld()->removeRefScript(this); + mCount = count; } @@ -135,6 +149,11 @@ namespace MWWorld return mPosition; } + LocalRotation& RefData::getLocalRotation() + { + return mLocalRotation; + } + void RefData::setCustomData (CustomData *data) { delete mCustomData; diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index 3a6e0fc9f..642f5412c 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -18,6 +18,10 @@ namespace ESM namespace MWWorld { + struct LocalRotation{ + float rot[3]; + }; + class CustomData; class RefData @@ -34,6 +38,8 @@ namespace MWWorld ESM::Position mPosition; + LocalRotation mLocalRotation; + CustomData *mCustomData; void copy (const RefData& refData); @@ -54,7 +60,7 @@ namespace MWWorld RefData& operator= (const RefData& refData); /// Return OGRE handle (may be empty). - std::string getHandle(); + const std::string &getHandle(); /// Return OGRE base node (can be a null pointer). Ogre::SceneNode* getBaseNode(); @@ -78,6 +84,8 @@ namespace MWWorld ESM::Position& getPosition(); + LocalRotation& getLocalRotation(); + void setCustomData (CustomData *data); ///< Set custom data (potentially replacing old custom data). The ownership of \æ data is /// transferred to this. diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index b917a8916..aec00a8c6 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -1,5 +1,6 @@ #include "scene.hpp" +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" /// FIXME @@ -7,9 +8,11 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "physicssystem.hpp" #include "player.hpp" #include "localscripts.hpp" #include "esmstore.hpp" +#include "class.hpp" #include "cellfunctors.hpp" @@ -18,22 +21,27 @@ namespace template void insertCellRefList(MWRender::RenderingManager& rendering, - T& cellRefList, MWWorld::CellStore &cell, MWWorld::PhysicsSystem& physics) + T& cellRefList, MWWorld::CellStore &cell, MWWorld::PhysicsSystem& physics, bool rescale) { if (!cellRefList.mList.empty()) { const MWWorld::Class& class_ = MWWorld::Class::get (MWWorld::Ptr (&*cellRefList.mList.begin(), &cell)); - - int numRefs = cellRefList.mList.size(); int current = 0; for (typename T::List::iterator it = cellRefList.mList.begin(); it != cellRefList.mList.end(); it++) { - MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Loading cells", 1, current, numRefs); + if (rescale) + { + if (it->mRef.mScale<0.5) + it->mRef.mScale = 0.5; + else if (it->mRef.mScale>2) + it->mRef.mScale = 2; + } + ++current; - if (it->mData.getCount() || it->mData.isEnabled()) + if (it->mData.getCount() && it->mData.isEnabled()) { MWWorld::Ptr ptr (&*it, &cell); @@ -41,8 +49,14 @@ namespace { rendering.addObject(ptr); class_.insertObject(ptr, physics); - MWBase::Environment::get().getWorld()->rotateObject(ptr, 0, 0, 0, true); + + float ax = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees(); + float ay = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees(); + float az = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees(); + MWBase::Environment::get().getWorld()->localRotateObject(ptr, ax, ay, az); + MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().mScale); + class_.adjustPosition(ptr); } catch (const std::exception& e) { @@ -52,10 +66,6 @@ namespace } } } - else - { - MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Loading cells", 1, 0, 1); - } } } @@ -71,34 +81,34 @@ namespace MWWorld void Scene::unloadCell (CellStoreCollection::iterator iter) { std::cout << "Unloading cell\n"; - ListHandles functor; - - (*iter)->forEach(functor); + ListAndResetHandles functor; + + (*iter)->forEach(functor); { // silence annoying g++ warning for (std::vector::const_iterator iter2 (functor.mHandles.begin()); - iter2!=functor.mHandles.end(); ++iter2){ - Ogre::SceneNode* node = *iter2; + iter2!=functor.mHandles.end(); ++iter2) + { + Ogre::SceneNode* node = *iter2; mPhysics->removeObject (node->getName()); } + } - if ((*iter)->mCell->isExterior()) - { - ESM::Land* land = - MWBase::Environment::get().getWorld()->getStore().get().search( - (*iter)->mCell->getGridX(), - (*iter)->mCell->getGridY() - ); - if (land) - mPhysics->removeHeightField( (*iter)->mCell->getGridX(), (*iter)->mCell->getGridY() ); - } + if ((*iter)->mCell->isExterior()) + { + ESM::Land* land = + MWBase::Environment::get().getWorld()->getStore().get().search( + (*iter)->mCell->getGridX(), + (*iter)->mCell->getGridY() + ); + if (land) + mPhysics->removeHeightField( (*iter)->mCell->getGridX(), (*iter)->mCell->getGridY() ); } mRendering.removeCell(*iter); - //mPhysics->removeObject("Unnamed_43"); MWBase::Environment::get().getWorld()->getLocalScripts().clearCell (*iter); - MWBase::Environment::get().getMechanicsManager()->dropActors (*iter); + MWBase::Environment::get().getMechanicsManager()->drop (*iter); MWBase::Environment::get().getSoundManager()->stopSound (*iter); mActiveCells.erase(*iter); } @@ -111,12 +121,10 @@ namespace MWWorld if(result.second) { - insertCell(*cell); - mRendering.cellAdded (cell); - float verts = ESM::Land::LAND_SIZE; float worldsize = ESM::Land::REAL_SIZE; + // Load terrain physics first... if (cell->mCell->isExterior()) { ESM::Land* land = @@ -136,6 +144,12 @@ namespace MWWorld } } + // ... then references. This is important for adjustPosition to work correctly. + /// \todo rescale depending on the state of a new GMST + insertCell (*cell, true); + + mRendering.cellAdded (cell); + mRendering.configureAmbient(*cell); mRendering.requestMap(cell); mRendering.configureAmbient(*cell); @@ -159,25 +173,42 @@ namespace MWWorld float y = Ogre::Radian(pos.rot[1]).valueDegrees(); float z = Ogre::Radian(pos.rot[2]).valueDegrees(); world->rotateObject(player, x, y, z); + + MWWorld::Class::get(player).adjustPosition(player); } MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); - mechMgr->addActor(player); + mechMgr->add(player); mechMgr->watchActor(player); MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); } + void Scene::changeToVoid() + { + CellStoreCollection::iterator active = mActiveCells.begin(); + while (active!=mActiveCells.end()) + unloadCell (active++); + assert(mActiveCells.empty()); + mCurrentCell = NULL; + } + void Scene::changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos) { Nif::NIFFile::CacheLock cachelock; + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); mRendering.preCellChange(mCurrentCell); // remove active - MWBase::Environment::get().getMechanicsManager()->removeActor (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + MWBase::Environment::get().getMechanicsManager()->remove(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + + std::string loadingExteriorText; + + loadingExteriorText = gmst.find ("sLoadingMessage3")->getString(); CellStoreCollection::iterator active = mActiveCells.begin(); @@ -213,8 +244,6 @@ namespace MWWorld continue; } } - - MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Unloading cells", 0, current, numUnload); unloadCell (active++); ++current; } @@ -263,7 +292,9 @@ namespace MWWorld { CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(x, y); - MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Loading cells", 0, current, numLoad); + //Loading Exterior loading text + MWBase::Environment::get().getWindowManager ()->setLoadingProgress (loadingExteriorText, 0, current, numLoad); + loadCell (cell); ++current; } @@ -322,11 +353,18 @@ namespace MWWorld void Scene::changeToInteriorCell (const std::string& cellName, const ESM::Position& position) { + + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + + std::string loadingInteriorText; + loadingInteriorText = gmst.find ("sLoadingMessage2")->getString(); + CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(cellName); bool loadcell = (mCurrentCell == NULL); if(!loadcell) loadcell = *mCurrentCell != *cell; - + if(!loadcell) { MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -336,9 +374,11 @@ namespace MWWorld float y = Ogre::Radian(position.rot[1]).valueDegrees(); float z = Ogre::Radian(position.rot[2]).valueDegrees(); world->rotateObject(world->getPlayer().getPlayer(), x, y, z); + + MWWorld::Class::get(world->getPlayer().getPlayer()).adjustPosition(world->getPlayer().getPlayer()); return; } - + std::cout << "Changing to interior\n"; // remove active @@ -357,8 +397,6 @@ namespace MWWorld active = mActiveCells.begin(); while (active!=mActiveCells.end()) { - MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Unloading cells", 0, current, numUnload); - unloadCell (active++); ++current; } @@ -366,7 +404,9 @@ namespace MWWorld // Load cell. std::cout << "cellName: " << cell->mCell->mName << std::endl; - MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Loading cells", 0, 0, 1); + //Loading Interior loading text + MWBase::Environment::get().getWindowManager ()->setLoadingProgress (loadingInteriorText, 0, 0, 1); + loadCell (cell); mCurrentCell = cell; @@ -374,7 +414,7 @@ namespace MWWorld // adjust fog mRendering.switchToInterior(); mRendering.configureFog(*mCurrentCell); - + // adjust player playerCellChange (mCurrentCell, position); @@ -406,29 +446,30 @@ namespace MWWorld mCellChanged = false; } - void Scene::insertCell (Ptr::CellStore &cell) + void Scene::insertCell (Ptr::CellStore &cell, bool rescale) { // Loop through all references in the cell - insertCellRefList(mRendering, cell.mActivators, cell, *mPhysics); - insertCellRefList(mRendering, cell.mPotions, cell, *mPhysics); - insertCellRefList(mRendering, cell.mAppas, cell, *mPhysics); - insertCellRefList(mRendering, cell.mArmors, cell, *mPhysics); - insertCellRefList(mRendering, cell.mBooks, cell, *mPhysics); - insertCellRefList(mRendering, cell.mClothes, cell, *mPhysics); - insertCellRefList(mRendering, cell.mContainers, cell, *mPhysics); - insertCellRefList(mRendering, cell.mCreatures, cell, *mPhysics); - insertCellRefList(mRendering, cell.mDoors, cell, *mPhysics); - insertCellRefList(mRendering, cell.mIngreds, cell, *mPhysics); - insertCellRefList(mRendering, cell.mCreatureLists, cell, *mPhysics); - insertCellRefList(mRendering, cell.mItemLists, cell, *mPhysics); - insertCellRefList(mRendering, cell.mLights, cell, *mPhysics); - insertCellRefList(mRendering, cell.mLockpicks, cell, *mPhysics); - insertCellRefList(mRendering, cell.mMiscItems, cell, *mPhysics); - insertCellRefList(mRendering, cell.mNpcs, cell, *mPhysics); - insertCellRefList(mRendering, cell.mProbes, cell, *mPhysics); - insertCellRefList(mRendering, cell.mRepairs, cell, *mPhysics); - insertCellRefList(mRendering, cell.mStatics, cell, *mPhysics); - insertCellRefList(mRendering, cell.mWeapons, cell, *mPhysics); + insertCellRefList(mRendering, cell.mActivators, cell, *mPhysics, rescale); + insertCellRefList(mRendering, cell.mPotions, cell, *mPhysics, rescale); + insertCellRefList(mRendering, cell.mAppas, cell, *mPhysics, rescale); + insertCellRefList(mRendering, cell.mArmors, cell, *mPhysics, rescale); + insertCellRefList(mRendering, cell.mBooks, cell, *mPhysics, rescale); + insertCellRefList(mRendering, cell.mClothes, cell, *mPhysics, rescale); + insertCellRefList(mRendering, cell.mContainers, cell, *mPhysics, rescale); + insertCellRefList(mRendering, cell.mDoors, cell, *mPhysics, rescale); + insertCellRefList(mRendering, cell.mIngreds, cell, *mPhysics, rescale); + insertCellRefList(mRendering, cell.mCreatureLists, cell, *mPhysics, rescale); + insertCellRefList(mRendering, cell.mItemLists, cell, *mPhysics, rescale); + insertCellRefList(mRendering, cell.mLights, cell, *mPhysics, rescale); + insertCellRefList(mRendering, cell.mLockpicks, cell, *mPhysics, rescale); + insertCellRefList(mRendering, cell.mMiscItems, cell, *mPhysics, rescale); + insertCellRefList(mRendering, cell.mProbes, cell, *mPhysics, rescale); + insertCellRefList(mRendering, cell.mRepairs, cell, *mPhysics, rescale); + insertCellRefList(mRendering, cell.mStatics, cell, *mPhysics, rescale); + insertCellRefList(mRendering, cell.mWeapons, cell, *mPhysics, rescale); + // Load NPCs and creatures _after_ everything else (important for adjustPosition to work correctly) + insertCellRefList(mRendering, cell.mCreatures, cell, *mPhysics, rescale); + insertCellRefList(mRendering, cell.mNpcs, cell, *mPhysics, rescale); } void Scene::addObjectToScene (const Ptr& ptr) @@ -441,7 +482,7 @@ namespace MWWorld void Scene::removeObjectFromScene (const Ptr& ptr) { - MWBase::Environment::get().getMechanicsManager()->removeActor (ptr); + MWBase::Environment::get().getMechanicsManager()->remove (ptr); MWBase::Environment::get().getSoundManager()->stopSound3D (ptr); mPhysics->removeObject (ptr.getRefData().getHandle()); mRendering.removeObject (ptr); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index ad6ad1559..3dfbd1045 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -3,7 +3,7 @@ #include "../mwrender/renderingmanager.hpp" -#include "physicssystem.hpp" +#include "ptr.hpp" #include "globals.hpp" namespace Ogre @@ -34,9 +34,9 @@ namespace MWRender namespace MWWorld { + class PhysicsSystem; class Player; class CellStore; - class Ptr; class Scene { @@ -47,7 +47,7 @@ namespace MWWorld private: //OEngine::Render::OgreRenderer& mRenderer; - CellStore* mCurrentCell; // the cell, the player is in + CellStore* mCurrentCell; // the cell the player is in CellStoreCollection mActiveCells; bool mCellChanged; PhysicsSystem *mPhysics; @@ -56,6 +56,7 @@ namespace MWWorld void playerCellChange (CellStore *cell, const ESM::Position& position, bool adjustPlayerPos = true); + void insertCell (Ptr::CellStore &cell, bool rescale); public: @@ -84,9 +85,10 @@ namespace MWWorld void changeToExteriorCell (const ESM::Position& position); ///< Move to exterior cell. - void markCellAsUnchanged(); + void changeToVoid(); + ///< Change into a void - void insertCell (Ptr::CellStore &cell); + void markCellAsUnchanged(); void update (float duration, bool paused); diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp new file mode 100644 index 000000000..ebc7ef03f --- /dev/null +++ b/apps/openmw/mwworld/store.cpp @@ -0,0 +1,97 @@ +#include "store.hpp" +#include "esmstore.hpp" + +namespace MWWorld { + + +void Store::load(ESM::ESMReader &esm, const std::string &id) +{ + // Don't automatically assume that a new cell must be spawned. Multiple plugins write to the same cell, + // 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; + cell->mName = id; + + //First part of cell loading + cell->preLoad(esm); + + //Handling MovedCellRefs, there is no way to do it inside loadcell + while (esm.isNextSub("MVRF")) { + ESM::CellRef ref; + ESM::MovedCellRef cMRef; + cell->getNextMVRF(esm, cMRef); + + MWWorld::Store &cStore = const_cast&>(mEsmStore->get()); + ESM::Cell *cellAlt = const_cast(cStore.searchOrCreate(cMRef.mTarget[0], cMRef.mTarget[1])); + + // 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. + 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); + if (iter == cellAlt->mLeasedRefs.end()) + cellAlt->mLeasedRefs.push_back(ref); + else + *iter = ref; + } + + //Second part of cell loading + cell->postLoad(esm); + + if(cell->mData.mFlags & ESM::Cell::Interior) + { + // Store interior cell by name, try to merge with existing parent data. + ESM::Cell *oldcell = const_cast(search(idLower)); + if (oldcell) { + // push the new references on the list of references to manage + oldcell->mContextList.push_back(cell->mContextList.at(0)); + // copy list into new cell + cell->mContextList = oldcell->mContextList; + // have new cell replace old cell + *oldcell = *cell; + } else + mInt[idLower] = *cell; + } + else + { + // Store exterior cells by grid position, try to merge with existing parent data. + ESM::Cell *oldcell = const_cast(search(cell->getGridX(), cell->getGridY())); + if (oldcell) { + // push the new references on the list of references to manage + oldcell->mContextList.push_back(cell->mContextList.at(0)); + // copy list into new cell + cell->mContextList = oldcell->mContextList; + // 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); + 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); + wipecell->mLeasedRefs.erase(it_lease); + *itold = *it; + } + } + cell->mMovedRefs = oldcell->mMovedRefs; + // have new cell replace old cell + *oldcell = *cell; + } else + mExt[std::make_pair(cell->mData.mX, cell->mData.mY)] = *cell; + } + delete cell; +} + +} diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 431cd3cf9..b49569f24 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -17,8 +17,11 @@ namespace MWWorld virtual void setUp() {} virtual void listIdentifier(std::vector &list) const {} - virtual int getSize() const = 0; + virtual size_t getSize() const = 0; virtual void load(ESM::ESMReader &esm, const std::string &id) = 0; + + virtual bool eraseStatic(const std::string &id) {return false;} + virtual void clearDynamic() {} }; template @@ -85,11 +88,12 @@ namespace MWWorld template class Store : public StoreBase { - std::vector mStatic; + std::map mStatic; std::vector mShared; std::map mDynamic; typedef std::map Dynamic; + typedef std::map Static; friend class ESMStore; @@ -103,15 +107,21 @@ namespace MWWorld typedef SharedIterator iterator; + // setUp needs to be called again after + virtual void clearDynamic() + { + mDynamic.clear(); + mShared.clear(); + } + const T *search(const std::string &id) const { T item; item.mId = Misc::StringUtils::lowerCase(id); - typename std::vector::const_iterator it = - std::lower_bound(mStatic.begin(), mStatic.end(), item, RecordCmp()); + typename std::map::const_iterator it = mStatic.find(item.mId); - if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->mId, id)) { - return &(*it); + if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { + return &(it->second); } typename Dynamic::const_iterator dit = mDynamic.find(item.mId); @@ -133,18 +143,19 @@ namespace MWWorld } void load(ESM::ESMReader &esm, const std::string &id) { - mStatic.push_back(T()); - mStatic.back().mId = Misc::StringUtils::lowerCase(id); - mStatic.back().load(esm); + std::string idLower = Misc::StringUtils::lowerCase(id); + mStatic[idLower] = T(); + mStatic[idLower].mId = idLower; + mStatic[idLower].load(esm); } void setUp() { - std::sort(mStatic.begin(), mStatic.end(), RecordCmp()); + //std::sort(mStatic.begin(), mStatic.end(), RecordCmp()); mShared.reserve(mStatic.size()); - typename std::vector::iterator it = mStatic.begin(); + typename std::map::iterator it = mStatic.begin(); for (; it != mStatic.end(); ++it) { - mShared.push_back(&(*it)); + mShared.push_back(&(it->second)); } } @@ -156,7 +167,7 @@ namespace MWWorld return mShared.end(); } - int getSize() const { + size_t getSize() const { return mShared.size(); } @@ -181,6 +192,44 @@ namespace MWWorld return ptr; } + T *insertStatic(const T &item) { + std::string id = Misc::StringUtils::lowerCase(item.mId); + std::pair result = + mStatic.insert(std::pair(id, item)); + T *ptr = &result.first->second; + if (result.second) { + mShared.push_back(ptr); + } else { + *ptr = item; + } + return ptr; + } + + + bool eraseStatic(const std::string &id) { + T item; + item.mId = Misc::StringUtils::lowerCase(id); + + typename std::map::iterator it = mStatic.find(item.mId); + + if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { + // delete from the static part of mShared + typename std::vector::iterator sharedIter = mShared.begin(); + typename std::vector::iterator end = sharedIter + mStatic.size(); + + while (sharedIter != mShared.end() && sharedIter != end) { + if((*sharedIter)->mId == item.mId) { + mShared.erase(sharedIter); + break; + } + ++sharedIter; + } + mStatic.erase(it); + } + + return true; + } + bool erase(const std::string &id) { std::string key = Misc::StringUtils::lowerCase(id); typename Dynamic::iterator it = mDynamic.find(key); @@ -204,39 +253,54 @@ namespace MWWorld template <> inline void Store::load(ESM::ESMReader &esm, const std::string &id) { - mStatic.push_back(ESM::Dialogue()); - mStatic.back().mId = id; - mStatic.back().load(esm); + std::string idLower = Misc::StringUtils::lowerCase(id); + + std::map::iterator it = mStatic.find(idLower); + if (it == mStatic.end()) { + it = mStatic.insert( std::make_pair( idLower, ESM::Dialogue() ) ).first; + it->second.mId = id; // don't smash case here, as this line is printed... I think + } + + //I am not sure is it need to load the dialog from a plugin if it was already loaded from prevois plugins + it->second.load(esm); } template <> inline void Store::load(ESM::ESMReader &esm, const std::string &id) { - mStatic.push_back(ESM::Script()); - mStatic.back().load(esm); - Misc::StringUtils::toLower(mStatic.back().mId); + ESM::Script scpt; + scpt.load(esm); + Misc::StringUtils::toLower(scpt.mId); + mStatic[scpt.mId] = scpt; } template <> class Store : public StoreBase { - std::vector mStatic; + // For multiple ESM/ESP files we need one list per file. + typedef std::vector LandTextureList; + std::vector mStatic; public: Store() { - mStatic.reserve(128); + mStatic.push_back(LandTextureList()); + LandTextureList <exl = mStatic[0]; + // More than enough to hold Morrowind.esm. Extra lists for plugins will we + // added on-the-fly in a different method. + ltexl.reserve(128); } typedef std::vector::const_iterator iterator; - const ESM::LandTexture *search(size_t index) const { - if (index < mStatic.size()) { - return &mStatic.at(index); - } - return 0; + const ESM::LandTexture *search(size_t index, size_t plugin) const { + assert(plugin < mStatic.size()); + const LandTextureList <exl = mStatic[plugin]; + + assert(index < ltexl.size()); + return <exl.at(index); } - const ESM::LandTexture *find(size_t index) const { - const ESM::LandTexture *ptr = search(index); + const ESM::LandTexture *find(size_t index, size_t plugin) const { + const ESM::LandTexture *ptr = search(index, plugin); if (ptr == 0) { std::ostringstream msg; msg << "Land texture with index " << index << " not found"; @@ -245,27 +309,44 @@ namespace MWWorld return ptr; } - int getSize() const { + size_t getSize() const { return mStatic.size(); } - void load(ESM::ESMReader &esm, const std::string &id) { - ESM::LandTexture ltex; - ltex.load(esm); + size_t getSize(size_t plugin) const { + assert(plugin < mStatic.size()); + return mStatic[plugin].size(); + } - if (ltex.mIndex >= (int) mStatic.size()) { - mStatic.resize(ltex.mIndex + 1); + void load(ESM::ESMReader &esm, const std::string &id, size_t plugin) { + ESM::LandTexture lt; + lt.load(esm); + lt.mId = id; + + // Make sure we have room for the structure + if (plugin >= mStatic.size()) { + mStatic.resize(plugin+1); } - mStatic[ltex.mIndex] = ltex; - mStatic[ltex.mIndex].mId = id; + LandTextureList <exl = mStatic[plugin]; + if(lt.mIndex + 1 > (int)ltexl.size()) + ltexl.resize(lt.mIndex+1); + + // Store it + ltexl[lt.mIndex] = lt; } - iterator begin() const { - return mStatic.begin(); + void load(ESM::ESMReader &esm, const std::string &id) { + load(esm, id, esm.getIndex()); } - iterator end() const { - return mStatic.end(); + iterator begin(size_t plugin) const { + assert(plugin < mStatic.size()); + return mStatic[plugin].begin(); + } + + iterator end(size_t plugin) const { + assert(plugin < mStatic.size()); + return mStatic[plugin].end(); } }; @@ -297,7 +378,7 @@ namespace MWWorld } - int getSize() const { + size_t getSize() const { return mStatic.size(); } @@ -357,19 +438,28 @@ namespace MWWorld } }; - std::vector mInt; - std::vector mExt; + struct DynamicExtCmp + { + bool operator()(const std::pair &left, const std::pair &right) const { + if (left.first == right.first) { + return left.second < right.second; + } + return left.first < right.first; + } + }; + + typedef std::map DynamicInt; + typedef std::map, ESM::Cell, DynamicExtCmp> DynamicExt; + + DynamicInt mInt; + DynamicExt mExt; std::vector mSharedInt; std::vector mSharedExt; - typedef std::map DynamicInt; - typedef std::map, ESM::Cell> DynamicExt; - DynamicInt mDynamicInt; DynamicExt mDynamicExt; - const ESM::Cell *search(const ESM::Cell &cell) const { if (cell.isExterior()) { return search(cell.getGridX(), cell.getGridY()); @@ -378,6 +468,8 @@ namespace MWWorld } public: + ESMStore *mEsmStore; + typedef SharedIterator iterator; Store() @@ -387,11 +479,10 @@ namespace MWWorld ESM::Cell cell; cell.mName = Misc::StringUtils::lowerCase(id); - std::vector::const_iterator it = - std::lower_bound(mInt.begin(), mInt.end(), cell, RecordCmp()); + std::map::const_iterator it = mInt.find(cell.mName); - if (it != mInt.end() && Misc::StringUtils::ciEqual(it->mName, id)) { - return &(*it); + if (it != mInt.end() && Misc::StringUtils::ciEqual(it->second.mName, id)) { + return &(it->second); } DynamicInt::const_iterator dit = mDynamicInt.find(cell.mName); @@ -406,20 +497,42 @@ namespace MWWorld ESM::Cell cell; cell.mData.mX = x, cell.mData.mY = y; - std::vector::const_iterator it = - std::lower_bound(mExt.begin(), mExt.end(), cell, ExtCmp()); + std::pair key(x, y); + DynamicExt::const_iterator it = mExt.find(key); + if (it != mExt.end()) { + return &(it->second); + } - if (it != mExt.end() && it->mData.mX == x && it->mData.mY == y) { - return &(*it); + DynamicExt::const_iterator dit = mDynamicExt.find(key); + if (dit != mDynamicExt.end()) { + return &dit->second; } + return 0; + } + + const ESM::Cell *searchOrCreate(int x, int y) { + ESM::Cell cell; + cell.mData.mX = x, cell.mData.mY = y; + std::pair key(x, y); + DynamicExt::const_iterator it = mExt.find(key); + if (it != mExt.end()) { + return &(it->second); + } + DynamicExt::const_iterator dit = mDynamicExt.find(key); if (dit != mDynamicExt.end()) { return &dit->second; } - return 0; + ESM::Cell *newCell = new ESM::Cell; + newCell->mData.mX = x; + newCell->mData.mY = y; + mExt[std::make_pair(x, y)] = *newCell; + delete newCell; + + return &mExt[std::make_pair(x, y)]; } const ESM::Cell *find(const std::string &id) const { @@ -443,32 +556,28 @@ namespace MWWorld } void setUp() { - typedef std::vector::iterator Iterator; + //typedef std::vector::iterator Iterator; + typedef DynamicExt::iterator ExtIterator; + typedef std::map::iterator IntIterator; - std::sort(mInt.begin(), mInt.end(), RecordCmp()); + //std::sort(mInt.begin(), mInt.end(), RecordCmp()); mSharedInt.reserve(mInt.size()); - for (Iterator it = mInt.begin(); it != mInt.end(); ++it) { - mSharedInt.push_back(&(*it)); + for (IntIterator it = mInt.begin(); it != mInt.end(); ++it) { + mSharedInt.push_back(&(it->second)); } - std::sort(mExt.begin(), mExt.end(), ExtCmp()); + //std::sort(mExt.begin(), mExt.end(), ExtCmp()); mSharedExt.reserve(mExt.size()); - for (Iterator it = mExt.begin(); it != mExt.end(); ++it) { - mSharedExt.push_back(&(*it)); + for (ExtIterator it = mExt.begin(); it != mExt.end(); ++it) { + mSharedExt.push_back(&(it->second)); } } - void load(ESM::ESMReader &esm, const std::string &id) { - ESM::Cell cell; - cell.mName = id; - cell.load(esm); - - if (cell.isExterior()) { - mExt.push_back(cell); - } else { - mInt.push_back(cell); - } - } + // HACK: Method implementation had to be moved to a separate cpp file, as we would otherwise get + // errors related to the compare operator used in std::find for ESM::MovedCellRefTracker::find. + // There some nasty three-way cyclic header dependency involved, which I could only fix by moving + // this method. + void load(ESM::ESMReader &esm, const std::string &id); iterator intBegin() const { return iterator(mSharedInt.begin()); @@ -508,7 +617,7 @@ namespace MWWorld return 0; } - int getSize() const { + size_t getSize() const { return mSharedInt.size() + mSharedExt.size(); } @@ -642,7 +751,7 @@ namespace MWWorld mStatic.back().load(esm); } - int getSize() const { + size_t getSize() const { return mStatic.size(); } @@ -786,7 +895,28 @@ namespace MWWorld } void setUp() { - std::sort(mStatic.begin(), mStatic.end(), Compare()); + /// \note This method sorts indexed values for further + /// searches. Every loaded item is present in storage, but + /// latest loaded shadows any previous while searching. + /// If memory cost will be too high, it is possible to remove + /// unused values. + + Compare cmp; + + std::stable_sort(mStatic.begin(), mStatic.end(), cmp); + + typename std::vector::iterator first, next; + next = first = mStatic.begin(); + + while (first != mStatic.end() && ++next != mStatic.end()) { + while (next != mStatic.end() && !cmp(*first, *next)) { + ++next; + } + if (first != --next) { + std::swap(*first, *next); + } + first = ++next; + } } const T *search(int index) const { @@ -871,7 +1001,7 @@ namespace MWWorld } } - int getSize() const { + size_t getSize() const { return mStatic.size(); } diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 917a8d7d4..8b3c5f6ff 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -4,7 +4,6 @@ #include #include - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" @@ -13,327 +12,161 @@ #include "player.hpp" #include "esmstore.hpp" +#include "fallback.hpp" using namespace Ogre; using namespace MWWorld; using namespace MWSound; -#define lerp(x, y) (x * (1-factor) + y * factor) +namespace +{ + float lerp (float x, float y, float factor) + { + return x * (1-factor) + y * factor; + } + + Ogre::ColourValue lerp (const Ogre::ColourValue& x, const Ogre::ColourValue& y, float factor) + { + return x * (1-factor) + y * factor; + } +} + +void WeatherManager::setFallbackWeather(Weather& weather,const std::string& name) +{ + std::string upper=name; + upper[0]=toupper(name[0]); + weather.mCloudsMaximumPercent = mFallback->getFallbackFloat("Weather_"+upper+"_Clouds_Maximum_Percent"); + weather.mTransitionDelta = mFallback->getFallbackFloat("Weather_"+upper+"_Transition_Delta"); + weather.mSkySunriseColor=mFallback->getFallbackColour("Weather_"+upper+"_Sky_Sunrise_Color"); + weather.mSkyDayColor = mFallback->getFallbackColour("Weather_"+upper+"_Sky_Day_Color"); + weather.mSkySunsetColor = mFallback->getFallbackColour("Weather_"+upper+"_Sky_Sunset_Color"); + weather.mSkyNightColor = mFallback->getFallbackColour("Weather_"+upper+"_Sky_Night_Color"); + weather.mFogSunriseColor = mFallback->getFallbackColour("Weather_"+upper+"_Fog_Sunrise_Color"); + weather.mFogDayColor = mFallback->getFallbackColour("Weather_"+upper+"_Fog_Day_Color"); + weather.mFogSunsetColor = mFallback->getFallbackColour("Weather_"+upper+"_Fog_Sunset_Color"); + weather.mFogNightColor = mFallback->getFallbackColour("Weather_"+upper+"_Fog_Night_Color"); + weather.mAmbientSunriseColor = mFallback->getFallbackColour("Weather_"+upper+"_Ambient_Sunrise_Color"); + weather.mAmbientDayColor = mFallback->getFallbackColour("Weather_"+upper+"_Ambient_Day_Color"); + weather.mAmbientSunsetColor = mFallback->getFallbackColour("Weather_"+upper+"_Ambient_Sunset_Color"); + weather.mAmbientNightColor = mFallback->getFallbackColour("Weather_"+upper+"_Ambient_Night_Color"); + weather.mSunSunriseColor = mFallback->getFallbackColour("Weather_"+upper+"_Sun_Sunrise_Color"); + weather.mSunDayColor = mFallback->getFallbackColour("Weather_"+upper+"_Sun_Day_Color"); + weather.mSunSunsetColor = mFallback->getFallbackColour("Weather_"+upper+"_Sun_Sunset_Color"); + weather.mSunNightColor = mFallback->getFallbackColour("Weather_"+upper+"_Sun_Night_Color"); + weather.mSunDiscSunsetColor = mFallback->getFallbackColour("Weather_"+upper+"_Sun_Disc_Sunset_Color"); + weather.mLandFogDayDepth = mFallback->getFallbackFloat("Weather_"+upper+"_Land_Fog_Day_Depth"); + weather.mLandFogNightDepth = mFallback->getFallbackFloat("Weather_"+upper+"_Land_Fog_Night_Depth"); + weather.mWindSpeed = mFallback->getFallbackFloat("Weather_"+upper+"_Wind_Speed"); + weather.mCloudSpeed = mFallback->getFallbackFloat("Weather_"+upper+"_Cloud_Speed"); + weather.mGlareView = mFallback->getFallbackFloat("Weather_"+upper+"_Glare_View"); + mWeatherSettings[name] = weather; +} -const std::string WeatherGlobals::mThunderSoundID0 = "Thunder0"; -const std::string WeatherGlobals::mThunderSoundID1 = "Thunder1"; -const std::string WeatherGlobals::mThunderSoundID2 = "Thunder2"; -const std::string WeatherGlobals::mThunderSoundID3 = "Thunder3"; -const float WeatherGlobals::mSunriseTime = 8; -const float WeatherGlobals::mSunsetTime = 18; -const float WeatherGlobals::mSunriseDuration = 2; -const float WeatherGlobals::mSunsetDuration = 2; -const float WeatherGlobals::mWeatherUpdateTime = 20.f; +float WeatherManager::calculateHourFade (const std::string& moonName) const +{ + float fadeOutStart=mFallback->getFallbackFloat("Moons_"+moonName+"_Fade_Out_Start"); + float fadeOutFinish=mFallback->getFallbackFloat("Moons_"+moonName+"_Fade_Out_Finish"); + float fadeInStart=mFallback->getFallbackFloat("Moons_"+moonName+"_Fade_In_Start"); + float fadeInFinish=mFallback->getFallbackFloat("Moons_"+moonName+"_Fade_In_Finish"); + + if (mHour >= fadeOutStart && mHour <= fadeOutFinish) + return (1 - ((mHour - fadeOutStart) / (fadeOutFinish - fadeOutStart))); + if (mHour >= fadeInStart && mHour <= fadeInFinish) + return (1 - ((mHour - fadeInStart) / (fadeInFinish - fadeInStart))); + else + return 1; +} -// morrowind sets these per-weather, but since they are only used by 'thunderstorm' -// weather setting anyway, we can just as well set them globally -const float WeatherGlobals::mThunderFrequency = .4; -const float WeatherGlobals::mThunderThreshold = 0.6; -const float WeatherGlobals::mThunderSoundDelay = 0.25; +float WeatherManager::calculateAngleFade (const std::string& moonName, float angle) const +{ + float endAngle=mFallback->getFallbackFloat("Moons_"+moonName+"_Fade_End_Angle"); + float startAngle=mFallback->getFallbackFloat("Moons_"+moonName+"_Fade_Start_Angle"); + if (angle <= startAngle && angle >= endAngle) + return (1 - ((angle - endAngle)/(startAngle-endAngle))); + else if (angle > startAngle) + return 0.f; + else + return 1.f; +} -WeatherManager::WeatherManager(MWRender::RenderingManager* rendering) : +WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fallback* fallback) : mHour(14), mCurrentWeather("clear"), mFirstUpdate(true), mWeatherUpdateTime(0), mThunderFlash(0), mThunderChance(0), mThunderChanceNeeded(50), mThunderSoundDelay(0), mRemainingTransitionTime(0), mMonth(0), mDay(0), - mTimePassed(0) + mTimePassed(0), mFallback(fallback), mWindSpeed(0.f), mRendering(rendering) { - mRendering = rendering; - - #define clr(r,g,b) ColourValue(r/255.f, g/255.f, b/255.f) - - /// \todo read these from Morrowind.ini + //Globals + mThunderSoundID0 = mFallback->getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_0"); + mThunderSoundID1 = mFallback->getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_1"); + mThunderSoundID2 = mFallback->getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_2"); + mThunderSoundID3 = mFallback->getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_3"); + mSunriseTime = mFallback->getFallbackFloat("Weather_Sunrise_Time"); + mSunsetTime = mFallback->getFallbackFloat("Weather_Sunset_Time"); + mSunriseDuration = mFallback->getFallbackFloat("Weather_Sunrise_Duration"); + mSunsetDuration = mFallback->getFallbackFloat("Weather_Sunset_Duration"); + mHoursBetweenWeatherChanges = mFallback->getFallbackFloat("Weather_Hours_Between_Weather_Changes"); + mWeatherUpdateTime = mHoursBetweenWeatherChanges * 3600; + mThunderFrequency = mFallback->getFallbackFloat("Weather_Thunderstorm_Thunder_Frequency"); + mThunderThreshold = mFallback->getFallbackFloat("Weather_Thunderstorm_Thunder_Threshold"); + mThunderSoundDelay = 0.25; + + //Some useful values + /* TODO: Use pre-sunrise_time, pre-sunset_time, + * post-sunrise_time, and post-sunset_time to better + * describe sunrise/sunset time. + * These values are fallbacks attached to weather. + */ + mNightStart = mSunsetTime + mSunsetDuration; + mNightEnd = mSunriseTime - 0.5; + mDayStart = mSunriseTime + mSunriseDuration; + mDayEnd = mSunsetTime; + + //Weather Weather clear; clear.mCloudTexture = "tx_sky_clear.dds"; - clear.mCloudsMaximumPercent = 1.0; - clear.mTransitionDelta = 0.015; - clear.mSkySunriseColor = clr(118, 141, 164); - clear.mSkyDayColor = clr(95, 135, 203); - clear.mSkySunsetColor = clr(56, 89, 129); - clear.mSkyNightColor = clr(9, 10, 11); - clear.mFogSunriseColor = clr(255, 189, 157); - clear.mFogDayColor = clr(206, 227, 255); - clear.mFogSunsetColor = clr(255, 189, 157); - clear.mFogNightColor = clr(9, 10, 11); - clear.mAmbientSunriseColor = clr(47, 66, 96); - clear.mAmbientDayColor = clr(137, 140, 160); - clear.mAmbientSunsetColor = clr(68, 75, 96); - clear.mAmbientNightColor = clr(32, 35, 42); - clear.mSunSunriseColor = clr(242, 159, 99); - clear.mSunDayColor = clr(255, 252, 238); - clear.mSunSunsetColor = clr(255, 115, 79); - clear.mSunNightColor = clr(59, 97, 176); - clear.mSunDiscSunsetColor = clr(255, 189, 157); - clear.mLandFogDayDepth = 0.69; - clear.mLandFogNightDepth = 0.69; - clear.mWindSpeed = 0.1; - clear.mCloudSpeed = 1.25; - clear.mGlareView = 1.0; - mWeatherSettings["clear"] = clear; + setFallbackWeather(clear,"clear"); Weather cloudy; cloudy.mCloudTexture = "tx_sky_cloudy.dds"; - cloudy.mCloudsMaximumPercent = 1.0; - cloudy.mTransitionDelta = 0.015; - cloudy.mSkySunriseColor = clr(126, 158, 173); - cloudy.mSkyDayColor = clr(117, 160, 215); - cloudy.mSkySunsetColor = clr(111, 114, 159); - cloudy.mSkyNightColor = clr(9, 10, 11); - cloudy.mFogSunriseColor = clr(255, 207, 149); - cloudy.mFogDayColor = clr(245, 235, 224); - cloudy.mFogSunsetColor = clr(255, 155, 106); - cloudy.mFogNightColor = clr(9, 10, 11); - cloudy.mAmbientSunriseColor = clr(66, 74, 87); - cloudy.mAmbientDayColor = clr(137, 145, 160); - cloudy.mAmbientSunsetColor = clr(71, 80, 92); - cloudy.mAmbientNightColor = clr(32, 39, 54); - cloudy.mSunSunriseColor = clr(241, 177, 99); - cloudy.mSunDayColor = clr(255, 236, 221); - cloudy.mSunSunsetColor = clr(255, 89, 00); - cloudy.mSunNightColor = clr(77, 91, 124); - cloudy.mSunDiscSunsetColor = clr(255, 202, 179); - cloudy.mLandFogDayDepth = 0.72; - cloudy.mLandFogNightDepth = 0.72; - cloudy.mWindSpeed = 0.2; - cloudy.mCloudSpeed = 2; - cloudy.mGlareView = 1.0; - mWeatherSettings["cloudy"] = cloudy; + setFallbackWeather(cloudy,"cloudy"); Weather foggy; foggy.mCloudTexture = "tx_sky_foggy.dds"; - foggy.mCloudsMaximumPercent = 1.0; - foggy.mTransitionDelta = 0.015; - foggy.mSkySunriseColor = clr(197, 190, 180); - foggy.mSkyDayColor = clr(184, 211, 228); - foggy.mSkySunsetColor = clr(142, 159, 176); - foggy.mSkyNightColor = clr(18, 23, 28); - foggy.mFogSunriseColor = clr(173, 164, 148); - foggy.mFogDayColor = clr(150, 187, 209); - foggy.mFogSunsetColor = clr(113, 135, 157); - foggy.mFogNightColor = clr(19, 24, 29); - foggy.mAmbientSunriseColor = clr(48, 43, 37); - foggy.mAmbientDayColor = clr(92, 109, 120); - foggy.mAmbientSunsetColor = clr(28, 33, 39); - foggy.mAmbientNightColor = clr(28, 33, 39); - foggy.mSunSunriseColor = clr(177, 162, 137); - foggy.mSunDayColor = clr(111, 131, 151); - foggy.mSunSunsetColor = clr(125, 157, 189); - foggy.mSunNightColor = clr(81, 100, 119); - foggy.mSunDiscSunsetColor = clr(223, 223, 223); - foggy.mLandFogDayDepth = 1.0; - foggy.mLandFogNightDepth = 1.9; - foggy.mWindSpeed = 0; - foggy.mCloudSpeed = 1.25; - foggy.mGlareView = 0.25; - mWeatherSettings["foggy"] = foggy; + setFallbackWeather(foggy,"foggy"); Weather thunderstorm; thunderstorm.mCloudTexture = "tx_sky_thunder.dds"; - thunderstorm.mCloudsMaximumPercent = 0.66; - thunderstorm.mTransitionDelta = 0.03; - thunderstorm.mSkySunriseColor = clr(35, 36, 39); - thunderstorm.mSkyDayColor = clr(97, 104, 115); - thunderstorm.mSkySunsetColor = clr(35, 36, 39); - thunderstorm.mSkyNightColor = clr(19, 20, 22); - thunderstorm.mFogSunriseColor = clr(70, 74, 85); - thunderstorm.mFogDayColor = clr(97, 104, 115); - thunderstorm.mFogSunsetColor = clr(70, 74, 85); - thunderstorm.mFogNightColor = clr(19, 20, 22); - thunderstorm.mAmbientSunriseColor = clr(54, 54, 54); - thunderstorm.mAmbientDayColor = clr(90, 90, 90); - thunderstorm.mAmbientSunsetColor = clr(54, 54, 54); - thunderstorm.mAmbientNightColor = clr(49, 51, 54); - thunderstorm.mSunSunriseColor = clr(91, 99, 122); - thunderstorm.mSunDayColor = clr(138, 144, 155); - thunderstorm.mSunSunsetColor = clr(96, 101, 117); - thunderstorm.mSunNightColor = clr(55, 76, 110); - thunderstorm.mSunDiscSunsetColor = clr(128, 128, 128); - thunderstorm.mLandFogDayDepth = 1; - thunderstorm.mLandFogNightDepth = 1.15; - thunderstorm.mWindSpeed = 0.5; - thunderstorm.mCloudSpeed = 3; - thunderstorm.mGlareView = 0; thunderstorm.mRainLoopSoundID = "rain heavy"; - mWeatherSettings["thunderstorm"] = thunderstorm; + setFallbackWeather(thunderstorm,"thunderstorm"); Weather rain; rain.mCloudTexture = "tx_sky_rainy.dds"; - rain.mCloudsMaximumPercent = 0.66; - rain.mTransitionDelta = 0.015; - rain.mSkySunriseColor = clr(71, 74, 75); - rain.mSkyDayColor = clr(116, 120, 122); - rain.mSkySunsetColor = clr(73, 73, 73); - rain.mSkyNightColor = clr(24, 25, 26); - rain.mFogSunriseColor = clr(71, 74, 75); - rain.mFogDayColor = clr(116, 120, 122); - rain.mFogSunsetColor = clr(73, 73, 73); - rain.mFogNightColor = clr(24, 25, 26); - rain.mAmbientSunriseColor = clr(97, 90, 88); - rain.mAmbientDayColor = clr(105, 110, 113); - rain.mAmbientSunsetColor = clr(88, 97, 97); - rain.mAmbientNightColor = clr(50, 55, 67); - rain.mSunSunriseColor = clr(131, 122, 120); - rain.mSunDayColor = clr(149, 157, 170); - rain.mSunSunsetColor = clr(120, 126, 131); - rain.mSunNightColor = clr(50, 62, 101); - rain.mSunDiscSunsetColor = clr(128, 128, 128); - rain.mLandFogDayDepth = 0.8; - rain.mLandFogNightDepth = 0.8; - rain.mWindSpeed = 0.3; - rain.mCloudSpeed = 2; - rain.mGlareView = 0; rain.mRainLoopSoundID = "rain"; - mWeatherSettings["rain"] = rain; + setFallbackWeather(rain,"rain"); Weather overcast; overcast.mCloudTexture = "tx_sky_overcast.dds"; - overcast.mCloudsMaximumPercent = 1.0; - overcast.mTransitionDelta = 0.015; - overcast.mSkySunriseColor = clr(91, 99, 106); - overcast.mSkyDayColor = clr(143, 146, 149); - overcast.mSkySunsetColor = clr(108, 115, 121); - overcast.mSkyNightColor = clr(19, 22, 25); - overcast.mFogSunriseColor = clr(91, 99, 106); - overcast.mFogDayColor = clr(143, 146, 149); - overcast.mFogSunsetColor = clr(108, 115, 121); - overcast.mFogNightColor = clr(19, 22, 25); - overcast.mAmbientSunriseColor = clr(84, 88, 92); - overcast.mAmbientDayColor = clr(93, 96, 105); - overcast.mAmbientSunsetColor = clr(83, 77, 75); - overcast.mAmbientNightColor = clr(57, 60, 66); - overcast.mSunSunriseColor = clr(87, 125, 163); - overcast.mSunDayColor = clr(163, 169, 183); - overcast.mSunSunsetColor = clr(85, 103, 157); - overcast.mSunNightColor = clr(32, 54, 100); - overcast.mSunDiscSunsetColor = clr(128, 128, 128); - overcast.mLandFogDayDepth = 0.7; - overcast.mLandFogNightDepth = 0.7; - overcast.mWindSpeed = 0.2; - overcast.mCloudSpeed = 1.5; - overcast.mGlareView = 0; - mWeatherSettings["overcast"] = overcast; + setFallbackWeather(overcast,"overcast"); Weather ashstorm; ashstorm.mCloudTexture = "tx_sky_ashstorm.dds"; - ashstorm.mCloudsMaximumPercent = 1.0; - ashstorm.mTransitionDelta = 0.035; - ashstorm.mSkySunriseColor = clr(91, 56, 51); - ashstorm.mSkyDayColor = clr(124, 73, 58); - ashstorm.mSkySunsetColor = clr(106, 55, 40); - ashstorm.mSkyNightColor = clr(20, 21, 22); - ashstorm.mFogSunriseColor = clr(91, 56, 51); - ashstorm.mFogDayColor = clr(124, 73, 58); - ashstorm.mFogSunsetColor = clr(106, 55, 40); - ashstorm.mFogNightColor = clr(20, 21, 22); - ashstorm.mAmbientSunriseColor = clr(52, 42, 37); - ashstorm.mAmbientDayColor = clr(75, 49, 41); - ashstorm.mAmbientSunsetColor = clr(48, 39, 35); - ashstorm.mAmbientNightColor = clr(36, 42, 49); - ashstorm.mSunSunriseColor = clr(184, 91, 71); - ashstorm.mSunDayColor = clr(228, 139, 114); - ashstorm.mSunSunsetColor = clr(185, 86, 57); - ashstorm.mSunNightColor = clr(54, 66, 74); - ashstorm.mSunDiscSunsetColor = clr(128, 128, 128); - ashstorm.mLandFogDayDepth = 1.1; - ashstorm.mLandFogNightDepth = 1.2; - ashstorm.mWindSpeed = 0.8; - ashstorm.mCloudSpeed = 7; - ashstorm.mGlareView = 0; ashstorm.mAmbientLoopSoundID = "ashstorm"; - mWeatherSettings["ashstorm"] = ashstorm; + setFallbackWeather(ashstorm,"ashstorm"); Weather blight; blight.mCloudTexture = "tx_sky_blight.dds"; - blight.mCloudsMaximumPercent = 1.0; - blight.mTransitionDelta = 0.04; - blight.mSkySunriseColor = clr(90, 35, 35); - blight.mSkyDayColor = clr(90, 35, 35); - blight.mSkySunsetColor = clr(92, 33, 33); - blight.mSkyNightColor = clr(44, 14, 14); - blight.mFogSunriseColor = clr(90, 35, 35); - blight.mFogDayColor = clr(128, 19, 19); - blight.mFogSunsetColor = clr(92, 33, 33); - blight.mFogNightColor = clr(44, 14, 14); - blight.mAmbientSunriseColor = clr(61, 40, 40); - blight.mAmbientDayColor = clr(79, 54, 54); - blight.mAmbientSunsetColor = clr(61, 40, 40); - blight.mAmbientNightColor = clr(56, 58, 62); - blight.mSunSunriseColor = clr(180, 78, 78); - blight.mSunDayColor = clr(224, 84, 84); - blight.mSunSunsetColor = clr(180, 78, 78); - blight.mSunNightColor = clr(61, 91, 143); - blight.mSunDiscSunsetColor = clr(128, 128, 128); - blight.mLandFogDayDepth = 1.1; - blight.mLandFogNightDepth = 1.2; - blight.mWindSpeed = 0.9; - blight.mCloudSpeed = 9; - blight.mGlareView = 0; blight.mAmbientLoopSoundID = "blight"; - mWeatherSettings["blight"] = blight; + setFallbackWeather(blight,"blight"); - /* Weather snow; snow.mCloudTexture = "tx_bm_sky_snow.dds"; - snow.mCloudsMaximumPercent = 1.0; - snow.mTransitionDelta = 0.014; - snow.mSkySunriseColor = clr(196, 91, 91); - snow.mSkyDayColor = clr(153, 158, 166); - snow.mSkySunsetColor = clr(96, 115, 134); - snow.mSkyNightColor = clr(31, 35, 39); - snow.mFogSunriseColor = clr(106, 91, 91); - snow.mFogDayColor = clr(153, 158, 166); - snow.mFogSunsetColor = clr(96, 115, 134); - snow.mFogNightColor = clr(31, 35, 39); - snow.mAmbientSunriseColor = clr(92, 84, 84); - snow.mAmbientDayColor = clr(93, 96, 105); - snow.mAmbientSunsetColor = clr(70, 79, 87); - snow.mAmbientNightColor = clr(49, 58, 68); - snow.mSunSunriseColor = clr(141, 109, 109); - snow.mSunDayColor = clr(163, 169, 183); - snow.mSunSunsetColor = clr(101, 121, 141); - snow.mSunNightColor = clr(55, 66, 77); - snow.mSunDiscSunsetColor = clr(128, 128, 128); - snow.mLandFogDayDepth = 1.0; - snow.mLandFogNightDepth = 1.2; - snow.mWindSpeed = 0; - snow.mCloudSpeed = 1.5; - snow.mGlareView = 0; - mWeatherSettings["snow"] = snow; + setFallbackWeather(snow, "snow"); Weather blizzard; blizzard.mCloudTexture = "tx_bm_sky_blizzard.dds"; - blizzard.mCloudsMaximumPercent = 1.0; - blizzard.mTransitionDelta = 0.030; - blizzard.mSkySunriseColor = clr(91, 99, 106); - blizzard.mSkyDayColor = clr(121, 133, 145); - blizzard.mSkySunsetColor = clr(108, 115, 121); - blizzard.mSkyNightColor = clr(27, 29, 31); - blizzard.mFogSunriseColor = clr(91, 99, 106); - blizzard.mFogDayColor = clr(121, 133, 145); - blizzard.mFogSunsetColor = clr(108, 115, 121); - blizzard.mFogNightColor = clr(21, 24, 28); - blizzard.mAmbientSunriseColor = clr(84, 88, 92); - blizzard.mAmbientDayColor = clr(93, 96, 105); - blizzard.mAmbientSunsetColor = clr(83, 77, 75); - blizzard.mAmbientNightColor = clr(53, 62, 70); - blizzard.mSunSunriseColor = clr(114, 128, 146); - blizzard.mSunDayColor = clr(163, 169, 183); - blizzard.mSunSunsetColor = clr(106, 114, 136); - blizzard.mSunNightColor = clr(57, 66, 74); - blizzard.mSunDiscSunsetColor = clr(128, 128, 128); - blizzard.mLandFogDayDepth = 2.8; - blizzard.mLandFogNightDepth = 3.0; - blizzard.mWindSpeed = 0.9; - blizzard.mCloudSpeed = 7.5; - blizzard.mGlareView = 0; blizzard.mAmbientLoopSoundID = "BM Blizzard"; - mWeatherSettings["blizzard"] = blizzard; - */ + setFallbackWeather(blizzard,"blizzard"); } void WeatherManager::setWeather(const String& weather, bool instant) @@ -354,12 +187,12 @@ void WeatherManager::setWeather(const String& weather, bool instant) if (mNextWeather != "") { // transition more than 50% finished? - if (mRemainingTransitionTime/(mWeatherSettings[mCurrentWeather].mTransitionDelta*24.f*3600) <= 0.5) + if (mRemainingTransitionTime/(mWeatherSettings[mCurrentWeather].mTransitionDelta * 24.f * 3600) <= 0.5) mCurrentWeather = mNextWeather; } mNextWeather = weather; - mRemainingTransitionTime = mWeatherSettings[mCurrentWeather].mTransitionDelta*24.f*3600; + mRemainingTransitionTime = mWeatherSettings[mCurrentWeather].mTransitionDelta * 24.f * 3600; } mFirstUpdate = false; } @@ -377,13 +210,13 @@ WeatherResult WeatherManager::getResult(const String& weather) result.mGlareView = current.mGlareView; result.mAmbientLoopSoundID = current.mAmbientLoopSoundID; result.mSunColor = current.mSunDiscSunsetColor; - - result.mNight = (mHour < 6 || mHour > 19); + + result.mNight = (mHour < mSunriseTime || mHour > mNightStart - 1); result.mFogDepth = result.mNight ? current.mLandFogNightDepth : current.mLandFogDayDepth; // night - if (mHour <= 5.5f || mHour >= 21) + if (mHour <= mNightEnd || mHour >= mNightStart + 1) { result.mFogColor = current.mFogNightColor; result.mAmbientColor = current.mAmbientNightColor; @@ -393,33 +226,33 @@ WeatherResult WeatherManager::getResult(const String& weather) } // sunrise - else if (mHour >= 5.5f && mHour <= 9) + else if (mHour >= mNightEnd && mHour <= mDayStart + 1) { - if (mHour <= 6) + if (mHour <= mSunriseTime) { // fade in - float advance = 6-mHour; + float advance = mSunriseTime - mHour; float factor = advance / 0.5f; - result.mFogColor = lerp(current.mFogSunriseColor, current.mFogNightColor); - result.mAmbientColor = lerp(current.mAmbientSunriseColor, current.mAmbientNightColor); - result.mSunColor = lerp(current.mSunSunriseColor, current.mSunNightColor); - result.mSkyColor = lerp(current.mSkySunriseColor, current.mSkyNightColor); + result.mFogColor = lerp(current.mFogSunriseColor, current.mFogNightColor, factor); + result.mAmbientColor = lerp(current.mAmbientSunriseColor, current.mAmbientNightColor, factor); + result.mSunColor = lerp(current.mSunSunriseColor, current.mSunNightColor, factor); + result.mSkyColor = lerp(current.mSkySunriseColor, current.mSkyNightColor, factor); result.mNightFade = factor; } else //if (mHour >= 6) { // fade out - float advance = mHour-6; + float advance = mHour - mSunriseTime; float factor = advance / 3.f; - result.mFogColor = lerp(current.mFogSunriseColor, current.mFogDayColor); - result.mAmbientColor = lerp(current.mAmbientSunriseColor, current.mAmbientDayColor); - result.mSunColor = lerp(current.mSunSunriseColor, current.mSunDayColor); - result.mSkyColor = lerp(current.mSkySunriseColor, current.mSkyDayColor); + result.mFogColor = lerp(current.mFogSunriseColor, current.mFogDayColor, factor); + result.mAmbientColor = lerp(current.mAmbientSunriseColor, current.mAmbientDayColor, factor); + result.mSunColor = lerp(current.mSunSunriseColor, current.mSunDayColor, factor); + result.mSkyColor = lerp(current.mSkySunriseColor, current.mSkyDayColor, factor); } } // day - else if (mHour >= 9 && mHour <= 17) + else if (mHour >= mDayStart + 1 && mHour <= mDayEnd - 1) { result.mFogColor = current.mFogDayColor; result.mAmbientColor = current.mAmbientDayColor; @@ -428,27 +261,27 @@ WeatherResult WeatherManager::getResult(const String& weather) } // sunset - else if (mHour >= 17 && mHour <= 21) + else if (mHour >= mDayEnd - 1 && mHour <= mNightStart + 1) { - if (mHour <= 19) + if (mHour <= mDayEnd + 1) { // fade in - float advance = 19-mHour; + float advance = (mDayEnd + 1) - mHour; float factor = (advance / 2); - result.mFogColor = lerp(current.mFogSunsetColor, current.mFogDayColor); - result.mAmbientColor = lerp(current.mAmbientSunsetColor, current.mAmbientDayColor); - result.mSunColor = lerp(current.mSunSunsetColor, current.mSunDayColor); - result.mSkyColor = lerp(current.mSkySunsetColor, current.mSkyDayColor); + result.mFogColor = lerp(current.mFogSunsetColor, current.mFogDayColor, factor); + result.mAmbientColor = lerp(current.mAmbientSunsetColor, current.mAmbientDayColor, factor); + result.mSunColor = lerp(current.mSunSunsetColor, current.mSunDayColor, factor); + result.mSkyColor = lerp(current.mSkySunsetColor, current.mSkyDayColor, factor); } else //if (mHour >= 19) { // fade out - float advance = mHour-19; + float advance = mHour - (mDayEnd + 1); float factor = advance / 2.f; - result.mFogColor = lerp(current.mFogSunsetColor, current.mFogNightColor); - result.mAmbientColor = lerp(current.mAmbientSunsetColor, current.mAmbientNightColor); - result.mSunColor = lerp(current.mSunSunsetColor, current.mSunNightColor); - result.mSkyColor = lerp(current.mSkySunsetColor, current.mSkyNightColor); + result.mFogColor = lerp(current.mFogSunsetColor, current.mFogNightColor, factor); + result.mAmbientColor = lerp(current.mAmbientSunsetColor, current.mAmbientNightColor, factor); + result.mSunColor = lerp(current.mSunSunsetColor, current.mSunNightColor, factor); + result.mSkyColor = lerp(current.mSkySunsetColor, current.mSkyNightColor, factor); result.mNightFade = factor; } } @@ -466,19 +299,19 @@ WeatherResult WeatherManager::transition(float factor) result.mNextCloudTexture = other.mCloudTexture; result.mCloudBlendFactor = factor; - result.mCloudOpacity = lerp(current.mCloudOpacity, other.mCloudOpacity); - result.mFogColor = lerp(current.mFogColor, other.mFogColor); - result.mSunColor = lerp(current.mSunColor, other.mSunColor); - result.mSkyColor = lerp(current.mSkyColor, other.mSkyColor); + result.mCloudOpacity = lerp(current.mCloudOpacity, other.mCloudOpacity, factor); + result.mFogColor = lerp(current.mFogColor, other.mFogColor, factor); + result.mSunColor = lerp(current.mSunColor, other.mSunColor, factor); + result.mSkyColor = lerp(current.mSkyColor, other.mSkyColor, factor); - result.mAmbientColor = lerp(current.mAmbientColor, other.mAmbientColor); - result.mSunDiscColor = lerp(current.mSunDiscColor, other.mSunDiscColor); - result.mFogDepth = lerp(current.mFogDepth, other.mFogDepth); - result.mWindSpeed = lerp(current.mWindSpeed, other.mWindSpeed); - result.mCloudSpeed = lerp(current.mCloudSpeed, other.mCloudSpeed); - result.mCloudOpacity = lerp(current.mCloudOpacity, other.mCloudOpacity); - result.mGlareView = lerp(current.mGlareView, other.mGlareView); - result.mNightFade = lerp(current.mNightFade, other.mNightFade); + result.mAmbientColor = lerp(current.mAmbientColor, other.mAmbientColor, factor); + result.mSunDiscColor = lerp(current.mSunDiscColor, other.mSunDiscColor, factor); + result.mFogDepth = lerp(current.mFogDepth, other.mFogDepth, factor); + result.mWindSpeed = lerp(current.mWindSpeed, other.mWindSpeed, factor); + result.mCloudSpeed = lerp(current.mCloudSpeed, other.mCloudSpeed, factor); + result.mCloudOpacity = lerp(current.mCloudOpacity, other.mCloudOpacity, factor); + result.mGlareView = lerp(current.mGlareView, other.mGlareView, factor); + result.mNightFade = lerp(current.mNightFade, other.mNightFade, factor); result.mNight = current.mNight; @@ -496,13 +329,12 @@ void WeatherManager::update(float duration) if (exterior) { - std::string regionstr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion; - Misc::StringUtils::toLower(regionstr); + std::string regionstr = Misc::StringUtils::lowerCase(MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion); if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) { mCurrentRegion = regionstr; - mWeatherUpdateTime = WeatherGlobals::mWeatherUpdateTime*3600; + mWeatherUpdateTime = mHoursBetweenWeatherChanges * 3600; std::string weather = "clear"; @@ -524,19 +356,19 @@ void WeatherManager::update(float duration) float thunder = region->mData.mThunder/255.f; float ash = region->mData.mAsh/255.f; float blight = region->mData.mBlight/255.f; - //float snow = region->mData.a/255.f; - //float blizzard = region->mData.b/255.f; + float snow = region->mData.mA/255.f; + float blizzard = region->mData.mB/255.f; // re-scale to 100 percent - const float total = clear+cloudy+foggy+overcast+rain+thunder+ash+blight;//+snow+blizzard; + const float total = clear+cloudy+foggy+overcast+rain+thunder+ash+blight+snow+blizzard; float random = ((rand()%100)/100.f) * total; - //if (random >= snow+blight+ash+thunder+rain+overcast+foggy+cloudy+clear) - // weather = "blizzard"; - //else if (random >= blight+ash+thunder+rain+overcast+foggy+cloudy+clear) - // weather = "snow"; - /*else*/ if (random >= ash+thunder+rain+overcast+foggy+cloudy+clear) + if (random >= snow+blight+ash+thunder+rain+overcast+foggy+cloudy+clear) + weather = "blizzard"; + else if (random >= blight+ash+thunder+rain+overcast+foggy+cloudy+clear) + weather = "snow"; + else if (random >= ash+thunder+rain+overcast+foggy+cloudy+clear) weather = "blight"; else if (random >= thunder+rain+overcast+foggy+cloudy+clear) weather = "ashstorm"; @@ -571,14 +403,16 @@ void WeatherManager::update(float duration) } if (mNextWeather != "") - result = transition(1-(mRemainingTransitionTime/(mWeatherSettings[mCurrentWeather].mTransitionDelta*24.f*3600))); + result = transition(1 - (mRemainingTransitionTime / (mWeatherSettings[mCurrentWeather].mTransitionDelta * 24.f * 3600))); else result = getResult(mCurrentWeather); + mWindSpeed = result.mWindSpeed; + mRendering->configureFog(result.mFogDepth, result.mFogColor); // disable sun during night - if (mHour >= 20 || mHour <= 6.f) + if (mHour >= mNightStart || mHour <= mSunriseTime) mRendering->getSkyManager()->sunDisable(); else mRendering->getSkyManager()->sunEnable(); @@ -586,45 +420,55 @@ void WeatherManager::update(float duration) // sun angle float height; + //Day duration + float dayDuration = (mNightStart - 1) - mSunriseTime; + // rise at 6, set at 20 - if (mHour >= 6 && mHour <= 20) - height = 1-std::abs(((mHour-13)/7.f)); - else if (mHour > 20) - height = (mHour-20.f)/4.f; + if (mHour >= mSunriseTime && mHour <= mNightStart) + height = 1 - std::abs(((mHour - dayDuration) / 7.f)); + else if (mHour > mNightStart) + height = (mHour - mNightStart) / 4.f; else //if (mHour > 0 && mHour < 6) - height = 1-(mHour/6.f); + height = 1 - (mHour / mSunriseTime); int facing = (mHour > 13.f) ? 1 : -1; Vector3 final( - -(1-height)*facing, - -(1-height)*facing, + -(1 - height) * facing, + -(1 - height) * facing, height); mRendering->setSunDirection(final); - // moon calculations - float night; - if (mHour >= 14) - night = mHour-14; - else if (mHour <= 10) - night = mHour+10; + /* + * TODO: import separated fadeInStart/Finish, fadeOutStart/Finish + * for masser and secunda + */ + + float fadeOutFinish=mFallback->getFallbackFloat("Moons_Masser_Fade_Out_Finish"); + float fadeInStart=mFallback->getFallbackFloat("Moons_Masser_Fade_In_Start"); + + //moon calculations + float moonHeight; + if (mHour >= fadeInStart) + moonHeight = mHour - fadeInStart; + else if (mHour <= fadeOutFinish) + moonHeight = mHour + fadeOutFinish; else - night = 0; + moonHeight = 0; - night /= 20.f; + moonHeight /= (24.f - (fadeInStart - fadeOutFinish)); - if (night != 0) + if (moonHeight != 0) { - float moonHeight = 1-std::abs((night-0.5)*2); - int facing = (mHour > 0.f && mHour<12.f) ? 1 : -1; + int facing = (moonHeight <= 1) ? 1 : -1; Vector3 masser( - (1-moonHeight)*facing, - (1-moonHeight)*facing, + (moonHeight - 1) * facing, + (1 - moonHeight) * facing, moonHeight); Vector3 secunda( - (1-moonHeight)*facing*0.8, - (1-moonHeight)*facing*1.25, + (moonHeight - 1) * facing * 1.25, + (1 - moonHeight) * facing * 0.8, moonHeight); mRendering->getSkyManager()->setMasserDirection(masser); @@ -632,37 +476,17 @@ void WeatherManager::update(float duration) mRendering->getSkyManager()->masserEnable(); mRendering->getSkyManager()->secundaEnable(); - float hour_fade; - if (mHour >= 7.f && mHour <= 14.f) - hour_fade = 1-(mHour-7)/3.f; - else if (mHour >= 14 && mHour <= 15.f) - hour_fade = mHour-14; - else - hour_fade = 1; - - float secunda_angle_fade; - float masser_angle_fade; - float angle = moonHeight*90.f; + float angle = (1-moonHeight) * 90.f * facing; + float masserHourFade = calculateHourFade("Masser"); + float secundaHourFade = calculateHourFade("Secunda"); + float masserAngleFade = calculateAngleFade("Masser", angle); + float secundaAngleFade = calculateAngleFade("Secunda", angle); - if (angle >= 30 && angle <= 50) - secunda_angle_fade = (angle-30)/20.f; - else if (angle <30) - secunda_angle_fade = 0.f; - else - secunda_angle_fade = 1.f; - - if (angle >= 40 && angle <= 50) - masser_angle_fade = (angle-40)/10.f; - else if (angle <40) - masser_angle_fade = 0.f; - else - masser_angle_fade = 1.f; + masserAngleFade *= masserHourFade; + secundaAngleFade *= secundaHourFade; - masser_angle_fade *= hour_fade; - secunda_angle_fade *= hour_fade; - - mRendering->getSkyManager()->setMasserFade(masser_angle_fade); - mRendering->getSkyManager()->setSecundaFade(secunda_angle_fade); + mRendering->getSkyManager()->setMasserFade(masserAngleFade); + mRendering->getSkyManager()->setSecundaFade(secundaAngleFade); } else { @@ -681,17 +505,17 @@ void WeatherManager::update(float duration) // pick a random sound int sound = rand() % 4; std::string soundname; - if (sound == 0) soundname = WeatherGlobals::mThunderSoundID0; - else if (sound == 1) soundname = WeatherGlobals::mThunderSoundID1; - else if (sound == 2) soundname = WeatherGlobals::mThunderSoundID2; - else if (sound == 3) soundname = WeatherGlobals::mThunderSoundID3; + if (sound == 0) soundname = mThunderSoundID0; + else if (sound == 1) soundname = mThunderSoundID1; + else if (sound == 2) soundname = mThunderSoundID2; + else if (sound == 3) soundname = mThunderSoundID3; MWBase::Environment::get().getSoundManager()->playSound(soundname, 1.0, 1.0); mThunderSoundDelay = 1000; } mThunderFlash -= duration; if (mThunderFlash > 0) - mRendering->getSkyManager()->setLightningStrength( mThunderFlash / WeatherGlobals::mThunderThreshold ); + mRendering->getSkyManager()->setLightningStrength( mThunderFlash / mThunderThreshold ); else { srand(time(NULL)); @@ -706,11 +530,11 @@ void WeatherManager::update(float duration) mThunderChance += duration*4; // chance increases by 4 percent every second if (mThunderChance >= mThunderChanceNeeded) { - mThunderFlash = WeatherGlobals::mThunderThreshold; + mThunderFlash = mThunderThreshold; - mRendering->getSkyManager()->setLightningStrength( mThunderFlash / WeatherGlobals::mThunderThreshold ); + mRendering->getSkyManager()->setLightningStrength( mThunderFlash / mThunderThreshold ); - mThunderSoundDelay = WeatherGlobals::mThunderSoundDelay; + mThunderSoundDelay = 0.25; } } } @@ -718,14 +542,14 @@ void WeatherManager::update(float duration) mRendering->getSkyManager()->setLightningStrength(0.f); mRendering->setAmbientColour(result.mAmbientColor); - mRendering->sunEnable(); + mRendering->sunEnable(false); mRendering->setSunColour(result.mSunColor); mRendering->getSkyManager()->setWeather(result); } else { - mRendering->sunDisable(); + mRendering->sunDisable(false); mRendering->skyDisable(); mRendering->getSkyManager()->setLightningStrength(0.f); } @@ -809,6 +633,9 @@ unsigned int WeatherManager::getWeatherID() const void WeatherManager::changeWeather(const std::string& region, const unsigned int id) { + // make sure this region exists + MWBase::Environment::get().getWorld()->getStore().get().find(region); + std::string weather; if (id==0) weather = "clear"; @@ -833,5 +660,14 @@ void WeatherManager::changeWeather(const std::string& region, const unsigned int else weather = "clear"; - mRegionOverrides[region] = weather; + mRegionOverrides[Misc::StringUtils::lowerCase(region)] = weather; + + std::string playerRegion = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion; + if (Misc::StringUtils::ciEqual(region, playerRegion)) + setWeather(weather); +} + +float WeatherManager::getWindSpeed() const +{ + return mWindSpeed; } diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 589dff3eb..081bd7f87 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -11,105 +11,7 @@ namespace MWRender namespace MWWorld { - /// Global weather manager properties (according to INI) - struct WeatherGlobals - { - /* - [Weather] - EnvReduceColor=255,255,255,255 - LerpCloseColor=037,046,048,255 - BumpFadeColor=230,239,255,255 - AlphaReduce=0.35 - Minimum Time Between Environmental Sounds=1.0 - Maximum Time Between Environmental Sounds=5.0 - Sun Glare Fader Max=0.5 - Sun Glare Fader Angle Max=30.0 - Sun Glare Fader Color=222,095,039 - Timescale Clouds=0 - Precip Gravity=575 - Hours Between Weather Changes=20 - Rain Ripples=1 - Rain Ripple Radius=1024 - Rain Ripples Per Drop=1 - Rain Ripple Scale=0.3 - Rain Ripple Speed=1.0 - Fog Depth Change Speed=3 - Sunrise Time=6 - Sunset Time=18 - Sunrise Duration=2 - Sunset Duration=2 - Sky Pre-Sunrise Time=.5 - Sky Post-Sunrise Time=1 - Sky Pre-Sunset Time=1.5 - Sky Post-Sunset Time=.5 - Ambient Pre-Sunrise Time=.5 - Ambient Post-Sunrise Time=2 - Ambient Pre-Sunset Time=1 - Ambient Post-Sunset Time=1.25 - Fog Pre-Sunrise Time=.5 - Fog Post-Sunrise Time=1 - Fog Pre-Sunset Time=2 - Fog Post-Sunset Time=1 - Sun Pre-Sunrise Time=0 - Sun Post-Sunrise Time=0 - Sun Pre-Sunset Time=1 - Sun Post-Sunset Time=1.25 - Stars Post-Sunset Start=1 - Stars Pre-Sunrise Finish=2 - Stars Fading Duration=2 - Snow Ripples=0 - Snow Ripple Radius=1024 - Snow Ripples Per Flake=1 - Snow Ripple Scale=0.3 - Snow Ripple Speed=1.0 - Snow Gravity Scale=0.1 - Snow High Kill=700 - Snow Low Kill=150 - - - [Moons] - Masser Size=94 - Masser Fade In Start=14 - Masser Fade In Finish=15 - Masser Fade Out Start=7 - Masser Fade Out Finish=10 - Masser Axis Offset=35 - Masser Speed=.5 - Masser Daily Increment=1 - Masser Fade Start Angle=50 - Masser Fade End Angle=40 - Masser Moon Shadow Early Fade Angle=0.5 - Secunda Size=40 - Secunda Fade In Start=14 - Secunda Fade In Finish=15 - Secunda Fade Out Start=7 - Secunda Fade Out Finish=10 - Secunda Axis Offset=50 - Secunda Speed=.6 - Secunda Daily Increment=1.2 - Secunda Fade Start Angle=50 - Secunda Fade End Angle=30 - Secunda Moon Shadow Early Fade Angle=0.5 - Script Color=255,20,20 - */ - - static const float mSunriseTime; - static const float mSunsetTime; - static const float mSunriseDuration; - static const float mSunsetDuration; - - static const float mWeatherUpdateTime; - - // morrowind sets these per-weather, but since they are only used by 'thunderstorm' - // weather setting anyway, we can just as well set them globally - static const float mThunderFrequency; - static const float mThunderThreshold; - static const float mThunderSoundDelay; - static const std::string mThunderSoundID0; - static const std::string mThunderSoundID1; - static const std::string mThunderSoundID2; - static const std::string mThunderSoundID3; - }; + class Fallback; /// Defines the actual weather that results from weather setting (see below), time of day and weather transition struct WeatherResult @@ -212,7 +114,7 @@ namespace MWWorld class WeatherManager { public: - WeatherManager(MWRender::RenderingManager*); + WeatherManager(MWRender::RenderingManager*,MWWorld::Fallback* fallback); /** * Change the weather in the specified region @@ -229,6 +131,8 @@ namespace MWWorld void setHour(const float hour); + float getWindSpeed() const; + void setDate(const int day, const int month); void advanceTime(double hours) @@ -241,7 +145,9 @@ namespace MWWorld private: float mHour; int mDay, mMonth; - + float mWindSpeed; + MWWorld::Fallback* mFallback; + void setFallbackWeather(Weather& weather,const std::string& name); MWRender::RenderingManager* mRendering; std::map mWeatherSettings; @@ -257,21 +163,38 @@ namespace MWWorld bool mFirstUpdate; - float mWeatherUpdateTime; - float mRemainingTransitionTime; float mThunderFlash; float mThunderChance; float mThunderChanceNeeded; - float mThunderSoundDelay; double mTimePassed; // time passed since last update WeatherResult transition(const float factor); WeatherResult getResult(const Ogre::String& weather); + float calculateHourFade (const std::string& moonName) const; + float calculateAngleFade (const std::string& moonName, float angle) const; + void setWeather(const Ogre::String& weather, bool instant=false); + float mSunriseTime; + float mSunsetTime; + float mSunriseDuration; + float mSunsetDuration; + float mWeatherUpdateTime; + float mHoursBetweenWeatherChanges; + float mThunderFrequency; + float mThunderThreshold; + float mThunderSoundDelay; + float mNightStart; + float mNightEnd; + float mDayStart; + float mDayEnd; + std::string mThunderSoundID0; + std::string mThunderSoundID1; + std::string mThunderSoundID2; + std::string mThunderSoundID3; }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f49b4bf9b..16cba2ea8 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1,21 +1,31 @@ #include "worldimp.hpp" +#include + #include #include +#include + +#include #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/scriptmanager.hpp" + +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/movement.hpp" #include "../mwrender/sky.hpp" -#include "../mwrender/player.hpp" #include "../mwclass/door.hpp" #include "player.hpp" #include "manualref.hpp" #include "cellfunctors.hpp" +#include "containerstore.hpp" +#include "inventorystore.hpp" using namespace Ogre; @@ -53,7 +63,7 @@ namespace for (iterator iter (refList.mList.begin()); iter!=refList.mList.end(); ++iter) { - if(iter->mData.getCount() > 0 && iter->mData.getBaseNode()){ + if (iter->mData.getCount() > 0 && iter->mData.getBaseNode()){ if (iter->mData.getHandle()==handle) { return &*iter; @@ -94,7 +104,7 @@ namespace MWWorld return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mLights)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mLockpicks)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mLockpicks)) return Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mMiscItems)) return Ptr (ref, &cell); @@ -147,74 +157,168 @@ namespace MWWorld mRendering->skyDisable(); } - void World::setFallbackValues (const std::map& fallbackMap) + World::World (OEngine::Render::OgreRenderer& renderer, + const Files::Collections& fileCollections, + const std::vector& master, const std::vector& plugins, + 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), + mSky (true), mCells (mStore, mEsm), + mNumFacing(0), mActivationDistanceOverride (mActivationDistanceOverride), + mFallback(fallbackMap), mPlayIntro(0) { - mFallback = fallbackMap; - } + mPhysics = new PhysicsSystem(renderer); + mPhysEngine = mPhysics->getEngine(); - std::string World::getFallback (const std::string& key) const - { - return getFallback(key, ""); - } + mRendering = new MWRender::RenderingManager(renderer, resDir, cacheDir, mPhysEngine,&mFallback); - std::string World::getFallback (const std::string& key, const std::string& def) const - { - std::map::const_iterator it; - if((it = mFallback.find(key)) == mFallback.end()) + mPhysEngine->setSceneManager(renderer.getScene()); + + mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback); + + int idx = 0; + // NOTE: We might need to reserve one more for the running game / save. + mEsm.resize(master.size() + plugins.size()); + for (std::vector::size_type i = 0; i < master.size(); i++, idx++) + { + boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master[i])); + + std::cout << "Loading ESM " << masterPath.string() << "\n"; + + // This parses the ESM file + ESM::ESMReader lEsm; + lEsm.setEncoder(encoder); + lEsm.setIndex(idx); + lEsm.setGlobalReaderList(&mEsm); + lEsm.open (masterPath.string()); + mEsm[idx] = lEsm; + mStore.load (mEsm[idx]); + } + + for (std::vector::size_type i = 0; i < plugins.size(); i++, idx++) { - return def; + boost::filesystem::path pluginPath (fileCollections.getCollection (".esp").getPath (plugins[i])); + + std::cout << "Loading ESP " << pluginPath.string() << "\n"; + + // This parses the ESP file + ESM::ESMReader lEsm; + lEsm.setEncoder(encoder); + lEsm.setIndex(idx); + lEsm.setGlobalReaderList(&mEsm); + lEsm.open (pluginPath.string()); + mEsm[idx] = lEsm; + mStore.load (mEsm[idx]); } - return it->second; + + // insert records that may not be present in all versions of MW + if (mEsm[0].getFormat() == 0) + ensureNeededRecords(); + + mStore.movePlayerRecord(); + mStore.setUp(); + + mGlobalVariables = new Globals (mStore); + + mWorldScene = new Scene(*mRendering, mPhysics); } - World::World (OEngine::Render::OgreRenderer& renderer, - const Files::Collections& fileCollections, - const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, - ToUTF8::Utf8Encoder* encoder, std::map fallbackMap, int mActivationDistanceOverride) - : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), - mSky (true), mCells (mStore, mEsm), - mNumFacing(0), mActivationDistanceOverride (mActivationDistanceOverride) + void World::startNewGame() { - mPhysics = new PhysicsSystem(renderer); - mPhysEngine = mPhysics->getEngine(); + mWorldScene->changeToVoid(); - mRendering = new MWRender::RenderingManager(renderer, resDir, cacheDir, mPhysEngine); + mStore.clearDynamic(); + mStore.setUp(); - mWeatherManager = new MWWorld::WeatherManager(mRendering); + mCells.clear(); - boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master)); + // Rebuild player + setupPlayer(); + const ESM::NPC* playerNpc = mStore.get().find("player"); + MWWorld::Ptr player = mPlayer->getPlayer(); + renderPlayer(); + mRendering->resetCamera(); - std::cout << "Loading ESM " << masterPath.string() << "\n"; + // removes NpcStats, ContainerStore etc + player.getRefData().setCustomData(NULL); - // This parses the ESM file and loads a sample cell - mEsm.setEncoder(encoder); - mEsm.open (masterPath.string()); - mStore.load (mEsm); + // make sure to do this so that local scripts from items that were in the players inventory are removed + mLocalScripts.clear(); - mPlayer = new MWWorld::Player (mStore.get().find ("player"), *this); - mRendering->attachCameraTo(mPlayer->getPlayer()); + MWWorld::Class::get(player).getContainerStore(player).fill(playerNpc->mInventory, "", mStore); + MWWorld::Class::get(player).getInventoryStore(player).autoEquip(player); - mPhysics->addActor(mPlayer->getPlayer()); + MWBase::Environment::get().getWindowManager()->updatePlayer(); + + ESM::Position pos; + const int cellSize = 8192; + pos.pos[0] = cellSize/2; + pos.pos[1] = cellSize/2; + pos.pos[2] = 0; + pos.rot[0] = 0; + pos.rot[1] = 0; + 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; // global variables + delete mGlobalVariables; mGlobalVariables = new Globals (mStore); - if (newGame) - { - // set new game mark - mGlobalVariables->setInt ("chargenstate", 1); - } - + // set new game mark + mGlobalVariables->setInt ("chargenstate", 1); mGlobalVariables->setInt ("pcrace", 3); - mWorldScene = new Scene(*mRendering, mPhysics); - - setFallbackValues(fallbackMap); + // we don't want old weather to persist on a new game + delete mWeatherManager; + mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback); - lastTick = mTimer.getMilliseconds(); + MWBase::Environment::get().getScriptManager()->resetGlobalScripts(); } + void World::ensureNeededRecords() + { + if (!mStore.get().search("sCompanionShare")) + { + ESM::GameSetting sCompanionShare; + sCompanionShare.mId = "sCompanionShare"; + ESM::Variant value; + value.setType(ESM::VT_String); + value.setString("Companion Share"); + sCompanionShare.mValue = value; + mStore.insertStatic(sCompanionShare); + } + if (!mStore.get().search("dayspassed")) + { + // vanilla Morrowind does not define dayspassed. + ESM::Global dayspassed; + dayspassed.mId = "dayspassed"; + ESM::Variant value; + value.setType(ESM::VT_Long); + value.setInteger(1); // but the addons start counting at 1 :( + dayspassed.mValue = value; + mStore.insertStatic(dayspassed); + } + if (!mStore.get().search("fWereWolfRunMult")) + { + ESM::GameSetting fWereWolfRunMult; + fWereWolfRunMult.mId = "fWereWolfRunMult"; + ESM::Variant value; + value.setType(ESM::VT_Float); + value.setFloat(1.f); + fWereWolfRunMult.mValue = value; + mStore.insertStatic(fWereWolfRunMult); + } + } + World::~World() { delete mWeatherManager; @@ -248,6 +352,11 @@ namespace MWWorld return 0; } + const MWWorld::Fallback *World::getFallback() const + { + return &mFallback; + } + Ptr::CellStore *World::getExterior (int x, int y) { return mCells.getExterior (x, y); @@ -268,7 +377,7 @@ namespace MWWorld return mStore; } - ESM::ESMReader& World::getEsmReader() + std::vector& World::getEsmReader() { return mEsm; } @@ -302,45 +411,48 @@ namespace MWWorld { return mGlobalVariables->getGlobals(); } - + std::string World::getCurrentCellName () const { std::string name; Ptr::CellStore *cell = mWorldScene->getCurrentCell(); if (cell->mCell->isExterior()) - { + { if (cell->mCell->mName != "") - { + { name = cell->mCell->mName; - } - else - { + } + 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 (setting && setting->mType == ESM::VT_String) - name = setting->getString(); - else - name = "Wilderness"; + if (setting && setting->mValue.getType()==ESM::VT_String) + name = setting->mValue.getString(); } - } - } - else - { + } + } + else + { name = cell->mCell->mName; - } - + } + return name; } + void World::removeRefScript (MWWorld::RefData *ref) + { + mLocalScripts.remove (ref); + } + Ptr World::getPtr (const std::string& name, bool activeOnly) { // the player is always in an active cell. @@ -396,6 +508,26 @@ namespace MWWorld return MWWorld::Ptr(); } + void World::addContainerScripts(const Ptr& reference, Ptr::CellStore * cell) + { + if( reference.getTypeName()==typeid (ESM::Container).name() || + reference.getTypeName()==typeid (ESM::NPC).name() || + reference.getTypeName()==typeid (ESM::Creature).name()) + { + MWWorld::ContainerStore& container = MWWorld::Class::get(reference).getContainerStore(reference); + for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it) + { + std::string script = MWWorld::Class::get(*it).getScript(*it); + if(script != "") + { + MWWorld::Ptr item = *it; + item.mCell = cell; + mLocalScripts.add (script, item); + } + } + } + } + void World::enable (const Ptr& reference) { if (!reference.getRefData().isEnabled()) @@ -407,6 +539,25 @@ namespace MWWorld } } + void World::removeContainerScripts(const Ptr& reference) + { + if( reference.getTypeName()==typeid (ESM::Container).name() || + reference.getTypeName()==typeid (ESM::NPC).name() || + reference.getTypeName()==typeid (ESM::Creature).name()) + { + MWWorld::ContainerStore& container = MWWorld::Class::get(reference).getContainerStore(reference); + for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it) + { + std::string script = MWWorld::Class::get(*it).getScript(*it); + if(script != "") + { + MWWorld::Ptr item = *it; + mLocalScripts.remove (item); + } + } + } + } + void World::disable (const Ptr& reference) { if (reference.getRefData().isEnabled()) @@ -610,10 +761,11 @@ namespace MWWorld return MWWorld::Ptr (); MWWorld::Ptr object = searchPtrViaHandle (result.second); - float ActivationDistance; - if (object.getTypeName ().find("NPC") != std::string::npos) + if (MWBase::Environment::get().getWindowManager()->isConsoleMode()) + ActivationDistance = getObjectActivationDistance ()*50; + else if (object.getTypeName ().find("NPC") != std::string::npos) ActivationDistance = getNpcActivationDistance (); else ActivationDistance = getObjectActivationDistance (); @@ -635,6 +787,7 @@ namespace MWWorld { mWorldScene->removeObjectFromScene (ptr); mLocalScripts.remove (ptr); + removeContainerScripts (ptr); } } } @@ -642,16 +795,23 @@ namespace MWWorld void World::moveObject(const Ptr &ptr, CellStore &newCell, float x, float y, float z) { ESM::Position &pos = ptr.getRefData().getPosition(); - pos.pos[0] = x, pos.pos[1] = y, pos.pos[2] = z; + + pos.pos[0] = x; + pos.pos[1] = y; + pos.pos[2] = z; + Ogre::Vector3 vec(x, y, z); CellStore *currCell = ptr.getCell(); bool isPlayer = ptr == mPlayer->getPlayer(); - bool haveToMove = mWorldScene->isCellActive(*currCell) || isPlayer; + bool haveToMove = isPlayer || mWorldScene->isCellActive(*currCell); if (*currCell != newCell) { + removeContainerScripts(ptr); + if (isPlayer) + { if (!newCell.isExterior()) changeToInteriorCell(Misc::StringUtils::lowerCase(newCell.mCell->mName), pos); else @@ -660,40 +820,41 @@ namespace MWWorld int cellY = newCell.mCell->getGridY(); mWorldScene->changeCell(cellX, cellY, pos, false); } - else { + } + else + { if (!mWorldScene->isCellActive(*currCell)) copyObjectToCell(ptr, newCell, pos); else if (!mWorldScene->isCellActive(newCell)) { - MWWorld::Class::get(ptr).copyToCell(ptr, newCell); + MWWorld::Class::get(ptr) + .copyToCell(ptr, newCell) + .getRefData() + .setBaseNode(0); + mWorldScene->removeObjectFromScene(ptr); mLocalScripts.remove(ptr); + removeContainerScripts (ptr); haveToMove = false; } else { MWWorld::Ptr copy = - MWWorld::Class::get(ptr).copyToCell(ptr, newCell); + MWWorld::Class::get(ptr).copyToCell(ptr, newCell, pos); - mRendering->moveObjectToCell(copy, vec, currCell); + mRendering->updateObjectCell(ptr, copy); - if (MWWorld::Class::get(ptr).isActor()) - { - MWBase::MechanicsManager *mechMgr = - MWBase::Environment::get().getMechanicsManager(); + MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); + mechMgr->updateCell(ptr, copy); - mechMgr->removeActor(ptr); - mechMgr->addActor(copy); - } - else + std::string script = + MWWorld::Class::get(ptr).getScript(ptr); + if (!script.empty()) { - std::string script = - MWWorld::Class::get(ptr).getScript(ptr); - if (!script.empty()) - { - mLocalScripts.remove(ptr); - mLocalScripts.add(script, copy); - } + mLocalScripts.remove(ptr); + removeContainerScripts (ptr); + mLocalScripts.add(script, copy); + addContainerScripts (copy, &newCell); } } ptr.getRefData().setCount(0); @@ -709,12 +870,14 @@ namespace MWWorld bool World::moveObjectImp(const Ptr& ptr, float x, float y, float z) { CellStore *cell = ptr.getCell(); + if (cell->isExterior()) { int cellX, cellY; positionToIndex(x, y, cellX, cellY); cell = getExterior(cellX, cellY); } + moveObject(ptr, *cell, x, y, z); return cell != ptr.getCell(); @@ -727,8 +890,8 @@ namespace MWWorld void World::scaleObject (const Ptr& ptr, float scale) { - MWWorld::Class::get(ptr).adjustScale(ptr,scale); ptr.getCellRef().mScale = scale; + MWWorld::Class::get(ptr).adjustScale(ptr,scale); if(ptr.getRefData().getBaseNode() == 0) return; @@ -736,23 +899,127 @@ namespace MWWorld mPhysics->scaleObject(ptr); } - void World::rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust) + void World::rotateObjectImp (const Ptr& ptr, Ogre::Vector3 rot, bool adjust) { - Ogre::Vector3 rot; - rot.x = Ogre::Degree(x).valueRadians(); - rot.y = Ogre::Degree(y).valueRadians(); - rot.z = Ogre::Degree(z).valueRadians(); + const float two_pi = Ogre::Math::TWO_PI; + const float pi = Ogre::Math::PI; float *objRot = ptr.getRefData().getPosition().rot; - if(ptr.getRefData().getBaseNode() == 0 || !mRendering->rotateObject(ptr, rot, adjust)) + if(adjust) + { + objRot[0] += rot.x; + objRot[1] += rot.y; + objRot[2] += rot.z; + } + else + { + objRot[0] = rot.x; + objRot[1] = rot.y; + objRot[2] = rot.z; + } + + if(Class::get(ptr).isActor()) + { + /* HACK? Actors shouldn't really be rotating around X (or Y), but + * currently it's done so for rotating the camera, which needs + * clamping. + */ + const float half_pi = Ogre::Math::HALF_PI; + + if(objRot[0] < -half_pi) objRot[0] = -half_pi; + else if(objRot[0] > half_pi) objRot[0] = half_pi; + } + else + { + while(objRot[0] < -pi) objRot[0] += two_pi; + while(objRot[0] > pi) objRot[0] -= two_pi; + } + + while(objRot[1] < -pi) objRot[1] += two_pi; + while(objRot[1] > pi) objRot[1] -= two_pi; + + while(objRot[2] < -pi) objRot[2] += two_pi; + while(objRot[2] > pi) objRot[2] -= two_pi; + + if(ptr.getRefData().getBaseNode() != 0) { - objRot[0] = (adjust ? objRot[0] + rot.x : rot.x), objRot[1] = (adjust ? objRot[1] + rot.y : rot.y), objRot[2] = (adjust ? objRot[2] + rot.z : rot.z); + mRendering->rotateObject(ptr); + mPhysics->rotateObject(ptr); + } + } + + void World::localRotateObject (const Ptr& ptr, float x, float y, float z) + { + if (ptr.getRefData().getBaseNode() != 0) { + + ptr.getRefData().getLocalRotation().rot[0]=Ogre::Degree(x).valueRadians(); + ptr.getRefData().getLocalRotation().rot[1]=Ogre::Degree(y).valueRadians(); + ptr.getRefData().getLocalRotation().rot[2]=Ogre::Degree(z).valueRadians(); + + float fullRotateRad=Ogre::Degree(360).valueRadians(); + + while(ptr.getRefData().getLocalRotation().rot[0]>=fullRotateRad) + ptr.getRefData().getLocalRotation().rot[0]-=fullRotateRad; + while(ptr.getRefData().getLocalRotation().rot[1]>=fullRotateRad) + ptr.getRefData().getLocalRotation().rot[1]-=fullRotateRad; + while(ptr.getRefData().getLocalRotation().rot[2]>=fullRotateRad) + ptr.getRefData().getLocalRotation().rot[2]-=fullRotateRad; + + while(ptr.getRefData().getLocalRotation().rot[0]<=-fullRotateRad) + ptr.getRefData().getLocalRotation().rot[0]+=fullRotateRad; + while(ptr.getRefData().getLocalRotation().rot[1]<=-fullRotateRad) + ptr.getRefData().getLocalRotation().rot[1]+=fullRotateRad; + while(ptr.getRefData().getLocalRotation().rot[2]<=-fullRotateRad) + ptr.getRefData().getLocalRotation().rot[2]+=fullRotateRad; + + float *worldRot = ptr.getRefData().getPosition().rot; + + Ogre::Quaternion worldRotQuat(Ogre::Quaternion(Ogre::Radian(-worldRot[0]), Ogre::Vector3::UNIT_X)* + Ogre::Quaternion(Ogre::Radian(-worldRot[1]), Ogre::Vector3::UNIT_Y)* + Ogre::Quaternion(Ogre::Radian(-worldRot[2]), Ogre::Vector3::UNIT_Z)); + + Ogre::Quaternion rot(Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-x).valueRadians()), Ogre::Vector3::UNIT_X)* + Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-y).valueRadians()), Ogre::Vector3::UNIT_Y)* + Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-z).valueRadians()), Ogre::Vector3::UNIT_Z)); + + ptr.getRefData().getBaseNode()->setOrientation(worldRotQuat*rot); + mPhysics->rotateObject(ptr); + } + } + + void World::adjustPosition(const Ptr &ptr) + { + Ogre::Vector3 pos (ptr.getRefData().getPosition().pos[0], ptr.getRefData().getPosition().pos[1], ptr.getRefData().getPosition().pos[2]); + + if(!ptr.getRefData().getBaseNode()) + { + // will be adjusted when Ptr's cell becomes active return; - } + } + + float terrainHeight = mRendering->getTerrainHeightAt(pos); + + if (pos.z < terrainHeight) + pos.z = terrainHeight; + + ptr.getRefData().getPosition().pos[2] = pos.z + 20; // place slightly above. will snap down to ground with code below + + if (!isFlying(ptr)) + { + Ogre::Vector3 traced = mPhysics->traceDown(ptr); + if (traced.z < pos.z) + pos.z = traced.z; + } - // do this after rendering rotated the object so it gets changed by Class->adjustRotation - objRot[0] = rot.x, objRot[1] = rot.y, objRot[2] = rot.z; - mPhysics->rotateObject(ptr); + moveObject(ptr, *ptr.getCell(), pos.x, pos.y, pos.z); + } + + void World::rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust) + { + rotateObjectImp(ptr, Ogre::Vector3(Ogre::Degree(x).valueRadians(), + Ogre::Degree(y).valueRadians(), + Ogre::Degree(z).valueRadians()), + adjust); } void World::safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos) @@ -789,51 +1056,100 @@ namespace MWWorld --cellY; } - void World::doPhysics (const std::vector >& actors, - float duration) + void World::doPhysics(const PtrMovementList &actors, float duration) { - mPhysics->doPhysics(duration, actors); + /* No duration? Shouldn't be any movement, then. */ + if(duration <= 0.0f) + return; - const int tick = 16; // 16 ms ^= 60 Hz + processDoors(duration); - // Game clock part of the loop, contains everything that has to be executed in a fixed timestep - long long dt = mTimer.getMilliseconds() - lastTick; - if (dt >= 100) + PtrMovementList::const_iterator player(actors.end()); + for(PtrMovementList::const_iterator iter(actors.begin());iter != actors.end();iter++) { - // throw away wall clock time if necessary to keep the framerate above the minimum of 10 fps - lastTick += (dt - 100); - dt = 100; + if(iter->first.getRefData().getHandle() == "player") + { + /* Handle player last, in case a cell transition occurs */ + player = iter; + continue; + } + + rotateObjectImp(iter->first, Ogre::Vector3(iter->second.mRotation), true); + + Ogre::Vector3 vec = mPhysics->move(iter->first, Ogre::Vector3(iter->second.mPosition), duration, + !isSwimming(iter->first) && !isFlying(iter->first)); + moveObjectImp(iter->first, vec.x, vec.y, vec.z); } - while (dt >= tick) + if(player != actors.end()) { - dt -= tick; - lastTick += tick; + rotateObjectImp(player->first, Ogre::Vector3(player->second.mRotation), true); - std::vector< std::pair > vectors = mPhysics->doPhysicsFixed (actors); + Ogre::Vector3 vec = mPhysics->move(player->first, Ogre::Vector3(player->second.mPosition), duration, + !isSwimming(player->first) && !isFlying(player->first)); + moveObjectImp(player->first, vec.x, vec.y, vec.z); + } - std::vector< std::pair >::iterator player = vectors.end(); + mPhysEngine->stepSimulation (duration); + } + + bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2) + { + Ogre::Vector3 a(x1,y1,z1);std::cout << x1 << " " << x2; + Ogre::Vector3 b(x2,y2,z2); + return mPhysics->castRay(a,b,false,true); + } - for (std::vector< std::pair >::iterator it = vectors.begin(); - it!= vectors.end(); ++it) + void World::processDoors(float duration) + { + std::map::iterator it = mDoorStates.begin(); + while (it != mDoorStates.end()) + { + if (!mWorldScene->isCellActive(*it->first.getCell())) + mDoorStates.erase(it++); + else { - if (it->first=="player") + float oldRot = Ogre::Radian(it->first.getRefData().getLocalRotation().rot[2]).valueDegrees(); + float diff = duration * 90; + float targetRot = std::min(std::max(0.f, oldRot + diff * (it->second ? 1 : -1)), 90.f); + localRotateObject(it->first, 0, 0, targetRot); + + // AABB of the door + Ogre::Vector3 min,max; + mPhysics->getObjectAABB(it->first, min, max); + Ogre::Vector3 dimensions = max-min; + + /// \todo should use convexSweepTest here + std::vector collisions = mPhysics->getCollisions(it->first); + for (std::vector::iterator cit = collisions.begin(); cit != collisions.end(); ++cit) { - player = it; - } - else - { - MWWorld::Ptr ptr = getPtrViaHandle (it->first); - moveObjectImp (ptr, it->second.x, it->second.y, it->second.z); + MWWorld::Ptr ptr = getPtrViaHandle(*cit); + if (MWWorld::Class::get(ptr).isActor()) + { + // we collided with an actor, we need to undo the rotation and push the door away from the actor + + // figure out on which side of the door the actor we collided with is + Ogre::Vector3 relativePos = it->first.getRefData().getBaseNode()-> + convertWorldToLocalPosition(ptr.getRefData().getBaseNode()->_getDerivedPosition()); + + float axisToCheck; + if (dimensions.x > dimensions.y) + axisToCheck = relativePos.y * boost::math::sign((min+max).y); + else + axisToCheck = relativePos.x * boost::math::sign((min+max).x); + if (axisToCheck >= 0) + targetRot = std::min(std::max(0.f, oldRot + diff*0.5f), 90.f); + else + targetRot = std::min(std::max(0.f, oldRot - diff*0.5f), 90.f); + + localRotateObject(it->first, 0, 0, targetRot); + break; + } } - } - // Make sure player is moved last (otherwise the cell might change in the middle of an update - // loop) - if (player!=vectors.end()) - { - if (moveObjectImp (getPtrViaHandle (player->first), - player->second.x, player->second.y, player->second.z) == true) - return; // abort the current loop if the cell has changed + if ((targetRot == 90.f && it->second) || targetRot == 0.f) + mDoorStates.erase(it++); + else + ++it; } } } @@ -874,19 +1190,16 @@ namespace MWWorld if (Misc::StringUtils::ciEqual(record.mId, "player")) { - static const char *sRaces[] = - { - "Argonian", "Breton", "Dark Elf", "High Elf", "Imperial", "Khajiit", "Nord", "Orc", "Redguard", - "Woodelf", 0 - }; + std::vector ids; + getStore().get().listIdentifier(ids); - int i=0; + unsigned int i=0; - for (; sRaces[i]; ++i) - if (Misc::StringUtils::ciEqual (sRaces[i], record.mRace)) + for (; isetInt ("pcrace", sRaces[i] ? i+1 : 0); + mGlobalVariables->setInt ("pcrace", (i == ids.size()) ? 0 : i+1); const ESM::NPC *player = mPlayer->getPlayer().get()->mBase; @@ -903,27 +1216,48 @@ namespace MWWorld return ret; } - void World::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, - int number) + const ESM::Armor *World::createRecord (const ESM::Armor& record) { - mRendering->playAnimationGroup (ptr, groupName, mode, number); + return mStore.insert(record); } - void World::skipAnimation (const MWWorld::Ptr& ptr) + const ESM::Weapon *World::createRecord (const ESM::Weapon& record) { - mRendering->skipAnimation (ptr); + return mStore.insert(record); + } + + const ESM::Clothing *World::createRecord (const ESM::Clothing& record) + { + return mStore.insert(record); + } + + const ESM::Enchantment *World::createRecord (const ESM::Enchantment& record) + { + return mStore.insert(record); + } + + const ESM::Book *World::createRecord (const ESM::Book& record) + { + return mStore.insert(record); } void World::update (float duration, bool paused) { + if (mPlayIntro) + { + --mPlayIntro; + if (mPlayIntro == 0) + mRendering->playVideo(mFallback.getFallbackString("Movies_New_Game"), true); + } + + mWeatherManager->update (duration); + mWorldScene->update (duration, paused); float pitch, yaw; Ogre::Vector3 eyepos; - mRendering->getPlayerData(eyepos, pitch, yaw); - mPhysics->updatePlayerData(eyepos, pitch, yaw); - - mWeatherManager->update (duration); + mRendering->getCameraData(eyepos, pitch, yaw); + mPhysics->updateCameraData(eyepos, pitch, yaw); performUpdateSceneQueries (); @@ -941,13 +1275,7 @@ namespace MWWorld if (!object.isEmpty ()) { Ogre::SceneNode* node = object.getRefData().getBaseNode(); - Ogre::AxisAlignedBox bounds; - int i; - for (i=0; inumAttachedObjects(); ++i) - { - Ogre::MovableObject* ob = node->getAttachedObject(i); - bounds.merge(ob->getWorldBoundingBox()); - } + Ogre::AxisAlignedBox bounds = node->_getWorldAABB(); if (bounds.isFinite()) { Vector4 screenCoords = mRendering->boundingBoxToScreen(bounds); @@ -966,46 +1294,13 @@ namespace MWWorld // currently its here because we need to access the physics system float* p = mPlayer->getPlayer().getRefData().getPosition().pos; Vector3 sun = mRendering->getSkyManager()->getRealSunPos(); - sun = Vector3(sun.x, -sun.z, sun.y); mRendering->getSkyManager()->setGlare(!mPhysics->castRay(Ogre::Vector3(p[0], p[1], p[2]), sun)); } - // update faced handle (object the player is looking at) - // this uses a mixture of raycasts and occlusion queries. - else // if (mRendering->occlusionQuerySupported()) - { - MWRender::OcclusionQuery* query = mRendering->getOcclusionQuery(); - if (!query->occlusionTestPending()) - { - processFacedQueryResults (query); - beginFacedQueryProcess (query); - } - } + updateFacedHandle (); } - void World::processFacedQueryResults (MWRender::OcclusionQuery* query) - { - // get result of last query - if (mNumFacing == 0) - { - mFacedHandle = ""; - mFacedDistance = FLT_MAX; - } - else if (mNumFacing == 1) - { - bool result = query->getTestResult(); - mFacedHandle = result ? mFaced1Name : ""; - mFacedDistance = result ? mFaced1Distance : FLT_MAX; - } - else if (mNumFacing == 2) - { - bool result = query->getTestResult(); - mFacedHandle = result ? mFaced2Name : mFaced1Name; - mFacedDistance = result ? mFaced1Distance : mFaced1Distance; - } - } - - void World::beginFacedQueryProcess (MWRender::OcclusionQuery* query) + void World::updateFacedHandle () { // send new query // figure out which object we want to test against @@ -1015,6 +1310,8 @@ namespace MWWorld float x, y; MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); results = mPhysics->getFacedHandles(x, y, getMaxActivationDistance ()); + if (MWBase::Environment::get().getWindowManager()->isConsoleMode()) + results = mPhysics->getFacedHandles(x, y, getMaxActivationDistance ()*50); } else { @@ -1036,93 +1333,14 @@ namespace MWWorld if (results.size() == 0) { - mNumFacing = 0; - } - else if (results.size() == 1) - { - beginSingleFacedQueryProcess (query, results); - } - else - { - beginDoubleFacedQueryProcess (query, results); - } - } - - void World::beginSingleFacedQueryProcess (MWRender::OcclusionQuery* query, std::vector < std::pair < float, std::string > > const & results) - { - mFaced1 = getPtrViaHandle(results.front().second); - mFaced1Name = results.front().second; - mFaced1Distance = results.front().first; - mNumFacing = 1; - - btVector3 p; - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) - { - float x, y; - MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); - p = mPhysics->getRayPoint(results.front().first, x, y); - } - else - p = mPhysics->getRayPoint(results.front().first); - Ogre::Vector3 pos(p.x(), p.z(), -p.y()); - Ogre::SceneNode* node = mFaced1.getRefData().getBaseNode(); - - //std::cout << "Num facing 1 : " << mFaced1Name << std::endl; - //std::cout << "Type 1 " << mFaced1.getTypeName() << std::endl; - - query->occlusionTest(pos, node); - } - - void World::beginDoubleFacedQueryProcess (MWRender::OcclusionQuery* query, std::vector < std::pair < float, std::string > > const & results) - { - mFaced1Name = results.at (0).second; - mFaced2Name = results.at (1).second; - mFaced1Distance = results.at (0).first; - mFaced2Distance = results.at (1).first; - mFaced1 = getPtrViaHandle(results.at (0).second); - mFaced2 = getPtrViaHandle(results.at (1).second); - mNumFacing = 2; - - btVector3 p; - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) - { - float x, y; - MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); - p = mPhysics->getRayPoint(results.at (1).first, x, y); + mFacedHandle = ""; + mFacedDistance = FLT_MAX; } else - p = mPhysics->getRayPoint(results.at (1).first); - Ogre::Vector3 pos(p.x(), p.z(), -p.y()); - Ogre::SceneNode* node1 = mFaced1.getRefData().getBaseNode(); - Ogre::SceneNode* node2 = mFaced2.getRefData().getBaseNode(); - - // no need to test if the first node is not occluder - if (!query->isPotentialOccluder(node1) && (mFaced1.getTypeName().find("Static") == std::string::npos)) - { - mFacedHandle = mFaced1Name; - mFacedDistance = mFaced1Distance; - //std::cout << "node1 Not an occluder" << std::endl; - return; - } - - // no need to test if the second object is static (thus cannot be activated) - if (mFaced2.getTypeName().find("Static") != std::string::npos) - { - mFacedHandle = mFaced1Name; - mFacedDistance = mFaced1Distance; - return; - } - - // work around door problems - if (mFaced1.getTypeName().find("Static") != std::string::npos - && mFaced2.getTypeName().find("Door") != std::string::npos) { - mFacedHandle = mFaced2Name; - mFacedDistance = mFaced2Distance; - return; + mFacedHandle = results.front().second; + mFacedDistance = results.front().first; } - - query->occlusionTest(pos, node2); } bool World::isCellExterior() const @@ -1170,8 +1388,8 @@ namespace MWWorld if (!ref) return Vector2(0, 1); Ogre::SceneNode* node = ref->mData.getBaseNode(); - Vector3 dir = node->_getDerivedOrientation().yAxis(); - Vector2 d = Vector2(dir.x, dir.z); + Vector3 dir = node->_getDerivedOrientation() * Ogre::Vector3(0,1,0); + Vector2 d = Vector2(dir.x, dir.y); return d; } @@ -1180,8 +1398,8 @@ namespace MWWorld std::vector result; MWWorld::CellRefList& doors = cell->mDoors; - std::list< MWWorld::LiveCellRef >& refList = doors.mList; - for (std::list< MWWorld::LiveCellRef >::iterator it = refList.begin(); it != refList.end(); ++it) + CellRefList::List& refList = doors.mList; + for (CellRefList::List::iterator it = refList.begin(); it != refList.end(); ++it) { MWWorld::LiveCellRef& ref = *it; @@ -1221,6 +1439,15 @@ namespace MWWorld mRendering->toggleWater(); } + void World::PCDropped (const Ptr& item) + { + std::string script = MWWorld::Class::get(item).getScript(item); + + // Set OnPCDrop Variable on item's script, if it has a script with that variable declared + if(script != "") + item.mRefData->getLocals().setVarByInt(script, "onpcdrop", 1); + } + bool World::placeObject (const Ptr& object, float cursorX, float cursorY) { std::pair result = mPhysics->castRay(cursorX, cursorY); @@ -1232,7 +1459,7 @@ namespace MWWorld if (isCellExterior()) { int cellX, cellY; - positionToIndex(result.second[0], -result.second[2], cellX, cellY); + positionToIndex(result.second[0], result.second[1], cellX, cellY); cell = mCells.getExterior(cellX, cellY); } else @@ -1240,10 +1467,14 @@ namespace MWWorld ESM::Position pos = getPlayer().getPlayer().getRefData().getPosition(); pos.pos[0] = result.second[0]; - pos.pos[1] = -result.second[2]; - pos.pos[2] = result.second[1]; - - copyObjectToCell(object, *cell, pos); + pos.pos[1] = result.second[1]; + pos.pos[2] = result.second[2]; + // We want only the Z part of the player's rotation + pos.rot[0] = 0; + pos.rot[1] = 0; + + Ptr dropped = copyObjectToCell(object, *cell, pos); + PCDropped(dropped); object.getRefData().setCount(0); return true; @@ -1260,8 +1491,8 @@ namespace MWWorld return true; } - void - World::copyObjectToCell(const Ptr &object, CellStore &cell, const ESM::Position &pos) + + Ptr World::copyObjectToCell(const Ptr &object, CellStore &cell, const ESM::Position &pos) { /// \todo add searching correct cell for position specified MWWorld::Ptr dropped = @@ -1283,7 +1514,10 @@ namespace MWWorld if (!script.empty()) { mLocalScripts.add(script, dropped); } + addContainerScripts(dropped, &cell); } + + return dropped; } void World::dropObjectOnGround (const Ptr& actor, const Ptr& object) @@ -1292,6 +1526,9 @@ namespace MWWorld ESM::Position pos = actor.getRefData().getPosition(); + // We want only the Z part of the actor's rotation + pos.rot[0] = 0; + pos.rot[1] = 0; Ogre::Vector3 orig = Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2]); @@ -1304,7 +1541,9 @@ namespace MWWorld mPhysics->castRay(orig, dir, len); pos.pos[2] = hit.second.z; - copyObjectToCell(object, *cell, pos); + Ptr dropped = copyObjectToCell(object, *cell, pos); + if(actor == mPlayer->getPlayer()) // Only call if dropped by player + PCDropped(dropped); object.getRefData().setCount(0); } @@ -1319,30 +1558,65 @@ namespace MWWorld } bool - World::isSwimming(const MWWorld::Ptr &object) + World::isFlying(const MWWorld::Ptr &ptr) const + { + const MWWorld::Class &cls = MWWorld::Class::get(ptr); + if(cls.isActor() && cls.getCreatureStats(ptr).getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Levitate)).mMagnitude > 0) + return true; + return false; + } + + bool + World::isSwimming(const MWWorld::Ptr &object) const { /// \todo add check ifActor() - only actors can swim float *fpos = object.getRefData().getPosition().pos; Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]); - /// \fixme should rely on object height - pos.z += 30; + /// \fixme 3/4ths submerged? + const OEngine::Physic::PhysicActor *actor = mPhysEngine->getCharacter(object.getRefData().getHandle()); + if(actor) pos.z += actor->getHalfExtents().z * 1.5; - return isUnderwater(*object.getCell()->mCell, pos); + return isUnderwater(object.getCell(), pos); } bool - World::isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) + World::isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const { - if (!(cell.mData.mFlags & ESM::Cell::HasWater)) { + if (!(cell->mCell->mData.mFlags & ESM::Cell::HasWater)) { return false; } - return pos.z < cell.mWater; + return pos.z < cell->mWaterLevel; + } + + bool World::isOnGround(const MWWorld::Ptr &ptr) const + { + RefData &refdata = ptr.getRefData(); + const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); + return physactor && physactor->getOnGround(); + } + + bool World::vanityRotateCamera(float * rot) + { + return mRendering->vanityRotateCamera(rot); + } + + void World::setupPlayer() + { + const ESM::NPC *player = mStore.get().find("player"); + if (!mPlayer) + mPlayer = new MWWorld::Player(player, *this); + else + mPlayer->set(player); + + Ptr ptr = mPlayer->getPlayer(); + mRendering->setupPlayer(ptr); } void World::renderPlayer() { mRendering->renderPlayer(mPlayer->getPlayer()); + mPhysics->addActor(mPlayer->getPlayer()); } void World::setupExternalRendering (MWRender::ExternalRendering& rendering) @@ -1354,24 +1628,21 @@ namespace MWWorld { Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); - Ogre::Vector3 playerPos; - float* pos = mPlayer->getPlayer ().getRefData ().getPosition ().pos; - playerPos.x = pos[0]; - playerPos.y = pos[1]; - playerPos.z = pos[2]; - - std::pair hit = - mPhysics->castRay(playerPos, Ogre::Vector3(0,0,-1), 50); - bool isOnGround = (hit.first ? (hit.second.distance (playerPos) < 25) : false); + RefData &refdata = mPlayer->getPlayer().getRefData(); + Ogre::Vector3 playerPos(refdata.getPosition().pos); - if (!isOnGround || isUnderwater (*currentCell->mCell, playerPos)) + const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); + if((!physactor->getOnGround()&&physactor->getCollisionMode()) || isUnderwater(currentCell, playerPos)) return 2; - - if (currentCell->mCell->mData.mFlags & ESM::Cell::NoSleep) + if((currentCell->mCell->mData.mFlags&ESM::Cell::NoSleep)) return 1; return 0; + } + MWRender::Animation* World::getAnimation(const MWWorld::Ptr &ptr) + { + return mRendering->getAnimation(ptr); } void World::playVideo (const std::string &name, bool allowSkipping) @@ -1383,4 +1654,101 @@ namespace MWWorld { mRendering->stopVideo(); } + + void World::frameStarted (float dt) + { + mRendering->frameStarted(dt); + } + + void World::activateDoor(const MWWorld::Ptr& door) + { + if (mDoorStates.find(door) != mDoorStates.end()) + { + // if currently opening, then close, if closing, then open + mDoorStates[door] = !mDoorStates[door]; + } + else + { + if (door.getRefData().getLocalRotation().rot[2] == 0) + mDoorStates[door] = 1; // open + else + mDoorStates[door] = 0; // close + } + } + + bool World::getOpenOrCloseDoor(const Ptr &door) + { + if (mDoorStates.find(door) != mDoorStates.end()) + return !mDoorStates[door]; // if currently opening or closing, then do the opposite + return door.getRefData().getLocalRotation().rot[2] == 0; + } + + bool World::getPlayerStandingOn (const MWWorld::Ptr& object) + { + MWWorld::Ptr player = mPlayer->getPlayer(); + if (!mPhysEngine->getCharacter("player")->getOnGround()) + return false; + btVector3 from (player.getRefData().getPosition().pos[0], player.getRefData().getPosition().pos[1], player.getRefData().getPosition().pos[2]); + btVector3 to = from - btVector3(0,0,5); + std::pair result = mPhysEngine->rayTest(from, to); + return result.first == object.getRefData().getBaseNode()->getName(); + } + + bool World::getActorStandingOn (const MWWorld::Ptr& object) + { + return mPhysEngine->isAnyActorStandingOn(object.getRefData().getBaseNode()->getName()); + } + + float World::getWindSpeed() + { + if (isCellExterior() || isCellQuasiExterior()) + return mWeatherManager->getWindSpeed(); + else + return 0.f; + } + + void World::getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector& out) + { + std::string refId = npc.getCellRef().mRefID; + + const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells(); + for (Scene::CellStoreCollection::const_iterator cellIt = collection.begin(); cellIt != collection.end(); ++cellIt) + { + MWWorld::CellRefList& containers = (*cellIt)->mContainers; + CellRefList::List& refList = containers.mList; + for (CellRefList::List::iterator container = refList.begin(); container != refList.end(); ++container) + { + MWWorld::Ptr ptr (&*container, *cellIt); + if (Misc::StringUtils::ciEqual(ptr.getCellRef().mOwner, npc.getCellRef().mRefID)) + out.push_back(ptr); + } + } + } + + struct ListHandlesFunctor + { + std::vector mHandles; + + bool operator() (ESM::CellRef& ref, RefData& data) + { + Ogre::SceneNode* handle = data.getBaseNode(); + if (handle) + mHandles.push_back(handle->getName()); + return true; + } + }; + + void World::getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector& out) + { + const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells(); + for (Scene::CellStoreCollection::const_iterator cellIt = collection.begin(); cellIt != collection.end(); ++cellIt) + { + ListHandlesFunctor functor; + (*cellIt)->forEach(functor); + + for (std::vector::iterator it = functor.mHandles.begin(); it != functor.mHandles.end(); ++it) + if (Misc::StringUtils::ciEqual(searchPtrViaHandle(*it).mCellRef->mOwner, npc.getCellRef().mRefID)) + out.push_back(searchPtrViaHandle(*it)); + } + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7e89c1d87..12438efd4 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -10,6 +10,7 @@ #include "cells.hpp" #include "localscripts.hpp" #include "timestamp.hpp" +#include "fallback.hpp" #include "../mwbase/world.hpp" @@ -37,6 +38,7 @@ namespace MWRender { class SkyManager; class CellRender; + class Animation; } namespace MWWorld @@ -48,13 +50,14 @@ namespace MWWorld class World : public MWBase::World { + MWWorld::Fallback mFallback; MWRender::RenderingManager* mRendering; MWWorld::WeatherManager* mWeatherManager; MWWorld::Scene *mWorldScene; MWWorld::Player *mPlayer; - ESM::ESMReader mEsm; + std::vector mEsm; MWWorld::ESMStore mStore; LocalScripts mLocalScripts; MWWorld::Globals *mGlobalVariables; @@ -81,39 +84,51 @@ namespace MWWorld float mFaced1Distance; float mFaced2Distance; int mNumFacing; - std::map mFallback; - unsigned long lastTick; - Ogre::Timer mTimer; + std::map mDoorStates; + ///< only holds doors that are currently moving. 0 means closing, 1 opening int getDaysPerMonth (int month) const; + void rotateObjectImp (const Ptr& ptr, Ogre::Vector3 rot, bool adjust); + bool moveObjectImp (const Ptr& ptr, float x, float y, float z); ///< @return true if the active cell (cell player is in) changed - virtual void - copyObjectToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos); + + Ptr copyObjectToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos); void updateWindowManager (); void performUpdateSceneQueries (); - void processFacedQueryResults (MWRender::OcclusionQuery* query); - void beginFacedQueryProcess (MWRender::OcclusionQuery* query); - void beginSingleFacedQueryProcess (MWRender::OcclusionQuery* query, std::vector < std::pair < float, std::string > > const & results); - void beginDoubleFacedQueryProcess (MWRender::OcclusionQuery* query, std::vector < std::pair < float, std::string > > const & results); + void updateFacedHandle (); float getMaxActivationDistance (); float getNpcActivationDistance (); float getObjectActivationDistance (); + void removeContainerScripts(const Ptr& reference); + void addContainerScripts(const Ptr& reference, Ptr::CellStore* cell); + void PCDropped (const Ptr& item); + + void processDoors(float duration); + ///< Run physics simulation and modify \a world accordingly. + + void ensureNeededRecords(); + + int mPlayIntro; + public: World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, - ToUTF8::Utf8Encoder* encoder, std::map fallbackMap, int mActivationDistanceOverride); + const std::vector& master, const std::vector& plugins, + const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, + ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, int mActivationDistanceOverride); virtual ~World(); + virtual void startNewGame(); + virtual OEngine::Render::Fader* getFader(); ///< \ŧodo remove this function. Rendering details should not be exposed. @@ -129,17 +144,13 @@ namespace MWWorld virtual void getTriangleBatchCount(unsigned int &triangles, unsigned int &batches); - virtual void setFallbackValues (const std::map& fallbackMap); - - virtual std::string getFallback (const std::string& key) const; - - virtual std::string getFallback (const std::string& key, const std::string& def) const; + virtual const Fallback *getFallback() const; virtual Player& getPlayer(); virtual const MWWorld::ESMStore& getStore() const; - virtual ESM::ESMReader& getEsmReader(); + virtual std::vector& getEsmReader(); virtual LocalScripts& getLocalScripts(); @@ -168,11 +179,14 @@ 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 getCurrentCellName () const; + virtual void removeRefScript (MWWorld::RefData *ref); + //< Remove the script attached to ref from mLocalScripts + virtual Ptr getPtr (const std::string& name, bool activeOnly); ///< Return a pointer to a liveCellRef with the given name. /// \param activeOnly do non search inactive cells. @@ -183,6 +197,9 @@ namespace MWWorld virtual Ptr searchPtrViaHandle (const std::string& handle); ///< Return a pointer to a liveCellRef with the given Ogre handle or Ptr() if not found + virtual void adjustPosition (const Ptr& ptr); + ///< Adjust position after load to be on ground. Must be called after model load. + virtual void enable (const Ptr& ptr); virtual void disable (const Ptr& ptr); @@ -246,6 +263,8 @@ namespace MWWorld /// \param adjust indicates rotation should be set or adjusted virtual void rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust = false); + virtual void localRotateObject (const Ptr& ptr, float x, float y, float z); + virtual void safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos); ///< place an object in a "safe" location (ie not in the void, etc). Makes a copy of the Ptr. @@ -256,10 +275,12 @@ namespace MWWorld virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const; ///< Convert position to cell numbers - virtual void doPhysics (const std::vector >& actors, - float duration); + virtual void doPhysics(const PtrMovementList &actors, float duration); ///< Run physics simulation and modify \a world accordingly. + virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2); + ///< cast a Ray and return true if there is an object in the ray path. + virtual bool toggleCollisionMode(); ///< Toggle collision mode for player. If disabled player object should ignore /// collisions and gravity. @@ -270,37 +291,44 @@ namespace MWWorld ///< \return Resulting mode virtual const ESM::Potion *createRecord (const ESM::Potion& record); - ///< Create a new recrod (of type potion) in the ESM store. + ///< Create a new record (of type potion) in the ESM store. /// \return pointer to created record virtual const ESM::Spell *createRecord (const ESM::Spell& record); - ///< Create a new recrod (of type spell) in the ESM store. + ///< Create a new record (of type spell) in the ESM store. /// \return pointer to created record virtual const ESM::Class *createRecord (const ESM::Class& record); - ///< Create a new recrod (of type class) in the ESM store. + ///< Create a new record (of type class) in the ESM store. /// \return pointer to created record virtual const ESM::Cell *createRecord (const ESM::Cell& record); - ///< Create a new recrod (of type cell) in the ESM store. + ///< Create a new record (of type cell) in the ESM store. /// \return pointer to created record virtual const ESM::NPC *createRecord(const ESM::NPC &record); - ///< Create a new recrod (of type npc) in the ESM store. + ///< Create a new record (of type npc) in the ESM store. + /// \return pointer to created record + + virtual const ESM::Armor *createRecord (const ESM::Armor& record); + ///< Create a new record (of type armor) in the ESM store. + /// \return pointer to created record + + virtual const ESM::Weapon *createRecord (const ESM::Weapon& record); + ///< Create a new record (of type weapon) in the ESM store. /// \return pointer to created record + virtual const ESM::Clothing *createRecord (const ESM::Clothing& record); + ///< Create a new record (of type clothing) in the ESM store. + /// \return pointer to created record - virtual void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, - int mode, int number = 1); - ///< Run animation for a MW-reference. Calls to this function for references that are - /// currently not in the rendered scene should be ignored. - /// - /// \param mode: 0 normal, 1 immediate start, 2 immediate loop - /// \param number How offen the animation should be run + virtual const ESM::Enchantment *createRecord (const ESM::Enchantment& record); + ///< Create a new record (of type enchantment) in the ESM store. + /// \return pointer to created record - virtual void skipAnimation (const MWWorld::Ptr& ptr); - ///< Skip the animation for the given MW-reference for one frame. Calls to this function for - /// references that are currently not in the rendered scene should be ignored. + virtual const ESM::Book *createRecord (const ESM::Book& record); + ///< Create a new record (of type book) in the ESM store. + /// \return pointer to created record virtual void update (float duration, bool paused); @@ -318,8 +346,10 @@ namespace MWWorld virtual void processChangedSettings(const Settings::CategorySettingVector& settings); - virtual bool isSwimming(const MWWorld::Ptr &object); - virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos); + virtual bool isFlying(const MWWorld::Ptr &ptr) const; + virtual bool isSwimming(const MWWorld::Ptr &object) const; + virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const; + virtual bool isOnGround(const MWWorld::Ptr &ptr) const; virtual void togglePOV() { mRendering->togglePOV(); @@ -329,8 +359,8 @@ namespace MWWorld mRendering->togglePreviewMode(enable); } - virtual bool toggleVanityMode(bool enable, bool force) { - return mRendering->toggleVanityMode(enable, force); + virtual bool toggleVanityMode(bool enable) { + return mRendering->toggleVanityMode(enable); } virtual void allowVanityMode(bool allow) { @@ -341,8 +371,29 @@ namespace MWWorld mRendering->togglePlayerLooking(enable); } + virtual void changeVanityModeScale(float factor) { + mRendering->changeVanityModeScale(factor); + } + + virtual bool vanityRotateCamera(float * rot); + + virtual void setupPlayer(); virtual void renderPlayer(); + virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door); + ///< if activated, should this door be opened or closed? + virtual void activateDoor(const MWWorld::Ptr& door); + ///< activate (open or close) an non-teleport door + + virtual bool getPlayerStandingOn (const MWWorld::Ptr& object); ///< @return true if the player is standing on \a object + virtual bool getActorStandingOn (const MWWorld::Ptr& object); ///< @return true if any actor is standing on \a object + virtual float getWindSpeed(); + + virtual void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector& out); + ///< get all containers in active cells owned by this Npc + virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector& out); + ///< get all items in active cells owned by this Npc + virtual void setupExternalRendering (MWRender::ExternalRendering& rendering); virtual int canRest(); @@ -352,9 +403,13 @@ namespace MWWorld /// 2 - player is underwater \n /// 3 - enemies are nearby (not implemented) + /// \todo Probably shouldn't be here + virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr); + /// \todo this does not belong here virtual void playVideo(const std::string& name, bool allowSkipping); virtual void stopVideo(); + virtual void frameStarted (float dt); }; } diff --git a/cmake/FindFFmpeg.cmake b/cmake/FindFFmpeg.cmake index c80203a25..a3509597b 100644 --- a/cmake/FindFFmpeg.cmake +++ b/cmake/FindFFmpeg.cmake @@ -68,6 +68,7 @@ macro(find_component _component _pkgconfig _library _header) find_path(${_component}_INCLUDE_DIRS ${_header} HINTS + ${FFMPEGSDK_INC} ${PC_LIB${_component}_INCLUDEDIR} ${PC_LIB${_component}_INCLUDE_DIRS} PATH_SUFFIXES @@ -76,6 +77,7 @@ macro(find_component _component _pkgconfig _library _header) find_library(${_component}_LIBRARIES NAMES ${_library} HINTS + ${FFMPEGSDK_LIB} ${PC_LIB${_component}_LIBDIR} ${PC_LIB${_component}_LIBRARY_DIRS} ) @@ -97,6 +99,12 @@ endmacro() # Check for cached results. If there are skip the costly part. if (NOT FFMPEG_LIBRARIES) + set (FFMPEGSDK $ENV{FFMPEG_HOME}) + if (FFMPEGSDK) + set (FFMPEGSDK_INC "${FFMPEGSDK}/include") + set (FFMPEGSDK_LIB "${FFMPEGSDK}/lib") + endif () + # Check for all possible component. find_component(AVCODEC libavcodec avcodec libavcodec/avcodec.h) find_component(AVFORMAT libavformat avformat libavformat/avformat.h) diff --git a/cmake/OpenMWMacros.cmake b/cmake/OpenMWMacros.cmake index e6f45fdb1..f66dbf2c4 100644 --- a/cmake/OpenMWMacros.cmake +++ b/cmake/OpenMWMacros.cmake @@ -2,7 +2,7 @@ macro (add_openmw_dir dir) set (files) foreach (u ${ARGN}) -file (GLOB ALL ${CMAKE_CURRENT_SOURCE_DIR} "${dir}/${u}.[ch]pp") +file (GLOB ALL "${dir}/${u}.[ch]pp") foreach (f ${ALL}) list (APPEND files "${f}") list (APPEND OPENMW_FILES "${f}") @@ -14,7 +14,7 @@ endmacro (add_openmw_dir) macro (add_component_dir dir) set (files) foreach (u ${ARGN}) -file (GLOB ALL ${CMAKE_CURRENT_SOURCE_DIR} "${dir}/${u}.[ch]pp") +file (GLOB ALL "${dir}/${u}.[ch]pp") foreach (f ${ALL}) list (APPEND files "${f}") list (APPEND COMPONENT_FILES "${f}") @@ -23,9 +23,73 @@ endforeach (u) source_group ("components\\${dir}" FILES ${files}) endmacro (add_component_dir) +macro (add_component_qt_dir dir) +set (files) +foreach (u ${ARGN}) +file (GLOB ALL "${dir}/${u}.[ch]pp") +foreach (f ${ALL}) +list (APPEND files "${f}") +list (APPEND COMPONENT_FILES "${f}") +endforeach (f) +file (GLOB MOC_H "${dir}/${u}.hpp") +foreach (fi ${MOC_H}) +list (APPEND COMPONENT_MOC_FILES "${fi}") +endforeach (fi) +endforeach (u) +source_group ("components\\${dir}" FILES ${files}) +endmacro (add_component_qt_dir) + macro (copy_all_files source_dir destination_dir files) foreach (f ${files}) get_filename_component(filename ${f} NAME) configure_file(${source_dir}/${f} ${destination_dir}/${filename} COPYONLY) endforeach (f) endmacro (copy_all_files) + +macro (add_file project type file) +list (APPEND ${project}${type} ${file}) +endmacro (add_file) + +macro (add_unit project dir unit) +add_file (${project} _HDR ${comp} "${dir}/${unit}.hpp") +add_file (${project} _SRC ${comp} "${dir}/${unit}.cpp") +endmacro (add_unit) + +macro (add_qt_unit project dir unit) +add_file (${project} _HDR ${comp} "${dir}/${unit}.hpp") +add_file (${project} _HDR_QT ${comp} "${dir}/${unit}.hpp") +add_file (${project} _SRC ${comp} "${dir}/${unit}.cpp") +endmacro (add_qt_unit) + +macro (add_hdr project dir unit) +add_file (${project} _HDR ${comp} "${dir}/${unit}.hpp") +endmacro (add_hdr) + +macro (add_qt_hdr project dir unit) +add_file (${project} _HDR ${comp} "${dir}/${unit}.hpp") +add_file (${project} _HDR_QT ${comp} "${dir}/${unit}.hpp") +endmacro (add_qt_hdr) + +macro (opencs_units dir) +foreach (u ${ARGN}) +add_qt_unit (OPENCS ${dir} ${u}) +endforeach (u) +endmacro (opencs_units) + +macro (opencs_units_noqt dir) +foreach (u ${ARGN}) +add_unit (OPENCS ${dir} ${u}) +endforeach (u) +endmacro (opencs_units_noqt) + +macro (opencs_hdrs dir) +foreach (u ${ARGN}) +add_qt_hdr (OPENCS ${dir} ${u}) +endforeach (u) +endmacro (opencs_hdrs) + +macro (opencs_hdrs_noqt dir) +foreach (u ${ARGN}) +add_hdr (OPENCS ${dir} ${u}) +endforeach (u) +endmacro (opencs_hdrs_noqt) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 3da09ecb8..e1e5fbe9b 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -15,15 +15,15 @@ add_component_dir (bsa ) add_component_dir (nif - controlled effect nif_types record controller extra node record_ptr data nif_file property + controlled effect niftypes record controller extra node record_ptr data niffile property ) add_component_dir (nifogre - ogre_nif_loader + ogrenifloader skeleton material mesh ) add_component_dir (nifbullet - bullet_nif_loader + bulletnifloader ) add_component_dir (to_utf8 @@ -37,9 +37,9 @@ add_component_dir (file_finder add_component_dir (esm attr defs esmcommon esmreader esmwriter loadacti loadalch loadappa loadarmo loadbody loadbook loadbsgn loadcell loadclas loadclot loadcont loadcrea loadcrec loaddial loaddoor loadench loadfact loadglob loadgmst - loadinfo loadingr loadland loadlevlist loadligh loadlocks loadltex loadmgef loadmisc loadnpcc + 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 + loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref ) add_component_dir (misc @@ -54,7 +54,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 + stringparser tokenloc nullerrorhandler ) add_component_dir (interpreter @@ -66,9 +66,21 @@ add_component_dir (translation translation ) +find_package(Qt4 COMPONENTS QtCore QtGui) + +if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) + add_component_qt_dir (fileorderlist + model/modelitem model/datafilesmodel model/pluginsproxymodel model/esm/esmfile + utils/profilescombobox utils/comboboxlineedit utils/lineedit utils/naturalsort + ) + + include(${QT_USE_FILE}) + QT4_WRAP_CPP(MOC_SRCS ${COMPONENT_MOC_FILES}) +endif(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) + include_directories(${BULLET_INCLUDE_DIRS}) -add_library(components STATIC ${COMPONENT_FILES}) +add_library(components STATIC ${COMPONENT_FILES} ${MOC_SRCS}) target_link_libraries(components ${Boost_LIBRARIES} ${OGRE_LIBRARIES}) diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index 9913fb8aa..7d3df1590 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -31,150 +31,30 @@ #include "../files/constrainedfiledatastream.hpp" -namespace -{ - using namespace Ogre; -using namespace Bsa; - -struct PathPatternMatcher -{ - PathPatternMatcher (char const * pattern) : pattern (pattern) - { - } - - bool operator () (char const * input) - { - char const * p = pattern; - char const * i = input; - - while (*p && *i) - { - if (*p == '*') - { - ++p; - - while (*i && *i != *p && *i != '/' && *i != '\\') - ++i; - } - else - if (*p == '?') - { - if (*i == '/' || *i == '\\') - break; - - ++i, ++p; - } - if (*p == '/' || *p == '\\') - { - if (*i != '/' && *i != '\\') - break; - ++i, ++p; - } - else - { - if (*i != *p) - break; - - ++i, ++p; - } - } - - return *p == 0 && *i == 0; - } - -private: - char const * pattern; -}; +static bool fsstrict = false; -struct FileNameGatherer +static char strict_normalize_char(char ch) { - StringVectorPtr ptr; - - FileNameGatherer (StringVectorPtr ptr) : ptr (ptr) - { - } - - void operator () (std::string const & filename) const - { - ptr->push_back (filename); - } -}; + return ch == '\\' ? '/' : ch; +} -struct FileInfoGatherer +static char nonstrict_normalize_char(char ch) { - Archive const * archive; - FileInfoListPtr ptr; - - FileInfoGatherer (Archive const * archive, FileInfoListPtr ptr) : - archive (archive), ptr (ptr) - { - } - - void operator () (std::string filename) const - { - FileInfo fi; - - std::size_t pt = filename.rfind ('/'); - if (pt == std::string::npos) - pt = 0; - - fi.archive = const_cast (archive); - fi.path = filename.substr (0, pt); - fi.filename = filename.substr (pt); - fi.compressedSize = fi.uncompressedSize = 0; - - ptr->push_back(fi); - } -}; + return ch == '\\' ? '/' : std::tolower(ch); +} -template -void matchFiles (bool recursive, std::string const & pattern, file_iterator begin, file_iterator end, filename_extractor filenameExtractor, match_handler matchHandler) +template +static std::string normalize_path(T1 begin, T2 end) { - if (recursive && pattern == "*") - { - for (file_iterator i = begin; i != end; ++i) - matchHandler (filenameExtractor (*i)); - } - else - { - PathPatternMatcher matcher (pattern.c_str ()); - - if (recursive) - { - for (file_iterator i = begin; i != end; ++i) - { - char const * filename = filenameExtractor (*i); - char const * matchable_part = filename; - - for (char const * j = matchable_part; *j; ++j) - { - if (*j == '/' || *j == '\\') - matchable_part = j + 1; - } - - if (matcher (matchable_part)) - matchHandler (filename); - } - } - else - { - for (file_iterator i = begin; i != end; ++i) - { - char const * filename = filenameExtractor (*i); - - if (matcher (filename)) - matchHandler (filename); - } - } - } + std::string normalized; + normalized.reserve(std::distance(begin, end)); + char (*normalize_char)(char) = fsstrict ? &strict_normalize_char : &nonstrict_normalize_char; + std::transform(begin, end, std::back_inserter(normalized), normalize_char); + return normalized; } - - -static bool fsstrict = false; - /// An OGRE Archive wrapping a BSAFile archive class DirArchive: public Ogre::Archive { @@ -182,37 +62,12 @@ class DirArchive: public Ogre::Archive index mIndex; - static char strict_normalize_char(char ch) - { - return ch == '\\' ? '/' : ch; - } - - static char nonstrict_normalize_char(char ch) - { - return ch == '\\' ? '/' : std::tolower (ch); - } - - static std::string normalize_path (std::string::const_iterator begin, std::string::const_iterator end) - { - std::string normalized; - normalized.reserve (end-begin); - char (*normalize_char) (char) = fsstrict ? &strict_normalize_char : &nonstrict_normalize_char; - std::transform (begin, end, std::back_inserter (normalized), normalize_char); - return normalized; - } - index::const_iterator lookup_filename (std::string const & filename) const { std::string normalized = normalize_path (filename.begin (), filename.end ()); - return mIndex.find (normalized); } - static char const * extractFilename (index::value_type const & entry) - { - return entry.first.c_str (); - } - public: DirArchive(const String& name) @@ -236,7 +91,7 @@ public: std::string searchable = normalize_path (proper.begin () + prefix, proper.end ()); - mIndex.insert (std::make_pair (std::move (searchable), std::move (proper))); + mIndex.insert (std::make_pair (searchable, proper)); } } @@ -273,15 +128,20 @@ public: StringVectorPtr find(const String& pattern, bool recursive = true, bool dirs = false) { - std::string normalizedPattern = normalize_path (pattern.begin (), pattern.end ()); + std::string normalizedPattern = normalize_path(pattern.begin(), pattern.end()); StringVectorPtr ptr = StringVectorPtr(new StringVector()); - matchFiles (recursive, normalizedPattern, mIndex.begin (), mIndex.end (), extractFilename, FileNameGatherer (ptr)); + for(index::const_iterator iter = mIndex.begin();iter != mIndex.end();iter++) + { + if(Ogre::StringUtil::match(iter->first, normalizedPattern) || + (recursive && Ogre::StringUtil::match(iter->first, "*/"+normalizedPattern))) + ptr->push_back(iter->first); + } return ptr; } bool exists(const String& filename) { - return lookup_filename (filename) != mIndex.end (); + return lookup_filename(filename) != mIndex.end (); } time_t getModifiedTime(const String&) { return 0; } @@ -289,21 +149,44 @@ public: FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true, bool dirs = false) const { + std::string normalizedPattern = normalize_path(pattern.begin(), pattern.end()); FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList()); - FileInfoGatherer gatherer (this, ptr); - std::string normalizedPattern = normalize_path (pattern.begin (), pattern.end ()); + index::const_iterator i = mIndex.find(normalizedPattern); + if(i != mIndex.end()) + { + std::string::size_type pt = i->first.rfind('/'); + if(pt == std::string::npos) + pt = 0; - index::const_iterator i = mIndex.find (normalizedPattern); + FileInfo fi; + fi.archive = const_cast(this); + fi.path = i->first.substr(0, pt); + fi.filename = i->first.substr((i->first[pt]=='/') ? pt+1 : pt); + fi.compressedSize = fi.uncompressedSize = 0; - if (i != mIndex.end ()) - { - gatherer (i->first); + ptr->push_back(fi); } else { + for(index::const_iterator iter = mIndex.begin();iter != mIndex.end();iter++) + { + if(Ogre::StringUtil::match(iter->first, normalizedPattern) || + (recursive && Ogre::StringUtil::match(iter->first, "*/"+normalizedPattern))) + { + std::string::size_type pt = iter->first.rfind('/'); + if(pt == std::string::npos) + pt = 0; + + FileInfo fi; + fi.archive = const_cast(this); + fi.path = iter->first.substr(0, pt); + fi.filename = iter->first.substr((iter->first[pt]=='/') ? pt+1 : pt); + fi.compressedSize = fi.uncompressedSize = 0; - matchFiles (recursive, normalizedPattern, mIndex.begin (), mIndex.end (), extractFilename, gatherer); + ptr->push_back(fi); + } + } } return ptr; @@ -312,9 +195,9 @@ public: class BSAArchive : public Archive { - BSAFile arc; + Bsa::BSAFile arc; - static char const * extractFilename (BSAFile::FileStruct const & entry) + static const char *extractFilename(const Bsa::BSAFile::FileStruct &entry) { return entry.name; } @@ -330,13 +213,13 @@ public: void load() {} void unload() {} - DataStreamPtr open(const String& filename, bool recursive = true) const + DataStreamPtr open(const String& filename, bool readonly = true) const { // Get a non-const reference to arc. This is a hack and it's all // OGRE's fault. You should NOT expect an open() command not to // have any side effects on the archive, and hence this function // should not have been declared const in the first place. - BSAFile *narc = const_cast(&arc); + Bsa::BSAFile *narc = const_cast(&arc); // Open the file return narc->getFile(filename.c_str()); @@ -360,32 +243,51 @@ public: return findFileInfo ("*", recursive, dirs); } - // After load() is called, find("*") is called once. It doesn't seem - // to matter that we return an empty list, exists() gets called on - // the correct files anyway. - StringVectorPtr find(const String& pattern, bool recursive = true, - bool dirs = false) - { + StringVectorPtr find(const String& pattern, bool recursive = true, + bool dirs = false) + { + std::string normalizedPattern = normalize_path(pattern.begin(), pattern.end()); + const Bsa::BSAFile::FileList &filelist = arc.getList(); StringVectorPtr ptr = StringVectorPtr(new StringVector()); - matchFiles (recursive, pattern, arc.getList ().begin (), arc.getList ().end (), extractFilename, FileNameGatherer (ptr)); + for(Bsa::BSAFile::FileList::const_iterator iter = filelist.begin();iter != filelist.end();iter++) + { + std::string ent = normalize_path(iter->name, iter->name+std::strlen(iter->name)); + if(Ogre::StringUtil::match(ent, normalizedPattern) || + (recursive && Ogre::StringUtil::match(ent, "*/"+normalizedPattern))) + ptr->push_back(iter->name); + } return ptr; - } + } + + FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true, + bool dirs = false) const + { + std::string normalizedPattern = normalize_path(pattern.begin(), pattern.end()); + FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList()); + const Bsa::BSAFile::FileList &filelist = arc.getList(); - /* Gets called once for each of the ogre formats, *.program, - *.material etc. We ignore all these. + for(Bsa::BSAFile::FileList::const_iterator iter = filelist.begin();iter != filelist.end();iter++) + { + std::string ent = normalize_path(iter->name, iter->name+std::strlen(iter->name)); + if(Ogre::StringUtil::match(ent, normalizedPattern) || + (recursive && Ogre::StringUtil::match(ent, "*/"+normalizedPattern))) + { + std::string::size_type pt = ent.rfind('/'); + if(pt == std::string::npos) + pt = 0; - However, it's also called by MyGUI to find individual textures, - and we can't ignore these since many of the GUI textures are - located in BSAs. So instead we channel it through exists() and - set up a single-element result list if the file is found. - */ - FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true, - bool dirs = false) const - { - FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList()); - matchFiles (recursive, pattern, arc.getList ().begin (), arc.getList ().end (), extractFilename, FileInfoGatherer (this, ptr)); - return ptr; - } + FileInfo fi; + fi.archive = const_cast(this); + fi.path = std::string(iter->name, pt); + fi.filename = std::string(iter->name + ((ent[pt]=='/') ? pt+1 : pt)); + fi.compressedSize = fi.uncompressedSize = iter->fileSize; + + ptr->push_back(fi); + } + } + + return ptr; + } }; // An archive factory for BSA archives @@ -403,24 +305,34 @@ public: return new BSAArchive(name); } + virtual Archive* createInstance(const String& name, bool readOnly) + { + return new BSAArchive(name); + } + void destroyInstance( Archive* arch) { delete arch; } }; -class DirArchiveFactory : public FileSystemArchiveFactory +class DirArchiveFactory : public ArchiveFactory { public: - const String& getType() const - { - static String name = "Dir"; - return name; - } + const String& getType() const + { + static String name = "Dir"; + return name; + } - Archive *createInstance( const String& name ) - { - return new DirArchive(name); - } + Archive *createInstance( const String& name ) + { + return new DirArchive(name); + } - void destroyInstance( Archive* arch) { delete arch; } + virtual Archive* createInstance(const String& name, bool readOnly) + { + return new DirArchive(name); + } + + void destroyInstance( Archive* arch) { delete arch; } }; @@ -445,7 +357,6 @@ static void insertDirFactory() } } -} namespace Bsa { diff --git a/components/bsa/bsa_file.cpp b/components/bsa/bsa_file.cpp index 5e529e18e..8db4fa888 100644 --- a/components/bsa/bsa_file.cpp +++ b/components/bsa/bsa_file.cpp @@ -23,9 +23,7 @@ #include "bsa_file.hpp" -//#include -//#include -//#include +#include #include "../files/constrainedfiledatastream.hpp" diff --git a/components/bsa/tests/Makefile b/components/bsa/tests/Makefile index bc2bf4e50..73e20d7b3 100644 --- a/components/bsa/tests/Makefile +++ b/components/bsa/tests/Makefile @@ -1,6 +1,6 @@ GCC=g++ -all: bsa_file_test bsatool ogre_archive_test +all: bsa_file_test ogre_archive_test I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) @@ -11,12 +11,5 @@ bsa_file_test: bsa_file_test.cpp ../bsa_file.cpp ogre_archive_test: ogre_archive_test.cpp ../bsa_file.cpp ../bsa_archive.cpp $(GCC) $^ -o $@ $(I_OGRE) $(L_OGRE) -bsatool: bsatool.cpp ../bsa_file.cpp bsatool_cmd.c - $(GCC) $^ -o $@ - -bsatool_cmd.c: bsatool.ggo - gengetopt < bsatool.ggo - clean: rm *_test - rm bsatool diff --git a/components/bsa/tests/bsatool.cpp b/components/bsa/tests/bsatool.cpp deleted file mode 100644 index df37e3827..000000000 --- a/components/bsa/tests/bsatool.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "../bsa_file.hpp" - -#include "bsatool_cmd.h" - -#include -#include -#include -#include - -#include "../../mangle/stream/filters/buffer_stream.hpp" - -using namespace std; -using namespace Mangle::Stream; -using namespace Bsa; - -int main(int argc, char** argv) -{ - gengetopt_args_info info; - - if(cmdline_parser(argc, argv, &info) != 0) - return 1; - - if(info.inputs_num != 1) - { - if(info.inputs_num == 0) - cout << "ERROR: missing BSA file\n\n"; - else - cout << "ERROR: more than one BSA file specified\n\n"; - cmdline_parser_print_help(); - return 1; - } - - // Open file - BSAFile bsa; - char *arcname = info.inputs[0]; - try { bsa.open(arcname); } - catch(exception &e) - { - cout << "ERROR reading BSA archive '" << arcname - << "'\nDetails:\n" << e.what() << endl; - return 2; - } - - if(info.extract_given) - { - char *file = info.extract_arg; - - if(!bsa.exists(file)) - { - cout << "ERROR: file '" << file << "' not found\n"; - cout << "In archive: " << arcname << endl; - return 3; - } - - // Find the base name of the file - int pos = strlen(file); - while(pos > 0 && file[pos] != '\\') pos--; - char *base = file+pos+1; - - // TODO: We might add full directory name extraction later. We - // could also allow automatic conversion from / to \ in - // parameter file names. - - // Load the file into a memory buffer - BufferStream data(bsa.getFile(file)); - - // Write the file to disk - ofstream out(base, ios::binary); - out.write((char*)data.getPtr(), data.size()); - out.close(); - - return 0; - } - - // List all files - const BSAFile::FileList &files = bsa.getList(); - for(int i=0; i" -#description "" -args "--unamed-opts=BSA-FILE -F bsatool_cmd -G" - -option "extract" x "Extract file from archive" string optional -option "long" l "Include extra information in archive listing" optional - -text "\nIf no option is given, the default action is to list all files in the archive." diff --git a/components/bsa/tests/bsatool_cmd.c b/components/bsa/tests/bsatool_cmd.c deleted file mode 100644 index caa8cd720..000000000 --- a/components/bsa/tests/bsatool_cmd.c +++ /dev/null @@ -1,1146 +0,0 @@ -/* - File autogenerated by gengetopt version 2.22.2 - generated with the following command: - gengetopt --unamed-opts=BSA-FILE -F bsatool_cmd -G - - The developers of gengetopt consider the fixed text that goes in all - gengetopt output files to be in the public domain: - we make no copyright claims on it. -*/ - -/* If we use autoconf. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include - -#ifndef FIX_UNUSED -#define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */ -#endif - - -#include "bsatool_cmd.h" - -const char *gengetopt_args_info_purpose = "Inspect and extract files from Bethesda BSA archives"; - -const char *gengetopt_args_info_usage = "Usage: bsatool [OPTIONS]... [BSA-FILE]..."; - -const char *gengetopt_args_info_description = ""; - -const char *gengetopt_args_info_help[] = { - " -h, --help Print help and exit", - " -V, --version Print version and exit", - " -x, --extract=STRING Extract file from archive", - " -l, --long Include extra information in archive listing", - "\nIf no option is given, the default action is to list all files in the archive.", - 0 -}; - -typedef enum {ARG_NO - , ARG_STRING -} cmdline_parser_arg_type; - -static -void clear_given (struct gengetopt_args_info *args_info); -static -void clear_args (struct gengetopt_args_info *args_info); - -static int -cmdline_parser_internal (int argc, char * const *argv, struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params, const char *additional_error); - - -static char * -gengetopt_strdup (const char *s); - -static -void clear_given (struct gengetopt_args_info *args_info) -{ - args_info->help_given = 0 ; - args_info->version_given = 0 ; - args_info->extract_given = 0 ; - args_info->long_given = 0 ; -} - -static -void clear_args (struct gengetopt_args_info *args_info) -{ - FIX_UNUSED (args_info); - args_info->extract_arg = NULL; - args_info->extract_orig = NULL; - -} - -static -void init_args_info(struct gengetopt_args_info *args_info) -{ - - - args_info->help_help = gengetopt_args_info_help[0] ; - args_info->version_help = gengetopt_args_info_help[1] ; - args_info->extract_help = gengetopt_args_info_help[2] ; - args_info->long_help = gengetopt_args_info_help[3] ; - -} - -void -cmdline_parser_print_version (void) -{ - printf ("%s %s\n", - (strlen(CMDLINE_PARSER_PACKAGE_NAME) ? CMDLINE_PARSER_PACKAGE_NAME : CMDLINE_PARSER_PACKAGE), - CMDLINE_PARSER_VERSION); -} - -static void print_help_common(void) { - cmdline_parser_print_version (); - - if (strlen(gengetopt_args_info_purpose) > 0) - printf("\n%s\n", gengetopt_args_info_purpose); - - if (strlen(gengetopt_args_info_usage) > 0) - printf("\n%s\n", gengetopt_args_info_usage); - - printf("\n"); - - if (strlen(gengetopt_args_info_description) > 0) - printf("%s\n\n", gengetopt_args_info_description); -} - -void -cmdline_parser_print_help (void) -{ - int i = 0; - print_help_common(); - while (gengetopt_args_info_help[i]) - printf("%s\n", gengetopt_args_info_help[i++]); -} - -void -cmdline_parser_init (struct gengetopt_args_info *args_info) -{ - clear_given (args_info); - clear_args (args_info); - init_args_info (args_info); - - args_info->inputs = 0; - args_info->inputs_num = 0; -} - -void -cmdline_parser_params_init(struct cmdline_parser_params *params) -{ - if (params) - { - params->override = 0; - params->initialize = 1; - params->check_required = 1; - params->check_ambiguity = 0; - params->print_errors = 1; - } -} - -struct cmdline_parser_params * -cmdline_parser_params_create(void) -{ - struct cmdline_parser_params *params = - (struct cmdline_parser_params *)malloc(sizeof(struct cmdline_parser_params)); - cmdline_parser_params_init(params); - return params; -} - -static void -free_string_field (char **s) -{ - if (*s) - { - free (*s); - *s = 0; - } -} - - -static void -cmdline_parser_release (struct gengetopt_args_info *args_info) -{ - unsigned int i; - free_string_field (&(args_info->extract_arg)); - free_string_field (&(args_info->extract_orig)); - - - for (i = 0; i < args_info->inputs_num; ++i) - free (args_info->inputs [i]); - - if (args_info->inputs_num) - free (args_info->inputs); - - clear_given (args_info); -} - - -static void -write_into_file(FILE *outfile, const char *opt, const char *arg, const char *values[]) -{ - FIX_UNUSED (values); - if (arg) { - fprintf(outfile, "%s=\"%s\"\n", opt, arg); - } else { - fprintf(outfile, "%s\n", opt); - } -} - - -int -cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) -{ - int i = 0; - - if (!outfile) - { - fprintf (stderr, "%s: cannot dump options to stream\n", CMDLINE_PARSER_PACKAGE); - return EXIT_FAILURE; - } - - if (args_info->help_given) - write_into_file(outfile, "help", 0, 0 ); - if (args_info->version_given) - write_into_file(outfile, "version", 0, 0 ); - if (args_info->extract_given) - write_into_file(outfile, "extract", args_info->extract_orig, 0); - if (args_info->long_given) - write_into_file(outfile, "long", 0, 0 ); - - - i = EXIT_SUCCESS; - return i; -} - -int -cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info) -{ - FILE *outfile; - int i = 0; - - outfile = fopen(filename, "w"); - - if (!outfile) - { - fprintf (stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename); - return EXIT_FAILURE; - } - - i = cmdline_parser_dump(outfile, args_info); - fclose (outfile); - - return i; -} - -void -cmdline_parser_free (struct gengetopt_args_info *args_info) -{ - cmdline_parser_release (args_info); -} - -/** @brief replacement of strdup, which is not standard */ -char * -gengetopt_strdup (const char *s) -{ - char *result = 0; - if (!s) - return result; - - result = (char*)malloc(strlen(s) + 1); - if (result == (char*)0) - return (char*)0; - strcpy(result, s); - return result; -} - -int -cmdline_parser (int argc, char * const *argv, struct gengetopt_args_info *args_info) -{ - return cmdline_parser2 (argc, argv, args_info, 0, 1, 1); -} - -int -cmdline_parser_ext (int argc, char * const *argv, struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params) -{ - int result; - result = cmdline_parser_internal (argc, argv, args_info, params, 0); - - if (result == EXIT_FAILURE) - { - cmdline_parser_free (args_info); - exit (EXIT_FAILURE); - } - - return result; -} - -int -cmdline_parser2 (int argc, char * const *argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required) -{ - int result; - struct cmdline_parser_params params; - - params.override = override; - params.initialize = initialize; - params.check_required = check_required; - params.check_ambiguity = 0; - params.print_errors = 1; - - result = cmdline_parser_internal (argc, argv, args_info, ¶ms, 0); - - if (result == EXIT_FAILURE) - { - cmdline_parser_free (args_info); - exit (EXIT_FAILURE); - } - - return result; -} - -int -cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name) -{ - FIX_UNUSED (args_info); - FIX_UNUSED (prog_name); - return EXIT_SUCCESS; -} - -/* - * Extracted from the glibc source tree, version 2.3.6 - * - * Licensed under the GPL as per the whole glibc source tree. - * - * This file was modified so that getopt_long can be called - * many times without risking previous memory to be spoiled. - * - * Modified by Andre Noll and Lorenzo Bettini for use in - * GNU gengetopt generated files. - * - */ - -/* - * we must include anything we need since this file is not thought to be - * inserted in a file already using getopt.h - * - * Lorenzo - */ - -struct option -{ - const char *name; - /* has_arg can't be an enum because some compilers complain about - type mismatches in all the code that assumes it is an int. */ - int has_arg; - int *flag; - int val; -}; - -/* This version of `getopt' appears to the caller like standard Unix `getopt' - but it behaves differently for the user, since it allows the user - to intersperse the options with the other arguments. - - As `getopt' works, it permutes the elements of ARGV so that, - when it is done, all the options precede everything else. Thus - all application programs are extended to handle flexible argument order. -*/ -/* - If the field `flag' is not NULL, it points to a variable that is set - to the value given in the field `val' when the option is found, but - left unchanged if the option is not found. - - To have a long-named option do something other than set an `int' to - a compiled-in constant, such as set a value from `custom_optarg', set the - option's `flag' field to zero and its `val' field to a nonzero - value (the equivalent single-letter option character, if there is - one). For long options that have a zero `flag' field, `getopt' - returns the contents of the `val' field. */ - -/* Names for the values of the `has_arg' field of `struct option'. */ -#ifndef no_argument -#define no_argument 0 -#endif - -#ifndef required_argument -#define required_argument 1 -#endif - -#ifndef optional_argument -#define optional_argument 2 -#endif - -struct custom_getopt_data { - /* - * These have exactly the same meaning as the corresponding global variables, - * except that they are used for the reentrant versions of getopt. - */ - int custom_optind; - int custom_opterr; - int custom_optopt; - char *custom_optarg; - - /* True if the internal members have been initialized. */ - int initialized; - - /* - * The next char to be scanned in the option-element in which the last option - * character we returned was found. This allows us to pick up the scan where - * we left off. If this is zero, or a null string, it means resume the scan by - * advancing to the next ARGV-element. - */ - char *nextchar; - - /* - * Describe the part of ARGV that contains non-options that have been skipped. - * `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is - * the index after the last of them. - */ - int first_nonopt; - int last_nonopt; -}; - -/* - * the variables optarg, optind, opterr and optopt are renamed with - * the custom_ prefix so that they don't interfere with getopt ones. - * - * Moreover they're static so they are visible only from within the - * file where this very file will be included. - */ - -/* - * For communication from `custom_getopt' to the caller. When `custom_getopt' finds an - * option that takes an argument, the argument value is returned here. - */ -static char *custom_optarg; - -/* - * Index in ARGV of the next element to be scanned. This is used for - * communication to and from the caller and for communication between - * successive calls to `custom_getopt'. - * - * On entry to `custom_getopt', 1 means this is the first call; initialize. - * - * When `custom_getopt' returns -1, this is the index of the first of the non-option - * elements that the caller should itself scan. - * - * Otherwise, `custom_optind' communicates from one call to the next how much of ARGV - * has been scanned so far. - * - * 1003.2 says this must be 1 before any call. - */ -static int custom_optind = 1; - -/* - * Callers store zero here to inhibit the error message for unrecognized - * options. - */ -static int custom_opterr = 1; - -/* - * Set to an option character which was unrecognized. This must be initialized - * on some systems to avoid linking in the system's own getopt implementation. - */ -static int custom_optopt = '?'; - -/* - * Exchange two adjacent subsequences of ARGV. One subsequence is elements - * [first_nonopt,last_nonopt) which contains all the non-options that have been - * skipped so far. The other is elements [last_nonopt,custom_optind), which contains - * all the options processed since those non-options were skipped. - * `first_nonopt' and `last_nonopt' are relocated so that they describe the new - * indices of the non-options in ARGV after they are moved. - */ -static void exchange(char **argv, struct custom_getopt_data *d) -{ - int bottom = d->first_nonopt; - int middle = d->last_nonopt; - int top = d->custom_optind; - char *tem; - - /* - * Exchange the shorter segment with the far end of the longer segment. - * That puts the shorter segment into the right place. It leaves the - * longer segment in the right place overall, but it consists of two - * parts that need to be swapped next. - */ - while (top > middle && middle > bottom) { - if (top - middle > middle - bottom) { - /* Bottom segment is the short one. */ - int len = middle - bottom; - int i; - - /* Swap it with the top part of the top segment. */ - for (i = 0; i < len; i++) { - tem = argv[bottom + i]; - argv[bottom + i] = - argv[top - (middle - bottom) + i]; - argv[top - (middle - bottom) + i] = tem; - } - /* Exclude the moved bottom segment from further swapping. */ - top -= len; - } else { - /* Top segment is the short one. */ - int len = top - middle; - int i; - - /* Swap it with the bottom part of the bottom segment. */ - for (i = 0; i < len; i++) { - tem = argv[bottom + i]; - argv[bottom + i] = argv[middle + i]; - argv[middle + i] = tem; - } - /* Exclude the moved top segment from further swapping. */ - bottom += len; - } - } - /* Update records for the slots the non-options now occupy. */ - d->first_nonopt += (d->custom_optind - d->last_nonopt); - d->last_nonopt = d->custom_optind; -} - -/* Initialize the internal data when the first call is made. */ -static void custom_getopt_initialize(struct custom_getopt_data *d) -{ - /* - * Start processing options with ARGV-element 1 (since ARGV-element 0 - * is the program name); the sequence of previously skipped non-option - * ARGV-elements is empty. - */ - d->first_nonopt = d->last_nonopt = d->custom_optind; - d->nextchar = NULL; - d->initialized = 1; -} - -#define NONOPTION_P (argv[d->custom_optind][0] != '-' || argv[d->custom_optind][1] == '\0') - -/* return: zero: continue, nonzero: return given value to user */ -static int shuffle_argv(int argc, char *const *argv,const struct option *longopts, - struct custom_getopt_data *d) -{ - /* - * Give FIRST_NONOPT & LAST_NONOPT rational values if CUSTOM_OPTIND has been - * moved back by the user (who may also have changed the arguments). - */ - if (d->last_nonopt > d->custom_optind) - d->last_nonopt = d->custom_optind; - if (d->first_nonopt > d->custom_optind) - d->first_nonopt = d->custom_optind; - /* - * If we have just processed some options following some - * non-options, exchange them so that the options come first. - */ - if (d->first_nonopt != d->last_nonopt && - d->last_nonopt != d->custom_optind) - exchange((char **) argv, d); - else if (d->last_nonopt != d->custom_optind) - d->first_nonopt = d->custom_optind; - /* - * Skip any additional non-options and extend the range of - * non-options previously skipped. - */ - while (d->custom_optind < argc && NONOPTION_P) - d->custom_optind++; - d->last_nonopt = d->custom_optind; - /* - * The special ARGV-element `--' means premature end of options. Skip - * it like a null option, then exchange with previous non-options as if - * it were an option, then skip everything else like a non-option. - */ - if (d->custom_optind != argc && !strcmp(argv[d->custom_optind], "--")) { - d->custom_optind++; - if (d->first_nonopt != d->last_nonopt - && d->last_nonopt != d->custom_optind) - exchange((char **) argv, d); - else if (d->first_nonopt == d->last_nonopt) - d->first_nonopt = d->custom_optind; - d->last_nonopt = argc; - d->custom_optind = argc; - } - /* - * If we have done all the ARGV-elements, stop the scan and back over - * any non-options that we skipped and permuted. - */ - if (d->custom_optind == argc) { - /* - * Set the next-arg-index to point at the non-options that we - * previously skipped, so the caller will digest them. - */ - if (d->first_nonopt != d->last_nonopt) - d->custom_optind = d->first_nonopt; - return -1; - } - /* - * If we have come to a non-option and did not permute it, either stop - * the scan or describe it to the caller and pass it by. - */ - if (NONOPTION_P) { - d->custom_optarg = argv[d->custom_optind++]; - return 1; - } - /* - * We have found another option-ARGV-element. Skip the initial - * punctuation. - */ - d->nextchar = (argv[d->custom_optind] + 1 + (longopts != NULL && argv[d->custom_optind][1] == '-')); - return 0; -} - -/* - * Check whether the ARGV-element is a long option. - * - * If there's a long option "fubar" and the ARGV-element is "-fu", consider - * that an abbreviation of the long option, just like "--fu", and not "-f" with - * arg "u". - * - * This distinction seems to be the most useful approach. - * - */ -static int check_long_opt(int argc, char *const *argv, const char *optstring, - const struct option *longopts, int *longind, - int print_errors, struct custom_getopt_data *d) -{ - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = -1; - int option_index; - - for (nameend = d->nextchar; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match or abbreviated matches */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp(p->name, d->nextchar, nameend - d->nextchar)) { - if ((unsigned int) (nameend - d->nextchar) - == (unsigned int) strlen(p->name)) { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } else if (pfound == NULL) { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } else if (pfound->has_arg != p->has_arg - || pfound->flag != p->flag - || pfound->val != p->val) - /* Second or later nonexact match found. */ - ambig = 1; - } - if (ambig && !exact) { - if (print_errors) { - fprintf(stderr, - "%s: option `%s' is ambiguous\n", - argv[0], argv[d->custom_optind]); - } - d->nextchar += strlen(d->nextchar); - d->custom_optind++; - d->custom_optopt = 0; - return '?'; - } - if (pfound) { - option_index = indfound; - d->custom_optind++; - if (*nameend) { - if (pfound->has_arg != no_argument) - d->custom_optarg = nameend + 1; - else { - if (print_errors) { - if (argv[d->custom_optind - 1][1] == '-') { - /* --option */ - fprintf(stderr, "%s: option `--%s' doesn't allow an argument\n", - argv[0], pfound->name); - } else { - /* +option or -option */ - fprintf(stderr, "%s: option `%c%s' doesn't allow an argument\n", - argv[0], argv[d->custom_optind - 1][0], pfound->name); - } - - } - d->nextchar += strlen(d->nextchar); - d->custom_optopt = pfound->val; - return '?'; - } - } else if (pfound->has_arg == required_argument) { - if (d->custom_optind < argc) - d->custom_optarg = argv[d->custom_optind++]; - else { - if (print_errors) { - fprintf(stderr, - "%s: option `%s' requires an argument\n", - argv[0], - argv[d->custom_optind - 1]); - } - d->nextchar += strlen(d->nextchar); - d->custom_optopt = pfound->val; - return optstring[0] == ':' ? ':' : '?'; - } - } - d->nextchar += strlen(d->nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - /* - * Can't find it as a long option. If this is not getopt_long_only, or - * the option starts with '--' or is not a valid short option, then - * it's an error. Otherwise interpret it as a short option. - */ - if (print_errors) { - if (argv[d->custom_optind][1] == '-') { - /* --option */ - fprintf(stderr, - "%s: unrecognized option `--%s'\n", - argv[0], d->nextchar); - } else { - /* +option or -option */ - fprintf(stderr, - "%s: unrecognized option `%c%s'\n", - argv[0], argv[d->custom_optind][0], - d->nextchar); - } - } - d->nextchar = (char *) ""; - d->custom_optind++; - d->custom_optopt = 0; - return '?'; -} - -static int check_short_opt(int argc, char *const *argv, const char *optstring, - int print_errors, struct custom_getopt_data *d) -{ - char c = *d->nextchar++; - const char *temp = strchr(optstring, c); - - /* Increment `custom_optind' when we start to process its last character. */ - if (*d->nextchar == '\0') - ++d->custom_optind; - if (!temp || c == ':') { - if (print_errors) - fprintf(stderr, "%s: invalid option -- %c\n", argv[0], c); - - d->custom_optopt = c; - return '?'; - } - if (temp[1] == ':') { - if (temp[2] == ':') { - /* This is an option that accepts an argument optionally. */ - if (*d->nextchar != '\0') { - d->custom_optarg = d->nextchar; - d->custom_optind++; - } else - d->custom_optarg = NULL; - d->nextchar = NULL; - } else { - /* This is an option that requires an argument. */ - if (*d->nextchar != '\0') { - d->custom_optarg = d->nextchar; - /* - * If we end this ARGV-element by taking the - * rest as an arg, we must advance to the next - * element now. - */ - d->custom_optind++; - } else if (d->custom_optind == argc) { - if (print_errors) { - fprintf(stderr, - "%s: option requires an argument -- %c\n", - argv[0], c); - } - d->custom_optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - } else - /* - * We already incremented `custom_optind' once; - * increment it again when taking next ARGV-elt - * as argument. - */ - d->custom_optarg = argv[d->custom_optind++]; - d->nextchar = NULL; - } - } - return c; -} - -/* - * Scan elements of ARGV for option characters given in OPTSTRING. - * - * If an element of ARGV starts with '-', and is not exactly "-" or "--", - * then it is an option element. The characters of this element - * (aside from the initial '-') are option characters. If `getopt' - * is called repeatedly, it returns successively each of the option characters - * from each of the option elements. - * - * If `getopt' finds another option character, it returns that character, - * updating `custom_optind' and `nextchar' so that the next call to `getopt' can - * resume the scan with the following option character or ARGV-element. - * - * If there are no more option characters, `getopt' returns -1. - * Then `custom_optind' is the index in ARGV of the first ARGV-element - * that is not an option. (The ARGV-elements have been permuted - * so that those that are not options now come last.) - * - * OPTSTRING is a string containing the legitimate option characters. - * If an option character is seen that is not listed in OPTSTRING, - * return '?' after printing an error message. If you set `custom_opterr' to - * zero, the error message is suppressed but we still return '?'. - * - * If a char in OPTSTRING is followed by a colon, that means it wants an arg, - * so the following text in the same ARGV-element, or the text of the following - * ARGV-element, is returned in `custom_optarg'. Two colons mean an option that - * wants an optional arg; if there is text in the current ARGV-element, - * it is returned in `custom_optarg', otherwise `custom_optarg' is set to zero. - * - * If OPTSTRING starts with `-' or `+', it requests different methods of - * handling the non-option ARGV-elements. - * See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. - * - * Long-named options begin with `--' instead of `-'. - * Their names may be abbreviated as long as the abbreviation is unique - * or is an exact match for some defined option. If they have an - * argument, it follows the option name in the same ARGV-element, separated - * from the option name by a `=', or else the in next ARGV-element. - * When `getopt' finds a long-named option, it returns 0 if that option's - * `flag' field is nonzero, the value of the option's `val' field - * if the `flag' field is zero. - * - * The elements of ARGV aren't really const, because we permute them. - * But we pretend they're const in the prototype to be compatible - * with other systems. - * - * LONGOPTS is a vector of `struct option' terminated by an - * element containing a name which is zero. - * - * LONGIND returns the index in LONGOPT of the long-named option found. - * It is only valid when a long-named option has been found by the most - * recent call. - * - * Return the option character from OPTS just read. Return -1 when there are - * no more options. For unrecognized options, or options missing arguments, - * `custom_optopt' is set to the option letter, and '?' is returned. - * - * The OPTS string is a list of characters which are recognized option letters, - * optionally followed by colons, specifying that that letter takes an - * argument, to be placed in `custom_optarg'. - * - * If a letter in OPTS is followed by two colons, its argument is optional. - * This behavior is specific to the GNU `getopt'. - * - * The argument `--' causes premature termination of argument scanning, - * explicitly telling `getopt' that there are no more options. If OPTS begins - * with `--', then non-option arguments are treated as arguments to the option - * '\0'. This behavior is specific to the GNU `getopt'. - */ - -static int getopt_internal_r(int argc, char *const *argv, const char *optstring, - const struct option *longopts, int *longind, - struct custom_getopt_data *d) -{ - int ret, print_errors = d->custom_opterr; - - if (optstring[0] == ':') - print_errors = 0; - if (argc < 1) - return -1; - d->custom_optarg = NULL; - - /* - * This is a big difference with GNU getopt, since optind == 0 - * means initialization while here 1 means first call. - */ - if (d->custom_optind == 0 || !d->initialized) { - if (d->custom_optind == 0) - d->custom_optind = 1; /* Don't scan ARGV[0], the program name. */ - custom_getopt_initialize(d); - } - if (d->nextchar == NULL || *d->nextchar == '\0') { - ret = shuffle_argv(argc, argv, longopts, d); - if (ret) - return ret; - } - if (longopts && (argv[d->custom_optind][1] == '-' )) - return check_long_opt(argc, argv, optstring, longopts, - longind, print_errors, d); - return check_short_opt(argc, argv, optstring, print_errors, d); -} - -static int custom_getopt_internal(int argc, char *const *argv, const char *optstring, - const struct option *longopts, int *longind) -{ - int result; - /* Keep a global copy of all internal members of d */ - static struct custom_getopt_data d; - - d.custom_optind = custom_optind; - d.custom_opterr = custom_opterr; - result = getopt_internal_r(argc, argv, optstring, longopts, - longind, &d); - custom_optind = d.custom_optind; - custom_optarg = d.custom_optarg; - custom_optopt = d.custom_optopt; - return result; -} - -static int custom_getopt_long (int argc, char *const *argv, const char *options, - const struct option *long_options, int *opt_index) -{ - return custom_getopt_internal(argc, argv, options, long_options, - opt_index); -} - - -static char *package_name = 0; - -/** - * @brief updates an option - * @param field the generic pointer to the field to update - * @param orig_field the pointer to the orig field - * @param field_given the pointer to the number of occurrence of this option - * @param prev_given the pointer to the number of occurrence already seen - * @param value the argument for this option (if null no arg was specified) - * @param possible_values the possible values for this option (if specified) - * @param default_value the default value (in case the option only accepts fixed values) - * @param arg_type the type of this option - * @param check_ambiguity @see cmdline_parser_params.check_ambiguity - * @param override @see cmdline_parser_params.override - * @param no_free whether to free a possible previous value - * @param multiple_option whether this is a multiple option - * @param long_opt the corresponding long option - * @param short_opt the corresponding short option (or '-' if none) - * @param additional_error possible further error specification - */ -static -int update_arg(void *field, char **orig_field, - unsigned int *field_given, unsigned int *prev_given, - char *value, const char *possible_values[], - const char *default_value, - cmdline_parser_arg_type arg_type, - int check_ambiguity, int override, - int no_free, int multiple_option, - const char *long_opt, char short_opt, - const char *additional_error) -{ - FIX_UNUSED (field); - char *stop_char = 0; - const char *val = value; - int found; - char **string_field; - - stop_char = 0; - found = 0; - - if (!multiple_option && prev_given && (*prev_given || (check_ambiguity && *field_given))) - { - if (short_opt != '-') - fprintf (stderr, "%s: `--%s' (`-%c') option given more than once%s\n", - package_name, long_opt, short_opt, - (additional_error ? additional_error : "")); - else - fprintf (stderr, "%s: `--%s' option given more than once%s\n", - package_name, long_opt, - (additional_error ? additional_error : "")); - return 1; /* failure */ - } - - FIX_UNUSED (default_value); - - if (field_given && *field_given && ! override) - return 0; - if (prev_given) - (*prev_given)++; - if (field_given) - (*field_given)++; - if (possible_values) - val = possible_values[found]; - - switch(arg_type) { - case ARG_STRING: - if (val) { - string_field = (char **)field; - if (!no_free && *string_field) - free (*string_field); /* free previous string */ - *string_field = gengetopt_strdup (val); - } - break; - default: - break; - }; - - - /* store the original value */ - switch(arg_type) { - case ARG_NO: - break; - default: - if (value && orig_field) { - if (no_free) { - *orig_field = value; - } else { - if (*orig_field) - free (*orig_field); /* free previous string */ - *orig_field = gengetopt_strdup (value); - } - } - }; - - return 0; /* OK */ -} - - -int -cmdline_parser_internal ( - int argc, char * const *argv, struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params, const char *additional_error) -{ - int c; /* Character of the parsed option. */ - - int error = 0; - struct gengetopt_args_info local_args_info; - - int override; - int initialize; - int check_required; - int check_ambiguity; - - char *optarg; - int optind; - int opterr; - int optopt; - - package_name = argv[0]; - - override = params->override; - initialize = params->initialize; - check_required = params->check_required; - check_ambiguity = params->check_ambiguity; - - if (initialize) - cmdline_parser_init (args_info); - - cmdline_parser_init (&local_args_info); - - optarg = 0; - optind = 0; - opterr = params->print_errors; - optopt = '?'; - - while (1) - { - int option_index = 0; - - static struct option long_options[] = { - { "help", 0, NULL, 'h' }, - { "version", 0, NULL, 'V' }, - { "extract", 1, NULL, 'x' }, - { "long", 0, NULL, 'l' }, - { 0, 0, 0, 0 } - }; - - custom_optarg = optarg; - custom_optind = optind; - custom_opterr = opterr; - custom_optopt = optopt; - - c = custom_getopt_long (argc, argv, "hVx:l", long_options, &option_index); - - optarg = custom_optarg; - optind = custom_optind; - opterr = custom_opterr; - optopt = custom_optopt; - - if (c == -1) break; /* Exit from `while (1)' loop. */ - - switch (c) - { - case 'h': /* Print help and exit. */ - cmdline_parser_print_help (); - cmdline_parser_free (&local_args_info); - exit (EXIT_SUCCESS); - - case 'V': /* Print version and exit. */ - cmdline_parser_print_version (); - cmdline_parser_free (&local_args_info); - exit (EXIT_SUCCESS); - - case 'x': /* Extract file from archive. */ - - - if (update_arg( (void *)&(args_info->extract_arg), - &(args_info->extract_orig), &(args_info->extract_given), - &(local_args_info.extract_given), optarg, 0, 0, ARG_STRING, - check_ambiguity, override, 0, 0, - "extract", 'x', - additional_error)) - goto failure; - - break; - case 'l': /* Include extra information in archive listing. */ - - - if (update_arg( 0 , - 0 , &(args_info->long_given), - &(local_args_info.long_given), optarg, 0, 0, ARG_NO, - check_ambiguity, override, 0, 0, - "long", 'l', - additional_error)) - goto failure; - - break; - - case 0: /* Long option with no short option */ - case '?': /* Invalid option. */ - /* `getopt_long' already printed an error message. */ - goto failure; - - default: /* bug: option not considered. */ - fprintf (stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : "")); - abort (); - } /* switch */ - } /* while */ - - - - - cmdline_parser_release (&local_args_info); - - if ( error ) - return (EXIT_FAILURE); - - if (optind < argc) - { - int i = 0 ; - int found_prog_name = 0; - /* whether program name, i.e., argv[0], is in the remaining args - (this may happen with some implementations of getopt, - but surely not with the one included by gengetopt) */ - - - args_info->inputs_num = argc - optind - found_prog_name; - args_info->inputs = - (char **)(malloc ((args_info->inputs_num)*sizeof(char *))) ; - while (optind < argc) - args_info->inputs[ i++ ] = gengetopt_strdup (argv[optind++]) ; - } - - return 0; - -failure: - - cmdline_parser_release (&local_args_info); - return (EXIT_FAILURE); -} diff --git a/components/bsa/tests/bsatool_cmd.h b/components/bsa/tests/bsatool_cmd.h deleted file mode 100644 index 98fe2633f..000000000 --- a/components/bsa/tests/bsatool_cmd.h +++ /dev/null @@ -1,179 +0,0 @@ -/** @file bsatool_cmd.h - * @brief The header file for the command line option parser - * generated by GNU Gengetopt version 2.22.2 - * http://www.gnu.org/software/gengetopt. - * DO NOT modify this file, since it can be overwritten - * @author GNU Gengetopt by Lorenzo Bettini */ - -#ifndef BSATOOL_CMD_H -#define BSATOOL_CMD_H - -/* If we use autoconf. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include /* for FILE */ - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifndef CMDLINE_PARSER_PACKAGE -/** @brief the program name (used for printing errors) */ -#define CMDLINE_PARSER_PACKAGE "bsatool" -#endif - -#ifndef CMDLINE_PARSER_PACKAGE_NAME -/** @brief the complete program name (used for help and version) */ -#define CMDLINE_PARSER_PACKAGE_NAME "bsatool" -#endif - -#ifndef CMDLINE_PARSER_VERSION -/** @brief the program version */ -#define CMDLINE_PARSER_VERSION "1.0" -#endif - -/** @brief Where the command line options are stored */ -struct gengetopt_args_info -{ - const char *help_help; /**< @brief Print help and exit help description. */ - const char *version_help; /**< @brief Print version and exit help description. */ - char * extract_arg; /**< @brief Extract file from archive. */ - char * extract_orig; /**< @brief Extract file from archive original value given at command line. */ - const char *extract_help; /**< @brief Extract file from archive help description. */ - const char *long_help; /**< @brief Include extra information in archive listing help description. */ - - unsigned int help_given ; /**< @brief Whether help was given. */ - unsigned int version_given ; /**< @brief Whether version was given. */ - unsigned int extract_given ; /**< @brief Whether extract was given. */ - unsigned int long_given ; /**< @brief Whether long was given. */ - - char **inputs ; /**< @brief unamed options (options without names) */ - unsigned inputs_num ; /**< @brief unamed options number */ -} ; - -/** @brief The additional parameters to pass to parser functions */ -struct cmdline_parser_params -{ - int override; /**< @brief whether to override possibly already present options (default 0) */ - int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */ - int check_required; /**< @brief whether to check that all required options were provided (default 1) */ - int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */ - int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */ -} ; - -/** @brief the purpose string of the program */ -extern const char *gengetopt_args_info_purpose; -/** @brief the usage string of the program */ -extern const char *gengetopt_args_info_usage; -/** @brief all the lines making the help output */ -extern const char *gengetopt_args_info_help[]; - -/** - * The command line parser - * @param argc the number of command line options - * @param argv the command line options - * @param args_info the structure where option information will be stored - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser (int argc, char * const *argv, - struct gengetopt_args_info *args_info); - -/** - * The command line parser (version with additional parameters - deprecated) - * @param argc the number of command line options - * @param argv the command line options - * @param args_info the structure where option information will be stored - * @param override whether to override possibly already present options - * @param initialize whether to initialize the option structure my_args_info - * @param check_required whether to check that all required options were provided - * @return 0 if everything went fine, NON 0 if an error took place - * @deprecated use cmdline_parser_ext() instead - */ -int cmdline_parser2 (int argc, char * const *argv, - struct gengetopt_args_info *args_info, - int override, int initialize, int check_required); - -/** - * The command line parser (version with additional parameters) - * @param argc the number of command line options - * @param argv the command line options - * @param args_info the structure where option information will be stored - * @param params additional parameters for the parser - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser_ext (int argc, char * const *argv, - struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params); - -/** - * Save the contents of the option struct into an already open FILE stream. - * @param outfile the stream where to dump options - * @param args_info the option struct to dump - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser_dump(FILE *outfile, - struct gengetopt_args_info *args_info); - -/** - * Save the contents of the option struct into a (text) file. - * This file can be read by the config file parser (if generated by gengetopt) - * @param filename the file where to save - * @param args_info the option struct to save - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser_file_save(const char *filename, - struct gengetopt_args_info *args_info); - -/** - * Print the help - */ -void cmdline_parser_print_help(void); -/** - * Print the version - */ -void cmdline_parser_print_version(void); - -/** - * Initializes all the fields a cmdline_parser_params structure - * to their default values - * @param params the structure to initialize - */ -void cmdline_parser_params_init(struct cmdline_parser_params *params); - -/** - * Allocates dynamically a cmdline_parser_params structure and initializes - * all its fields to their default values - * @return the created and initialized cmdline_parser_params structure - */ -struct cmdline_parser_params *cmdline_parser_params_create(void); - -/** - * Initializes the passed gengetopt_args_info structure's fields - * (also set default values for options that have a default) - * @param args_info the structure to initialize - */ -void cmdline_parser_init (struct gengetopt_args_info *args_info); -/** - * Deallocates the string fields of the gengetopt_args_info structure - * (but does not deallocate the structure itself) - * @param args_info the structure to deallocate - */ -void cmdline_parser_free (struct gengetopt_args_info *args_info); - -/** - * Checks that all the required options were specified - * @param args_info the structure to check - * @param prog_name the name of the program that will be used to print - * possible errors - * @return - */ -int cmdline_parser_required (struct gengetopt_args_info *args_info, - const char *prog_name); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* BSATOOL_CMD_H */ diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 027f3de71..94240c5eb 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -603,8 +603,8 @@ namespace Compiler switch (code) { - case Scanner::S_plus: pushBinaryOperator ('+'); return true; - case Scanner::S_minus: pushBinaryOperator ('-'); return true; + case Scanner::S_plus: c = '+'; break; + case Scanner::S_minus: c = '-'; break; case Scanner::S_mult: pushBinaryOperator ('*'); return true; case Scanner::S_div: pushBinaryOperator ('/'); return true; case Scanner::S_cmpEQ: c = 'e'; break; diff --git a/components/compiler/fileparser.cpp b/components/compiler/fileparser.cpp index 9b184f1ff..185af4a51 100644 --- a/components/compiler/fileparser.cpp +++ b/components/compiler/fileparser.cpp @@ -45,7 +45,10 @@ namespace Compiler reportWarning ("Names for script " + mName + " do not match", loc); mState = EndCompleteState; - return true; + return false; // we are stopping here, because there might be more garbage on the end line, + // that we must ignore. + // + /// \todo allow this workaround to be disabled for newer scripts } return Parser::parseName (name, loc, scanner); @@ -68,6 +71,19 @@ namespace Compiler return true; } + if (mState==EndNameState) + { + // optional repeated name after end statement + if (mName!=loc.mLiteral) + reportWarning ("Names for script " + mName + " do not match", loc); + + mState = EndCompleteState; + return false; // we are stopping here, because there might be more garbage on the end line, + // that we must ignore. + // + /// \todo allow this workaround to be disabled for newer scripts + } + return Parser::parseKeyword (keyword, loc, scanner); } diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 210d18dc9..40462c488 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -98,10 +98,12 @@ namespace Compiler if (type!=' ') { - getErrorHandler().error ("catoLowern't re-declare local variable", loc); + /// \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); - return false; + mState = EndState; + return true; } mLocals.declare (mState==ShortState ? 's' : (mState==LongState ? 'l' : 'f'), @@ -139,7 +141,7 @@ namespace Compiler if (mState==SetMemberVarState) { - mMemberName = Misc::StringUtils::lowerCase (name); + mMemberName = name; char type = getContext().getMemberType (mMemberName, mName); if (type!=' ') diff --git a/components/compiler/nullerrorhandler.cpp b/components/compiler/nullerrorhandler.cpp new file mode 100644 index 000000000..3071701e8 --- /dev/null +++ b/components/compiler/nullerrorhandler.cpp @@ -0,0 +1,6 @@ + +#include "nullerrorhandler.hpp" + +void Compiler::NullErrorHandler::report (const std::string& message, const TokenLoc& loc, Type type) {} + +void Compiler::NullErrorHandler::report (const std::string& message, Type type) {} \ No newline at end of file diff --git a/components/compiler/nullerrorhandler.hpp b/components/compiler/nullerrorhandler.hpp new file mode 100644 index 000000000..bb4db99a2 --- /dev/null +++ b/components/compiler/nullerrorhandler.hpp @@ -0,0 +1,21 @@ + +#ifndef COMPILER_NULLERRORHANDLER_H_INCLUDED +#define COMPILER_NULLERRORHANDLER_H_INCLUDED + +#include "errorhandler.hpp" + +namespace Compiler +{ + /// \brief Error handler implementation: Ignore all error messages + + class NullErrorHandler : public ErrorHandler + { + virtual void report (const std::string& message, const TokenLoc& loc, Type type); + ///< Report error to the user. + + virtual void report (const std::string& message, Type type); + ///< Report a file related error + }; +} + +#endif diff --git a/components/compiler/parser.cpp b/components/compiler/parser.cpp index 896458e48..8d11c4086 100644 --- a/components/compiler/parser.cpp +++ b/components/compiler/parser.cpp @@ -148,6 +148,11 @@ namespace Compiler return false; } + bool Parser::parseComment (const std::string& comment, const TokenLoc& loc, Scanner& scanner) + { + return true; + } + // Handle an EOF token. // // - Default-implementation: Report an error. diff --git a/components/compiler/parser.hpp b/components/compiler/parser.hpp index 221e7c2c9..4fec570e9 100644 --- a/components/compiler/parser.hpp +++ b/components/compiler/parser.hpp @@ -82,6 +82,13 @@ namespace Compiler /// /// - Default-implementation: Report an error. + virtual bool parseComment (const std::string& comment, const TokenLoc& loc, + Scanner& scanner); + ///< Handle comment token. + /// \return fetch another token? + /// + /// - Default-implementation: ignored (and return true). + virtual void parseEOF (Scanner& scanner); ///< Handle EOF token. /// diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 7f43c36a5..b45015d1e 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -1,6 +1,7 @@ #include "scanner.hpp" +#include #include #include #include @@ -88,6 +89,10 @@ namespace Compiler } else if (c==';') { + std::string comment; + + comment += c; + while (get (c)) { if (c=='\n') @@ -95,17 +100,26 @@ namespace Compiler putback (c); break; } + else + comment += c; } + TokenLoc loc (mLoc); mLoc.mLiteral.clear(); - return true; + return parser.parseComment (comment, loc, *this); } else if (isWhitespace (c)) { mLoc.mLiteral.clear(); return true; } + else if (c==':') + { + // treat : as a whitespace :( + mLoc.mLiteral.clear(); + return true; + } else if (std::isdigit (c)) { bool cont = false; @@ -150,10 +164,9 @@ namespace Compiler bool Scanner::scanInt (char c, Parser& parser, bool& cont) { + assert(c != '\0'); std::string value; - value += c; - bool empty = false; bool error = false; @@ -162,7 +175,6 @@ namespace Compiler if (std::isdigit (c)) { value += c; - empty = false; } else if (std::isalpha (c) || c=='_') error = true; @@ -177,7 +189,7 @@ namespace Compiler } } - if (empty || error) + if (error) return false; TokenLoc loc (mLoc); @@ -359,7 +371,18 @@ namespace Compiler else if (c==')') special = S_close; else if (c=='.') + { + // check, if this starts a float literal + if (get (c)) + { + putback (c); + + if (std::isdigit (c)) + return scanFloat ("", parser, cont); + } + special = S_member; + } else if (c=='=') { if (get (c)) @@ -415,7 +438,12 @@ namespace Compiler if (get (c)) { if (c=='=') + { special = S_cmpLE; + + if (get (c) && c!='=') // <== is a allowed as an alternative to <= :( + putback (c); + } else { putback (c); @@ -430,7 +458,12 @@ namespace Compiler if (get (c)) { if (c=='=') + { special = S_cmpGE; + + if (get (c) && c!='=') // >== is a allowed as an alternative to >= :( + putback (c); + } else { putback (c); diff --git a/components/compiler/scriptparser.cpp b/components/compiler/scriptparser.cpp index ac0ee63f1..5b3d244b0 100644 --- a/components/compiler/scriptparser.cpp +++ b/components/compiler/scriptparser.cpp @@ -2,6 +2,8 @@ #include "scriptparser.hpp" #include "scanner.hpp" +#include "skipparser.hpp" +#include "errorhandler.hpp" namespace Compiler { @@ -41,6 +43,17 @@ namespace Compiler return true; } + /// \todo add an option to disable this nonsense + if (keyword==Scanner::K_endif) + { + // surplus endif + getErrorHandler().warning ("endif without matching if/elseif", loc); + + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + return true; + } + if (keyword==Scanner::K_end && mEnd) { return false; diff --git a/components/esm/aipackage.cpp b/components/esm/aipackage.cpp index 1e6220c3a..1440dbd13 100644 --- a/components/esm/aipackage.cpp +++ b/components/esm/aipackage.cpp @@ -5,6 +5,12 @@ namespace ESM { + void AIData::blank() + { + mHello = mU1 = mFight = mFlee = mAlarm = mU2 = mU3 = mU4 = 0; + mServices = 0; + } + void AIPackageList::load(ESMReader &esm) { while (esm.hasMoreSubs()) { diff --git a/components/esm/aipackage.hpp b/components/esm/aipackage.hpp index 3128fe0c6..38499b2dd 100644 --- a/components/esm/aipackage.hpp +++ b/components/esm/aipackage.hpp @@ -19,6 +19,9 @@ namespace ESM // These are probabilities char mHello, mU1, mFight, mFlee, mAlarm, mU2, mU3, mU4; int mServices; // See the Services enum + + void blank(); + ///< Set record to default state (does not touch the ID). }; // 12 bytes struct AIWander diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp new file mode 100644 index 000000000..fb03a9084 --- /dev/null +++ b/components/esm/cellref.hpp @@ -0,0 +1,90 @@ +#ifndef OPENMW_ESM_CELLREF_H +#define OPENMW_ESM_CELLREF_H + +#include + +#include "defs.hpp" + +namespace ESM +{ + class ESMWriter; + + /* Cell reference. This represents ONE object (of many) inside the + cell. The cell references are not loaded as part of the normal + loading process, but are rather loaded later on demand when we are + setting up a specific cell. + */ + + class CellRef + { + public: + + int mRefnum; // Reference number + std::string mRefID; // ID of object being referenced + + float mScale; // Scale applied to mesh + + // The NPC that owns this object (and will get angry if you steal + // it) + std::string mOwner; + + // I have no idea, looks like a link to a global variable? + std::string mGlob; + + // ID of creature trapped in this soul gem (?) + std::string mSoul; + + // ?? CNAM has a faction name, might be for objects/beds etc + // belonging to a faction. + std::string mFaction; + + // INDX might be PC faction rank required to use the item? Sometimes + // is -1, which I assume means "any rank". + int mFactIndex; + + // For weapon or armor, this is the remaining item health. + // For tools (lockpicks, probes, repair hammer) it is the remaining uses. + int mCharge; + + // Remaining enchantment charge + float mEnchantmentCharge; + + // This is 5 for Gold_005 references, 100 for Gold_100 and so on. + int mGoldValue; + + // For doors - true if this door teleports to somewhere else, false + // if it should open through animation. + bool mTeleport; + + // Teleport location for the door, if this is a teleporting door. + Position mDoorDest; + + // Destination cell for doors (optional) + std::string mDestCell; + + // Lock level for doors and containers + int mLockLevel; + std::string mKey, mTrap; // Key and trap ID names, if any + + // This corresponds to the "Reference Blocked" checkbox in the construction set, + // which prevents editing that reference. + // -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. + int mFltv; + int mNam0; + + // Position and rotation of this object within the cell + Position mPos; + + void save(ESMWriter &esm); + }; +} + +#endif \ No newline at end of file diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index aa870f925..bd86f9ba0 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -9,18 +9,6 @@ namespace ESM // Pixel color value. Standard four-byte rr,gg,bb,aa format. typedef int32_t Color; -enum VarType -{ - VT_Unknown, - VT_None, - VT_Short, - VT_Int, - VT_Long, - VT_Float, - VT_String, - VT_Ignored -}; - enum Specialization { SPC_Combat = 0, diff --git a/components/esm/esmcommon.hpp b/components/esm/esmcommon.hpp index e0c5c08af..6f51c767e 100644 --- a/components/esm/esmcommon.hpp +++ b/components/esm/esmcommon.hpp @@ -2,6 +2,7 @@ #define OPENMW_ESM_COMMON_H #include +#include #include #include @@ -14,24 +15,6 @@ enum Version VER_13 = 0x3fa66666 }; -enum FileType - { - FT_ESP = 0, // Plugin - FT_ESM = 1, // Master - FT_ESS = 32 // Savegame - }; - -// Used to mark special files. The original ESM files are given -// special treatment in a few places, most noticably in loading and -// filtering out "dirtly" GMST entries correctly. -enum SpecialFile - { - SF_Other, - SF_Morrowind, - SF_Tribunal, - SF_Bloodmoon - }; - /* A structure used for holding fixed-length strings. In the case of LEN=4, it can be more efficient to match the string as a 32 bit number, therefore the struct is implemented as a union with an int. @@ -41,7 +24,7 @@ union NAME_T { char name[LEN]; int32_t val; - + bool operator==(const char *str) const { for(int i=0; i NAME; @@ -70,27 +55,6 @@ typedef NAME_T<256> NAME256; #pragma pack(push) #pragma pack(1) -/// File header data for all ES files -struct HEDRstruct -{ - /* File format version. This is actually a float, the supported - versions are 1.2 and 1.3. These correspond to: - 1.2 = 0x3f99999a and 1.3 = 0x3fa66666 - */ - int version; - int type; // 0=esp, 1=esm, 32=ess - NAME32 author; // Author's name - NAME256 desc; // File description - int records; // Number of records? Not used. -}; - -// Defines another files (esm or esp) that this file depends upon. -struct MasterData -{ - std::string name; - uint64_t size; -}; - // Data that is only present in save game files struct SaveData { @@ -112,7 +76,10 @@ struct ESM_Context uint32_t leftRec, leftSub; size_t leftFile; NAME recName, subName; - HEDRstruct header; + // When working with multiple esX files, we will generate lists of all files that + // actually contribute to a specific cell. Therefore, we need to store the index + // of the file belonging to this contest. See CellStore::(list/load)refs for details. + int index; // True if subName has been read but not used. bool subCached; diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index eca0d7854..0a4c1a3fe 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -15,11 +15,17 @@ ESM_Context ESMReader::getContext() return mCtx; } -ESMReader::ESMReader(void): - mBuffer(50*1024) +ESMReader::ESMReader() + : mBuffer(50*1024) + , mRecordFlags(0) { } +int ESMReader::getFormat() const +{ + return mHeader.mFormat; +} + void ESMReader::restoreContext(const ESM_Context &rc) { // Reopen the file if necessary @@ -51,18 +57,6 @@ void ESMReader::openRaw(Ogre::DataStreamPtr _esm, const std::string &name) mEsm = _esm; mCtx.filename = name; mCtx.leftFile = mEsm->size(); - - // Flag certain files for special treatment, based on the file - // name. - const char *cstr = mCtx.filename.c_str(); - if (iends(cstr, "Morrowind.esm")) - mSpf = SF_Morrowind; - else if (iends(cstr, "Tribunal.esm")) - mSpf = SF_Tribunal; - else if (iends(cstr, "Bloodmoon.esm")) - mSpf = SF_Bloodmoon; - else - mSpf = SF_Other; } void ESMReader::open(Ogre::DataStreamPtr _esm, const std::string &name) @@ -74,43 +68,7 @@ void ESMReader::open(Ogre::DataStreamPtr _esm, const std::string &name) getRecHeader(); - // Get the header - getHNT(mCtx.header, "HEDR", 300); - - if (mCtx.header.version != VER_12 && mCtx.header.version != VER_13) - fail("Unsupported file format version"); - - while (isNextSub("MAST")) - { - MasterData m; - m.name = getHString(); - m.size = getHNLong("DATA"); - mMasters.push_back(m); - } - - if (mCtx.header.type == FT_ESS) - { - // Savegame-related data - - // Player position etc - getHNT(mSaveData, "GMDT", 124); - - /* Image properties, five ints. Is always: - Red-mask: 0xff0000 - Blue-mask: 0x00ff00 - Green-mask: 0x0000ff - Alpha-mask: 0x000000 - Bpp: 32 - */ - getSubNameIs("SCRD"); - skipHSubSize(20); - - /* Savegame screenshot: - 128x128 pixels * 4 bytes per pixel - */ - getSubNameIs("SCRS"); - skipHSubSize(65536); - } + mHeader.load (*this); } void ESMReader::open(const std::string &file) diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index d52be25aa..ff10a202c 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -12,7 +12,9 @@ #include #include + #include "esmcommon.hpp" +#include "loadtes3.hpp" namespace ESM { @@ -20,15 +22,7 @@ class ESMReader { public: - ESMReader(void); - - /************************************************************************* - * - * Public type definitions - * - *************************************************************************/ - - typedef std::vector MasterList; + ESMReader(); /************************************************************************* * @@ -36,15 +30,13 @@ public: * *************************************************************************/ - int getVer() const { return mCtx.header.version; } - float getFVer() { if(mCtx.header.version == VER_12) return 1.2; else return 1.3; } - int getSpecial() { return mSpf; } - int getType() { return mCtx.header.type; } - const std::string getAuthor() { return mCtx.header.author.toString(); } - const std::string getDesc() { return mCtx.header.desc.toString(); } - const SaveData &getSaveData() const { return mSaveData; } - const MasterList &getMasters() { return mMasters; } - const NAME &retSubName() { return mCtx.subName; } + int getVer() const { return mHeader.mData.version; } + float getFVer() const { if(mHeader.mData.version == VER_12) return 1.2; else return 1.3; } + const std::string getAuthor() const { return mHeader.mData.author.toString(); } + const std::string getDesc() const { return mHeader.mData.desc.toString(); } + const std::vector &getMasters() const { return mHeader.mMaster; } + int getFormat() const; + const NAME &retSubName() const { return mCtx.subName; } uint32_t getSubSize() const { return mCtx.leftSub; } /************************************************************************* @@ -78,6 +70,17 @@ public: void openRaw(const std::string &file); + // This is a quick hack for multiple esm/esp files. Each plugin introduces its own + // terrain palette, but ESMReader does not pass a reference to the correct plugin + // to the individual load() methods. This hack allows to pass this reference + // indirectly to the load() method. + int mIdx; + void setIndex(const int index) {mIdx = index; mCtx.index = index;} + const int getIndex() {return mIdx;} + + void setGlobalReaderList(std::vector *list) {mGlobalReaderList = list;} + std::vector *getGlobalReaderList() {return mGlobalReaderList;} + /************************************************************************* * * Medium-level reading shortcuts @@ -110,6 +113,14 @@ public: getHT(x); } + template + void getHNOT(X &x, const char* name, int size) + { + assert(sizeof(X) == size); + if(isNextSub(name)) + getHT(x); + } + int64_t getHNLong(const char *name); // Get data of a given type/size, including subrecord header @@ -205,7 +216,7 @@ public: follows the header, ie beyond the entire record. You should use leftRec to orient yourself inside the record itself. */ - void getRecHeader() { uint32_t u; getRecHeader(u); } + void getRecHeader() { getRecHeader(mRecordFlags); } void getRecHeader(uint32_t &flags); bool hasMoreRecs() const { return mCtx.leftFile > 0; } @@ -238,19 +249,24 @@ public: /// Sets font encoder for ESM strings void setEncoder(ToUTF8::Utf8Encoder* encoder); + /// Get record flags of last record + unsigned int getRecordFlags() { return mRecordFlags; } + private: Ogre::DataStreamPtr mEsm; ESM_Context mCtx; + unsigned int mRecordFlags; + // Special file signifier (see SpecialFile enum above) - int mSpf; // Buffer for ESM strings std::vector mBuffer; - SaveData mSaveData; - MasterList mMasters; + Header mHeader; + + std::vector *mGlobalReaderList; ToUTF8::Utf8Encoder* mEncoder; }; } diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index e2f878a25..3ea6bd350 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -1,6 +1,8 @@ #include "esmwriter.hpp" + +#include #include -#include +#include bool count = true; @@ -9,40 +11,40 @@ namespace ESM int ESMWriter::getVersion() { - return m_header.version; + return mHeader.mData.version; } void ESMWriter::setVersion(int ver) { - m_header.version = ver; + mHeader.mData.version = ver; } -int ESMWriter::getType() +void ESMWriter::setAuthor(const std::string& auth) { - return m_header.type; + mHeader.mData.author.assign (auth); } -void ESMWriter::setType(int type) +void ESMWriter::setDescription(const std::string& desc) { - m_header.type = type; + mHeader.mData.desc.assign (desc); } -void ESMWriter::setAuthor(const std::string& auth) +void ESMWriter::setRecordCount (int count) { - strncpy((char*)&m_header.author, auth.c_str(), 32); + mHeader.mData.records = count; } -void ESMWriter::setDescription(const std::string& desc) +void ESMWriter::setFormat (int format) { - strncpy((char*)&m_header.desc, desc.c_str(), 256); + mHeader.mFormat = format; } void ESMWriter::addMaster(const std::string& name, uint64_t size) { - MasterData d; + Header::MasterData d; d.name = name; d.size = size; - m_masters.push_back(d); + mHeader.mMaster.push_back(d); } void ESMWriter::save(const std::string& file) @@ -58,25 +60,13 @@ void ESMWriter::save(std::ostream& file) startRecord("TES3", 0); - m_header.records = 0; - writeHNT("HEDR", m_header, 300); - m_headerPos = m_stream->tellp() - (std::streampos)4; - - for (std::list::iterator it = m_masters.begin(); it != m_masters.end(); ++it) - { - writeHNCString("MAST", it->name); - writeHNT("DATA", it->size); - } + mHeader.save (*this); endRecord("TES3"); } void ESMWriter::close() { - std::cout << "Writing amount of saved records (" << m_recordCount - 1 << ")" << std::endl; - m_stream->seekp(m_headerPos); - writeT(m_recordCount-1); - m_stream->seekp(0, std::ios::end); m_stream->flush(); if (!m_records.empty()) @@ -86,7 +76,7 @@ void ESMWriter::close() void ESMWriter::startRecord(const std::string& name, uint32_t flags) { m_recordCount++; - + writeName(name); RecordData rec; rec.name = name; @@ -109,7 +99,7 @@ void ESMWriter::startSubRecord(const std::string& name) rec.size = 0; writeT(0); // Size goes here m_records.push_back(rec); - + assert(m_records.back().size == 0); } @@ -118,7 +108,7 @@ void ESMWriter::endRecord(const std::string& name) RecordData rec = m_records.back(); assert(rec.name == name); m_records.pop_back(); - + m_stream->seekp(rec.position); count = false; @@ -136,15 +126,15 @@ void ESMWriter::writeHNString(const std::string& name, const std::string& data) endRecord(name); } -void ESMWriter::writeHNString(const std::string& name, const std::string& data, int size) +void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size) { - assert(static_cast (data.size()) <= size); + assert(data.size() <= size); startSubRecord(name); writeHString(data); - if (static_cast (data.size()) < size) + if (data.size() < size) { - for (int i = data.size(); i < size; ++i) + for (size_t i = data.size(); i < size; ++i) write("\0",1); } @@ -177,7 +167,7 @@ void ESMWriter::writeName(const std::string& name) write(name.c_str(), name.size()); } -void ESMWriter::write(const char* data, int size) +void ESMWriter::write(const char* data, size_t size) { if (count && !m_records.empty()) { diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index b557a29ad..be3ae33ab 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -1,13 +1,14 @@ #ifndef OPENMW_ESM_WRITER_H #define OPENMW_ESM_WRITER_H -#include +#include #include -#include -#include "esmcommon.hpp" #include +#include "esmcommon.hpp" +#include "loadtes3.hpp" + namespace ESM { class ESMWriter @@ -16,17 +17,17 @@ class ESMWriter { std::string name; std::streampos position; - int size; + size_t size; }; public: int getVersion(); void setVersion(int ver); - int getType(); - void setType(int type); void setEncoder(ToUTF8::Utf8Encoder *encoding); // Write strings as UTF-8? void setAuthor(const std::string& author); void setDescription(const std::string& desc); + void setRecordCount (int count); + void setFormat (int format); void addMaster(const std::string& name, uint64_t size); @@ -35,7 +36,7 @@ public: void close(); void writeHNString(const std::string& name, const std::string& data); - void writeHNString(const std::string& name, const std::string& data, int size); + void writeHNString(const std::string& name, const std::string& data, size_t size); void writeHNCString(const std::string& name, const std::string& data) { startSubRecord(name); @@ -76,28 +77,27 @@ public: } template - void writeT(const T& data, int size) + void writeT(const T& data, size_t size) { write((char*)&data, size); } - + void startRecord(const std::string& name, uint32_t flags); void startSubRecord(const std::string& name); void endRecord(const std::string& name); void writeHString(const std::string& data); void writeHCString(const std::string& data); void writeName(const std::string& data); - void write(const char* data, int size); + void write(const char* data, size_t size); private: - std::list m_masters; std::list m_records; std::ostream* m_stream; std::streampos m_headerPos; ToUTF8::Utf8Encoder* m_encoder; int m_recordCount; - HEDRstruct m_header; + Header mHeader; }; } diff --git a/components/esm/loadacti.cpp b/components/esm/loadacti.cpp index 0e214e2b6..fd022af7e 100644 --- a/components/esm/loadacti.cpp +++ b/components/esm/loadacti.cpp @@ -17,4 +17,11 @@ void Activator::save(ESMWriter &esm) esm.writeHNCString("FNAM", mName); esm.writeHNOCString("SCRI", mScript); } + + void Activator::blank() + { + mName.clear(); + mScript.clear(); + mModel.clear(); + } } diff --git a/components/esm/loadacti.hpp b/components/esm/loadacti.hpp index 8cb335feb..a62990590 100644 --- a/components/esm/loadacti.hpp +++ b/components/esm/loadacti.hpp @@ -15,6 +15,9 @@ struct Activator void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } diff --git a/components/esm/loadalch.cpp b/components/esm/loadalch.cpp index a4b1bb718..dbb69c066 100644 --- a/components/esm/loadalch.cpp +++ b/components/esm/loadalch.cpp @@ -23,4 +23,16 @@ void Potion::save(ESMWriter &esm) esm.writeHNT("ALDT", mData, 12); mEffects.save(esm); } + + void Potion::blank() + { + mData.mWeight = 0; + mData.mValue = 0; + mData.mAutoCalc = 0; + mName.clear(); + mModel.clear(); + mIcon.clear(); + mScript.clear(); + mEffects.mList.clear(); + } } diff --git a/components/esm/loadalch.hpp b/components/esm/loadalch.hpp index 1e571ac40..3ede85342 100644 --- a/components/esm/loadalch.hpp +++ b/components/esm/loadalch.hpp @@ -30,6 +30,10 @@ struct Potion void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). + }; } #endif diff --git a/components/esm/loadappa.cpp b/components/esm/loadappa.cpp index f5e7e10e1..4b8d2b763 100644 --- a/components/esm/loadappa.cpp +++ b/components/esm/loadappa.cpp @@ -7,12 +7,27 @@ namespace ESM { void Apparatus::load(ESMReader &esm) { - mModel = esm.getHNString("MODL"); - mName = esm.getHNString("FNAM"); - esm.getHNT(mData, "AADT", 16); - mScript = esm.getHNOString("SCRI"); - mIcon = esm.getHNString("ITEX"); + // we will not treat duplicated subrecords as errors here + while (esm.hasMoreSubs()) + { + esm.getSubName(); + NAME subName = esm.retSubName(); + + if (subName == "MODL") + mModel = esm.getHString(); + else if (subName == "FNAM") + mName = esm.getHString(); + else if (subName == "AADT") + esm.getHT(mData); + else if (subName == "SCRI") + mScript = esm.getHString(); + else if (subName == "ITEX") + mIcon = esm.getHString(); + else + esm.fail("wrong subrecord type " + subName.toString() + " for APPA record"); + } } + void Apparatus::save(ESMWriter &esm) { esm.writeHNCString("MODL", mModel); @@ -21,4 +36,16 @@ void Apparatus::save(ESMWriter &esm) esm.writeHNOCString("SCRI", mScript); esm.writeHNCString("ITEX", mIcon); } + + void Apparatus::blank() + { + mData.mType = 0; + mData.mQuality = 0; + mData.mWeight = 0; + mData.mValue = 0; + mModel.clear(); + mIcon.clear(); + mScript.clear(); + mName.clear(); + } } diff --git a/components/esm/loadappa.hpp b/components/esm/loadappa.hpp index 486a559f8..ed9d335be 100644 --- a/components/esm/loadappa.hpp +++ b/components/esm/loadappa.hpp @@ -36,6 +36,9 @@ struct Apparatus void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/loadarmo.cpp b/components/esm/loadarmo.cpp index 613346533..e64c8705d 100644 --- a/components/esm/loadarmo.cpp +++ b/components/esm/loadarmo.cpp @@ -50,4 +50,19 @@ void Armor::save(ESMWriter &esm) esm.writeHNOCString("ENAM", mEnchant); } + void Armor::blank() + { + mData.mType = 0; + mData.mWeight = 0; + mData.mValue = 0; + mData.mHealth = 0; + mData.mEnchant = 0; + mData.mArmor = 0; + mParts.mParts.clear(); + mName.clear(); + mModel.clear(); + mIcon.clear(); + mScript.clear(); + mEnchant.clear(); + } } diff --git a/components/esm/loadarmo.hpp b/components/esm/loadarmo.hpp index a94ae6735..eaef42be8 100644 --- a/components/esm/loadarmo.hpp +++ b/components/esm/loadarmo.hpp @@ -38,7 +38,9 @@ enum PartReferenceType PRT_RPauldron = 23, PRT_LPauldron = 24, PRT_Weapon = 25, - PRT_Tail = 26 + PRT_Tail = 26, + + PRT_Count = 27 }; // Reference to body parts @@ -88,6 +90,9 @@ struct Armor void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/loadbody.cpp b/components/esm/loadbody.cpp index 831ad8b64..e95a8a860 100644 --- a/components/esm/loadbody.cpp +++ b/components/esm/loadbody.cpp @@ -9,13 +9,13 @@ namespace ESM void BodyPart::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); - mName = esm.getHNString("FNAM"); + mRace = esm.getHNString("FNAM"); esm.getHNT(mData, "BYDT", 4); } void BodyPart::save(ESMWriter &esm) { esm.writeHNCString("MODL", mModel); - esm.writeHNCString("FNAM", mName); + esm.writeHNCString("FNAM", mRace); esm.writeHNT("BYDT", mData, 4); } diff --git a/components/esm/loadbody.hpp b/components/esm/loadbody.hpp index c91bb40bf..3ad9b1b95 100644 --- a/components/esm/loadbody.hpp +++ b/components/esm/loadbody.hpp @@ -27,13 +27,15 @@ struct BodyPart MP_Knee = 11, MP_Upperleg = 12, MP_Clavicle = 13, - MP_Tail = 14 + MP_Tail = 14, + + MP_Count = 15 }; enum Flags { BPF_Female = 1, - BPF_Playable = 2 + BPF_NotPlayable = 2 }; enum MeshType @@ -52,7 +54,7 @@ struct BodyPart }; BYDTstruct mData; - std::string mId, mModel, mName; + std::string mId, mModel, mRace; void load(ESMReader &esm); void save(ESMWriter &esm); diff --git a/components/esm/loadbook.cpp b/components/esm/loadbook.cpp index 8ed2f122a..3a70ac786 100644 --- a/components/esm/loadbook.cpp +++ b/components/esm/loadbook.cpp @@ -27,4 +27,18 @@ void Book::save(ESMWriter &esm) esm.writeHNOCString("ENAM", mEnchant); } + void Book::blank() + { + mData.mWeight = 0; + mData.mValue = 0; + mData.mIsScroll = 0; + mData.mSkillID = 0; + mData.mEnchant = 0; + mName.clear(); + mModel.clear(); + mIcon.clear(); + mScript.clear(); + mEnchant.clear(); + mText.clear(); + } } diff --git a/components/esm/loadbook.hpp b/components/esm/loadbook.hpp index 22201abed..68042e246 100644 --- a/components/esm/loadbook.hpp +++ b/components/esm/loadbook.hpp @@ -26,6 +26,9 @@ struct Book void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/loadbsgn.cpp b/components/esm/loadbsgn.cpp index b58071644..cb500f674 100644 --- a/components/esm/loadbsgn.cpp +++ b/components/esm/loadbsgn.cpp @@ -24,4 +24,12 @@ void BirthSign::save(ESMWriter &esm) mPowers.save(esm); } + void BirthSign::blank() + { + mName.clear(); + mDescription.clear(); + mTexture.clear(); + mPowers.mList.clear(); + } + } diff --git a/components/esm/loadbsgn.hpp b/components/esm/loadbsgn.hpp index b0bc28be4..434ddf68e 100644 --- a/components/esm/loadbsgn.hpp +++ b/components/esm/loadbsgn.hpp @@ -20,6 +20,9 @@ struct BirthSign void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID/index). }; } #endif diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 97ce17775..779f303f3 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include "esmreader.hpp" #include "esmwriter.hpp" @@ -9,6 +11,17 @@ namespace ESM { +/// Some overloaded compare operators. +bool operator==(const MovedCellRef& ref, int pRefnum) +{ + return (ref.mRefnum == pRefnum); +} + +bool operator==(const CellRef& ref, int pRefnum) +{ + return (ref.mRefnum == pRefnum); +} + void CellRef::save(ESMWriter &esm) { esm.writeHNT("FRMR", mRefnum); @@ -27,15 +40,14 @@ void CellRef::save(ESMWriter &esm) esm.writeHNT("INDX", mFactIndex); } - if (mCharge != -1.0) { - esm.writeHNT("XCHG", mCharge); - } + if (mEnchantmentCharge != -1) + esm.writeHNT("XCHG", mEnchantmentCharge); - if (mIntv != -1) { - esm.writeHNT("INTV", mIntv); - } - if (mNam9 != 0) { - esm.writeHNT("NAM9", mNam9); + if (mCharge != -1) + esm.writeHNT("INTV", mCharge); + + if (mGoldValue != 1) { + esm.writeHNT("NAM9", mGoldValue); } if (mTeleport) @@ -50,8 +62,8 @@ void CellRef::save(ESMWriter &esm) esm.writeHNOCString("KNAM", mKey); esm.writeHNOCString("TNAM", mTrap); - if (mUnam != -1) { - esm.writeHNT("UNAM", mUnam); + if (mReferenceBlocked != -1) { + esm.writeHNT("UNAM", mReferenceBlocked); } if (mFltv != 0) { esm.writeHNT("FLTV", mFltv); @@ -63,10 +75,11 @@ void CellRef::save(ESMWriter &esm) } } -void Cell::load(ESMReader &esm) +void Cell::load(ESMReader &esm, bool saveContext) { // Ignore this for now, it might mean we should delete the entire // cell? + // TODO: treat the special case "another plugin moved this ref, but we want to delete it"! if (esm.isNextSub("DELE")) { esm.skipHSub(); } @@ -95,8 +108,8 @@ void Cell::load(ESMReader &esm) // instead. if (mData.mFlags & QuasiEx) mRegion = esm.getHNOString("RGNN"); - else - esm.getHNT(mAmbi, "AMBI", 16); + else if (esm.isNextSub("AMBI")) + esm.getHT(mAmbi); } else { @@ -110,8 +123,21 @@ void Cell::load(ESMReader &esm) esm.getHT(mNAM0); } + if (saveContext) { + mContextList.push_back(esm.getContext()); + esm.skipRecord(); + } +} + +void Cell::preLoad(ESMReader &esm) //Can't be "load" because it conflicts with function in esmtool +{ + this->load(esm, false); +} + +void Cell::postLoad(ESMReader &esm) +{ // Save position of the cell references and move on - mContext = esm.getContext(); + mContextList.push_back(esm.getContext()); esm.skipRecord(); } @@ -141,14 +167,14 @@ void Cell::save(ESMWriter &esm) if (mMapColor != 0) esm.writeHNT("NAM5", mMapColor); } - + if (mNAM0 != 0) esm.writeHNT("NAM0", mNAM0); } -void Cell::restore(ESMReader &esm) const +void Cell::restore(ESMReader &esm, int iCtx) const { - esm.restoreContext(mContext); + esm.restoreContext(mContextList[iCtx]); } std::string Cell::getDescription() const @@ -167,17 +193,61 @@ std::string Cell::getDescription() const bool Cell::getNextRef(ESMReader &esm, CellRef &ref) { + // TODO: Try and document reference numbering, I don't think this has been done anywhere else. if (!esm.hasMoreSubs()) return false; + // NOTE: We should not need this check. It is a safety check until we have checked + // more plugins, and how they treat these moved references. + if (esm.isNextSub("MVRF")) { + esm.skipRecord(); // skip MVRF + esm.skipRecord(); // skip CNDT + // That should be it, I haven't seen any other fields yet. + } + esm.getHNT(ref.mRefnum, "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.getMasters(); + 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 + } + // 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"); @@ -186,13 +256,15 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) ref.mFactIndex = -2; esm.getHNOT(ref.mFactIndex, "INDX"); - ref.mCharge = -1.0; - esm.getHNOT(ref.mCharge, "XCHG"); + ref.mGoldValue = 1; + ref.mCharge = -1; + ref.mEnchantmentCharge = -1; + + esm.getHNOT(ref.mEnchantmentCharge, "XCHG"); - ref.mIntv = -1; - ref.mNam9 = 0; - esm.getHNOT(ref.mIntv, "INTV"); - esm.getHNOT(ref.mNam9, "NAM9"); + esm.getHNOT(ref.mCharge, "INTV"); + + esm.getHNOT(ref.mGoldValue, "NAM9"); // Present for doors that teleport you to another cell. if (esm.isNextSub("DODT")) @@ -210,16 +282,19 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) ref.mKey = esm.getHNOString("KNAM"); ref.mTrap = esm.getHNOString("TNAM"); - ref.mUnam = -1; + ref.mReferenceBlocked = -1; ref.mFltv = 0; - esm.getHNOT(ref.mUnam, "UNAM"); + esm.getHNOT(ref.mReferenceBlocked, "UNAM"); esm.getHNOT(ref.mFltv, "FLTV"); - esm.getHNT(ref.mPos, "DATA", 24); + 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. ref.mNam0 = 0; if (esm.isNextSub("NAM0")) { @@ -227,7 +302,48 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) //esm.getHNOT(NAM0, "NAM0"); } + if (esm.isNextSub("DELE")) { + esm.skipHSub(); + ref.mDeleted = 2; // Deleted, will not respawn. + // TODO: find out when references do respawn. + } else + ref.mDeleted = 0; + + return true; +} + +bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) +{ + esm.getHT(mref.mRefnum); + 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.getMasters(); + global = masters[local-1].index + 1; + mref.mRefnum |= global << 24; // insert global plugin ID + return true; } + void Cell::blank() + { + mName.clear(); + mRegion.clear(); + mWater = 0; + mWaterInt = false; + mMapColor = 0; + mNAM0 = 0; + + mData.mFlags = 0; + mData.mX = 0; + mData.mY = 0; + + mAmbi.mAmbient = 0; + mAmbi.mSunlight = 0; + mAmbi.mFog = 0; + mAmbi.mFogDensity = 0; + } } diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index fbd7c0456..eda8a5418 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -2,9 +2,17 @@ #define OPENMW_ESM_CELL_H #include +#include +#include #include "esmcommon.hpp" #include "defs.hpp" +#include "cellref.hpp" + +namespace MWWorld +{ + class ESMStore; +} namespace ESM { @@ -12,75 +20,30 @@ namespace ESM class ESMReader; class ESMWriter; -/* Cell reference. This represents ONE object (of many) inside the - cell. The cell references are not loaded as part of the normal - loading process, but are rather loaded later on demand when we are - setting up a specific cell. - */ -class CellRef +/* 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 + plugin tries to move it independently. + Unfortunately, we need to implement this here. + */ +class MovedCellRef { public: - int mRefnum; // Reference number - std::string mRefID; // ID of object being referenced - - float mScale; // Scale applied to mesh - - // The NPC that owns this object (and will get angry if you steal - // it) - std::string mOwner; - - // I have no idea, looks like a link to a global variable? - std::string mGlob; - - // ID of creature trapped in this soul gem (?) - std::string mSoul; - - // ?? CNAM has a faction name, might be for objects/beds etc - // belonging to a faction. - std::string mFaction; - - // INDX might be PC faction rank required to use the item? Sometimes - // is -1, which I assume means "any rank". - int mFactIndex; - - // Depends on context - possibly weapon health, number of uses left - // or weapon magic charge? - float mCharge; - - // I have no idea, these are present some times, often along with - // owner (ANAM) and sometimes otherwise. They are often (but not - // always) 1. INTV is big for lights (possibly a float?), might have - // something to do with remaining light "charge". - int mIntv, mNam9; + int mRefnum; - // For doors - true if this door teleports to somewhere else, false - // if it should open through animation. - bool mTeleport; + // Target cell (if exterior) + int mTarget[2]; - // Teleport location for the door, if this is a teleporting door. - Position mDoorDest; - - // Destination cell for doors (optional) - std::string mDestCell; - - // Lock level for doors and containers - int mLockLevel; - std::string mKey, mTrap; // Key and trap ID names, if any - - // No idea - occurs ONCE in Morrowind.esm, for an activator - char mUnam; + // TODO: Support moving references between exterior and interior cells! + // This may happen in saves, when an NPC follows the player. Tribunal + // introduces a henchman (which no one uses), so we may need this as well. +}; - // Occurs in Tribunal.esm, eg. in the cell "Mournhold, Plaza - // Brindisi Dorom", where it has the value 100. Also only for - // activators. - int mFltv; - int mNam0; +/// 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); - // Position and rotation of this object within the cell - Position mPos; - - void save(ESMWriter &esm); -}; +typedef std::list MovedCellRefTracker; +typedef std::list CellRefTracker; /* Cells hold data about objects, creatures, statics (rocks, walls, buildings) and landscape (for exterior cells). Cells frequently @@ -120,7 +83,7 @@ struct Cell // Optional region name for exterior and quasi-exterior cells. std::string mRegion; - ESM_Context mContext; // File position + std::vector mContextList; // File position; multiple positions for multiple plugin support DATAstruct mData; AMBIstruct mAmbi; float mWater; // Water level @@ -128,7 +91,17 @@ struct Cell int mMapColor; int mNAM0; - void load(ESMReader &esm); + // References "leased" from another cell (i.e. a different cell + // introduced this ref, and it has been moved here by a plugin) + CellRefTracker mLeasedRefs; + MovedCellRefTracker mMovedRefs; + + void preLoad(ESMReader &esm); + void postLoad(ESMReader &esm); + + // This method is left in for compatibility with esmtool. Parsing moved references currently requires + // passing ESMStore, bit it does not know about this parameter, so we do it this way. + void load(ESMReader &esm, bool saveContext = true); void save(ESMWriter &esm); bool isExterior() const @@ -151,7 +124,7 @@ struct Cell // somewhere other than the file system, you need to pre-open the // ESMReader, and the filename must match the stored filename // exactly. - void restore(ESMReader &esm) const; + void restore(ESMReader &esm, int iCtx) const; std::string getDescription() const; ///< Return a short string describing the cell (mostly used for debugging/logging purpose) @@ -163,6 +136,14 @@ struct Cell reuse one memory location without blanking it between calls. */ static bool getNextRef(ESMReader &esm, CellRef &ref); + + /* This fetches an MVRF record, which is used to track moved references. + * Since they are comparably rare, we use a separate method for this. + */ + static bool getNextMVRF(ESMReader &esm, MovedCellRef &mref); + + void blank(); + ///< Set record to default state (does not touch the ID/index). }; } #endif diff --git a/components/esm/loadclas.cpp b/components/esm/loadclas.cpp index a62668950..bdc461462 100644 --- a/components/esm/loadclas.cpp +++ b/components/esm/loadclas.cpp @@ -1,5 +1,7 @@ #include "loadclas.hpp" +#include + #include "esmreader.hpp" #include "esmwriter.hpp" @@ -18,6 +20,23 @@ const char *Class::sGmstSpecializationIds[3] = { "sSpecializationStealth" }; + + int& Class::CLDTstruct::getSkill (int index, bool major) + { + if (index<0 || index>=5) + throw std::logic_error ("skill index out of range"); + + return mSkills[index][major ? 1 : 0]; + } + + int Class::CLDTstruct::getSkill (int index, bool major) const + { + if (index<0 || index>=5) + throw std::logic_error ("skill index out of range"); + + return mSkills[index][major ? 1 : 0]; + } + void Class::load(ESMReader &esm) { mName = esm.getHNString("FNAM"); @@ -35,4 +54,18 @@ void Class::save(ESMWriter &esm) esm.writeHNOString("DESC", mDescription); } + void Class::blank() + { + mName.clear(); + mDescription.clear(); + + mData.mAttribute[0] = mData.mAttribute[1] = 0; + mData.mSpecialization = 0; + mData.mIsPlayable = 0; + mData.mCalc = 0; + + for (int i=0; i<5; ++i) + for (int i2=0; i2<2; ++i2) + mData.mSkills[i][i2] = 0; + } } diff --git a/components/esm/loadclas.hpp b/components/esm/loadclas.hpp index 264e342e6..4f85e6ee8 100644 --- a/components/esm/loadclas.hpp +++ b/components/esm/loadclas.hpp @@ -58,6 +58,12 @@ struct Class // I have no idea how to autocalculate these items... int mCalc; + + int& getSkill (int index, bool major); + ///< Throws an exception for invalid values of \a index. + + int getSkill (int index, bool major) const; + ///< Throws an exception for invalid values of \a index. }; // 60 bytes std::string mId, mName, mDescription; @@ -65,6 +71,10 @@ struct Class void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID/index). + }; } #endif diff --git a/components/esm/loadclot.cpp b/components/esm/loadclot.cpp index a37fbe8a4..10b00970f 100644 --- a/components/esm/loadclot.cpp +++ b/components/esm/loadclot.cpp @@ -16,7 +16,7 @@ void Clothing::load(ESMReader &esm) mIcon = esm.getHNOString("ITEX"); mParts.load(esm); - + mEnchant = esm.getHNOString("ENAM"); } @@ -28,10 +28,24 @@ void Clothing::save(ESMWriter &esm) esm.writeHNOCString("SCRI", mScript); esm.writeHNOCString("ITEX", mIcon); - + mParts.save(esm); - + esm.writeHNOCString("ENAM", mEnchant); } + void Clothing::blank() + { + mData.mType = 0; + mData.mWeight = 0; + mData.mValue = 0; + mData.mEnchant = 0; + mParts.mParts.clear(); + mName.clear(); + mModel.clear(); + mIcon.clear(); + mIcon.clear(); + mEnchant.clear(); + mScript.clear(); + } } diff --git a/components/esm/loadclot.hpp b/components/esm/loadclot.hpp index 623983ccf..816d03cb2 100644 --- a/components/esm/loadclot.hpp +++ b/components/esm/loadclot.hpp @@ -46,6 +46,9 @@ struct Clothing void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/loadcont.cpp b/components/esm/loadcont.cpp index e6ba91e7c..853c8bd50 100644 --- a/components/esm/loadcont.cpp +++ b/components/esm/loadcont.cpp @@ -53,4 +53,13 @@ void Container::save(ESMWriter &esm) mInventory.save(esm); } + void Container::blank() + { + mName.clear(); + mModel.clear(); + mScript.clear(); + mWeight = 0; + mFlags = 0; + mInventory.mList.clear(); + } } diff --git a/components/esm/loadcont.hpp b/components/esm/loadcont.hpp index b66ca086d..b2bbab73d 100644 --- a/components/esm/loadcont.hpp +++ b/components/esm/loadcont.hpp @@ -47,6 +47,9 @@ struct Container void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/loadcrea.cpp b/components/esm/loadcrea.cpp index b59835bd6..86d05b8a5 100644 --- a/components/esm/loadcrea.cpp +++ b/components/esm/loadcrea.cpp @@ -7,6 +7,8 @@ namespace ESM { void Creature::load(ESMReader &esm) { + mPersistent = esm.getRecordFlags() & 0x0400; + mModel = esm.getHNString("MODL"); mOriginal = esm.getHNOString("CNAM"); mName = esm.getHNOString("FNAM"); @@ -53,4 +55,28 @@ void Creature::save(ESMWriter &esm) mAiPackage.save(esm); } + void Creature::blank() + { + mData.mType = 0; + mData.mLevel = 0; + mData.mStrength = mData.mIntelligence = mData.mWillpower = mData.mAgility = + mData.mSpeed = mData.mEndurance = mData.mPersonality = mData.mLuck = 0; + mData.mHealth = mData.mMana = mData.mFatigue = 0; + mData.mSoul = 0; + mData.mCombat = mData.mMagic = mData.mStealth = 0; + for (int i=0; i<6; ++i) mData.mAttack[i] = 0; + mData.mGold = 0; + mFlags = 0; + mScale = 0; + mModel.clear(); + mName.clear(); + mScript.clear(); + mOriginal.clear(); + mInventory.mList.clear(); + mSpells.mList.clear(); + mHasAI = false; + mAiData.blank(); + mAiData.mServices = 0; + mAiPackage.mList.clear(); + } } diff --git a/components/esm/loadcrea.hpp b/components/esm/loadcrea.hpp index 1c93d995a..279e2ea3f 100644 --- a/components/esm/loadcrea.hpp +++ b/components/esm/loadcrea.hpp @@ -69,6 +69,9 @@ struct Creature NPDTstruct mData; int mFlags; + + bool mPersistent; + float mScale; std::string mId, mModel, mName, mScript; @@ -84,6 +87,9 @@ struct Creature void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } diff --git a/components/esm/loaddial.hpp b/components/esm/loaddial.hpp index 078c78811..61f3f763d 100644 --- a/components/esm/loaddial.hpp +++ b/components/esm/loaddial.hpp @@ -30,7 +30,7 @@ struct Dialogue }; std::string mId; - char mType; + signed char mType; std::vector mInfo; void load(ESMReader &esm); diff --git a/components/esm/loaddoor.cpp b/components/esm/loaddoor.cpp index b8ef029c5..a4c7b7d58 100644 --- a/components/esm/loaddoor.cpp +++ b/components/esm/loaddoor.cpp @@ -24,4 +24,12 @@ void Door::save(ESMWriter &esm) esm.writeHNOCString("ANAM", mCloseSound); } + void Door::blank() + { + mName.clear(); + mModel.clear(); + mScript.clear(); + mOpenSound.clear(); + mCloseSound.clear(); + } } diff --git a/components/esm/loaddoor.hpp b/components/esm/loaddoor.hpp index e992a592f..77ffc6489 100644 --- a/components/esm/loaddoor.hpp +++ b/components/esm/loaddoor.hpp @@ -15,6 +15,9 @@ struct Door void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/loadfact.cpp b/components/esm/loadfact.cpp index 6ea66977d..e2712d462 100644 --- a/components/esm/loadfact.cpp +++ b/components/esm/loadfact.cpp @@ -1,10 +1,27 @@ #include "loadfact.hpp" +#include + #include "esmreader.hpp" #include "esmwriter.hpp" namespace ESM { + int& Faction::FADTstruct::getSkill (int index, bool ignored) + { + if (index<0 || index>=6) + throw std::logic_error ("skill index out of range"); + + return mSkills[index]; + } + + int Faction::FADTstruct::getSkill (int index, bool ignored) const + { + if (index<0 || index>=6) + throw std::logic_error ("skill index out of range"); + + return mSkills[index]; + } void Faction::load(ESMReader &esm) { @@ -33,7 +50,7 @@ void Faction::load(ESMReader &esm) void Faction::save(ESMWriter &esm) { esm.writeHNCString("FNAM", mName); - + for (int i = 0; i < 10; i++) { if (mRanks[i].empty()) @@ -43,7 +60,7 @@ void Faction::save(ESMWriter &esm) } esm.writeHNT("FADT", mData, 240); - + for (std::vector::iterator it = mReactions.begin(); it != mReactions.end(); ++it) { esm.writeHNString("ANAM", it->mFaction); @@ -51,4 +68,25 @@ void Faction::save(ESMWriter &esm) } } + void Faction::blank() + { + mName.clear(); + mData.mAttribute[0] = mData.mAttribute[1] = 0; + mData.mUnknown = -1; + mData.mIsHidden = 0; + + for (int i=0; i<10; ++i) + { + mData.mRankData[i].mAttribute1 = mData.mRankData[i].mAttribute2 = 0; + mData.mRankData[i].mSkill1 = mData.mRankData[i].mSkill2 = 0; + mData.mRankData[i].mFactReaction = 0; + + mRanks[i].clear(); + } + + for (int i=0; i<6; ++i) + mData.mSkills[i] = 0; + + mReactions.clear(); + } } diff --git a/components/esm/loadfact.hpp b/components/esm/loadfact.hpp index 49898b1cf..891b99647 100644 --- a/components/esm/loadfact.hpp +++ b/components/esm/loadfact.hpp @@ -34,13 +34,19 @@ struct Faction struct FADTstruct { // Which attributes we like - int mAttribute1, mAttribute2; + int mAttribute[2]; RankData mRankData[10]; - int mSkillID[6]; // IDs of skills this faction require + int mSkills[6]; // IDs of skills this faction require int mUnknown; // Always -1? int mIsHidden; // 1 - hidden from player + + int& getSkill (int index, bool ignored = false); + ///< Throws an exception for invalid values of \a index. + + int getSkill (int index, bool ignored = false) const; + ///< Throws an exception for invalid values of \a index. }; // 240 bytes FADTstruct mData; @@ -58,6 +64,9 @@ struct Faction void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID/index). }; } #endif diff --git a/components/esm/loadglob.cpp b/components/esm/loadglob.cpp index ceaa86948..ccb519acd 100644 --- a/components/esm/loadglob.cpp +++ b/components/esm/loadglob.cpp @@ -1,57 +1,24 @@ #include "loadglob.hpp" -#include "esmreader.hpp" -#include "esmwriter.hpp" - namespace ESM { - -void Global::load(ESMReader &esm) -{ - std::string tmp = esm.getHNString("FNAM"); - if (tmp == "s") - mType = VT_Short; - else if (tmp == "l") - mType = VT_Int; - else if (tmp == "f") - mType = VT_Float; - else - esm.fail("Illegal global variable type " + tmp); - - // Note: Both floats and longs are represented as floats. - esm.getHNT(mValue, "FLTV"); -} - -void Global::save(ESMWriter &esm) -{ - switch(mType) + void Global::load (ESMReader &esm) { - case VT_Short: - esm.writeHNString("FNAM", "s"); - break; - - case VT_Int: - esm.writeHNString("FNAM", "l"); - break; - - case VT_Float: - esm.writeHNString("FNAM", "f"); - break; + mValue.read (esm, ESM::Variant::Format_Global); + } - default: - return; + void Global::save (ESMWriter &esm) + { + mValue.write (esm, ESM::Variant::Format_Global); } - esm.writeHNT("FLTV", mValue); -} void Global::blank() { - mValue = 0; - mType = VT_Float; + mValue.setType (ESM::VT_None); } bool operator== (const Global& left, const Global& right) { - return left.mId==right.mId && left.mValue==right.mValue && left.mType==right.mType; + return left.mId==right.mId && left.mValue==right.mValue; } } diff --git a/components/esm/loadglob.hpp b/components/esm/loadglob.hpp index 6111648a6..72e16c0ce 100644 --- a/components/esm/loadglob.hpp +++ b/components/esm/loadglob.hpp @@ -3,7 +3,7 @@ #include -#include "defs.hpp" +#include "variant.hpp" namespace ESM { @@ -18,8 +18,7 @@ class ESMWriter; struct Global { std::string mId; - float mValue; - VarType mType; + Variant mValue; void load(ESMReader &esm); void save(ESMWriter &esm); diff --git a/components/esm/loadgmst.cpp b/components/esm/loadgmst.cpp index a73095a66..fe1cc1b04 100644 --- a/components/esm/loadgmst.cpp +++ b/components/esm/loadgmst.cpp @@ -1,83 +1,39 @@ #include "loadgmst.hpp" -#include - -#include "esmreader.hpp" -#include "esmwriter.hpp" - namespace ESM { - -void GameSetting::load(ESMReader &esm) -{ - assert(mId != ""); - - // We are apparently allowed to be empty - if (!esm.hasMoreSubs()) + void GameSetting::load (ESMReader &esm) { - mType = VT_None; - return; + mValue.read (esm, ESM::Variant::Format_Gmst); } - // Load some data - esm.getSubName(); - NAME n = esm.retSubName(); - if (n == "STRV") + void GameSetting::save (ESMWriter &esm) { - mStr = esm.getHString(); - mType = VT_String; + mValue.write (esm, ESM::Variant::Format_Gmst); } - else if (n == "INTV") + + int GameSetting::getInt() const { - esm.getHT(mI); - mType = VT_Int; + return mValue.getInteger(); } - else if (n == "FLTV") + + float GameSetting::getFloat() const { - esm.getHT(mF); - mType = VT_Float; + return mValue.getFloat(); } - else - esm.fail("Unwanted subrecord type"); -} -void GameSetting::save(ESMWriter &esm) -{ - switch(mType) + std::string GameSetting::getString() const { - case VT_String: esm.writeHNString("STRV", mStr); break; - case VT_Int: esm.writeHNT("INTV", mI); break; - case VT_Float: esm.writeHNT("FLTV", mF); break; - default: break; + return mValue.getString(); } -} -int GameSetting::getInt() const -{ - switch (mType) + void GameSetting::blank() { - case VT_Float: return static_cast (mF); - case VT_Int: return mI; - default: throw std::runtime_error ("GMST " + mId + " is not of a numeric type"); + mValue.setType (ESM::VT_None); } -} -float GameSetting::getFloat() const -{ - switch (mType) + bool operator== (const GameSetting& left, const GameSetting& right) { - case VT_Float: return mF; - case VT_Int: return mI; - default: throw std::runtime_error ("GMST " + mId + " is not of a numeric type"); + return left.mValue==right.mValue; } } - -std::string GameSetting::getString() const -{ - if (mType==VT_String) - return mStr; - - throw std::runtime_error ("GMST " + mId + " is not a string"); -} - -} diff --git a/components/esm/loadgmst.hpp b/components/esm/loadgmst.hpp index ab9a9551e..a6e0c2ecb 100644 --- a/components/esm/loadgmst.hpp +++ b/components/esm/loadgmst.hpp @@ -3,7 +3,7 @@ #include -#include "defs.hpp" +#include "variant.hpp" namespace ESM { @@ -19,24 +19,28 @@ class ESMWriter; struct GameSetting { std::string mId; - // One of these is used depending on the variable type - std::string mStr; - int mI; - float mF; - VarType mType; + + Variant mValue; void load(ESMReader &esm); - + + /// \todo remove the get* functions (redundant, since mValue as equivalent functions now). + int getInt() const; ///< Throws an exception if GMST is not of type int or float. - + float getFloat() const; ///< Throws an exception if GMST is not of type int or float. - + std::string getString() const; ///< Throwns an exception if GMST is not of type string. void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; + + bool operator== (const GameSetting& left, const GameSetting& right); } #endif diff --git a/components/esm/loadinfo.cpp b/components/esm/loadinfo.cpp index f237cf780..90f8fcf35 100644 --- a/components/esm/loadinfo.cpp +++ b/components/esm/loadinfo.cpp @@ -84,22 +84,8 @@ void DialInfo::load(ESMReader &esm) SelectStruct ss; ss.mSelectRule = esm.getHString(); - esm.isEmptyOrGetName(); - - if (subName.val == REC_INTV) - { - ss.mType = VT_Int; - esm.getHT(ss.mI); - } - else if (subName.val == REC_FLTV) - { - ss.mType = VT_Float; - esm.getHT(ss.mF); - } - else - esm.fail( - "INFO.SCVR must precede INTV or FLTV, not " - + subName.toString()); + + ss.mValue.read (esm, Variant::Format_Info); mSelects.push_back(ss); @@ -152,16 +138,11 @@ void DialInfo::save(ESMWriter &esm) for (std::vector::iterator it = mSelects.begin(); it != mSelects.end(); ++it) { esm.writeHNString("SCVR", it->mSelectRule); - switch(it->mType) - { - case VT_Int: esm.writeHNT("INTV", it->mI); break; - case VT_Float: esm.writeHNT("FLTV", it->mF); break; - default: break; - } + it->mValue.write (esm, Variant::Format_Info); } esm.writeHNOString("BNAM", mResultScript); - + switch(mQuestStatus) { case QS_Name: esm.writeHNT("QSTN",'\1'); break; diff --git a/components/esm/loadinfo.hpp b/components/esm/loadinfo.hpp index f1decb9c6..2361ed9eb 100644 --- a/components/esm/loadinfo.hpp +++ b/components/esm/loadinfo.hpp @@ -5,6 +5,7 @@ #include #include "defs.hpp" +#include "variant.hpp" namespace ESM { @@ -12,8 +13,6 @@ namespace ESM class ESMReader; class ESMWriter; -// NOT DONE - /* * Dialogue information. A series of these follow after DIAL records, * and form a linked list of dialogue items. @@ -43,9 +42,7 @@ struct DialInfo struct SelectStruct { std::string mSelectRule; // This has a complicated format - float mF; // Only one of 'f' or 'i' is used - int mI; - VarType mType; + Variant mValue; }; // Journal quest indices (introduced with the quest system in Tribunal) @@ -93,8 +90,7 @@ struct DialInfo REC_SNAM = 0x4d414e53, REC_NAME = 0x454d414e, REC_SCVR = 0x52564353, - REC_INTV = 0x56544e49, - REC_FLTV = 0x56544c46, + REC_BNAM = 0x4d414e42, REC_QSTN = 0x4e545351, REC_QSTF = 0x46545351, @@ -106,42 +102,5 @@ struct DialInfo void save(ESMWriter &esm); }; -/* - Some old and unused D code and comments, that might be useful later: - -------- - - // We only need to put each item in ONE list. For if your NPC - // matches this response, then it must match ALL criteria, thus it - // will have to look up itself in all the lists. I think the order - // is well optimized in making the lists as small as possible. - if(this.actor.index != -1) actorDial[this.actor][parent]++; - else if(cell != "") cellDial[cell][parent]++; - else if(this.Class != -1) classDial[this.Class][parent]++; - else if(this.npcFaction != -1) - factionDial[this.npcFaction][parent]++; - else if(this.race != -1) raceDial[this.race][parent]++; - else allDial[parent]++; // Lists dialogues that might - // apply to all npcs. - */ - -// List of dialogue topics (and greetings, voices, etc.) that -// reference other objects. Eg. raceDial is indexed by the indices of -// all races referenced. The value of raceDial is a new AA, which is -// basically used as a map (the int value is just a count and isn't -// used for anything important.) The indices (or elements of the map) -// are the dialogues that reference the given race. I use an AA -// instead of a list or array, since each dialogue can be added lots -// of times. - -/* -int allDial[Dialogue*]; -int classDial[int][Dialogue*]; -int factionDial[int][Dialogue*]; -int actorDial[Item][Dialogue*]; -// If I look up cells on cell load, I don't have to resolve these -// names into anything! -int cellDial[char[]][Dialogue*]; -int raceDial[int][Dialogue*]; -*/ } #endif diff --git a/components/esm/loadingr.cpp b/components/esm/loadingr.cpp index 4312dc05b..7e31a4116 100644 --- a/components/esm/loadingr.cpp +++ b/components/esm/loadingr.cpp @@ -24,7 +24,7 @@ void Ingredient::load(ESMReader &esm) { mData.mAttributes[i] = -1; } - + // is this relevant in cycle from 0 to 4? if (mData.mEffectID[i] != 89 && mData.mEffectID[i] != 26 && @@ -46,4 +46,20 @@ void Ingredient::save(ESMWriter &esm) esm.writeHNOCString("ITEX", mIcon); } + void Ingredient::blank() + { + mData.mWeight = 0; + mData.mValue = 0; + for (int i=0; i<4; ++i) + { + mData.mEffectID[i] = 0; + mData.mSkills[i] = 0; + mData.mAttributes[i] = 0; + } + + mName.clear(); + mModel.clear(); + mIcon.clear(); + mScript.clear(); + } } diff --git a/components/esm/loadingr.hpp b/components/esm/loadingr.hpp index cd63cf39a..5e286535f 100644 --- a/components/esm/loadingr.hpp +++ b/components/esm/loadingr.hpp @@ -29,6 +29,9 @@ struct Ingredient void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index e1af0ee7c..89b48c27d 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -81,6 +81,7 @@ Land::~Land() void Land::load(ESMReader &esm) { mEsm = &esm; + mPlugin = mEsm->getIndex(); // Get the grid location esm.getSubNameIs("INTV"); diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 5bc439c96..c1cce5e7e 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -23,6 +23,7 @@ struct Land int mFlags; // Only first four bits seem to be used, don't know what // they mean. int mX, mY; // Map coordinates. + int mPlugin; // Plugin index, used to reference the correct material palette. // File context. This allows the ESM reader to be 'reset' to this // location later when we are ready to load the full data set. diff --git a/components/esm/loadlevlist.cpp b/components/esm/loadlevlist.cpp index 4cba1119b..b54a91276 100644 --- a/components/esm/loadlevlist.cpp +++ b/components/esm/loadlevlist.cpp @@ -38,7 +38,7 @@ void LeveledListBase::save(ESMWriter &esm) esm.writeHNT("DATA", mFlags); esm.writeHNT("NNAM", mChanceNone); esm.writeHNT("INDX", mList.size()); - + for (std::vector::iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNCString(mRecName, it->mId); @@ -46,4 +46,10 @@ void LeveledListBase::save(ESMWriter &esm) } } + void LeveledListBase::blank() + { + mFlags = 0; + mChanceNone = 0; + mList.clear(); + } } diff --git a/components/esm/loadlevlist.hpp b/components/esm/loadlevlist.hpp index b7db5db36..7339cac56 100644 --- a/components/esm/loadlevlist.hpp +++ b/components/esm/loadlevlist.hpp @@ -15,24 +15,27 @@ class ESMWriter; * to implement it once. * * We should later implement the ability to merge leveled lists from - * several files. + * several files. */ struct LeveledListBase { enum Flags { - AllLevels = 0x01, // Calculate from all levels <= player - // level, not just the closest below - // player. - Each = 0x02 // Select a new item each time this + + Each = 0x01, // Select a new item each time this // list is instantiated, instead of // giving several identical items - }; // (used when a container has more + // (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-255?) + unsigned char mChanceNone; // Chance that none are selected (0-100) std::string mId; // Record name used to read references. Must be set before load() is @@ -49,6 +52,9 @@ struct LeveledListBase void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; struct CreatureLevList: LeveledListBase diff --git a/components/esm/loadligh.cpp b/components/esm/loadligh.cpp index 48a56db3c..89a2b8c65 100644 --- a/components/esm/loadligh.cpp +++ b/components/esm/loadligh.cpp @@ -26,4 +26,18 @@ void Light::save(ESMWriter &esm) esm.writeHNOCString("SNAM", mSound); } + void Light::blank() + { + mData.mWeight = 0; + mData.mValue = 0; + mData.mTime = 0; + mData.mRadius = 0; + mData.mColor = 0; + mData.mFlags = 0; + mSound.clear(); + mScript.clear(); + mModel.clear(); + mIcon.clear(); + mName.clear(); + } } diff --git a/components/esm/loadligh.hpp b/components/esm/loadligh.hpp index b3d703cc2..3f0b76d6e 100644 --- a/components/esm/loadligh.hpp +++ b/components/esm/loadligh.hpp @@ -45,6 +45,9 @@ struct Light void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/loadlock.cpp b/components/esm/loadlock.cpp new file mode 100644 index 000000000..03eac52bd --- /dev/null +++ b/components/esm/loadlock.cpp @@ -0,0 +1,41 @@ +#include "loadlock.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +namespace ESM +{ + +void Lockpick::load(ESMReader &esm) +{ + mModel = esm.getHNString("MODL"); + mName = esm.getHNString("FNAM"); + + esm.getHNT(mData, "LKDT", 16); + + mScript = esm.getHNOString("SCRI"); + mIcon = esm.getHNOString("ITEX"); +} + +void Lockpick::save(ESMWriter &esm) +{ + esm.writeHNCString("MODL", mModel); + esm.writeHNCString("FNAM", mName); + + esm.writeHNT("LKDT", mData, 16); + esm.writeHNOString("SCRI", mScript); + esm.writeHNOCString("ITEX", mIcon); +} + + void Lockpick::blank() + { + mData.mWeight = 0; + mData.mValue = 0; + mData.mQuality = 0; + mData.mUses = 0; + mName.clear(); + mModel.clear(); + mIcon.clear(); + mScript.clear(); + } +} diff --git a/components/esm/loadlock.hpp b/components/esm/loadlock.hpp new file mode 100644 index 000000000..953066cb2 --- /dev/null +++ b/components/esm/loadlock.hpp @@ -0,0 +1,34 @@ +#ifndef OPENMW_ESM_LOCK_H +#define OPENMW_ESM_LOCK_H + +#include + +namespace ESM +{ + +class ESMReader; +class ESMWriter; + +struct Lockpick +{ + struct Data + { + float mWeight; + int mValue; + + float mQuality; + int mUses; + }; // Size = 16 + + Data mData; + std::string mId, mName, mModel, mIcon, mScript; + + void load(ESMReader &esm); + void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). +}; + +} +#endif diff --git a/components/esm/loadlocks.cpp b/components/esm/loadlocks.cpp deleted file mode 100644 index 057da595e..000000000 --- a/components/esm/loadlocks.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "loadlocks.hpp" - -#include "esmreader.hpp" -#include "esmwriter.hpp" - -namespace ESM -{ - -void Tool::load(ESMReader &esm) -{ - mModel = esm.getHNString("MODL"); - mName = esm.getHNString("FNAM"); - - esm.getSubName(); - NAME n = esm.retSubName(); - // The data name varies, RIDT for repair items, LKDT for lock - // picks, PBDT for probes - - esm.getHT(mData, 16); - - if (n == "RIDT") - { - mType = Type_Repair; - // Swap t.data.quality and t.data.uses for repair items (sigh) - float tmp = *((float*) &mData.mUses); - mData.mUses = *((int*) &mData.mQuality); - mData.mQuality = tmp; - } - else if (n == "LKDT") - mType = Type_Pick; - else if (n == "PBDT") - mType = Type_Probe; - - mScript = esm.getHNOString("SCRI"); - mIcon = esm.getHNOString("ITEX"); -} - -void Tool::save(ESMWriter &esm) -{ - esm.writeHNCString("MODL", mModel); - esm.writeHNCString("FNAM", mName); - - std::string typeName; - switch(mType) - { - case Type_Repair: typeName = "RIDT"; break; - case Type_Pick: typeName = "LKDT"; break; - case Type_Probe: typeName = "PBDT"; break; - } - - Data write = mData; - if (mType == Type_Repair) - { - float tmp = *((float*) &write.mUses); - write.mUses = *((int*) &write.mQuality); - write.mQuality = tmp; - } - - esm.writeHNT(typeName, write, 16); - esm.writeHNOString("SCRI", mScript); - esm.writeHNOCString("ITEX", mIcon); -} - - -} diff --git a/components/esm/loadlocks.hpp b/components/esm/loadlocks.hpp deleted file mode 100644 index 8e88c548e..000000000 --- a/components/esm/loadlocks.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef OPENMW_ESM_LOCKS_H -#define OPENMW_ESM_LOCKS_H - -#include - -namespace ESM -{ - -class ESMReader; -class ESMWriter; - -/* - * This file covers lockpicks (LOCK), probes (PROB) and armor repair - * items (REPA). These have nearly identical data structures. - */ - -struct Tool -{ - enum Type - { - Type_Pick, - Type_Probe, - Type_Repair - }; - - struct Data - { - float mWeight; - int mValue; - - float mQuality; // And when I say nearly identical structure, I - int mUses; // mean perfectly identical except that these two - // variables are swaped for repair items. Don't ask - // me why. - }; // Size = 16 - - Data mData; - Type mType; - std::string mId, mName, mModel, mIcon, mScript; - - void load(ESMReader &esm); - void save(ESMWriter &esm); -}; - -struct Probe: Tool -{ - Probe() { mType = Type_Probe; } -}; - -struct Repair: Tool -{ - Repair() { mType = Type_Repair; } -}; - -} -#endif diff --git a/components/esm/loadmgef.hpp b/components/esm/loadmgef.hpp index 93c59f9cb..7efc17aec 100644 --- a/components/esm/loadmgef.hpp +++ b/components/esm/loadmgef.hpp @@ -66,6 +66,158 @@ struct MagicEffect void load(ESMReader &esm); void save(ESMWriter &esm); + + + enum Effects + { + WaterBreathing = 0, + SwiftSwim = 1, + WaterWalking = 2, + Shield = 3, + FireShield = 4, + LightningShield = 5, + FrostShield = 6, + Burden = 7, + Feather = 8, + Jump = 9, + Levitate = 10, + SlowFall = 11, + Lock = 12, + Open = 13, + FireDamage = 14, + ShockDamage = 15, + FrostDamage = 16, + DrainAttribute = 17, + DrainHealth = 18, + DrainMagicka = 19, + DrainFatigue = 20, + DrainSkill = 21, + DamageAttribute = 22, + DamageHealth = 23, + DamageMagicka = 24, + DamageFatigue = 25, + DamageSkill = 26, + Poison = 27, + WeaknessToFire = 28, + WeaknessToFrost = 29, + WeaknessToShock = 30, + WeaknessToMagicka = 31, + WeaknessToCommonDisease = 32, + WeaknessToBlightDisease = 33, + WeaknessToCorprusDisease = 34, + WeaknessToPoison = 35, + WeaknessToNormalWeapons = 36, + DisintegrateWeapon = 37, + DisintegrateArmor = 38, + Invisibility = 39, + Chameleon = 40, + Light = 41, + Sanctuary = 42, + NightEye = 43, + Charm = 44, + Paralyze = 45, + Silence = 46, + Blind = 47, + Sound = 48, + CalmHumanoid = 49, + CalmCreature = 50, + FrenzyHumanoid = 51, + FrenzyCreature = 52, + DemoralizeHumanoid = 53, + DemoralizeCreature = 54, + RallyHumanoid = 55, + RallyCreature = 56, + Dispel = 57, + Soultrap = 58, + Telekinesis = 59, + Mark = 60, + Recall = 61, + DivineIntervention = 62, + AlmsiviIntervention = 63, + DetectAnimal = 64, + DetectEnchantment = 65, + DetectKey = 66, + SpellAbsorption = 67, + Reflect = 68, + CureCommonDisease = 69, + CureBlightDisease = 70, + CureCorprusDisease = 71, + CurePoison = 72, + CureParalyzation = 73, + RestoreAttribute = 74, + RestoreHealth = 75, + RestoreMagicka = 76, + RestoreFatigue = 77, + RestoreSkill = 78, + FortifyAttribute = 79, + FortifyHealth = 80, + FortifyMagicka= 81, + FortifyFatigue = 82, + FortifySkill = 83, + FortifyMaximumMagicka = 84, + AbsorbAttribute = 85, + AbsorbHealth = 86, + AbsorbMagicka = 87, + AbsorbFatigue = 88, + AbsorbSkill = 89, + ResistFire = 90, + ResistFrost = 91, + ResistShock = 92, + ResistMagicka = 93, + ResistCommonDisease = 94, + ResistBlightDisease = 95, + ResistCorprusDisease = 96, + ResistPoison = 97, + ResistNormalWeapons = 98, + ResistParalysis = 99, + RemoveCurse = 100, + TurnUndead = 101, + SummonScamp = 102, + SummonClannfear = 103, + SummonDaedroth = 104, + SummonDremora = 105, + SummonAncestralGhost = 106, + SummonSkeletalMinion = 107, + SummonBonewalker = 108, + SummonGreaterBonewalker = 109, + SummonBonelord = 110, + SummonWingedTwilight = 111, + SummonHunger = 112, + SummonGoldenSaint = 113, + SummonFlameAtronach = 114, + SummonFrostAtronach = 115, + SummonStormAtronach = 116, + FortifyAttack = 117, + CommandCreature = 118, + CommandHumanoid = 119, + BoundDagger = 120, + BoundLongsword = 121, + BoundMace = 122, + BoundBattleAxe = 123, + BoundSpear = 124, + BoundLongbow = 125, + ExtraSpell = 126, + BoundCuirass = 127, + BoundHelm = 128, + BoundBoots = 129, + BoundShield = 130, + BoundGloves = 131, + Corprus = 132, + Vampirism = 133, + SummonCenturionSphere = 134, + SunDamage = 135, + StuntedMagicka = 136, + + // Tribunal only + SummonFabricant = 137, + + // Bloodmoon only + SummonWolf = 138, + SummonBear = 139, + SummonBonewolf = 140, + SummonCreature04 = 141, + SummonCreature05 = 142 + }; }; } #endif diff --git a/components/esm/loadmisc.cpp b/components/esm/loadmisc.cpp index 3a5dded15..6006334ea 100644 --- a/components/esm/loadmisc.cpp +++ b/components/esm/loadmisc.cpp @@ -23,4 +23,14 @@ void Miscellaneous::save(ESMWriter &esm) esm.writeHNOCString("ITEX", mIcon); } + void Miscellaneous::blank() + { + mData.mWeight = 0; + mData.mValue = 0; + mData.mIsKey = 0; + mName.clear(); + mModel.clear(); + mIcon.clear(); + mScript.clear(); + } } diff --git a/components/esm/loadmisc.hpp b/components/esm/loadmisc.hpp index 26d25139c..25a9c5865 100644 --- a/components/esm/loadmisc.hpp +++ b/components/esm/loadmisc.hpp @@ -30,6 +30,9 @@ struct Miscellaneous void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/loadnpc.cpp b/components/esm/loadnpc.cpp index 72d0b3736..7e17a93dc 100644 --- a/components/esm/loadnpc.cpp +++ b/components/esm/loadnpc.cpp @@ -10,6 +10,8 @@ void NPC::load(ESMReader &esm) { mNpdt52.mGold = -10; + mPersistent = esm.getRecordFlags() & 0x0400; + mModel = esm.getHNOString("MODL"); mName = esm.getHNOString("FNAM"); @@ -56,7 +58,7 @@ void NPC::load(ESMReader &esm) mTransport.push_back(dodt); } else if (esm.retSubName() == 0x4d414e44) { // DNAM struct mTransport.back().mCellName = esm.getHString(); - } + } } mAiPackage.load(esm); esm.skipRecord(); @@ -71,14 +73,14 @@ void NPC::save(ESMWriter &esm) esm.writeHNCString("BNAM", mHead); esm.writeHNCString("KNAM", mHair); esm.writeHNOCString("SCRI", mScript); - + if (mNpdtType == 52) esm.writeHNT("NPDT", mNpdt52, 52); else if (mNpdtType == 12) esm.writeHNT("NPDT", mNpdt12, 12); esm.writeHNT("FLAG", mFlags); - + mInventory.save(esm); mSpells.save(esm); if (mHasAI) { @@ -93,4 +95,53 @@ void NPC::save(ESMWriter &esm) mAiPackage.save(esm); } + bool NPC::isMale() const { + return (mFlags & Female) == 0; + } + + void NPC::setIsMale(bool value) { + mFlags |= Female; + if (value) { + mFlags ^= Female; + } + } + + void NPC::blank() + { + mNpdtType = 0; + 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; + mNpdt52.mReputation = 0; + mNpdt52.mHealth = mNpdt52.mMana = mNpdt52.mFatigue = 0; + mNpdt52.mDisposition = 0; + mNpdt52.mFactionID = 0; + mNpdt52.mRank = 0; + mNpdt52.mUnknown = 0; + mNpdt52.mGold = 0; + mNpdt12.mLevel = 0; + mNpdt12.mDisposition = 0; + mNpdt12.mReputation = 0; + mNpdt12.mRank = 0; + mNpdt12.mUnknown1 = 0; + mNpdt12.mUnknown2 = 0; + mNpdt12.mUnknown3 = 0; + mNpdt12.mGold = 0; + mFlags = 0; + mInventory.mList.clear(); + mSpells.mList.clear(); + mAiData.blank(); + mHasAI = false; + mTransport.clear(); + mAiPackage.mList.clear(); + mName.clear(); + mModel.clear(); + mRace.clear(); + mClass.clear(); + mFaction.clear(); + mScript.clear(); + mHair.clear(); + mHead.clear(); + } } diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index 46be29961..009bc5ef3 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -35,11 +35,11 @@ struct NPC Apparatus = 0x00100, RepairItem = 0x00200, Misc = 0x00400, + Potions = 0x02000, // Other services Spells = 0x00800, MagicItems = 0x01000, - Potions = 0x02000, Training = 0x04000, // What skills? Spellmaking = 0x08000, Enchanting = 0x10000, @@ -62,7 +62,7 @@ struct NPC struct NPDTstruct52 { short mLevel; - char mStrength, + unsigned char mStrength, mIntelligence, mWillpower, mAgility, @@ -100,6 +100,8 @@ struct NPC int mFlags; + bool mPersistent; + InventoryList mInventory; SpellList mSpells; @@ -114,20 +116,15 @@ struct NPC // body parts std::string mHair, mHead; - // Implementation moved to load_impl.cpp void load(ESMReader &esm); void save(ESMWriter &esm); - bool isMale() const { - return (mFlags & Female) == 0; - } + bool isMale() const; + + void setIsMale(bool value); - void setIsMale(bool value) { - mFlags |= Female; - if (value) { - mFlags ^= Female; - } - } + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/loadprob.cpp b/components/esm/loadprob.cpp new file mode 100644 index 000000000..729f8404e --- /dev/null +++ b/components/esm/loadprob.cpp @@ -0,0 +1,41 @@ +#include "loadprob.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +namespace ESM +{ + +void Probe::load(ESMReader &esm) +{ + mModel = esm.getHNString("MODL"); + mName = esm.getHNString("FNAM"); + + esm.getHNT(mData, "PBDT", 16); + + mScript = esm.getHNOString("SCRI"); + mIcon = esm.getHNOString("ITEX"); +} + +void Probe::save(ESMWriter &esm) +{ + esm.writeHNCString("MODL", mModel); + esm.writeHNCString("FNAM", mName); + + esm.writeHNT("PBDT", mData, 16); + esm.writeHNOString("SCRI", mScript); + esm.writeHNOCString("ITEX", mIcon); +} + + void Probe::blank() + { + mData.mWeight = 0; + mData.mValue = 0; + mData.mQuality = 0; + mData.mUses = 0; + mName.clear(); + mModel.clear(); + mIcon.clear(); + mScript.clear(); + } +} diff --git a/components/esm/loadprob.hpp b/components/esm/loadprob.hpp new file mode 100644 index 000000000..55b896bcd --- /dev/null +++ b/components/esm/loadprob.hpp @@ -0,0 +1,34 @@ +#ifndef OPENMW_ESM_PROBE_H +#define OPENMW_ESM_PROBE_H + +#include + +namespace ESM +{ + +class ESMReader; +class ESMWriter; + +struct Probe +{ + struct Data + { + float mWeight; + int mValue; + + float mQuality; + int mUses; + }; // Size = 16 + + Data mData; + std::string mId, mName, mModel, mIcon, mScript; + + void load(ESMReader &esm); + void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). +}; + +} +#endif diff --git a/components/esm/loadrace.cpp b/components/esm/loadrace.cpp index 139ef081c..955424e2b 100644 --- a/components/esm/loadrace.cpp +++ b/components/esm/loadrace.cpp @@ -5,6 +5,15 @@ namespace ESM { + int Race::MaleFemale::getValue (bool male) const + { + return male ? mMale : mFemale; + } + + int Race::MaleFemaleF::getValue (bool male) const + { + return male ? mMale : mFemale; + } void Race::load(ESMReader &esm) { @@ -21,4 +30,25 @@ void Race::save(ESMWriter &esm) esm.writeHNOString("DESC", mDescription); } + void Race::blank() + { + mName.clear(); + mDescription.clear(); + + mPowers.mList.clear(); + + for (int i=0; i<7; ++i) + { + mData.mBonus[i].mSkill = -1; + mData.mBonus[i].mBonus = 0; + } + + for (int i=0; i<8; ++i) + mData.mAttributeValues[i].mMale = mData.mAttributeValues[i].mFemale = 1; + + mData.mHeight.mMale = mData.mHeight.mFemale = 1; + mData.mWeight.mMale = mData.mWeight.mFemale = 1; + + mData.mFlags = 0; + } } diff --git a/components/esm/loadrace.hpp b/components/esm/loadrace.hpp index 91a424c10..6ecec8ebb 100644 --- a/components/esm/loadrace.hpp +++ b/components/esm/loadrace.hpp @@ -26,11 +26,15 @@ struct Race struct MaleFemale { int mMale, mFemale; + + int getValue (bool male) const; }; struct MaleFemaleF { float mMale, mFemale; + + int getValue (bool male) const; }; enum Flags @@ -45,14 +49,7 @@ struct Race SkillBonus mBonus[7]; // Attribute values for male/female - MaleFemale mStrength, - mIntelligence, - mWillpower, - mAgility, - mSpeed, - mEndurance, - mPersonality, - mLuck; + MaleFemale mAttributeValues[8]; // The actual eye level height (in game units) is (probably) given // as 'height' times 128. This has not been tested yet. @@ -69,6 +66,9 @@ struct Race void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID/index). }; } diff --git a/components/esm/loadregn.cpp b/components/esm/loadregn.cpp index d39a29454..41c7f507a 100644 --- a/components/esm/loadregn.cpp +++ b/components/esm/loadregn.cpp @@ -31,14 +31,14 @@ void Region::load(ESMReader &esm) void Region::save(ESMWriter &esm) { esm.writeHNCString("FNAM", mName); - + if (esm.getVersion() == VER_12) esm.writeHNT("WEAT", mData, sizeof(mData) - 2); else esm.writeHNT("WEAT", mData); - + esm.writeHNOCString("BNAM", mSleepList); - + esm.writeHNT("CNAM", mMapColor); for (std::vector::iterator it = mSoundList.begin(); it != mSoundList.end(); ++it) { @@ -46,4 +46,17 @@ void Region::save(ESMWriter &esm) } } + void Region::blank() + { + mName.clear(); + + mData.mClear = mData.mCloudy = mData.mFoggy = mData.mOvercast = mData.mRain = + mData.mThunder = mData.mAsh, mData.mBlight = mData.mA = mData.mB = 0; + + mMapColor = 0; + + mName.clear(); + mSleepList.clear(); + mSoundList.clear(); + } } diff --git a/components/esm/loadregn.hpp b/components/esm/loadregn.hpp index 0496ef5af..f2a3d9a10 100644 --- a/components/esm/loadregn.hpp +++ b/components/esm/loadregn.hpp @@ -48,6 +48,9 @@ struct Region void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID/index). }; } #endif diff --git a/components/esm/loadrepa.cpp b/components/esm/loadrepa.cpp new file mode 100644 index 000000000..ced6daa2e --- /dev/null +++ b/components/esm/loadrepa.cpp @@ -0,0 +1,41 @@ +#include "loadrepa.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +namespace ESM +{ + +void Repair::load(ESMReader &esm) +{ + mModel = esm.getHNString("MODL"); + mName = esm.getHNString("FNAM"); + + esm.getHNT(mData, "RIDT", 16); + + mScript = esm.getHNOString("SCRI"); + mIcon = esm.getHNOString("ITEX"); +} + +void Repair::save(ESMWriter &esm) +{ + esm.writeHNCString("MODL", mModel); + esm.writeHNCString("FNAM", mName); + + esm.writeHNT("RIDT", mData, 16); + esm.writeHNOString("SCRI", mScript); + esm.writeHNOCString("ITEX", mIcon); +} + + void Repair::blank() + { + mData.mWeight = 0; + mData.mValue = 0; + mData.mQuality = 0; + mData.mUses = 0; + mName.clear(); + mModel.clear(); + mIcon.clear(); + mScript.clear(); + } +} diff --git a/components/esm/loadrepa.hpp b/components/esm/loadrepa.hpp new file mode 100644 index 000000000..83812bad9 --- /dev/null +++ b/components/esm/loadrepa.hpp @@ -0,0 +1,34 @@ +#ifndef OPENMW_ESM_REPA_H +#define OPENMW_ESM_REPA_H + +#include + +namespace ESM +{ + +class ESMReader; +class ESMWriter; + +struct Repair +{ + struct Data + { + float mWeight; + int mValue; + + int mUses; + float mQuality; + }; // Size = 16 + + Data mData; + std::string mId, mName, mModel, mIcon, mScript; + + void load(ESMReader &esm); + void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). +}; + +} +#endif diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index d9b6497d9..2c1b018d9 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -64,7 +64,7 @@ void Script::save(ESMWriter &esm) memcpy(data.mName.name, mId.c_str(), mId.size()); esm.writeHNT("SCHD", data, 52); - + if (!mVarNames.empty()) { esm.startSubRecord("SCVR"); @@ -76,10 +76,21 @@ void Script::save(ESMWriter &esm) } esm.startSubRecord("SCDT"); - esm.write(&mScriptData[0], mData.mScriptDataSize); + esm.write(reinterpret_cast(&mScriptData[0]), mData.mScriptDataSize); esm.endRecord("SCDT"); esm.writeHNOString("SCTX", mScriptText); } + void Script::blank() + { + mData.mNumShorts = mData.mNumLongs = mData.mNumFloats = 0; + mData.mScriptDataSize = 0; + mData.mStringTableSize = 0; + + mVarNames.clear(); + mScriptData.clear(); + mScriptText = "Begin " + mId + "\n\nEnd " + mId + "\n"; + } + } diff --git a/components/esm/loadscpt.hpp b/components/esm/loadscpt.hpp index 10a6d24b1..be7e83900 100644 --- a/components/esm/loadscpt.hpp +++ b/components/esm/loadscpt.hpp @@ -52,11 +52,14 @@ public: SCHDstruct mData; std::vector mVarNames; // Variable names - std::vector mScriptData; // Compiled bytecode + std::vector mScriptData; // Compiled bytecode std::string mScriptText; // Uncompiled script void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID/index). }; } #endif diff --git a/components/esm/loadskil.cpp b/components/esm/loadskil.cpp index a4d21c591..b57645f3b 100644 --- a/components/esm/loadskil.cpp +++ b/components/esm/loadskil.cpp @@ -1,5 +1,9 @@ #include "loadskil.hpp" +#include + +#include + #include "esmreader.hpp" #include "esmwriter.hpp" @@ -98,11 +102,44 @@ void Skill::load(ESMReader &esm) esm.getHNT(mIndex, "INDX"); esm.getHNT(mData, "SKDT", 24); mDescription = esm.getHNOString("DESC"); + + // create an ID from the index and the name (only used in the editor and likely to change in the + // future) + mId = indexToId (mIndex); } + void Skill::save(ESMWriter &esm) { esm.writeHNT("INDX", mIndex); esm.writeHNT("SKDT", mData, 24); esm.writeHNOString("DESC", mDescription); } + + void Skill::blank() + { + mData.mAttribute = 0; + mData.mSpecialization = 0; + mData.mUseValue[0] = mData.mUseValue[1] = mData.mUseValue[2] = mData.mUseValue[3] = 1.0; + mDescription.clear(); + } + + std::string Skill::indexToId (int index) + { + std::ostringstream stream; + + if (index!=-1) + { + stream << "#"; + + if (index<10) + stream << "0"; + + stream << index; + + if (index>=0 && index0) + esm.writeHNT ("FORM", mFormat); + + for (std::vector::iterator iter = mMaster.begin(); + iter != mMaster.end(); ++iter) + { + esm.writeHNCString ("MAST", iter->name); + esm.writeHNT ("DATA", iter->size); + } +} \ No newline at end of file diff --git a/components/esm/loadtes3.hpp b/components/esm/loadtes3.hpp new file mode 100644 index 000000000..b73a4c31e --- /dev/null +++ b/components/esm/loadtes3.hpp @@ -0,0 +1,55 @@ +#ifndef COMPONENT_ESM_TES3_H +#define COMPONENT_ESM_TES3_H + +#include + +#include "esmcommon.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + +#pragma pack(push) +#pragma pack(1) + + /// \brief File header record + struct Header + { + static const int CurrentFormat = 0; // most recent known format + + struct Data + { + /* File format version. This is actually a float, the supported + versions are 1.2 and 1.3. These correspond to: + 1.2 = 0x3f99999a and 1.3 = 0x3fa66666 + */ + int version; + int type; // 0=esp, 1=esm, 32=ess (unused) + NAME32 author; // Author's name + NAME256 desc; // File description + int records; // Number of records? Not used. + }; + + // Defines another files (esm or esp) that this file depends upon. + struct MasterData + { + std::string name; + uint64_t size; + int index; // Position of the parent file in the global list of loaded files + }; + + Data mData; + int mFormat; + std::vector mMaster; + + void blank(); + + void load (ESMReader &esm); + void save (ESMWriter &esm); + }; +#pragma pack(pop) + +} + +#endif \ No newline at end of file diff --git a/components/esm/loadweap.cpp b/components/esm/loadweap.cpp index 18d37e56c..253712396 100644 --- a/components/esm/loadweap.cpp +++ b/components/esm/loadweap.cpp @@ -25,4 +25,24 @@ void Weapon::save(ESMWriter &esm) esm.writeHNOCString("ENAM", mEnchant); } + void Weapon::blank() + { + mData.mWeight = 0; + mData.mValue = 0; + mData.mType = 0; + mData.mHealth = 0; + mData.mSpeed = 0; + mData.mReach = 0; + mData.mEnchant = 0; + mData.mChop[0] = mData.mChop[1] = 0; + mData.mSlash[0] = mData.mSlash[1] = 0; + mData.mThrust[0] = mData.mThrust[1] = 0; + mData.mFlags = 0; + + mName.clear(); + mModel.clear(); + mIcon.clear(); + mEnchant.clear(); + mScript.clear(); + } } diff --git a/components/esm/loadweap.hpp b/components/esm/loadweap.hpp index e482d3b10..b62179ccb 100644 --- a/components/esm/loadweap.hpp +++ b/components/esm/loadweap.hpp @@ -48,7 +48,7 @@ struct Weapon short mType; short mHealth; float mSpeed, mReach; - short mEnchant; // Enchantment points + short mEnchant; // Enchantment points. The real value is mEnchant/10.f unsigned char mChop[2], mSlash[2], mThrust[2]; // Min and max int mFlags; }; // 32 bytes @@ -60,6 +60,9 @@ struct Weapon void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; } #endif diff --git a/components/esm/records.hpp b/components/esm/records.hpp index 0662c797c..7a0452eb3 100644 --- a/components/esm/records.hpp +++ b/components/esm/records.hpp @@ -26,7 +26,9 @@ #include "loadland.hpp" #include "loadlevlist.hpp" #include "loadligh.hpp" -#include "loadlocks.hpp" +#include "loadlock.hpp" +#include "loadrepa.hpp" +#include "loadprob.hpp" #include "loadltex.hpp" #include "loadmgef.hpp" #include "loadmisc.hpp" diff --git a/components/esm/variant.cpp b/components/esm/variant.cpp new file mode 100644 index 000000000..a7859d128 --- /dev/null +++ b/components/esm/variant.cpp @@ -0,0 +1,282 @@ +#include "variant.hpp" + +#include +#include + +#include "esmreader.hpp" +#include "variantimp.hpp" + +ESM::Variant::Variant() : mType (VT_None), mData (0) {} + +ESM::Variant::~Variant() +{ + delete mData; +} + +ESM::Variant& ESM::Variant::operator= (const Variant& variant) +{ + if (&variant!=this) + { + VariantDataBase *newData = variant.mData ? variant.mData->clone() : 0; + + delete mData; + + mType = variant.mType; + mData = newData; + } + + return *this; +} + +ESM::Variant::Variant (const Variant& variant) +: mType (variant.mType), mData (variant.mData ? variant.mData->clone() : 0) +{} + +ESM::VarType ESM::Variant::getType() const +{ + return mType; +} + +std::string ESM::Variant::getString() const +{ + if (!mData) + throw std::runtime_error ("can not convert empty variant to string"); + + return mData->getString(); +} + +int ESM::Variant::getInteger() const +{ + if (!mData) + throw std::runtime_error ("can not convert empty variant to integer"); + + return mData->getInteger(); +} + +float ESM::Variant::getFloat() const +{ + if (!mData) + throw std::runtime_error ("can not convert empty variant to float"); + + return mData->getFloat(); +} + +void ESM::Variant::read (ESMReader& esm, Format format) +{ + // type + VarType type = VT_Unknown; + + if (format==Format_Global) + { + std::string typeId = esm.getHNString ("FNAM"); + + if (typeId == "s") + type = VT_Short; + else if (typeId == "l") + type = VT_Long; + else if (typeId == "f") + type = VT_Float; + else + esm.fail ("illegal global variable type " + typeId); + } + else if (format==Format_Gmst) + { + if (!esm.hasMoreSubs()) + { + type = VT_None; + } + else + { + esm.getSubName(); + NAME name = esm.retSubName(); + + if (name=="STRV") + { + type = VT_String; + } + else if (name=="INTV") + { + type = VT_Int; + } + else if (name=="FLTV") + { + type = VT_Float; + } + else + esm.fail ("invalid subrecord: " + name.toString()); + } + } + else // info + { + esm.getSubName(); + NAME name = esm.retSubName(); + + if (name=="INTV") + { + type = VT_Int; + } + else if (name=="FLTV") + { + type = VT_Float; + } + else + esm.fail ("invalid subrecord: " + name.toString()); + } + + setType (type); + + // data + if (mData) + mData->read (esm, format, mType); +} + +void ESM::Variant::write (ESMWriter& esm, Format format) const +{ + if (mType==VT_Unknown) + { + throw std::runtime_error ("can not serialise variant of unknown type"); + } + else if (mType==VT_None) + { + if (format==Format_Global) + throw std::runtime_error ("can not serialise variant of type none to global format"); + + if (format==Format_Info) + throw std::runtime_error ("can not serialise variant of type none to info format"); + + // nothing to do here for GMST format + } + else + mData->write (esm, format, mType); +} + +void ESM::Variant::write (std::ostream& stream) const +{ + switch (mType) + { + case VT_Unknown: + + stream << "variant unknown"; + break; + + case VT_None: + + stream << "variant none"; + break; + + case VT_Short: + + stream << "variant short: " << mData->getInteger(); + break; + + case VT_Int: + + stream << "variant int: " << mData->getInteger(); + break; + + case VT_Long: + + stream << "variant long: " << mData->getInteger(); + break; + + case VT_Float: + + stream << "variant float: " << mData->getFloat(); + break; + + case VT_String: + + stream << "variant string: \"" << mData->getString() << "\""; + break; + } +} + +void ESM::Variant::setType (VarType type) +{ + if (type!=mType) + { + VariantDataBase *newData = 0; + + switch (type) + { + case VT_Unknown: + case VT_None: + + break; // no data + + case VT_Short: + case VT_Int: + case VT_Long: + + newData = new VariantIntegerData (mData); + break; + + case VT_Float: + + newData = new VariantFloatData (mData); + break; + + case VT_String: + + newData = new VariantStringData (mData); + break; + } + + delete mData; + mData = newData; + mType = type; + } +} + +void ESM::Variant::setString (const std::string& value) +{ + if (!mData) + throw std::runtime_error ("can not assign string to empty variant"); + + mData->setString (value); +} + +void ESM::Variant::setInteger (int value) +{ + if (!mData) + throw std::runtime_error ("can not assign integer to empty variant"); + + mData->setInteger (value); +} + +void ESM::Variant::setFloat (float value) +{ + if (!mData) + throw std::runtime_error ("can not assign float to empty variant"); + + mData->setFloat (value); +} + +bool ESM::Variant::isEqual (const Variant& value) const +{ + if (mType!=value.mType) + return false; + + if (!mData) + return true; + + assert (value.mData); + + return mData->isEqual (*value.mData); +} + +std::ostream& ESM::operator<< (std::ostream& stream, const Variant& value) +{ + value.write (stream); + return stream; +} + +bool ESM::operator== (const Variant& left, const Variant& right) +{ + return left.isEqual (right); +} + +bool ESM::operator!= (const Variant& left, const Variant& right) +{ + return !(left==right); +} \ No newline at end of file diff --git a/components/esm/variant.hpp b/components/esm/variant.hpp new file mode 100644 index 000000000..8c5f3b3d4 --- /dev/null +++ b/components/esm/variant.hpp @@ -0,0 +1,86 @@ +#ifndef OPENMW_ESM_VARIANT_H +#define OPENMW_ESM_VARIANT_H + +#include +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + enum VarType + { + VT_Unknown, + VT_None, + VT_Short, // stored as a float, kinda + VT_Int, + VT_Long, // stored as a float + VT_Float, + VT_String + }; + + class VariantDataBase; + + class Variant + { + VarType mType; + VariantDataBase *mData; + + public: + + enum Format + { + Format_Global, + Format_Gmst, + Format_Info + }; + + Variant(); + + ~Variant(); + + Variant& operator= (const Variant& variant); + + Variant (const Variant& variant); + + VarType getType() const; + + std::string getString() const; + ///< Will throw an exception, if value can not be represented as a string. + + int getInteger() const; + ///< Will throw an exception, if value can not be represented as an integer (implicit + /// casting of float values is permitted). + + float getFloat() const; + ///< Will throw an exception, if value can not be represented as a float value. + + void read (ESMReader& esm, Format format); + + void write (ESMWriter& esm, Format format) const; + + void write (std::ostream& stream) const; + ///< Write in text format. + + void setType (VarType type); + + void setString (const std::string& value); + ///< Will throw an exception, if type is not compatible with string. + + void setInteger (int value); + ///< Will throw an exception, if type is not compatible with integer. + + void setFloat (float value); + ///< Will throw an exception, if type is not compatible with float. + + bool isEqual (const Variant& value) const; + }; + + std::ostream& operator<<(std::ostream& stream, const Variant& value); + + bool operator== (const Variant& left, const Variant& right); + bool operator!= (const Variant& left, const Variant& right); +} + +#endif \ No newline at end of file diff --git a/components/esm/variantimp.cpp b/components/esm/variantimp.cpp new file mode 100644 index 000000000..160402aa4 --- /dev/null +++ b/components/esm/variantimp.cpp @@ -0,0 +1,280 @@ + +#include "variantimp.hpp" + +#include + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +ESM::VariantDataBase::~VariantDataBase() {} + +std::string ESM::VariantDataBase::getString (bool default_) const +{ + if (default_) + return ""; + + throw std::runtime_error ("can not convert variant to string"); +} + +int ESM::VariantDataBase::getInteger (bool default_) const +{ + if (default_) + return 0; + + throw std::runtime_error ("can not convert variant to integer"); +} + +float ESM::VariantDataBase::getFloat (bool default_) const +{ + if (default_) + return 0; + + throw std::runtime_error ("can not convert variant to float"); +} + +void ESM::VariantDataBase::setString (const std::string& value) +{ + throw std::runtime_error ("conversion of string to variant not possible"); +} + +void ESM::VariantDataBase::setInteger (int value) +{ + throw std::runtime_error ("conversion of integer to variant not possible"); +} + +void ESM::VariantDataBase::setFloat (float value) +{ + throw std::runtime_error ("conversion of float to variant not possible"); +} + + + +ESM::VariantStringData::VariantStringData (const VariantDataBase *data) +{ + if (data) + mValue = data->getString (true); +} + +ESM::VariantDataBase *ESM::VariantStringData::clone() const +{ + return new VariantStringData (*this); +} + +std::string ESM::VariantStringData::getString (bool default_) const +{ + return mValue; +} + +void ESM::VariantStringData::setString (const std::string& value) +{ + mValue = value; +} + +void ESM::VariantStringData::read (ESMReader& esm, Variant::Format format, VarType type) +{ + if (type!=VT_String) + throw std::logic_error ("not a string type"); + + if (format==Variant::Format_Global) + esm.fail ("global variables of type string not supported"); + + if (format==Variant::Format_Info) + esm.fail ("info variables of type string not supported"); + + // GMST + mValue = esm.getHString(); +} + +void ESM::VariantStringData::write (ESMWriter& esm, Variant::Format format, VarType type) const +{ + if (type!=VT_String) + throw std::logic_error ("not a string type"); + + if (format==Variant::Format_Global) + throw std::runtime_error ("global variables of type string not supported"); + + if (format==Variant::Format_Info) + throw std::runtime_error ("info variables of type string not supported"); + + // GMST + esm.writeHNString ("STRV", mValue); +} + +bool ESM::VariantStringData::isEqual (const VariantDataBase& value) const +{ + return dynamic_cast (value).mValue==mValue; +} + + + +ESM::VariantIntegerData::VariantIntegerData (const VariantDataBase *data) : mValue (0) +{ + if (data) + mValue = data->getInteger (true); +} + +ESM::VariantDataBase *ESM::VariantIntegerData::clone() const +{ + return new VariantIntegerData (*this); +} + +int ESM::VariantIntegerData::getInteger (bool default_) const +{ + return mValue; +} + +float ESM::VariantIntegerData::getFloat (bool default_) const +{ + return mValue; +} + +void ESM::VariantIntegerData::setInteger (int value) +{ + mValue = value; +} + +void ESM::VariantIntegerData::setFloat (float value) +{ + mValue = static_cast (value); +} + +void ESM::VariantIntegerData::read (ESMReader& esm, Variant::Format format, VarType type) +{ + if (type!=VT_Short && type!=VT_Long && type!=VT_Int) + throw std::logic_error ("not an integer type"); + + if (format==Variant::Format_Global) + { + float value; + esm.getHNT (value, "FLTV"); + + if (type==VT_Short) + { + if (value!=value) + mValue = 0; // nan + else + mValue = static_cast (value); + } + else if (type==VT_Long) + mValue = static_cast (value); + else + esm.fail ("unsupported global variable integer type"); + } + else if (format==Variant::Format_Gmst || format==Variant::Format_Info) + { + if (type!=VT_Int) + { + std::ostringstream stream; + stream + << "unsupported " <<(format==Variant::Format_Gmst ? "gmst" : "info") + << " variable integer type"; + esm.fail (stream.str()); + } + + esm.getHT (mValue); + } +} + +void ESM::VariantIntegerData::write (ESMWriter& esm, Variant::Format format, VarType type) const +{ + if (type!=VT_Short && type!=VT_Long && type!=VT_Int) + throw std::logic_error ("not an integer type"); + + if (format==Variant::Format_Global) + { + if (type==VT_Short || type==VT_Long) + { + float value = mValue; + esm.writeHNString ("FNAM", type==VT_Short ? "s" : "l"); + esm.writeHNT ("FLTV", value); + } + else + throw std::runtime_error ("unsupported global variable integer type"); + } + else if (format==Variant::Format_Gmst || format==Variant::Format_Info) + { + if (type==VT_Int) + { + std::ostringstream stream; + stream + << "unsupported " <<(format==Variant::Format_Gmst ? "gmst" : "info") + << " variable integer type"; + throw std::runtime_error (stream.str()); + } + + esm.writeHNT ("INTV", mValue); + } +} + +bool ESM::VariantIntegerData::isEqual (const VariantDataBase& value) const +{ + return dynamic_cast (value).mValue==mValue; +} + + +ESM::VariantFloatData::VariantFloatData (const VariantDataBase *data) : mValue (0) +{ + if (data) + mValue = data->getFloat (true); +} + +ESM::VariantDataBase *ESM::VariantFloatData::clone() const +{ + return new VariantFloatData (*this); +} + +int ESM::VariantFloatData::getInteger (bool default_) const +{ + return static_cast (mValue); +} + +float ESM::VariantFloatData::getFloat (bool default_) const +{ + return mValue; +} + +void ESM::VariantFloatData::setInteger (int value) +{ + mValue = value; +} + +void ESM::VariantFloatData::setFloat (float value) +{ + mValue = value; +} + +void ESM::VariantFloatData::read (ESMReader& esm, Variant::Format format, VarType type) +{ + if (type!=VT_Float) + throw std::logic_error ("not a float type"); + + if (format==Variant::Format_Global) + { + esm.getHNT (mValue, "FLTV"); + } + else if (format==Variant::Format_Gmst || format==Variant::Format_Info) + { + esm.getHT (mValue); + } +} + +void ESM::VariantFloatData::write (ESMWriter& esm, Variant::Format format, VarType type) const +{ + if (type!=VT_Float) + throw std::logic_error ("not a float type"); + + if (format==Variant::Format_Global) + { + esm.writeHNString ("FNAM", "f"); + esm.writeHNT ("FLTV", mValue); + } + else if (format==Variant::Format_Gmst || format==Variant::Format_Info) + { + esm.writeHNT ("FLTV", mValue); + } +} + +bool ESM::VariantFloatData::isEqual (const VariantDataBase& value) const +{ + return dynamic_cast (value).mValue==mValue; +} \ No newline at end of file diff --git a/components/esm/variantimp.hpp b/components/esm/variantimp.hpp new file mode 100644 index 000000000..1dc20c21f --- /dev/null +++ b/components/esm/variantimp.hpp @@ -0,0 +1,179 @@ +#ifndef OPENMW_ESM_VARIANTIMP_H +#define OPENMW_ESM_VARIANTIMP_H + +#include + +#include "variant.hpp" + +namespace ESM +{ + class VariantDataBase + { + public: + + virtual ~VariantDataBase(); + + virtual VariantDataBase *clone() const = 0; + + virtual std::string getString (bool default_ = false) const; + ///< Will throw an exception, if value can not be represented as a string. + /// + /// \note Numeric values are not converted to strings. + /// + /// \param default_ Return a default value instead of throwing an exception. + /// + /// Default-implementation: throw an exception. + + virtual int getInteger (bool default_ = false) const; + ///< Will throw an exception, if value can not be represented as an integer (implicit + /// casting of float values is permitted). + /// + /// \param default_ Return a default value instead of throwing an exception. + /// + /// Default-implementation: throw an exception. + + virtual float getFloat (bool default_ = false) const; + ///< Will throw an exception, if value can not be represented as a float value. + /// + /// \param default_ Return a default value instead of throwing an exception. + /// + /// Default-implementation: throw an exception. + + virtual void setString (const std::string& value); + ///< Will throw an exception, if type is not compatible with string. + /// + /// Default-implementation: throw an exception. + + virtual void setInteger (int value); + ///< Will throw an exception, if type is not compatible with integer. + /// + /// Default-implementation: throw an exception. + + virtual void setFloat (float value); + ///< Will throw an exception, if type is not compatible with float. + /// + /// Default-implementation: throw an exception. + + virtual void read (ESMReader& esm, Variant::Format format, VarType type) = 0; + ///< If \a type is not supported by \a format, an exception is thrown via ESMReader::fail + + virtual void write (ESMWriter& esm, Variant::Format format, VarType type) const = 0; + ///< If \a type is not supported by \a format, an exception is thrown. + + virtual bool isEqual (const VariantDataBase& value) const = 0; + ///< If the (C++) type of \a value does not match the type of *this, an exception is thrown. + + }; + + class VariantStringData : public VariantDataBase + { + std::string mValue; + + public: + + VariantStringData (const VariantDataBase *data = 0); + ///< Calling the constructor with an incompatible data type will result in a silent + /// default initialisation. + + virtual VariantDataBase *clone() const; + + virtual std::string getString (bool default_ = false) const; + ///< Will throw an exception, if value can not be represented as a string. + /// + /// \note Numeric values are not converted to strings. + /// + /// \param default_ Return a default value instead of throwing an exception. + + virtual void setString (const std::string& value); + ///< Will throw an exception, if type is not compatible with string. + + virtual void read (ESMReader& esm, Variant::Format format, VarType type); + ///< If \a type is not supported by \a format, an exception is thrown via ESMReader::fail + + virtual void write (ESMWriter& esm, Variant::Format format, VarType type) const; + ///< If \a type is not supported by \a format, an exception is thrown. + + virtual bool isEqual (const VariantDataBase& value) const; + ///< If the (C++) type of \a value does not match the type of *this, an exception is thrown. + }; + + class VariantIntegerData : public VariantDataBase + { + int mValue; + + public: + + VariantIntegerData (const VariantDataBase *data = 0); + ///< Calling the constructor with an incompatible data type will result in a silent + /// default initialisation. + + virtual VariantDataBase *clone() const; + + virtual int getInteger (bool default_ = false) const; + ///< Will throw an exception, if value can not be represented as an integer (implicit + /// casting of float values is permitted). + /// + /// \param default_ Return a default value instead of throwing an exception. + + virtual float getFloat (bool default_ = false) const; + ///< Will throw an exception, if value can not be represented as a float value. + /// + /// \param default_ Return a default value instead of throwing an exception. + + virtual void setInteger (int value); + ///< Will throw an exception, if type is not compatible with integer. + + virtual void setFloat (float value); + ///< Will throw an exception, if type is not compatible with float. + + virtual void read (ESMReader& esm, Variant::Format format, VarType type); + ///< If \a type is not supported by \a format, an exception is thrown via ESMReader::fail + + virtual void write (ESMWriter& esm, Variant::Format format, VarType type) const; + ///< If \a type is not supported by \a format, an exception is thrown. + + virtual bool isEqual (const VariantDataBase& value) const; + ///< If the (C++) type of \a value does not match the type of *this, an exception is thrown. + }; + + class VariantFloatData : public VariantDataBase + { + float mValue; + + public: + + VariantFloatData (const VariantDataBase *data = 0); + ///< Calling the constructor with an incompatible data type will result in a silent + /// default initialisation. + + virtual VariantDataBase *clone() const; + + virtual int getInteger (bool default_ = false) const; + ///< Will throw an exception, if value can not be represented as an integer (implicit + /// casting of float values is permitted). + /// + /// \param default_ Return a default value instead of throwing an exception. + + virtual float getFloat (bool default_ = false) const; + ///< Will throw an exception, if value can not be represented as a float value. + /// + /// \param default_ Return a default value instead of throwing an exception. + + virtual void setInteger (int value); + ///< Will throw an exception, if type is not compatible with integer. + + virtual void setFloat (float value); + ///< Will throw an exception, if type is not compatible with float. + + virtual void read (ESMReader& esm, Variant::Format format, VarType type); + ///< If \a type is not supported by \a format, an exception is thrown via ESMReader::fail + + virtual void write (ESMWriter& esm, Variant::Format format, VarType type) const; + ///< If \a type is not supported by \a format, an exception is thrown. + + virtual bool isEqual (const VariantDataBase& value) const; + ///< If the (C++) type of \a value does not match the type of *this, an exception is thrown. + }; +} + +#endif diff --git a/apps/launcher/model/datafilesmodel.cpp b/components/fileorderlist/model/datafilesmodel.cpp similarity index 68% rename from apps/launcher/model/datafilesmodel.cpp rename to components/fileorderlist/model/datafilesmodel.cpp index e84dbe0ac..9f2e470b2 100644 --- a/apps/launcher/model/datafilesmodel.cpp +++ b/components/fileorderlist/model/datafilesmodel.cpp @@ -1,6 +1,8 @@ -#include +#include +#include #include #include +#include #include @@ -8,8 +10,6 @@ #include "esm/esmfile.hpp" -#include "../utils/naturalsort.hpp" - #include "datafilesmodel.hpp" DataFilesModel::DataFilesModel(QObject *parent) : @@ -159,7 +159,7 @@ Qt::ItemFlags DataFilesModel::flags(const QModelIndex &index) const if (!file) return Qt::NoItemFlags; - if (mAvailableFiles.contains(file->fileName())) { + if (canBeChecked(file)) { if (index.column() == 0) { return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; } else { @@ -206,53 +206,51 @@ bool DataFilesModel::setData(const QModelIndex &index, const QVariant &value, in return false; if (role == Qt::CheckStateRole) { - - emit layoutAboutToBeChanged(); - QString name = item(index.row())->fileName(); mCheckStates[name] = static_cast(value.toInt()); - emit checkedItemsChanged(checkedItems(), uncheckedItems()); - emit layoutChanged(); + // Force a redraw of the view since unchecking one item can affect another + QModelIndex firstIndex = indexFromItem(mFiles.first()); + QModelIndex lastIndex = indexFromItem(mFiles.last()); + + emit dataChanged(firstIndex, lastIndex); + emit checkedItemsChanged(checkedItems()); return true; } return false; } -void DataFilesModel::sort(int column, Qt::SortOrder order) +bool lessThanEsmFile(const EsmFile *e1, const EsmFile *e2) { - // TODO: Make this more efficient - emit layoutAboutToBeChanged(); - - QList sortedFiles; - - QMultiMap timestamps; - - foreach (EsmFile *file, mFiles) - timestamps.insert(file->modified().toString(Qt::ISODate), file->fileName()); - - QMapIterator ti(timestamps); - - while (ti.hasNext()) { - ti.next(); - - QModelIndex index = indexFromItem(findItem(ti.value())); + //Masters first then alphabetically + if (e1->fileName().endsWith(".esm") && !e2->fileName().endsWith(".esm")) + return true; + if (!e1->fileName().endsWith(".esm") && e2->fileName().endsWith(".esm")) + return false; - if (!index.isValid()) - continue; + return e1->fileName().toLower() < e2->fileName().toLower(); +} - EsmFile *file = item(index.row()); +bool lessThanDate(const EsmFile *e1, const EsmFile *e2) +{ + if (e1->modified().toString(Qt::ISODate) < e2->modified().toString(Qt::ISODate)) { + return true; + } else { + return false; + } +} - if (!file) - continue; +void DataFilesModel::sort(int column, Qt::SortOrder order) +{ + emit layoutAboutToBeChanged(); - sortedFiles.append(file); + if (column == 3) { + qSort(mFiles.begin(), mFiles.end(), lessThanDate); + } else { + qSort(mFiles.begin(), mFiles.end(), lessThanEsmFile); } - mFiles.clear(); - mFiles = sortedFiles; - emit layoutChanged(); } @@ -263,72 +261,27 @@ void DataFilesModel::addFile(EsmFile *file) emit endInsertRows(); } -void DataFilesModel::addMasters(const QString &path) +void DataFilesModel::addFiles(const QString &path) { QDir dir(path); - dir.setNameFilters(QStringList(QLatin1String("*.esp"))); - - // Read the dependencies from the plugins - foreach (const QString &path, dir.entryList()) { - try { - ESM::ESMReader fileReader; - ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(mEncoding.toStdString())); - fileReader.setEncoder(&encoder); - fileReader.open(dir.absoluteFilePath(path).toStdString()); - - ESM::ESMReader::MasterList mlist = fileReader.getMasters(); - - for (unsigned int i = 0; i < mlist.size(); ++i) { - QString master = QString::fromStdString(mlist[i].name); - - // Add the plugin to the internal dependency map - mDependencies[master].append(path); - - // Don't add esps - if (master.endsWith(".esp", Qt::CaseInsensitive)) - continue; - - QFileInfo info(dir.absoluteFilePath(master)); - - EsmFile *file = new EsmFile(master); - file->setDates(info.lastModified(), info.lastRead()); - - // Add the master to the table - if (findItem(master) == 0) - addFile(file); - - - } - - } catch(std::runtime_error &e) { - // An error occurred while reading the .esp - qWarning() << "Error reading esp: " << e.what(); - continue; - } - } - - // See if the masters actually exist in the filesystem - dir.setNameFilters(QStringList(QLatin1String("*.esm"))); - - foreach (const QString &path, dir.entryList()) { - QFileInfo info(dir.absoluteFilePath(path)); - - if (findItem(path) == 0) { - EsmFile *file = new EsmFile(path); - file->setDates(info.lastModified(), info.lastRead()); - - addFile(file); - } - - // Make the master selectable - mAvailableFiles.append(path); + QStringList filters; + filters << "*.esp" << "*.esm"; + dir.setNameFilters(filters); + + // Create a decoder for non-latin characters in esx metadata + QTextCodec *codec; + + if (mEncoding == QLatin1String("win1252")) { + codec = QTextCodec::codecForName("windows-1252"); + } else if (mEncoding == QLatin1String("win1251")) { + codec = QTextCodec::codecForName("windows-1251"); + } else if (mEncoding == QLatin1String("win1250")) { + codec = QTextCodec::codecForName("windows-1250"); + } else { + return; // This should never happen; } -} -void DataFilesModel::addPlugins(const QString &path) -{ - QDir dir(path); - dir.setNameFilters(QStringList(QLatin1String("*.esp"))); + QTextDecoder *decoder = codec->makeDecoder(); foreach (const QString &path, dir.entryList()) { QFileInfo info(dir.absoluteFilePath(path)); @@ -336,32 +289,31 @@ void DataFilesModel::addPlugins(const QString &path) try { ESM::ESMReader fileReader; - ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(mEncoding.toStdString())); + ToUTF8::Utf8Encoder encoder(ToUTF8::calculateEncoding(mEncoding.toStdString())); fileReader.setEncoder(&encoder); fileReader.open(dir.absoluteFilePath(path).toStdString()); - ESM::ESMReader::MasterList mlist = fileReader.getMasters(); + std::vector mlist = fileReader.getMasters(); + QStringList masters; for (unsigned int i = 0; i < mlist.size(); ++i) { QString master = QString::fromStdString(mlist[i].name); masters.append(master); - - // Add the plugin to the internal dependency map - mDependencies[master].append(path); } - file->setAuthor(QString::fromStdString(fileReader.getAuthor())); + file->setAuthor(decoder->toUnicode(fileReader.getAuthor().c_str())); file->setSize(info.size()); file->setDates(info.lastModified(), info.lastRead()); file->setVersion(fileReader.getFVer()); file->setPath(info.absoluteFilePath()); file->setMasters(masters); - file->setDescription(QString::fromStdString(fileReader.getDesc())); + file->setDescription(decoder->toUnicode(fileReader.getDesc().c_str())); // Put the file in the table - addFile(file); + if (findItem(path) == 0) + addFile(file); } catch(std::runtime_error &e) { // An error occurred while reading the .esp qWarning() << "Error reading esp: " << e.what(); @@ -369,6 +321,8 @@ void DataFilesModel::addPlugins(const QString &path) } } + + delete decoder; } QModelIndex DataFilesModel::indexFromItem(EsmFile *item) const @@ -420,13 +374,32 @@ QStringList DataFilesModel::checkedItems() QString name = file->fileName(); // Only add the items that are in the checked list and available - if (mCheckStates[name] == Qt::Checked && mAvailableFiles.contains(name)) + if (mCheckStates[name] == Qt::Checked && canBeChecked(file)) list << name; } return list; } +QStringList DataFilesModel::checkedItemsPaths() +{ + QStringList list; + + QList::ConstIterator it; + QList::ConstIterator itEnd = mFiles.constEnd(); + + int i = 0; + for (it = mFiles.constBegin(); it != itEnd; ++it) { + EsmFile *file = item(i); + ++i; + + if (mCheckStates[file->fileName()] == Qt::Checked && canBeChecked(file)) + list << file->path(); + } + + return list; +} + void DataFilesModel::uncheckAll() { emit layoutAboutToBeChanged(); @@ -455,24 +428,17 @@ QStringList DataFilesModel::uncheckedItems() return list; } -void DataFilesModel::slotcheckedItemsChanged(const QStringList &checkedItems, const QStringList &unCheckedItems) +bool DataFilesModel::canBeChecked(EsmFile *file) const { - emit layoutAboutToBeChanged(); - - QStringList list; - - foreach (const QString &file, checkedItems) { - list << mDependencies[file]; - } - - foreach (const QString &file, unCheckedItems) { - foreach (const QString &remove, mDependencies[file]) { - list.removeAll(remove); + //element can be checked if all its dependencies are + bool canBeChecked = true; + foreach (const QString &master, file->masters()) + { + if (!mCheckStates.contains(master) || mCheckStates[master] != Qt::Checked) + { + canBeChecked = false; + break; } } - - mAvailableFiles.clear(); - mAvailableFiles.append(list); - - emit layoutChanged(); + return canBeChecked; } diff --git a/apps/launcher/model/datafilesmodel.hpp b/components/fileorderlist/model/datafilesmodel.hpp similarity index 82% rename from apps/launcher/model/datafilesmodel.hpp rename to components/fileorderlist/model/datafilesmodel.hpp index 29a770a86..0a07a536f 100644 --- a/apps/launcher/model/datafilesmodel.hpp +++ b/components/fileorderlist/model/datafilesmodel.hpp @@ -34,15 +34,13 @@ public: void setEncoding(const QString &encoding); - void addFile(EsmFile *file); - - void addMasters(const QString &path); - void addPlugins(const QString &path); + void addFiles(const QString &path); void uncheckAll(); QStringList checkedItems(); QStringList uncheckedItems(); + QStringList checkedItemsPaths(); Qt::CheckState checkState(const QModelIndex &index); void setCheckState(const QModelIndex &index, Qt::CheckState state); @@ -52,16 +50,13 @@ public: EsmFile* item(int row) const; signals: - void checkedItemsChanged(const QStringList checkedItems, const QStringList unCheckedItems); - -public slots: - void slotcheckedItemsChanged(const QStringList &checkedItems, const QStringList &unCheckedItems); + void checkedItemsChanged(const QStringList &items); private: + bool canBeChecked(EsmFile *file) const; + void addFile(EsmFile *file); + QList mFiles; - QStringList mAvailableFiles; - - QHash mDependencies; QHash mCheckStates; QString mEncoding; diff --git a/apps/launcher/model/esm/esmfile.cpp b/components/fileorderlist/model/esm/esmfile.cpp similarity index 100% rename from apps/launcher/model/esm/esmfile.cpp rename to components/fileorderlist/model/esm/esmfile.cpp diff --git a/apps/launcher/model/esm/esmfile.hpp b/components/fileorderlist/model/esm/esmfile.hpp similarity index 64% rename from apps/launcher/model/esm/esmfile.hpp rename to components/fileorderlist/model/esm/esmfile.hpp index ad267aa75..52b3fbd00 100644 --- a/apps/launcher/model/esm/esmfile.hpp +++ b/components/fileorderlist/model/esm/esmfile.hpp @@ -26,15 +26,15 @@ public: void setMasters(const QStringList &masters); void setDescription(const QString &description); - inline QString fileName() { return mFileName; } - inline QString author() { return mAuthor; } - inline int size() { return mSize; } - inline QDateTime modified() { return mModified; } - inline QDateTime accessed() { return mAccessed; } - inline float version() { return mVersion; } - inline QString path() { return mPath; } - inline QStringList masters() { return mMasters; } - inline QString description() { return mDescription; } + inline QString fileName() const { return mFileName; } + inline QString author() const { return mAuthor; } + inline int size() const { return mSize; } + inline QDateTime modified() const { return mModified; } + inline QDateTime accessed() const { return mAccessed; } + inline float version() const { return mVersion; } + inline QString path() const { return mPath; } + inline QStringList masters() const { return mMasters; } + inline QString description() const { return mDescription; } private: diff --git a/apps/launcher/model/modelitem.cpp b/components/fileorderlist/model/modelitem.cpp similarity index 100% rename from apps/launcher/model/modelitem.cpp rename to components/fileorderlist/model/modelitem.cpp diff --git a/apps/launcher/model/modelitem.hpp b/components/fileorderlist/model/modelitem.hpp similarity index 100% rename from apps/launcher/model/modelitem.hpp rename to components/fileorderlist/model/modelitem.hpp diff --git a/components/fileorderlist/model/pluginsproxymodel.cpp b/components/fileorderlist/model/pluginsproxymodel.cpp new file mode 100644 index 000000000..6be152b55 --- /dev/null +++ b/components/fileorderlist/model/pluginsproxymodel.cpp @@ -0,0 +1,17 @@ +#include "pluginsproxymodel.hpp" + +PluginsProxyModel::PluginsProxyModel(QObject *parent) : + QSortFilterProxyModel(parent) +{ +} + +PluginsProxyModel::~PluginsProxyModel() +{ +} + +QVariant PluginsProxyModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation != Qt::Vertical || role != Qt::DisplayRole) + return QSortFilterProxyModel::headerData(section, orientation, role); + return section + 1; +} diff --git a/components/fileorderlist/model/pluginsproxymodel.hpp b/components/fileorderlist/model/pluginsproxymodel.hpp new file mode 100644 index 000000000..8fde73236 --- /dev/null +++ b/components/fileorderlist/model/pluginsproxymodel.hpp @@ -0,0 +1,18 @@ +#ifndef PLUGINSPROXYMODEL_HPP +#define PLUGINSPROXYMODEL_HPP + +#include + +class QVariant; + +class PluginsProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + explicit PluginsProxyModel(QObject *parent = 0); + ~PluginsProxyModel(); + + QVariant headerData(int section, Qt::Orientation orientation, int role) const; +}; + +#endif // PLUGINSPROXYMODEL_HPP diff --git a/components/fileorderlist/utils/comboboxlineedit.cpp b/components/fileorderlist/utils/comboboxlineedit.cpp new file mode 100644 index 000000000..4d62e1399 --- /dev/null +++ b/components/fileorderlist/utils/comboboxlineedit.cpp @@ -0,0 +1,35 @@ +#include +#include + +#include "comboboxlineedit.hpp" + +ComboBoxLineEdit::ComboBoxLineEdit(QWidget *parent) + : QLineEdit(parent) +{ + mClearButton = new QToolButton(this); + QPixmap pixmap(":images/clear.png"); + mClearButton->setIcon(QIcon(pixmap)); + mClearButton->setIconSize(pixmap.size()); + mClearButton->setCursor(Qt::ArrowCursor); + mClearButton->setStyleSheet("QToolButton { border: none; padding: 0px; }"); + mClearButton->hide(); + connect(mClearButton, SIGNAL(clicked()), this, SLOT(clear())); + connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateClearButton(const QString&))); + int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + + setObjectName(QString("ComboBoxLineEdit")); + setStyleSheet(QString("ComboBoxLineEdit { background-color: transparent; padding-right: %1px; } ").arg(mClearButton->sizeHint().width() + frameWidth + 1)); +} + +void ComboBoxLineEdit::resizeEvent(QResizeEvent *) +{ + QSize sz = mClearButton->sizeHint(); + int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + mClearButton->move(rect().right() - frameWidth - sz.width(), + (rect().bottom() + 1 - sz.height())/2); +} + +void ComboBoxLineEdit::updateClearButton(const QString& text) +{ + mClearButton->setVisible(!text.isEmpty()); +} diff --git a/components/fileorderlist/utils/comboboxlineedit.hpp b/components/fileorderlist/utils/comboboxlineedit.hpp new file mode 100644 index 000000000..ba10731ae --- /dev/null +++ b/components/fileorderlist/utils/comboboxlineedit.hpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (c) 2007 Trolltech ASA +** +** Use, modification and distribution is allowed without limitation, +** warranty, liability or support of any kind. +** +****************************************************************************/ + +#ifndef LINEEDIT_H +#define LINEEDIT_H + +#include + +class QToolButton; + +class ComboBoxLineEdit : public QLineEdit +{ + Q_OBJECT + +public: + ComboBoxLineEdit(QWidget *parent = 0); + +protected: + void resizeEvent(QResizeEvent *); + +private slots: + void updateClearButton(const QString &text); + +private: + QToolButton *mClearButton; +}; + +#endif // LIENEDIT_H + diff --git a/apps/launcher/utils/lineedit.cpp b/components/fileorderlist/utils/lineedit.cpp similarity index 82% rename from apps/launcher/utils/lineedit.cpp rename to components/fileorderlist/utils/lineedit.cpp index dac196425..b0f339589 100644 --- a/apps/launcher/utils/lineedit.cpp +++ b/components/fileorderlist/utils/lineedit.cpp @@ -1,7 +1,8 @@ -#include "lineedit.hpp" #include #include +#include "lineedit.hpp" + LineEdit::LineEdit(QWidget *parent) : QLineEdit(parent) { @@ -13,9 +14,11 @@ LineEdit::LineEdit(QWidget *parent) mClearButton->setStyleSheet("QToolButton { border: none; padding: 0px; }"); mClearButton->hide(); connect(mClearButton, SIGNAL(clicked()), this, SLOT(clear())); - connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateCloseButton(const QString&))); + connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateClearButton(const QString&))); int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); - setStyleSheet(QString("QLineEdit { padding-right: %1px; } ").arg(mClearButton->sizeHint().width() + frameWidth + 1)); + + setObjectName(QString("LineEdit")); + setStyleSheet(QString("LineEdit { padding-right: %1px; } ").arg(mClearButton->sizeHint().width() + frameWidth + 1)); QSize msz = minimumSizeHint(); setMinimumSize(qMax(msz.width(), mClearButton->sizeHint().height() + frameWidth * 2 + 2), qMax(msz.height(), mClearButton->sizeHint().height() + frameWidth * 2 + 2)); @@ -29,9 +32,7 @@ void LineEdit::resizeEvent(QResizeEvent *) (rect().bottom() + 1 - sz.height())/2); } -void LineEdit::updateCloseButton(const QString& text) +void LineEdit::updateClearButton(const QString& text) { mClearButton->setVisible(!text.isEmpty()); } - - diff --git a/apps/launcher/utils/lineedit.hpp b/components/fileorderlist/utils/lineedit.hpp similarity index 92% rename from apps/launcher/utils/lineedit.hpp rename to components/fileorderlist/utils/lineedit.hpp index 2ed76d6eb..14bd7b1b4 100644 --- a/apps/launcher/utils/lineedit.hpp +++ b/components/fileorderlist/utils/lineedit.hpp @@ -25,7 +25,7 @@ protected: void resizeEvent(QResizeEvent *); private slots: - void updateCloseButton(const QString &text); + void updateClearButton(const QString &text); private: QToolButton *mClearButton; diff --git a/apps/launcher/utils/naturalsort.cpp b/components/fileorderlist/utils/naturalsort.cpp similarity index 100% rename from apps/launcher/utils/naturalsort.cpp rename to components/fileorderlist/utils/naturalsort.cpp diff --git a/apps/launcher/utils/naturalsort.hpp b/components/fileorderlist/utils/naturalsort.hpp similarity index 100% rename from apps/launcher/utils/naturalsort.hpp rename to components/fileorderlist/utils/naturalsort.hpp diff --git a/components/fileorderlist/utils/profilescombobox.cpp b/components/fileorderlist/utils/profilescombobox.cpp new file mode 100644 index 000000000..c3ff953ae --- /dev/null +++ b/components/fileorderlist/utils/profilescombobox.cpp @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include + +#include "profilescombobox.hpp" +#include "comboboxlineedit.hpp" + +ProfilesComboBox::ProfilesComboBox(QWidget *parent) : + QComboBox(parent) +{ + mValidator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore + setEditEnabled(true); + setValidator(mValidator); + setCompleter(0); + + connect(this, SIGNAL(currentIndexChanged(int)), this, + SLOT(slotIndexChanged(int))); + + setInsertPolicy(QComboBox::NoInsert); +} + +void ProfilesComboBox::setEditEnabled(bool editable) +{ + if (isEditable() == editable) + return; + + if (!editable) { + disconnect(lineEdit(), SIGNAL(editingFinished()), this, SLOT(slotEditingFinished())); + disconnect(lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged(QString))); + return setEditable(false); + } + + // Reset the completer and validator + setEditable(true); + setValidator(mValidator); + + ComboBoxLineEdit *edit = new ComboBoxLineEdit(this); + setLineEdit(edit); + setCompleter(0); + + connect(lineEdit(), SIGNAL(editingFinished()), this, + SLOT(slotEditingFinished())); + + connect(lineEdit(), SIGNAL(textChanged(QString)), this, + SLOT(slotTextChanged(QString))); +} + +void ProfilesComboBox::slotTextChanged(const QString &text) +{ + QPalette *palette = new QPalette(); + palette->setColor(QPalette::Text,Qt::red); + + int index = findText(text); + + if (text.isEmpty() || (index != -1 && index != currentIndex())) { + lineEdit()->setPalette(*palette); + } else { + lineEdit()->setPalette(QApplication::palette()); + } +} + +void ProfilesComboBox::slotEditingFinished() +{ + QString current = currentText(); + QString previous = itemText(currentIndex()); + + if (currentIndex() == -1) + return; + + if (current.isEmpty()) + return; + + if (current == previous) + return; + + if (findText(current) != -1) + return; + + setItemText(currentIndex(), current); + emit(profileRenamed(previous, current)); +} + +void ProfilesComboBox::slotIndexChanged(int index) +{ + if (index == -1) + return; + + emit(profileChanged(mOldProfile, currentText())); + mOldProfile = itemText(index); +} diff --git a/apps/launcher/utils/profilescombobox.hpp b/components/fileorderlist/utils/profilescombobox.hpp similarity index 88% rename from apps/launcher/utils/profilescombobox.hpp rename to components/fileorderlist/utils/profilescombobox.hpp index c7da60d2a..08ead9a7a 100644 --- a/apps/launcher/utils/profilescombobox.hpp +++ b/components/fileorderlist/utils/profilescombobox.hpp @@ -4,7 +4,6 @@ #include class QString; - class QRegExpValidator; class ProfilesComboBox : public QComboBox @@ -19,8 +18,9 @@ signals: void profileRenamed(const QString &oldName, const QString &newName); private slots: - void slotReturnPressed(); + void slotEditingFinished(); void slotIndexChanged(int index); + void slotTextChanged(const QString &text); private: QString mOldProfile; diff --git a/components/files/collections.cpp b/components/files/collections.cpp index 50340dca4..c6195d88c 100644 --- a/components/files/collections.cpp +++ b/components/files/collections.cpp @@ -31,6 +31,32 @@ namespace Files return iter->second; } + boost::filesystem::path Collections::getPath(const std::string& file) const + { + for (Files::PathContainer::const_iterator iter = mDirectories.begin(); + iter != mDirectories.end(); ++iter) + { + const boost::filesystem::path path = *iter / file; + if (boost::filesystem::exists(path)) + return path.string(); + } + + throw std::runtime_error ("file " + file + " not found"); + } + + bool Collections::doesExist(const std::string& file) const + { + for (Files::PathContainer::const_iterator iter = mDirectories.begin(); + iter != mDirectories.end(); ++iter) + { + const boost::filesystem::path path = *iter / file; + if (boost::filesystem::exists(path)) + return true; + } + + return false; + } + const Files::PathContainer& Collections::getPaths() const { return mDirectories; diff --git a/components/files/collections.hpp b/components/files/collections.hpp index ed4aafa13..def61cf8e 100644 --- a/components/files/collections.hpp +++ b/components/files/collections.hpp @@ -19,6 +19,16 @@ namespace Files /// leading dot and must be all lower-case. const MultiDirCollection& getCollection(const std::string& extension) const; + boost::filesystem::path getPath(const std::string& file) const; + ///< Return full path (including filename) of \a file. + /// + /// If the file does not exist in any of the collection's + /// directories, an exception is thrown. \a file must include the + /// extension. + + bool doesExist(const std::string& file) const; + ///< \return Does a file with the given name exist? + const Files::PathContainer& getPaths() const; private: diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index 9056e792d..765f1cebf 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -3,6 +3,8 @@ #ifdef _WIN32 #include +#elif defined HAVE_UNORDERED_MAP +#include #else #include #endif @@ -48,7 +50,11 @@ struct ConfigurationManager typedef Files::FixedPath<> FixedPathType; typedef const boost::filesystem::path& (FixedPathType::*path_type_f)() const; - typedef std::tr1::unordered_map TokensMappingContainer; + #if defined HAVE_UNORDERED_MAP + typedef std::unordered_map TokensMappingContainer; + #else + typedef std::tr1::unordered_map TokensMappingContainer; + #endif void loadConfig(const boost::filesystem::path& path, boost::program_options::variables_map& variables, diff --git a/components/files/constrainedfiledatastream.cpp b/components/files/constrainedfiledatastream.cpp index dd2985e56..321bcf7c8 100644 --- a/components/files/constrainedfiledatastream.cpp +++ b/components/files/constrainedfiledatastream.cpp @@ -3,11 +3,8 @@ #include #include -#ifndef __clang__ -#include -#else -#include -#endif + +#include namespace { @@ -29,7 +26,7 @@ public: mBufferOrigin = 0; mBufferExtent = 0; } - + size_t read(void* buf, size_t count) { diff --git a/components/files/fixedpath.hpp b/components/files/fixedpath.hpp index dce4f96c2..a309dc9fb 100644 --- a/components/files/fixedpath.hpp +++ b/components/files/fixedpath.hpp @@ -1,25 +1,3 @@ -/** - * Open Morrowind - an opensource Elder Scrolls III: Morrowind - * engine implementation. - * - * Copyright (C) 2011 Open Morrowind Team - * - * 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 3 of the License, or - * (at your option) any later version. - * - * 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 . - */ - -/** \file components/files/fixedpath.hpp */ - #ifndef COMPONENTS_FILES_FIXEDPATH_HPP #define COMPONENTS_FILES_FIXEDPATH_HPP diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index 0f08b67fe..c974a91d3 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -1,25 +1,3 @@ -/** - * Open Morrowind - an opensource Elder Scrolls III: Morrowind - * engine implementation. - * - * Copyright (C) 2011 Open Morrowind Team - * - * 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 3 of the License, or - * (at your option) any later version. - * - * 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 . - */ - -/** \file components/files/linuxpath.cpp */ - #include "linuxpath.hpp" #if defined(__linux__) || defined(__FreeBSD__) diff --git a/components/files/linuxpath.hpp b/components/files/linuxpath.hpp index 09acd2be7..6acf2a2d5 100644 --- a/components/files/linuxpath.hpp +++ b/components/files/linuxpath.hpp @@ -1,25 +1,3 @@ -/** - * Open Morrowind - an opensource Elder Scrolls III: Morrowind - * engine implementation. - * - * Copyright (C) 2011 Open Morrowind Team - * - * 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 3 of the License, or - * (at your option) any later version. - * - * 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 . - */ - -/** \file components/files/linuxpath.hpp */ - #ifndef COMPONENTS_FILES_LINUXPATH_H #define COMPONENTS_FILES_LINUXPATH_H diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index 9625612ad..9edcd6ef2 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -1,25 +1,3 @@ -/** - * Open Morrowind - an opensource Elder Scrolls III: Morrowind - * engine implementation. - * - * Copyright (C) 2011 Open Morrowind Team - * - * 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 3 of the License, or - * (at your option) any later version. - * - * 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 . - */ - -/** \file components/files/macospath.cpp */ - #include "macospath.hpp" #if defined(macintosh) || defined(Macintosh) || defined(__APPLE__) || defined(__MACH__) diff --git a/components/files/macospath.hpp b/components/files/macospath.hpp index 591c978aa..576ec1681 100644 --- a/components/files/macospath.hpp +++ b/components/files/macospath.hpp @@ -1,25 +1,3 @@ -/** - * Open Morrowind - an opensource Elder Scrolls III: Morrowind - * engine implementation. - * - * Copyright (C) 2011 Open Morrowind Team - * - * 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 3 of the License, or - * (at your option) any later version. - * - * 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 . - */ - -/** \file components/files/macospath.hpp */ - #ifndef COMPONENTS_FILES_MACOSPATH_H #define COMPONENTS_FILES_MACOSPATH_H diff --git a/components/files/ogreplugin.hpp b/components/files/ogreplugin.hpp index 2d56bfb47..6fcf61376 100644 --- a/components/files/ogreplugin.hpp +++ b/components/files/ogreplugin.hpp @@ -1,25 +1,3 @@ -/** - * Open Morrowind - an opensource Elder Scrolls III: Morrowind - * engine implementation. - * - * Copyright (C) 2011 Open Morrowind Team - * - * 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 3 of the License, or - * (at your option) any later version. - * - * 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 . - */ - -/** \file components/files/ogreplugin.hpp */ - #ifndef COMPONENTS_FILES_OGREPLUGIN_H #define COMPONENTS_FILES_OGREPLUGIN_H diff --git a/components/files/windowspath.hpp b/components/files/windowspath.hpp index 7fe8bc955..6044b67c2 100644 --- a/components/files/windowspath.hpp +++ b/components/files/windowspath.hpp @@ -1,25 +1,3 @@ -/** - * Open Morrowind - an opensource Elder Scrolls III: Morrowind - * engine implementation. - * - * Copyright (C) 2011 Open Morrowind Team - * - * 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 3 of the License, or - * (at your option) any later version. - * - * 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 . - */ - -/** \file components/files/windowspath.hpp */ - #ifndef COMPONENTS_FILES_WINDOWSPATH_HPP #define COMPONENTS_FILES_WINDOWSPATH_HPP diff --git a/components/interpreter/controlopcodes.hpp b/components/interpreter/controlopcodes.hpp index 534ccc4dd..caa755989 100644 --- a/components/interpreter/controlopcodes.hpp +++ b/components/interpreter/controlopcodes.hpp @@ -11,66 +11,66 @@ namespace Interpreter class OpReturn : public Opcode0 { public: - + virtual void execute (Runtime& runtime) { runtime.setPC (-1); - } + } }; - + class OpSkipZero : public Opcode0 { public: - + virtual void execute (Runtime& runtime) { Type_Integer data = runtime[0].mInteger; runtime.pop(); - + if (data==0) runtime.setPC (runtime.getPC()+1); - } - }; - + } + }; + class OpSkipNonZero : public Opcode0 { public: - + virtual void execute (Runtime& runtime) { Type_Integer data = runtime[0].mInteger; runtime.pop(); - + if (data!=0) runtime.setPC (runtime.getPC()+1); - } - }; - + } + }; + class OpJumpForward : public Opcode1 { public: - + virtual void execute (Runtime& runtime, unsigned int arg0) { if (arg0==0) - throw std::logic_error ("inifite loop"); - + throw std::logic_error ("infinite loop"); + runtime.setPC (runtime.getPC()+arg0-1); - } - }; + } + }; class OpJumpBackward : public Opcode1 { public: - + virtual void execute (Runtime& runtime, unsigned int arg0) { if (arg0==0) - throw std::logic_error ("inifite loop"); - + throw std::logic_error ("infinite loop"); + runtime.setPC (runtime.getPC()-arg0-1); - } - }; + } + }; } #endif diff --git a/components/interpreter/defines.cpp b/components/interpreter/defines.cpp index 18e5f81c1..52f892b42 100644 --- a/components/interpreter/defines.cpp +++ b/components/interpreter/defines.cpp @@ -23,7 +23,6 @@ namespace Interpreter{ } std::string fixDefinesReal(std::string text, char eschar, bool isBook, Context& context){ - unsigned int start = 0; std::ostringstream retval; for(unsigned int i = 0; i < text.length(); i++){ diff --git a/components/interpreter/runtime.cpp b/components/interpreter/runtime.cpp index dcf17d255..8814ca7ff 100644 --- a/components/interpreter/runtime.cpp +++ b/components/interpreter/runtime.cpp @@ -34,17 +34,20 @@ namespace Interpreter std::string Runtime::getStringLiteral (int index) const { - assert (index>=0 && index (mCode[3])); + assert (index>=0 && static_cast (mCode[3])>0); const char *literalBlock = reinterpret_cast (mCode + 4 + mCode[0] + mCode[1] + mCode[2]); + int offset = 0; + for (; index; --index) { - literalBlock += std::strlen (literalBlock) + 1; + offset += std::strlen (literalBlock+offset) + 1; + assert (offset/4 (mCode[3])); } - return literalBlock; + return literalBlock+offset; } void Runtime::configure (const Interpreter::Type_Code *code, int codeSize, Context& context) diff --git a/components/misc/utf8stream.hpp b/components/misc/utf8stream.hpp new file mode 100644 index 000000000..19a0688b2 --- /dev/null +++ b/components/misc/utf8stream.hpp @@ -0,0 +1,116 @@ +#ifndef MISC_UTF8ITER_HPP +#define MISC_UTF8ITER_HPP + +#include + +class Utf8Stream +{ +public: + + typedef uint32_t UnicodeChar; + typedef unsigned char const * Point; + + //static const unicode_char sBadChar = 0xFFFFFFFF; gcc can't handle this + static UnicodeChar sBadChar () { return UnicodeChar (0xFFFFFFFF); } + + Utf8Stream (Point begin, Point end) : + cur (begin), nxt (begin), end (end) + { + } + + Utf8Stream (std::pair range) : + cur (range.first), nxt (range.first), end (range.second) + { + } + + bool eof () const + { + return cur == end; + } + + Point current () const + { + return cur; + } + + UnicodeChar peek () + { + if (cur == nxt) + next (); + return val; + } + + UnicodeChar consume () + { + if (cur == nxt) + next (); + cur = nxt; + return val; + } + + static std::pair decode (Point cur, Point end) + { + if ((*cur & 0x80) == 0) + { + UnicodeChar chr = *cur++; + + return std::make_pair (chr, cur); + } + + int octets; + UnicodeChar chr; + + boost::tie (octets, chr) = octet_count (*cur++); + + if (octets > 5) + return std::make_pair (sBadChar(), cur); + + Point eoc = cur + octets; + + if (eoc > end) + return std::make_pair (sBadChar(), cur); + + while (cur != eoc) + { + if ((*cur & 0xC0) != 0x80) // check continuation mark + return std::make_pair (sBadChar(), cur);; + + chr = (chr << 6) | UnicodeChar ((*cur++) & 0x3F); + } + + return std::make_pair (chr, cur); + } + +private: + + static std::pair octet_count (unsigned char octet) + { + int octets; + + unsigned char mark = 0xC0; + unsigned char mask = 0xE0; + + for (octets = 1; octets <= 5; ++octets) + { + if ((octet & mask) == mark) + break; + + mark = (mark >> 1) | 0x80; + mask = (mask >> 1) | 0x80; + } + + return std::make_pair (octets, octet & ~mask); + } + + void next () + { + boost::tie (val, nxt) = decode (nxt, end); + } + + Point cur; + Point nxt; + Point end; + UnicodeChar val; +}; + +#endif diff --git a/components/nif/controlled.hpp b/components/nif/controlled.hpp index b9d84b58a..6acb8ff20 100644 --- a/components/nif/controlled.hpp +++ b/components/nif/controlled.hpp @@ -21,8 +21,8 @@ */ -#ifndef _NIF_CONTROLLED_H_ -#define _NIF_CONTROLLED_H_ +#ifndef OPENMW_COMPONENTS_NIF_CONTROLLED_HPP +#define OPENMW_COMPONENTS_NIF_CONTROLLED_HPP #include "extra.hpp" #include "controller.hpp" @@ -36,7 +36,7 @@ class Controlled : public Extra public: ControllerPtr controller; - void read(NIFFile *nif) + void read(NIFStream *nif) { Extra::read(nif); controller.read(nif); @@ -55,7 +55,7 @@ class Named : public Controlled public: std::string name; - void read(NIFFile *nif) + void read(NIFStream *nif) { name = nif->getString(); Controlled::read(nif); @@ -66,12 +66,14 @@ typedef Named NiSequenceStreamHelper; class NiParticleGrowFade : public Controlled { public: - void read(NIFFile *nif) + float growTime; + float fadeTime; + + void read(NIFStream *nif) { Controlled::read(nif); - - // Two floats. - nif->skip(8); + growTime = nif->getFloat(); + fadeTime = nif->getFloat(); } }; @@ -80,7 +82,7 @@ class NiParticleColorModifier : public Controlled public: NiColorDataPtr data; - void read(NIFFile *nif) + void read(NIFStream *nif) { Controlled::read(nif); data.read(nif); @@ -96,12 +98,23 @@ public: class NiGravity : public Controlled { public: - void read(NIFFile *nif) + float mForce; + /* 0 - Wind (fixed direction) + * 1 - Point (fixed origin) + */ + int mType; + Ogre::Vector3 mPosition; + Ogre::Vector3 mDirection; + + void read(NIFStream *nif) { Controlled::read(nif); - // two floats, one int, six floats - nif->skip(9*4); + /*unknown*/nif->getFloat(); + mForce = nif->getFloat(); + mType = nif->getUInt(); + mPosition = nif->getVector3(); + mDirection = nif->getVector3(); } }; @@ -109,7 +122,7 @@ public: class NiPlanarCollider : public Controlled { public: - void read(NIFFile *nif) + void read(NIFStream *nif) { Controlled::read(nif); @@ -121,7 +134,7 @@ public: class NiParticleRotation : public Controlled { public: - void read(NIFFile *nif) + void read(NIFStream *nif) { Controlled::read(nif); diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index cbc19cd8f..011e0e445 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -21,12 +21,12 @@ */ -#ifndef _NIF_CONTROLLER_H_ -#define _NIF_CONTROLLER_H_ +#ifndef OPENMW_COMPONENTS_NIF_CONTROLLER_HPP +#define OPENMW_COMPONENTS_NIF_CONTROLLER_HPP #include "record.hpp" -#include "nif_file.hpp" -#include "record_ptr.hpp" +#include "niffile.hpp" +#include "recordptr.hpp" namespace Nif { @@ -40,7 +40,7 @@ public: float timeStart, timeStop; ControlledPtr target; - void read(NIFFile *nif) + void read(NIFStream *nif) { next.read(nif); @@ -62,27 +62,113 @@ public: } }; -class NiBSPArrayController : public Controller +class NiParticleSystemController : public Controller { public: - void read(NIFFile *nif) + struct Particle { + Ogre::Vector3 velocity; + float lifetime; + float lifespan; + float timestamp; + int vertex; + }; + + float velocity; + float velocityRandom; + + float verticalDir; // 0=up, pi/2=horizontal, pi=down + float verticalAngle; + float horizontalDir; + float horizontalAngle; + + float size; + float startTime; + float stopTime; + + float emitRate; + float lifetime; + float lifetimeRandom; + + int emitFlags; // Bit 0: Emit Rate toggle bit (0 = auto adjust, 1 = use Emit Rate value) + Ogre::Vector3 offsetRandom; + + NodePtr emitter; + + int numParticles; + int activeCount; + std::vector particles; + + ExtraPtr extra; + + void read(NIFStream *nif) { Controller::read(nif); - // At the moment, just skip it all - nif->skip(111); - int s = nif->getUShort(); - nif->skip(15 + s*40); + velocity = nif->getFloat(); + velocityRandom = nif->getFloat(); + verticalDir = nif->getFloat(); + verticalAngle = nif->getFloat(); + horizontalDir = nif->getFloat(); + horizontalAngle = nif->getFloat(); + /*normal?*/ nif->getVector3(); + /*color?*/ nif->getVector4(); + size = nif->getFloat(); + startTime = nif->getFloat(); + stopTime = nif->getFloat(); + nif->getChar(); + emitRate = nif->getFloat(); + lifetime = nif->getFloat(); + lifetimeRandom = nif->getFloat(); + + emitFlags = nif->getUShort(); + offsetRandom = nif->getVector3(); + + emitter.read(nif); + + /* Unknown Short, 0? + * Unknown Float, 1.0? + * Unknown Int, 1? + * Unknown Int, 0? + * Unknown Short, 0? + */ + nif->skip(16); + + numParticles = nif->getUShort(); + activeCount = nif->getUShort(); + + particles.resize(numParticles); + for(size_t i = 0;i < particles.size();i++) + { + particles[i].velocity = nif->getVector3(); + nif->getVector3(); /* unknown */ + particles[i].lifetime = nif->getFloat(); + particles[i].lifespan = nif->getFloat(); + particles[i].timestamp = nif->getFloat(); + nif->getUShort(); /* unknown */ + particles[i].vertex = nif->getUShort(); + } + + nif->getUInt(); /* -1? */ + extra.read(nif); + nif->getUInt(); /* -1? */ + nif->getChar(); + } + + void post(NIFFile *nif) + { + Controller::post(nif); + emitter.post(nif); + extra.post(nif); } }; -typedef NiBSPArrayController NiParticleSystemController; +typedef NiParticleSystemController NiBSPArrayController; class NiMaterialColorController : public Controller { public: NiPosDataPtr data; - void read(NIFFile *nif) + void read(NIFStream *nif) { Controller::read(nif); data.read(nif); @@ -101,7 +187,7 @@ public: NiPosDataPtr posData; NiFloatDataPtr floatData; - void read(NIFFile *nif) + void read(NIFStream *nif) { Controller::read(nif); @@ -129,7 +215,7 @@ class NiUVController : public Controller public: NiUVDataPtr data; - void read(NIFFile *nif) + void read(NIFStream *nif) { Controller::read(nif); @@ -149,7 +235,7 @@ class NiKeyframeController : public Controller public: NiKeyframeDataPtr data; - void read(NIFFile *nif) + void read(NIFStream *nif) { Controller::read(nif); data.read(nif); @@ -167,7 +253,7 @@ class NiAlphaController : public Controller public: NiFloatDataPtr data; - void read(NIFFile *nif) + void read(NIFStream *nif) { Controller::read(nif); data.read(nif); @@ -185,7 +271,7 @@ class NiGeomMorpherController : public Controller public: NiMorphDataPtr data; - void read(NIFFile *nif) + void read(NIFStream *nif) { Controller::read(nif); data.read(nif); @@ -204,7 +290,7 @@ class NiVisController : public Controller public: NiVisDataPtr data; - void read(NIFFile *nif) + void read(NIFStream *nif) { Controller::read(nif); data.read(nif); @@ -217,5 +303,28 @@ public: } }; +class NiFlipController : public Controller +{ +public: + int mTexSlot; + float mDelta; // Time between two flips. delta = (start_time - stop_time) / num_sources + NiSourceTextureList mSources; + + void read(NIFStream *nif) + { + Controller::read(nif); + mTexSlot = nif->getUInt(); + /*unknown=*/nif->getUInt();/*0?*/ + mDelta = nif->getFloat(); + mSources.read(nif); + } + + void post(NIFFile *nif) + { + Controller::post(nif); + mSources.post(nif); + } +}; + } // Namespace #endif diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 63df23b27..f1f34184b 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -21,8 +21,8 @@ */ -#ifndef _NIF_DATA_H_ -#define _NIF_DATA_H_ +#ifndef OPENMW_COMPONENTS_NIF_DATA_HPP +#define OPENMW_COMPONENTS_NIF_DATA_HPP #include "controlled.hpp" @@ -65,7 +65,7 @@ public: */ int alpha; - void read(NIFFile *nif) + void read(NIFStream *nif) { Named::read(nif); @@ -102,7 +102,7 @@ public: Ogre::Vector3 center; float radius; - void read(NIFFile *nif) + void read(NIFStream *nif) { int verts = nif->getUShort(); @@ -138,24 +138,22 @@ public: // Triangles, three vertex indices per triangle std::vector triangles; - void read(NIFFile *nif) + void read(NIFStream *nif) { ShapeData::read(nif); - int tris = nif->getUShort(); - if(tris) - { - // We have three times as many vertices as triangles, so this - // is always equal to tris*3. - int cnt = nif->getInt(); - nif->getShorts(triangles, cnt); - } + /*int tris =*/ nif->getUShort(); + + // We have three times as many vertices as triangles, so this + // is always equal to tris*3. + int cnt = nif->getInt(); + nif->getShorts(triangles, cnt); // Read the match list, which lists the vertices that are equal to // vertices. We don't actually need need this for anything, so // just skip it. int verts = nif->getUShort(); - for(int i=0;igetUShort(); @@ -167,23 +165,28 @@ public: class NiAutoNormalParticlesData : public ShapeData { public: + int numParticles; + + float particleRadius; + int activeCount; - void read(NIFFile *nif) + std::vector sizes; + + void read(NIFStream *nif) { ShapeData::read(nif); // Should always match the number of vertices - activeCount = nif->getUShort(); + numParticles = nif->getUShort(); - // Skip all the info, we don't support particles yet - nif->getFloat(); // Active radius ? - nif->getUShort(); // Number of valid entries in the following arrays ? + particleRadius = nif->getFloat(); + activeCount = nif->getUShort(); if(nif->getInt()) { // Particle sizes - nif->skip(activeCount * sizeof(float)); + nif->getFloats(sizes, vertices.size()); } } }; @@ -191,16 +194,16 @@ public: class NiRotatingParticlesData : public NiAutoNormalParticlesData { public: - void read(NIFFile *nif) + std::vector rotations; + + void read(NIFStream *nif) { NiAutoNormalParticlesData::read(nif); if(nif->getInt()) { - // Rotation quaternions. I THINK activeCount is correct here, - // but verts (vertex number) might also be correct, if there is - // any case where the two don't match. - nif->skip(activeCount * 4*sizeof(float)); + // Rotation quaternions. + nif->getQuaternions(rotations, vertices.size()); } } }; @@ -210,7 +213,7 @@ class NiPosData : public Record public: Vector3KeyList mKeyList; - void read(NIFFile *nif) + void read(NIFStream *nif) { mKeyList.read(nif); } @@ -221,7 +224,7 @@ class NiUVData : public Record public: FloatKeyList mKeyList[4]; - void read(NIFFile *nif) + void read(NIFStream *nif) { for(int i = 0;i < 4;i++) mKeyList[i].read(nif); @@ -233,7 +236,7 @@ class NiFloatData : public Record public: FloatKeyList mKeyList; - void read(NIFFile *nif) + void read(NIFStream *nif) { mKeyList.read(nif); } @@ -245,7 +248,7 @@ public: unsigned int rmask, gmask, bmask, amask; int bpp, mips; - void read(NIFFile *nif) + void read(NIFStream *nif) { nif->getInt(); // always 0 or 1 @@ -283,7 +286,7 @@ class NiColorData : public Record public: Vector4KeyList mKeyList; - void read(NIFFile *nif) + void read(NIFStream *nif) { mKeyList.read(nif); } @@ -296,13 +299,17 @@ public: float time; char isSet; }; + std::vector mVis; - void read(NIFFile *nif) + void read(NIFStream *nif) { int count = nif->getInt(); - - /* Skip VisData */ - nif->skip(count*5); + mVis.resize(count); + for(size_t i = 0;i < mVis.size();i++) + { + mVis[i].time = nif->getFloat(); + mVis[i].isSet = nif->getChar(); + } } }; @@ -313,7 +320,7 @@ public: NodePtr root; NodeList bones; - void read(NIFFile *nif) + void read(NIFStream *nif) { data.read(nif); root.read(nif); @@ -349,7 +356,7 @@ public: BoneTrafo trafo; std::vector bones; - void read(NIFFile *nif) + void read(NIFStream *nif) { trafo.rotation = nif->getMatrix3(); trafo.trans = nif->getVector3(); @@ -387,20 +394,17 @@ struct NiMorphData : public Record }; std::vector mMorphs; - void read(NIFFile *nif) + void read(NIFStream *nif) { int morphCount = nif->getInt(); int vertCount = nif->getInt(); - nif->getChar(); + /*relative targets?*/nif->getChar(); mMorphs.resize(morphCount); for(int i = 0;i < morphCount;i++) { mMorphs[i].mData.read(nif, true); - - mMorphs[i].mVertices.resize(vertCount); - for(int j = 0;j < vertCount;j++) - mMorphs[i].mVertices[j] = nif->getVector3(); + nif->getVector3s(mMorphs[i].mVertices, vertCount); } } }; @@ -412,7 +416,7 @@ struct NiKeyframeData : public Record Vector3KeyList mTranslations; FloatKeyList mScales; - void read(NIFFile *nif) + void read(NIFStream *nif) { mRotations.read(nif); mTranslations.read(nif); diff --git a/components/nif/effect.hpp b/components/nif/effect.hpp index 850415dad..cc1b0f41c 100644 --- a/components/nif/effect.hpp +++ b/components/nif/effect.hpp @@ -21,8 +21,8 @@ */ -#ifndef _NIF_EFFECT_H_ -#define _NIF_EFFECT_H_ +#ifndef OPENMW_COMPONENTS_NIF_EFFECT_HPP +#define OPENMW_COMPONENTS_NIF_EFFECT_HPP #include "node.hpp" @@ -42,7 +42,7 @@ struct NiLight : Effect Ogre::Vector3 diffuse; Ogre::Vector3 specular; - void read(NIFFile *nif) + void read(NIFStream *nif) { dimmer = nif->getFloat(); ambient = nif->getVector3(); @@ -52,7 +52,7 @@ struct NiLight : Effect }; SLight light; - void read(NIFFile *nif) + void read(NIFStream *nif) { Effect::read(nif); @@ -66,7 +66,7 @@ struct NiTextureEffect : Effect { NiSourceTexturePtr texture; - void read(NIFFile *nif) + void read(NIFStream *nif) { Effect::read(nif); diff --git a/components/nif/extra.hpp b/components/nif/extra.hpp index 35781dbf5..45c4fefc6 100644 --- a/components/nif/extra.hpp +++ b/components/nif/extra.hpp @@ -21,12 +21,12 @@ */ -#ifndef _NIF_EXTRA_H_ -#define _NIF_EXTRA_H_ +#ifndef OPENMW_COMPONENTS_NIF_EXTRA_HPP +#define OPENMW_COMPONENTS_NIF_EXTRA_HPP #include "record.hpp" -#include "nif_file.hpp" -#include "record_ptr.hpp" +#include "niffile.hpp" +#include "recordptr.hpp" namespace Nif { @@ -40,14 +40,14 @@ class Extra : public Record public: ExtraPtr extra; - void read(NIFFile *nif) { extra.read(nif); } + void read(NIFStream *nif) { extra.read(nif); } void post(NIFFile *nif) { extra.post(nif); } }; class NiVertWeightsExtraData : public Extra { public: - void read(NIFFile *nif) + void read(NIFStream *nif) { Extra::read(nif); @@ -70,7 +70,7 @@ public: }; std::vector list; - void read(NIFFile *nif) + void read(NIFStream *nif) { Extra::read(nif); @@ -95,7 +95,7 @@ public: */ std::string string; - void read(NIFFile *nif) + void read(NIFStream *nif) { Extra::read(nif); diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp deleted file mode 100644 index e6d3182ed..000000000 --- a/components/nif/nif_file.cpp +++ /dev/null @@ -1,376 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008-2010 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.sourceforge.net/ - - This file (nif_file.cpp) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - 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 - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -#include "nif_file.hpp" -#include "record.hpp" -#include "components/misc/stringops.hpp" - -#include "extra.hpp" -#include "controlled.hpp" -#include "node.hpp" -#include "property.hpp" -#include "data.hpp" -#include "effect.hpp" -#include "controller.hpp" - -#include - -//TODO: when threading is needed, enable these -//#include -//#include - -namespace Nif -{ - -class NIFFile::LoadedCache -{ - //TODO: enable this to make cache thread safe... - //typedef boost::mutex mutex; - - struct mutex - { - void lock () {}; - void unlock () {} - }; - - typedef boost::lock_guard lock_guard; - typedef std::map < std::string, boost::weak_ptr > loaded_map; - typedef std::vector < boost::shared_ptr > locked_files; - - static int sLockLevel; - static mutex sProtector; - static loaded_map sLoadedMap; - static locked_files sLockedFiles; - -public: - - static ptr create (const std::string &name) - { - lock_guard _ (sProtector); - - ptr result; - - // lookup the resource - loaded_map::iterator i = sLoadedMap.find (name); - - if (i == sLoadedMap.end ()) // it doesn't existing currently, - { // or hasn't in the very near past - - // create it now, for smoother threading if needed, the - // loading should be performed outside of the sLoaderMap - // lock and an alternate mechanism should be used to - // synchronize threads competing to load the same resource - result = boost::make_shared (name, psudo_private_modifier()); - - // if we are locking the cache add an extra reference - // to keep the file in memory - if (sLockLevel > 0) - sLockedFiles.push_back (result); - - // stash a reference to the resource so that future - // calls can benefit - sLoadedMap [name] = boost::weak_ptr (result); - } - else // it may (probably) still exists - { - // attempt to get the reference - result = i->second.lock (); - - if (!result) // resource is in the process of being destroyed - { - // create a new instance, to replace the one that has - // begun the irreversible process of being destroyed - result = boost::make_shared (name, psudo_private_modifier()); - - // respect the cache lock... - if (sLockLevel > 0) - sLockedFiles.push_back (result); - - // we potentially overwrite an expired pointer here - // but the other thread performing the delete on - // the previous copy of this resource will detect it - // and make sure not to erase the new reference - sLoadedMap [name] = boost::weak_ptr (result); - } - } - - // we made it! - return result; - } - - static void release (NIFFile * file) - { - lock_guard _ (sProtector); - - loaded_map::iterator i = sLoadedMap.find (file->filename); - - // its got to be in here, it just might not be us... - assert (i != sLoadedMap.end ()); - - // if weak_ptr is still expired, this resource hasn't been recreated - // between the initiation of the final release due to destruction - // of the last shared pointer and this thread acquiring the lock on - // the loader map - if (i->second.expired ()) - sLoadedMap.erase (i); - } - - static void lockCache () - { - lock_guard _ (sProtector); - - sLockLevel++; - } - - static void unlockCache () - { - locked_files resetList; - - { - lock_guard _ (sProtector); - - if (--sLockLevel) - sLockedFiles.swap(resetList); - } - - // this not necessary, but makes it clear that the - // deletion of the locked cache entries is being done - // outside the protection of sProtector - resetList.clear (); - } -}; - -int NIFFile::LoadedCache::sLockLevel = 0; -NIFFile::LoadedCache::mutex NIFFile::LoadedCache::sProtector; -NIFFile::LoadedCache::loaded_map NIFFile::LoadedCache::sLoadedMap; -NIFFile::LoadedCache::locked_files NIFFile::LoadedCache::sLockedFiles; - -// these three calls are forwarded to the cache implementation... -void NIFFile::lockCache () { LoadedCache::lockCache (); } -void NIFFile::unlockCache () { LoadedCache::unlockCache (); } -NIFFile::ptr NIFFile::create (const std::string &name) { return LoadedCache::create (name); } - -/// Open a NIF stream. The name is used for error messages. -NIFFile::NIFFile(const std::string &name, psudo_private_modifier) - : filename(name) -{ - inp = Ogre::ResourceGroupManager::getSingleton().openResource(name); - parse(); - // Make sure to close the file after it was loaded into memory - inp.setNull(); -} - -NIFFile::~NIFFile() -{ - LoadedCache::release (this); - - for(std::size_t i=0; irecType = RC_NiNode; } - - // Other nodes - else if(rec == "NiTriShape") { r = new NiTriShape; r->recType = RC_NiTriShape; } - else if(rec == "NiRotatingParticles") { r = new NiRotatingParticles; r->recType = RC_NiRotatingParticles; } - else if(rec == "NiAutoNormalParticles") { r = new NiAutoNormalParticles; r->recType = RC_NiAutoNormalParticles; } - else if(rec == "NiCamera") { r = new NiCamera; r->recType = RC_NiCamera; } - else if(rec == "RootCollisionNode"){ r = new NiNode; r->recType = RC_RootCollisionNode; }// a root collision node is exactly like a node - //that's why there is no need to create a new type - - // Properties - else if(rec == "NiTexturingProperty") { r = new NiTexturingProperty; r->recType = RC_NiTexturingProperty; } - else if(rec == "NiMaterialProperty") { r = new NiMaterialProperty; r->recType = RC_NiMaterialProperty; } - else if(rec == "NiZBufferProperty") { r = new NiZBufferProperty; r->recType = RC_NiZBufferProperty; } - else if(rec == "NiAlphaProperty") { r = new NiAlphaProperty; r->recType = RC_NiAlphaProperty; } - else if(rec == "NiVertexColorProperty") { r = new NiVertexColorProperty; r->recType = RC_NiVertexColorProperty; } - else if(rec == "NiShadeProperty") { r = new NiShadeProperty; r->recType = RC_NiShadeProperty; } - else if(rec == "NiDitherProperty") { r = new NiDitherProperty; r->recType = RC_NiDitherProperty; } - else if(rec == "NiWireframeProperty") { r = new NiWireframeProperty; r->recType = RC_NiWireframeProperty; } - else if(rec == "NiSpecularProperty") { r = new NiSpecularProperty; r->recType = RC_NiSpecularProperty; } - - // Controllers - else if(rec == "NiVisController") { r = new NiVisController; r->recType = RC_NiVisController; } - else if(rec == "NiGeomMorpherController") { r = new NiGeomMorpherController; r->recType = RC_NiGeomMorpherController; } - else if(rec == "NiKeyframeController") { r = new NiKeyframeController; r->recType = RC_NiKeyframeController; } - else if(rec == "NiAlphaController") { r = new NiAlphaController; r->recType = RC_NiAlphaController; } - else if(rec == "NiUVController") { r = new NiUVController; r->recType = RC_NiUVController; } - else if(rec == "NiPathController") { r = new NiPathController; r->recType = RC_NiPathController; } - else if(rec == "NiMaterialColorController") { r = new NiMaterialColorController; r->recType = RC_NiMaterialColorController; } - else if(rec == "NiBSPArrayController") { r = new NiBSPArrayController; r->recType = RC_NiBSPArrayController; } - else if(rec == "NiParticleSystemController") { r = new NiParticleSystemController; r->recType = RC_NiParticleSystemController; } - - // Effects - else if(rec == "NiAmbientLight" || - rec == "NiDirectionalLight") { r = new NiLight; r->recType = RC_NiLight; } - else if(rec == "NiTextureEffect") { r = new NiTextureEffect; r->recType = RC_NiTextureEffect; } - - // Extra Data - else if(rec == "NiVertWeightsExtraData") { r = new NiVertWeightsExtraData; r->recType = RC_NiVertWeightsExtraData; } - else if(rec == "NiTextKeyExtraData") { r = new NiTextKeyExtraData; r->recType = RC_NiTextKeyExtraData; } - else if(rec == "NiStringExtraData") { r = new NiStringExtraData; r->recType = RC_NiStringExtraData; } - - else if(rec == "NiGravity") { r = new NiGravity; r->recType = RC_NiGravity; } - else if(rec == "NiPlanarCollider") { r = new NiPlanarCollider; r->recType = RC_NiPlanarCollider; } - else if(rec == "NiParticleGrowFade") { r = new NiParticleGrowFade; r->recType = RC_NiParticleGrowFade; } - else if(rec == "NiParticleColorModifier") { r = new NiParticleColorModifier; r->recType = RC_NiParticleColorModifier; } - else if(rec == "NiParticleRotation") { r = new NiParticleRotation; r->recType = RC_NiParticleRotation; } - - // Data - else if(rec == "NiFloatData") { r = new NiFloatData; r->recType = RC_NiFloatData; } - else if(rec == "NiTriShapeData") { r = new NiTriShapeData; r->recType = RC_NiTriShapeData; } - else if(rec == "NiVisData") { r = new NiVisData; r->recType = RC_NiVisData; } - else if(rec == "NiColorData") { r = new NiColorData; r->recType = RC_NiColorData; } - else if(rec == "NiPixelData") { r = new NiPixelData; r->recType = RC_NiPixelData; } - else if(rec == "NiMorphData") { r = new NiMorphData; r->recType = RC_NiMorphData; } - else if(rec == "NiKeyframeData") { r = new NiKeyframeData; r->recType = RC_NiKeyframeData; } - else if(rec == "NiSkinData") { r = new NiSkinData; r->recType = RC_NiSkinData; } - else if(rec == "NiUVData") { r = new NiUVData; r->recType = RC_NiUVData; } - else if(rec == "NiPosData") { r = new NiPosData; r->recType = RC_NiPosData; } - else if(rec == "NiRotatingParticlesData") { r = new NiRotatingParticlesData; r->recType = RC_NiRotatingParticlesData; } - else if(rec == "NiAutoNormalParticlesData") { r = new NiAutoNormalParticlesData; r->recType = RC_NiAutoNormalParticlesData; } - - // Other - else if(rec == "NiSequenceStreamHelper") { r = new NiSequenceStreamHelper; r->recType = RC_NiSequenceStreamHelper; } - else if(rec == "NiSourceTexture") { r = new NiSourceTexture; r->recType = RC_NiSourceTexture; } - else if(rec == "NiSkinInstance") { r = new NiSkinInstance; r->recType = RC_NiSkinInstance; } - - // Failure - else - fail("Unknown record type " + rec); - - assert(r != NULL); - assert(r->recType != RC_MISSING); - r->recName = rec; - r->recIndex = i; - records[i] = r; - r->read(this); - - // Discard tranformations for the root node, otherwise some meshes - // occasionally get wrong orientation. Only for NiNode-s for now, but - // can be expanded if needed. - // This should be rewritten when the method is cleaned up. - if (0 == i && rec == "NiNode") - { - static_cast(r)->trafo = Nif::Transformation::getIdentity(); - } - } - - /* After the data, the nif contains an int N and then a list of N - ints following it. This might be a list of the root nodes in the - tree, but for the moment we ignore it. - */ - - // Once parsing is done, do post-processing. - for(size_t i=0; ipost(this); -} - -/// \todo move to the write cpp file - -void NiSkinInstance::post(NIFFile *nif) -{ - data.post(nif); - root.post(nif); - bones.post(nif); - - if(data.empty() || root.empty()) - nif->fail("NiSkinInstance missing root or data"); - - size_t bnum = bones.length(); - if(bnum != data->bones.size()) - nif->fail("Mismatch in NiSkinData bone count"); - - root->makeRootBone(&data->trafo); - - for(size_t i=0; ifail("Oops: Missing bone! Don't know how to handle this."); - bones[i]->makeBone(i, data->bones[i]); - } -} - -Ogre::Matrix4 Node::getLocalTransform() const -{ - Ogre::Matrix4 mat4(Ogre::Matrix4::IDENTITY); - mat4.makeTransform(trafo.pos, Ogre::Vector3(trafo.scale), Ogre::Quaternion(trafo.rotation)); - return mat4; -} - -Ogre::Matrix4 Node::getWorldTransform() const -{ - if(parent != NULL) - return parent->getWorldTransform() * getLocalTransform(); - return getLocalTransform(); -} - -} diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp new file mode 100644 index 000000000..cb7c2feb0 --- /dev/null +++ b/components/nif/niffile.cpp @@ -0,0 +1,440 @@ +/* + OpenMW - The completely unofficial reimplementation of Morrowind + Copyright (C) 2008-2010 Nicolay Korslund + Email: < korslund@gmail.com > + WWW: http://openmw.sourceforge.net/ + + This file (nif_file.cpp) is part of the OpenMW package. + + OpenMW is distributed as free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License + version 3, as published by the Free Software Foundation. + + 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 + version 3 along with this program. If not, see + http://www.gnu.org/licenses/ . + + */ + +#include "niffile.hpp" +#include "record.hpp" +#include "components/misc/stringops.hpp" + +#include "extra.hpp" +#include "controlled.hpp" +#include "node.hpp" +#include "property.hpp" +#include "data.hpp" +#include "effect.hpp" +#include "controller.hpp" + +#include + +//TODO: when threading is needed, enable these +//#include +#include + +namespace Nif +{ + +class NIFFile::LoadedCache +{ + //TODO: enable this to make cache thread safe... + //typedef boost::mutex mutex; + + struct mutex + { + void lock () {}; + void unlock () {} + }; + + typedef boost::lock_guard lock_guard; + typedef std::map < std::string, boost::weak_ptr > loaded_map; + typedef std::vector < boost::shared_ptr > locked_files; + + static int sLockLevel; + static mutex sProtector; + static loaded_map sLoadedMap; + static locked_files sLockedFiles; + +public: + + static ptr create (const std::string &name) + { + lock_guard _ (sProtector); + + ptr result; + + // lookup the resource + loaded_map::iterator i = sLoadedMap.find (name); + + if (i == sLoadedMap.end ()) // it doesn't existing currently, + { // or hasn't in the very near past + + // create it now, for smoother threading if needed, the + // loading should be performed outside of the sLoaderMap + // lock and an alternate mechanism should be used to + // synchronize threads competing to load the same resource + result = boost::make_shared (name, psudo_private_modifier()); + + // if we are locking the cache add an extra reference + // to keep the file in memory + if (sLockLevel > 0) + sLockedFiles.push_back (result); + + // stash a reference to the resource so that future + // calls can benefit + sLoadedMap [name] = boost::weak_ptr (result); + } + else // it may (probably) still exists + { + // attempt to get the reference + result = i->second.lock (); + + if (!result) // resource is in the process of being destroyed + { + // create a new instance, to replace the one that has + // begun the irreversible process of being destroyed + result = boost::make_shared (name, psudo_private_modifier()); + + // respect the cache lock... + if (sLockLevel > 0) + sLockedFiles.push_back (result); + + // we potentially overwrite an expired pointer here + // but the other thread performing the delete on + // the previous copy of this resource will detect it + // and make sure not to erase the new reference + sLoadedMap [name] = boost::weak_ptr (result); + } + } + + // we made it! + return result; + } + + static void release (NIFFile * file) + { + lock_guard _ (sProtector); + + loaded_map::iterator i = sLoadedMap.find (file->filename); + + // its got to be in here, it just might not be us... + assert (i != sLoadedMap.end ()); + + // if weak_ptr is still expired, this resource hasn't been recreated + // between the initiation of the final release due to destruction + // of the last shared pointer and this thread acquiring the lock on + // the loader map + if (i->second.expired ()) + sLoadedMap.erase (i); + } + + static void lockCache () + { + lock_guard _ (sProtector); + + sLockLevel++; + } + + static void unlockCache () + { + locked_files resetList; + + { + lock_guard _ (sProtector); + + if (--sLockLevel) + sLockedFiles.swap(resetList); + } + + // this not necessary, but makes it clear that the + // deletion of the locked cache entries is being done + // outside the protection of sProtector + resetList.clear (); + } +}; + +int NIFFile::LoadedCache::sLockLevel = 0; +NIFFile::LoadedCache::mutex NIFFile::LoadedCache::sProtector; +NIFFile::LoadedCache::loaded_map NIFFile::LoadedCache::sLoadedMap; +NIFFile::LoadedCache::locked_files NIFFile::LoadedCache::sLockedFiles; + +// these three calls are forwarded to the cache implementation... +void NIFFile::lockCache () { LoadedCache::lockCache (); } +void NIFFile::unlockCache () { LoadedCache::unlockCache (); } +NIFFile::ptr NIFFile::create (const std::string &name) { return LoadedCache::create (name); } + +/// Open a NIF stream. The name is used for error messages. +NIFFile::NIFFile(const std::string &name, psudo_private_modifier) + : filename(name) +{ + parse(); +} + +NIFFile::~NIFFile() +{ + LoadedCache::release (this); + + for(std::size_t i=0; i static Record* construct() { return new NodeType; } + +struct RecordFactoryEntry { + + typedef Record* (*create_t) (); + + char const * mName; + create_t mCreate; + RecordType mType; + +}; + +/* These are all the record types we know how to read. + + This can be heavily optimized later if needed. For example, a + hash table or a FSM-based parser could be used to look up + node names. +*/ + +static const RecordFactoryEntry recordFactories [] = { + + { "NiNode", &construct , RC_NiNode }, + { "AvoidNode", &construct , RC_AvoidNode }, + { "NiBSParticleNode", &construct , RC_NiBSParticleNode }, + { "NiBSAnimationNode", &construct , RC_NiBSAnimationNode }, + { "NiBillboardNode", &construct , RC_NiNode }, + { "NiTriShape", &construct , RC_NiTriShape }, + { "NiRotatingParticles", &construct , RC_NiRotatingParticles }, + { "NiAutoNormalParticles", &construct , RC_NiAutoNormalParticles }, + { "NiCamera", &construct , RC_NiCamera }, + { "RootCollisionNode", &construct , RC_RootCollisionNode }, + { "NiTexturingProperty", &construct , RC_NiTexturingProperty }, + { "NiMaterialProperty", &construct , RC_NiMaterialProperty }, + { "NiZBufferProperty", &construct , RC_NiZBufferProperty }, + { "NiAlphaProperty", &construct , RC_NiAlphaProperty }, + { "NiVertexColorProperty", &construct , RC_NiVertexColorProperty }, + { "NiShadeProperty", &construct , RC_NiShadeProperty }, + { "NiDitherProperty", &construct , RC_NiDitherProperty }, + { "NiWireframeProperty", &construct , RC_NiWireframeProperty }, + { "NiSpecularProperty", &construct , RC_NiSpecularProperty }, + { "NiStencilProperty", &construct , RC_NiStencilProperty }, + { "NiVisController", &construct , RC_NiVisController }, + { "NiGeomMorpherController", &construct , RC_NiGeomMorpherController }, + { "NiKeyframeController", &construct , RC_NiKeyframeController }, + { "NiAlphaController", &construct , RC_NiAlphaController }, + { "NiUVController", &construct , RC_NiUVController }, + { "NiPathController", &construct , RC_NiPathController }, + { "NiMaterialColorController", &construct , RC_NiMaterialColorController }, + { "NiBSPArrayController", &construct , RC_NiBSPArrayController }, + { "NiParticleSystemController", &construct , RC_NiParticleSystemController }, + { "NiFlipController", &construct , RC_NiFlipController }, + { "NiAmbientLight", &construct , RC_NiLight }, + { "NiDirectionalLight", &construct , RC_NiLight }, + { "NiTextureEffect", &construct , RC_NiTextureEffect }, + { "NiVertWeightsExtraData", &construct , RC_NiVertWeightsExtraData }, + { "NiTextKeyExtraData", &construct , RC_NiTextKeyExtraData }, + { "NiStringExtraData", &construct , RC_NiStringExtraData }, + { "NiGravity", &construct , RC_NiGravity }, + { "NiPlanarCollider", &construct , RC_NiPlanarCollider }, + { "NiParticleGrowFade", &construct , RC_NiParticleGrowFade }, + { "NiParticleColorModifier", &construct , RC_NiParticleColorModifier }, + { "NiParticleRotation", &construct , RC_NiParticleRotation }, + { "NiFloatData", &construct , RC_NiFloatData }, + { "NiTriShapeData", &construct , RC_NiTriShapeData }, + { "NiVisData", &construct , RC_NiVisData }, + { "NiColorData", &construct , RC_NiColorData }, + { "NiPixelData", &construct , RC_NiPixelData }, + { "NiMorphData", &construct , RC_NiMorphData }, + { "NiKeyframeData", &construct , RC_NiKeyframeData }, + { "NiSkinData", &construct , RC_NiSkinData }, + { "NiUVData", &construct , RC_NiUVData }, + { "NiPosData", &construct , RC_NiPosData }, + { "NiRotatingParticlesData", &construct , RC_NiRotatingParticlesData }, + { "NiAutoNormalParticlesData", &construct , RC_NiAutoNormalParticlesData }, + { "NiSequenceStreamHelper", &construct , RC_NiSequenceStreamHelper }, + { "NiSourceTexture", &construct , RC_NiSourceTexture }, + { "NiSkinInstance", &construct , RC_NiSkinInstance }, +}; + +static RecordFactoryEntry const * recordFactories_begin = &recordFactories [0]; +static RecordFactoryEntry const * recordFactories_end = &recordFactories [sizeof (recordFactories) / sizeof (recordFactories[0])]; + +RecordFactoryEntry const * lookupRecordFactory (char const * name) +{ + RecordFactoryEntry const * i; + + for (i = recordFactories_begin; i != recordFactories_end; ++i) + if (strcmp (name, i->mName) == 0) + break; + + if (i == recordFactories_end) + return NULL; + + return i; +} + +/* This file implements functions from the NIFFile class. It is also + where we stash all the functions we couldn't add as inline + definitions in the record types. + */ + +void NIFFile::parse() +{ + NIFStream nif (this, Ogre::ResourceGroupManager::getSingleton().openResource(filename)); + + // Check the header string + std::string head = nif.getString(40); + if(head.compare(0, 22, "NetImmerse File Format") != 0) + fail("Invalid NIF header"); + + // Get BCD version + ver = nif.getInt(); + if(ver != VER_MW) + fail("Unsupported NIF version"); + + // Number of records + size_t recNum = nif.getInt(); + records.resize(recNum); + + /* The format for 10.0.1.0 seems to be a bit different. After the + header, it contains the number of records, r (int), just like + 4.0.0.2, but following that it contains a short x, followed by x + strings. Then again by r shorts, one for each record, giving + which of the above strings to use to identify the record. After + this follows two ints (zero?) and then the record data. However + we do not support or plan to support other versions yet. + */ + + for(size_t i = 0;i < recNum;i++) + { + Record *r = NULL; + + std::string rec = nif.getString(); + + RecordFactoryEntry const * entry = lookupRecordFactory (rec.c_str ()); + + if (entry != NULL) + { + r = entry->mCreate (); + r->recType = entry->mType; + } + else + fail("Unknown record type " + rec); + + assert(r != NULL); + assert(r->recType != RC_MISSING); + r->recName = rec; + r->recIndex = i; + records[i] = r; + r->read(&nif); + + // Discard tranformations for the root node, otherwise some meshes + // occasionally get wrong orientation. Only for NiNode-s for now, but + // can be expanded if needed. + // This should be rewritten when the method is cleaned up. + if (0 == i && rec == "NiNode") + { + static_cast(r)->trafo = Nif::Transformation::getIdentity(); + } + } + + size_t rootNum = nif.getUInt(); + roots.resize(rootNum); + + for(size_t i = 0;i < rootNum;i++) + { + intptr_t idx = nif.getInt(); + roots[i] = ((idx >= 0) ? records.at(idx) : NULL); + } + + // Once parsing is done, do post-processing. + for(size_t i=0; ipost(this); +} + +/// \todo move to the write cpp file + +void NiSkinInstance::post(NIFFile *nif) +{ + data.post(nif); + root.post(nif); + bones.post(nif); + + if(data.empty() || root.empty()) + nif->fail("NiSkinInstance missing root or data"); + + size_t bnum = bones.length(); + if(bnum != data->bones.size()) + nif->fail("Mismatch in NiSkinData bone count"); + + root->makeRootBone(&data->trafo); + + for(size_t i=0; ifail("Oops: Missing bone! Don't know how to handle this."); + bones[i]->makeBone(i, data->bones[i]); + } +} + + +void Node::getProperties(const Nif::NiTexturingProperty *&texprop, + const Nif::NiMaterialProperty *&matprop, + const Nif::NiAlphaProperty *&alphaprop, + const Nif::NiVertexColorProperty *&vertprop, + const Nif::NiZBufferProperty *&zprop, + const Nif::NiSpecularProperty *&specprop, + const Nif::NiWireframeProperty *&wireprop) const +{ + if(parent) + parent->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop); + + for(size_t i = 0;i < props.length();i++) + { + // Entries may be empty + if(props[i].empty()) + continue; + + const Nif::Property *pr = props[i].getPtr(); + if(pr->recType == Nif::RC_NiTexturingProperty) + texprop = static_cast(pr); + else if(pr->recType == Nif::RC_NiMaterialProperty) + matprop = static_cast(pr); + else if(pr->recType == Nif::RC_NiAlphaProperty) + alphaprop = static_cast(pr); + else if(pr->recType == Nif::RC_NiVertexColorProperty) + vertprop = static_cast(pr); + else if(pr->recType == Nif::RC_NiZBufferProperty) + zprop = static_cast(pr); + else if(pr->recType == Nif::RC_NiSpecularProperty) + specprop = static_cast(pr); + else if(pr->recType == Nif::RC_NiWireframeProperty) + wireprop = static_cast(pr); + else + std::cerr<< "Unhandled property type: "<recName <getWorldTransform() * getLocalTransform(); + return getLocalTransform(); +} + +} diff --git a/components/nif/nif_file.hpp b/components/nif/niffile.hpp similarity index 54% rename from components/nif/nif_file.hpp rename to components/nif/niffile.hpp index e8884cd4d..6e629772e 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/niffile.hpp @@ -21,8 +21,8 @@ */ -#ifndef _NIF_FILE_H_ -#define _NIF_FILE_H_ +#ifndef OPENMW_COMPONENTS_NIF_NIFFILE_HPP +#define OPENMW_COMPONENTS_NIF_NIFFILE_HPP #include #include @@ -45,7 +45,8 @@ #include #include "record.hpp" -#include "nif_types.hpp" +#include "niftypes.hpp" +#include "nifstream.hpp" namespace Nif { @@ -59,45 +60,18 @@ class NIFFile /// Nif file version int ver; - /// Input stream - Ogre::DataStreamPtr inp; - /// File name, used for error messages std::string filename; /// Record list std::vector records; + /// Root list + std::vector roots; + /// Parse the file void parse(); - uint8_t read_byte() - { - uint8_t byte; - if(inp->read(&byte, 1) != 1) return 0; - return byte; - } - uint16_t read_le16() - { - uint8_t buffer[2]; - if(inp->read(buffer, 2) != 2) return 0; - return buffer[0] | (buffer[1]<<8); - } - uint32_t read_le32() - { - uint8_t buffer[4]; - if(inp->read(buffer, 4) != 4) return 0; - return buffer[0] | (buffer[1]<<8) | (buffer[2]<<16) | (buffer[3]<<24); - } - float read_le32f() - { - union { - uint32_t i; - float f; - } u = { read_le32() }; - return u.f; - } - class LoadedCache; friend class LoadedCache; @@ -117,8 +91,8 @@ public: void warn(const std::string &msg) { - std::cerr<< "NIFFile Warning: "< ptr; @@ -144,113 +118,18 @@ public: assert(res != NULL); return res; } - /// Number of records size_t numRecords() { return records.size(); } - /************************************************* - Parser functions - ****************************************************/ - - void skip(size_t size) { inp->skip(size); } - - char getChar() { return read_byte(); } - short getShort() { return read_le16(); } - unsigned short getUShort() { return read_le16(); } - int getInt() { return read_le32(); } - float getFloat() { return read_le32f(); } - Ogre::Vector2 getVector2() - { - float a[2]; - for(size_t i = 0;i < 2;i++) - a[i] = getFloat(); - return Ogre::Vector2(a); - } - Ogre::Vector3 getVector3() - { - float a[3]; - for(size_t i = 0;i < 3;i++) - a[i] = getFloat(); - return Ogre::Vector3(a); - } - Ogre::Vector4 getVector4() - { - float a[4]; - for(size_t i = 0;i < 4;i++) - a[i] = getFloat(); - return Ogre::Vector4(a); - } - Ogre::Matrix3 getMatrix3() - { - Ogre::Real a[3][3]; - for(size_t i = 0;i < 3;i++) - { - for(size_t j = 0;j < 3;j++) - a[i][j] = Ogre::Real(getFloat()); - } - return Ogre::Matrix3(a); - } - Ogre::Quaternion getQuaternion() - { - float a[4]; - for(size_t i = 0;i < 4;i++) - a[i] = getFloat(); - return Ogre::Quaternion(a); - } - Transformation getTrafo() - { - Transformation t; - t.pos = getVector3(); - t.rotation = getMatrix3(); - t.scale = getFloat(); - return t; - } - - std::string getString(size_t length) + /// Get a given root + Record *getRoot(size_t index=0) { - std::vector str (length+1, 0); - - if(inp->read(&str[0], length) != length) - throw std::runtime_error ("string length in NIF file does not match"); - - return &str[0]; - } - std::string getString() - { - size_t size = read_le32(); - return getString(size); - } - - void getShorts(std::vector &vec, size_t size) - { - vec.resize(size); - for(size_t i = 0;i < vec.size();i++) - vec[i] = getShort(); - } - void getFloats(std::vector &vec, size_t size) - { - vec.resize(size); - for(size_t i = 0;i < vec.size();i++) - vec[i] = getFloat(); - } - void getVector2s(std::vector &vec, size_t size) - { - vec.resize(size); - for(size_t i = 0;i < vec.size();i++) - vec[i] = getVector2(); - } - void getVector3s(std::vector &vec, size_t size) - { - vec.resize(size); - for(size_t i = 0;i < vec.size();i++) - vec[i] = getVector3(); - } - void getVector4s(std::vector &vec, size_t size) - { - vec.resize(size); - for(size_t i = 0;i < vec.size();i++) - vec[i] = getVector4(); + Record *res = roots.at(index); + assert(res != NULL); + return res; } + /// Number of roots + size_t numRoots() { return roots.size(); } }; @@ -269,7 +148,7 @@ typedef KeyT Vector3Key; typedef KeyT Vector4Key; typedef KeyT QuaternionKey; -template +template struct KeyListT { typedef std::vector< KeyT > VecType; @@ -280,7 +159,7 @@ struct KeyListT { int mInterpolationType; VecType mKeys; - void read(NIFFile *nif, bool force=false) + void read(NIFStream *nif, bool force=false) { size_t count = nif->getInt(); if(count == 0 && !force) @@ -321,13 +200,13 @@ struct KeyListT { } } else - nif->warn("Unhandled interpolation type: "+Ogre::StringConverter::toString(mInterpolationType)); + nif->file->warn("Unhandled interpolation type: "+Ogre::StringConverter::toString(mInterpolationType)); } }; -typedef KeyListT FloatKeyList; -typedef KeyListT Vector3KeyList; -typedef KeyListT Vector4KeyList; -typedef KeyListT QuaternionKeyList; +typedef KeyListT FloatKeyList; +typedef KeyListT Vector3KeyList; +typedef KeyListT Vector4KeyList; +typedef KeyListT QuaternionKeyList; } // Namespace #endif diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp new file mode 100644 index 000000000..a2595d17b --- /dev/null +++ b/components/nif/nifstream.hpp @@ -0,0 +1,181 @@ +#ifndef OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP +#define OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP + +namespace Nif +{ + +class NIFFile; + +class NIFStream { + + /// Input stream + Ogre::DataStreamPtr inp; + + uint8_t read_byte() + { + uint8_t byte; + if(inp->read(&byte, 1) != 1) return 0; + return byte; + } + uint16_t read_le16() + { + uint8_t buffer[2]; + if(inp->read(buffer, 2) != 2) return 0; + return buffer[0] | (buffer[1]<<8); + } + uint32_t read_le32() + { + uint8_t buffer[4]; + if(inp->read(buffer, 4) != 4) return 0; + return buffer[0] | (buffer[1]<<8) | (buffer[2]<<16) | (buffer[3]<<24); + } + float read_le32f() + { + union { + uint32_t i; + float f; + } u = { read_le32() }; + return u.f; + } + +public: + + NIFFile * const file; + + NIFStream (NIFFile * file, Ogre::DataStreamPtr inp): file (file), inp (inp) {} + + /************************************************* + Parser functions + ****************************************************/ + + template + struct GetHandler + { + typedef T (NIFStream::*fn_t)(); + + static const fn_t sValue; // this is specialized per supported type in the .cpp file + + static T read (NIFStream* nif) + { + return (nif->*sValue) (); + } + }; + + template + void read (NIFStream* nif, T & Value) + { + Value = GetHandler ::read (nif); + } + + void skip(size_t size) { inp->skip(size); } + void read (void * data, size_t size) { inp->read (data, size); } + + char getChar() { return read_byte(); } + short getShort() { return read_le16(); } + unsigned short getUShort() { return read_le16(); } + int getInt() { return read_le32(); } + int getUInt() { return read_le32(); } + float getFloat() { return read_le32f(); } + Ogre::Vector2 getVector2() + { + float a[2]; + for(size_t i = 0;i < 2;i++) + a[i] = getFloat(); + return Ogre::Vector2(a); + } + Ogre::Vector3 getVector3() + { + float a[3]; + for(size_t i = 0;i < 3;i++) + a[i] = getFloat(); + return Ogre::Vector3(a); + } + Ogre::Vector4 getVector4() + { + float a[4]; + for(size_t i = 0;i < 4;i++) + a[i] = getFloat(); + return Ogre::Vector4(a); + } + Ogre::Matrix3 getMatrix3() + { + Ogre::Real a[3][3]; + for(size_t i = 0;i < 3;i++) + { + for(size_t j = 0;j < 3;j++) + a[i][j] = Ogre::Real(getFloat()); + } + return Ogre::Matrix3(a); + } + Ogre::Quaternion getQuaternion() + { + float a[4]; + for(size_t i = 0;i < 4;i++) + a[i] = getFloat(); + return Ogre::Quaternion(a); + } + Transformation getTrafo() + { + Transformation t; + t.pos = getVector3(); + t.rotation = getMatrix3(); + t.scale = getFloat(); + return t; + } + + std::string getString(size_t length) + { + std::vector str (length+1, 0); + + if(inp->read(&str[0], length) != length) + throw std::runtime_error ("string length in NIF file does not match"); + + return &str[0]; + } + std::string getString() + { + size_t size = read_le32(); + return getString(size); + } + + void getShorts(std::vector &vec, size_t size) + { + vec.resize(size); + for(size_t i = 0;i < vec.size();i++) + vec[i] = getShort(); + } + void getFloats(std::vector &vec, size_t size) + { + vec.resize(size); + for(size_t i = 0;i < vec.size();i++) + vec[i] = getFloat(); + } + void getVector2s(std::vector &vec, size_t size) + { + vec.resize(size); + for(size_t i = 0;i < vec.size();i++) + vec[i] = getVector2(); + } + void getVector3s(std::vector &vec, size_t size) + { + vec.resize(size); + for(size_t i = 0;i < vec.size();i++) + vec[i] = getVector3(); + } + void getVector4s(std::vector &vec, size_t size) + { + vec.resize(size); + for(size_t i = 0;i < vec.size();i++) + vec[i] = getVector4(); + } + void getQuaternions(std::vector &quat, size_t size) + { + quat.resize(size); + for(size_t i = 0;i < quat.size();i++) + quat[i] = getQuaternion(); + } +}; + +} + +#endif diff --git a/components/nif/nif_types.hpp b/components/nif/niftypes.hpp similarity index 93% rename from components/nif/nif_types.hpp rename to components/nif/niftypes.hpp index a5fb61361..786c48b65 100644 --- a/components/nif/nif_types.hpp +++ b/components/nif/niftypes.hpp @@ -21,8 +21,8 @@ */ -#ifndef _NIF_TYPES_H_ -#define _NIF_TYPES_H_ +#ifndef OPENMW_COMPONENTS_NIF_NIFTYPES_HPP +#define OPENMW_COMPONENTS_NIF_NIFTYPES_HPP #include #include diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 07e7868cc..917bc8add 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -21,8 +21,8 @@ */ -#ifndef _NIF_NODE_H_ -#define _NIF_NODE_H_ +#ifndef OPENMW_COMPONENTS_NIF_NODE_HPP +#define OPENMW_COMPONENTS_NIF_NODE_HPP #include @@ -54,7 +54,7 @@ public: Ogre::Matrix3 boundRot; Ogre::Vector3 boundXYZ; // Box size - void read(NIFFile *nif) + void read(NIFStream *nif) { Named::read(nif); @@ -111,6 +111,14 @@ public: boneIndex = ind; } + void getProperties(const Nif::NiTexturingProperty *&texprop, + const Nif::NiMaterialProperty *&matprop, + const Nif::NiAlphaProperty *&alphaprop, + const Nif::NiVertexColorProperty *&vertprop, + const Nif::NiZBufferProperty *&zprop, + const Nif::NiSpecularProperty *&specprop, + const Nif::NiWireframeProperty *&wireprop) const; + Ogre::Matrix4 getLocalTransform() const; Ogre::Matrix4 getWorldTransform() const; }; @@ -120,15 +128,19 @@ struct NiNode : Node NodeList children; NodeList effects; - /* Known NiNode flags: - 0x01 hidden - 0x02 use mesh for collision - 0x04 use bounding box for collision (?) - 0x08 unknown, but common - 0x20, 0x40, 0x80 unknown - */ + enum Flags { + Flag_Hidden = 0x0001, + Flag_MeshCollision = 0x0002, + Flag_BBoxCollision = 0x0004 + }; + enum BSAnimFlags { + AnimFlag_AutoPlay = 0x0020 + }; + enum BSParticleFlags { + ParticleFlag_AutoPlay = 0x0020 + }; - void read(NIFFile *nif) + void read(NIFStream *nif) { Node::read(nif); children.read(nif); @@ -162,7 +174,7 @@ struct NiTriShape : Node NiTriShapeDataPtr data; NiSkinInstancePtr skin; - void read(NIFFile *nif) + void read(NIFStream *nif) { Node::read(nif); data.read(nif); @@ -190,7 +202,7 @@ struct NiCamera : Node // Level of detail modifier float LOD; - void read(NIFFile *nif) + void read(NIFStream *nif) { left = nif->getFloat(); right = nif->getFloat(); @@ -209,7 +221,7 @@ struct NiCamera : Node }; Camera cam; - void read(NIFFile *nif) + void read(NIFStream *nif) { Node::read(nif); @@ -224,7 +236,7 @@ struct NiAutoNormalParticles : Node { NiAutoNormalParticlesDataPtr data; - void read(NIFFile *nif) + void read(NIFStream *nif) { Node::read(nif); data.read(nif); @@ -242,7 +254,7 @@ struct NiRotatingParticles : Node { NiRotatingParticlesDataPtr data; - void read(NIFFile *nif) + void read(NIFStream *nif) { Node::read(nif); data.read(nif); diff --git a/components/nif/property.hpp b/components/nif/property.hpp index b24e49b47..06c8260ce 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -21,8 +21,8 @@ */ -#ifndef _NIF_PROPERTY_H_ -#define _NIF_PROPERTY_H_ +#ifndef OPENMW_COMPONENTS_NIF_PROPERTY_HPP +#define OPENMW_COMPONENTS_NIF_PROPERTY_HPP #include "controlled.hpp" @@ -35,7 +35,7 @@ public: // The meaning of these depends on the actual property type. int flags; - void read(NIFFile *nif) + void read(NIFStream *nif) { Named::read(nif); flags = nif->getUShort(); @@ -64,10 +64,10 @@ public: bool inUse; NiSourceTexturePtr texture; - int clamp, set, filter; + int clamp, uvSet, filter; short unknown2; - void read(NIFFile *nif) + void read(NIFStream *nif) { inUse = !!nif->getInt(); if(!inUse) return; @@ -75,7 +75,7 @@ public: texture.read(nif); clamp = nif->getInt(); filter = nif->getInt(); - set = nif->getInt(); + uvSet = nif->getInt(); // I have no idea, but I think these are actually two // PS2-specific shorts (ps2L and ps2K), followed by an unknown @@ -109,9 +109,20 @@ public: * 5 - Bump map texture * 6 - Decal texture */ + enum TextureType + { + BaseTexture = 0, + DarkTexture = 1, + DetailTexture = 2, + GlossTexture = 3, + GlowTexture = 4, + BumpTexture = 5, + DecalTexture = 6 + }; + Texture textures[7]; - void read(NIFFile *nif) + void read(NIFStream *nif) { Property::read(nif); apply = nif->getInt(); @@ -145,11 +156,11 @@ public: }; // These contain no other data than the 'flags' field in Property -typedef Property NiShadeProperty; -typedef Property NiDitherProperty; -typedef Property NiZBufferProperty; -typedef Property NiSpecularProperty; -typedef Property NiWireframeProperty; +class NiShadeProperty : public Property { }; +class NiDitherProperty : public Property { }; +class NiZBufferProperty : public Property { }; +class NiSpecularProperty : public Property { }; +class NiWireframeProperty : public Property { }; // The rest are all struct-based template @@ -157,7 +168,7 @@ struct StructPropT : Property { T data; - void read(NIFFile *nif) + void read(NIFStream *nif) { Property::read(nif); data.read(nif); @@ -170,7 +181,7 @@ struct S_MaterialProperty Ogre::Vector3 ambient, diffuse, specular, emissive; float glossiness, alpha; - void read(NIFFile *nif) + void read(NIFStream *nif) { ambient = nif->getVector3(); diffuse = nif->getVector3(); @@ -194,7 +205,7 @@ struct S_VertexColorProperty */ int vertmode, lightmode; - void read(NIFFile *nif) + void read(NIFStream *nif) { vertmode = nif->getInt(); lightmode = nif->getInt(); @@ -251,15 +262,72 @@ struct S_AlphaProperty // Tested against when certain flags are set (see above.) unsigned char threshold; - void read(NIFFile *nif) + void read(NIFStream *nif) { threshold = nif->getChar(); } }; -typedef StructPropT NiAlphaProperty; -typedef StructPropT NiMaterialProperty; -typedef StructPropT NiVertexColorProperty; +/* + Docs taken from: + http://niftools.sourceforge.net/doc/nif/NiStencilProperty.html + */ +struct S_StencilProperty +{ + // Is stencil test enabled? + unsigned char enabled; + + /* + 0 TEST_NEVER + 1 TEST_LESS + 2 TEST_EQUAL + 3 TEST_LESS_EQUAL + 4 TEST_GREATER + 5 TEST_NOT_EQUAL + 6 TEST_GREATER_EQUAL + 7 TEST_ALWAYS + */ + int compareFunc; + unsigned stencilRef; + unsigned stencilMask; + /* + Stencil test fail action, depth test fail action and depth test pass action: + 0 ACTION_KEEP + 1 ACTION_ZERO + 2 ACTION_REPLACE + 3 ACTION_INCREMENT + 4 ACTION_DECREMENT + 5 ACTION_INVERT + */ + int failAction; + int zFailAction; + int zPassAction; + /* + Face draw mode: + 0 DRAW_CCW_OR_BOTH + 1 DRAW_CCW [default] + 2 DRAW_CW + 3 DRAW_BOTH + */ + int drawMode; + + void read(NIFStream *nif) + { + enabled = nif->getChar(); + compareFunc = nif->getInt(); + stencilRef = nif->getUInt(); + stencilMask = nif->getUInt(); + failAction = nif->getInt(); + zFailAction = nif->getInt(); + zPassAction = nif->getInt(); + drawMode = nif->getInt(); + } +}; + +class NiAlphaProperty : public StructPropT { }; +class NiMaterialProperty : public StructPropT { }; +class NiVertexColorProperty : public StructPropT { }; +class NiStencilProperty : public StructPropT { }; } // Namespace #endif diff --git a/components/nif/record.hpp b/components/nif/record.hpp index d5f65e83a..87e342dca 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -21,8 +21,8 @@ */ -#ifndef _NIF_RECORD_H_ -#define _NIF_RECORD_H_ +#ifndef OPENMW_COMPONENTS_NIF_RECORD_HPP +#define OPENMW_COMPONENTS_NIF_RECORD_HPP #include @@ -30,14 +30,17 @@ namespace Nif { class NIFFile; +class NIFStream; enum RecordType { RC_MISSING = 0, RC_NiNode, + RC_AvoidNode, RC_NiTriShape, RC_NiRotatingParticles, RC_NiAutoNormalParticles, + RC_NiBSParticleNode, RC_NiCamera, RC_NiTexturingProperty, RC_NiMaterialProperty, @@ -48,6 +51,7 @@ enum RecordType RC_NiDitherProperty, RC_NiWireframeProperty, RC_NiSpecularProperty, + RC_NiStencilProperty, RC_NiVisController, RC_NiGeomMorpherController, RC_NiKeyframeController, @@ -57,6 +61,8 @@ enum RecordType RC_NiMaterialColorController, RC_NiBSPArrayController, RC_NiParticleSystemController, + RC_NiFlipController, + RC_NiBSAnimationNode, RC_NiLight, RC_NiTextureEffect, RC_NiVertWeightsExtraData, @@ -96,7 +102,7 @@ struct Record Record() : recType(RC_MISSING), recIndex(~(size_t)0) {} /// Parses the record from file - virtual void read(NIFFile *nif) = 0; + virtual void read(NIFStream *nif) = 0; /// Does post-processing, after the entire tree is loaded virtual void post(NIFFile *nif) {} diff --git a/components/nif/record_ptr.hpp b/components/nif/recordptr.hpp similarity index 94% rename from components/nif/record_ptr.hpp rename to components/nif/recordptr.hpp index ef5bb1dee..2ecde7f60 100644 --- a/components/nif/record_ptr.hpp +++ b/components/nif/recordptr.hpp @@ -21,10 +21,10 @@ */ -#ifndef _NIF_RECORD_PTR_H_ -#define _NIF_RECORD_PTR_H_ +#ifndef OPENMW_COMPONENTS_NIF_RECORDPTR_HPP +#define OPENMW_COMPONENTS_NIF_RECORDPTR_HPP -#include "nif_file.hpp" +#include "niffile.hpp" #include namespace Nif @@ -46,7 +46,7 @@ public: RecordPtrT() : index(-2) {} /// Read the index from the nif - void read(NIFFile *nif) + void read(NIFStream *nif) { // Can only read the index once assert(index == -2); @@ -99,7 +99,7 @@ class RecordListT std::vector list; public: - void read(NIFFile *nif) + void read(NIFStream *nif) { int len = nif->getInt(); list.resize(len); @@ -163,6 +163,7 @@ typedef RecordPtrT NiAutoNormalParticlesDataPtr; typedef RecordListT NodeList; typedef RecordListT PropertyList; +typedef RecordListT NiSourceTextureList; } // Namespace #endif diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp deleted file mode 100644 index 9c6dafa34..000000000 --- a/components/nifbullet/bullet_nif_loader.cpp +++ /dev/null @@ -1,322 +0,0 @@ - /* -OpenMW - The completely unofficial reimplementation of Morrowind -Copyright (C) 2008-2010 Nicolay Korslund -Email: < korslund@gmail.com > -WWW: http://openmw.sourceforge.net/ - -This file (ogre_nif_loader.cpp) is part of the OpenMW package. - -OpenMW is distributed as free software: you can redistribute it -and/or modify it under the terms of the GNU General Public License -version 3, as published by the Free Software Foundation. - -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 -version 3 along with this program. If not, see -http://www.gnu.org/licenses/ . - -*/ - -#include "bullet_nif_loader.hpp" -#include -#include - -#include "../nif/nif_file.hpp" -#include "../nif/node.hpp" -#include "../nif/data.hpp" -#include "../nif/property.hpp" -#include "../nif/controller.hpp" -#include "../nif/extra.hpp" -#include - -#include -#include -// For warning messages -#include - -// float infinity -#include - -typedef unsigned char ubyte; - -using namespace NifBullet; - - -ManualBulletShapeLoader::~ManualBulletShapeLoader() -{ -} - - -btQuaternion ManualBulletShapeLoader::getbtQuat(Ogre::Matrix3 const &m) -{ - Ogre::Quaternion oquat(m); - btQuaternion quat; - quat.setW(oquat.w); - quat.setX(oquat.x); - quat.setY(oquat.y); - quat.setZ(oquat.z); - return quat; -} - -btVector3 ManualBulletShapeLoader::getbtVector(Ogre::Vector3 const &v) -{ - return btVector3(v[0], v[1], v[2]); -} - -void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) -{ - cShape = static_cast(resource); - resourceName = cShape->getName(); - cShape->mCollide = false; - mBoundingBox = NULL; - cShape->boxTranslation = Ogre::Vector3(0,0,0); - cShape->boxRotation = Ogre::Quaternion::IDENTITY; - - mTriMesh = new btTriangleMesh(); - - // Load the NIF. TODO: Wrap this in a try-catch block once we're out - // of the early stages of development. Right now we WANT to catch - // every error as early and intrusively as possible, as it's most - // likely a sign of incomplete code rather than faulty input. - Nif::NIFFile::ptr pnif (Nif::NIFFile::create (resourceName.substr(0, resourceName.length()-7))); - Nif::NIFFile & nif = *pnif.get (); - if (nif.numRecords() < 1) - { - warn("Found no records in NIF."); - return; - } - - - // The first record is assumed to be the root node - Nif::Record *r = nif.getRecord(0); - assert(r != NULL); - - Nif::Node *node = dynamic_cast(r); - if (node == NULL) - { - warn("First record in file was not a node, but a " + - r->recName + ". Skipping file."); - return; - } - - bool hasCollisionNode = hasRootCollisionNode(node); - - //do a first pass - handleNode(node,0,NULL,hasCollisionNode,false,false); - - //if collide = false, then it does a second pass which create a shape for raycasting. - if(cShape->mCollide == false) - { - handleNode(node,0,NULL,hasCollisionNode,false,true); - } - - //cShape->collide = hasCollisionNode&&cShape->collide; - - struct TriangleMeshShape : public btBvhTriangleMeshShape - { - TriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression) - : btBvhTriangleMeshShape(meshInterface, useQuantizedAabbCompression) - { - } - - virtual ~TriangleMeshShape() - { - delete getTriangleInfoMap(); - delete m_meshInterface; - } - }; - if(mBoundingBox != NULL) - cShape->Shape = mBoundingBox; - - else - { - currentShape = new TriangleMeshShape(mTriMesh,true); - cShape->Shape = currentShape; - } -} - -bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node const * node) -{ - if (node->recType == Nif::RC_NiNode) - { - Nif::NodeList &list = ((Nif::NiNode*)node)->children; - int n = list.length(); - for (int i=0; irecType == Nif::RC_NiTriShape) - { - return false; - } - else if(node->recType == Nif::RC_RootCollisionNode) - { - return true; - } - - return false; -} - -void ManualBulletShapeLoader::handleNode(Nif::Node const *node, int flags, - const Nif::Transformation *parentTrafo,bool hasCollisionNode,bool isCollisionNode,bool raycastingOnly) -{ - - // Accumulate the flags from all the child nodes. This works for all - // the flags we currently use, at least. - flags |= node->flags; - - // Marker objects: no collision - /// \todo don't do this in the editor - if (node->name.find("marker") != std::string::npos) - { - flags |= 0x800; - cShape->mIgnore = true; - } - - // Check for extra data - Nif::Extra const *e = node; - while (!e->extra.empty()) - { - // Get the next extra data in the list - e = e->extra.getPtr(); - assert(e != NULL); - - if (e->recType == Nif::RC_NiStringExtraData) - { - // String markers may contain important information - // affecting the entire subtree of this node - Nif::NiStringExtraData *sd = (Nif::NiStringExtraData*)e; - - // not sure what the difference between NCO and NCC is, or if there even is one - if (sd->string == "NCO" || sd->string == "NCC") - { - // No collision. Use an internal flag setting to mark this. - flags |= 0x800; - } - else if (sd->string == "MRK" && !raycastingOnly) - // Marker objects. These are only visible in the - // editor. Until and unless we add an editor component to - // the engine, just skip this entire node. - return; - } - } - - Nif::Transformation childTrafo = node->trafo; - - if (parentTrafo) - { - - // Get a non-const reference to the node's data, since we're - // overwriting it. TODO: Is this necessary? - - // For both position and rotation we have that: - // final_vector = old_vector + old_rotation*new_vector*old_scale - childTrafo.pos = parentTrafo->pos + parentTrafo->rotation*childTrafo.pos*parentTrafo->scale; - - // Merge the rotations together - childTrafo.rotation = parentTrafo->rotation * childTrafo.rotation; - - // Scale - childTrafo.scale *= parentTrafo->scale; - - } - - if(node->hasBounds) - { - - - btVector3 boxsize = getbtVector(node->boundXYZ); - cShape->boxTranslation = node->boundPos; - cShape->boxRotation = node->boundRot; - - mBoundingBox = new btBoxShape(boxsize); - } - - - // For NiNodes, loop through children - if (node->recType == Nif::RC_NiNode) - { - Nif::NodeList const &list = ((Nif::NiNode const *)node)->children; - int n = list.length(); - for (int i=0; irecType == Nif::RC_NiTriShape && (isCollisionNode || !hasCollisionNode)) - { - cShape->mCollide = !(flags&0x800); - handleNiTriShape(dynamic_cast(node), flags,childTrafo.rotation,childTrafo.pos,childTrafo.scale,raycastingOnly); - } - else if(node->recType == Nif::RC_RootCollisionNode) - { - Nif::NodeList &list = ((Nif::NiNode*)node)->children; - int n = list.length(); - for (int i=0; i

ColoredTabWidget.hpp
+ 1 + + + + + diff --git a/extern/shiny/Editor/newmaterialdialog.ui b/extern/shiny/Editor/newmaterialdialog.ui new file mode 100644 index 000000000..f24561cf7 --- /dev/null +++ b/extern/shiny/Editor/newmaterialdialog.ui @@ -0,0 +1,98 @@ + + + NewMaterialDialog + + + + 0 + 0 + 385 + 198 + + + + Dialog + + + + + + + + Name + + + + + + + Parent material + + + + + + + + + + + + + File + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + NewMaterialDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + NewMaterialDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/extern/shiny/Main/Factory.cpp b/extern/shiny/Main/Factory.cpp index 82d664811..c27848b5f 100644 --- a/extern/shiny/Main/Factory.cpp +++ b/extern/shiny/Main/Factory.cpp @@ -15,6 +15,7 @@ namespace sh { Factory* Factory::sThis = 0; + const std::string Factory::mBinaryCacheName = "binaryCache"; Factory& Factory::getInstance() { @@ -50,8 +51,6 @@ namespace sh { assert(mCurrentLanguage != Language_None); - bool anyShaderDirty = false; - if (boost::filesystem::exists (mPlatform->getCacheFolder () + "/lastModified.txt")) { std::ifstream file; @@ -85,8 +84,9 @@ namespace sh break; } - PropertySetGet newConfiguration; + Configuration newConfiguration; newConfiguration.setParent(&mGlobalSettings); + newConfiguration.setSourceFile (it->second->mFileName); std::vector props = it->second->getChildren(); for (std::vector::const_iterator propIt = props.begin(); propIt != props.end(); ++propIt) @@ -136,73 +136,7 @@ namespace sh } // load shader sets - { - ScriptLoader shaderSetLoader(".shaderset"); - ScriptLoader::loadAllFiles (&shaderSetLoader, mPlatform->getBasePath()); - std::map nodes = shaderSetLoader.getAllConfigScripts(); - for (std::map ::const_iterator it = nodes.begin(); - it != nodes.end(); ++it) - { - if (!(it->second->getName() == "shader_set")) - { - std::cerr << "sh::Factory: Warning: Unsupported root node type \"" << it->second->getName() << "\" for file type .shaderset" << std::endl; - break; - } - - if (!it->second->findChild("profiles_cg")) - throw std::runtime_error ("missing \"profiles_cg\" field for \"" + it->first + "\""); - if (!it->second->findChild("profiles_hlsl")) - throw std::runtime_error ("missing \"profiles_hlsl\" field for \"" + it->first + "\""); - if (!it->second->findChild("source")) - throw std::runtime_error ("missing \"source\" field for \"" + it->first + "\""); - if (!it->second->findChild("type")) - throw std::runtime_error ("missing \"type\" field for \"" + it->first + "\""); - - std::vector profiles_cg; - boost::split (profiles_cg, it->second->findChild("profiles_cg")->getValue(), boost::is_any_of(" ")); - std::string cg_profile; - for (std::vector::iterator it2 = profiles_cg.begin(); it2 != profiles_cg.end(); ++it2) - { - if (mPlatform->isProfileSupported(*it2)) - { - cg_profile = *it2; - break; - } - } - - std::vector profiles_hlsl; - boost::split (profiles_hlsl, it->second->findChild("profiles_hlsl")->getValue(), boost::is_any_of(" ")); - std::string hlsl_profile; - for (std::vector::iterator it2 = profiles_hlsl.begin(); it2 != profiles_hlsl.end(); ++it2) - { - if (mPlatform->isProfileSupported(*it2)) - { - hlsl_profile = *it2; - break; - } - } - - std::string sourceFile = mPlatform->getBasePath() + "/" + it->second->findChild("source")->getValue(); - - ShaderSet newSet (it->second->findChild("type")->getValue(), cg_profile, hlsl_profile, - sourceFile, - mPlatform->getBasePath(), - it->first, - &mGlobalSettings); - - int lastModified = boost::filesystem::last_write_time (boost::filesystem::path(sourceFile)); - if (mShadersLastModified.find(sourceFile) != mShadersLastModified.end() - && mShadersLastModified[sourceFile] != lastModified) - { - newSet.markDirty (); - anyShaderDirty = true; - } - - mShadersLastModified[sourceFile] = lastModified; - - mShaderSets.insert(std::make_pair(it->first, newSet)); - } - } + bool removeBinaryCache = reloadShaders(); // load materials { @@ -293,9 +227,9 @@ namespace sh } } - if (mPlatform->supportsShaderSerialization () && mReadMicrocodeCache && !anyShaderDirty) + if (mPlatform->supportsShaderSerialization () && mReadMicrocodeCache && !removeBinaryCache) { - std::string file = mPlatform->getCacheFolder () + "/shShaderCache.txt"; + std::string file = mPlatform->getCacheFolder () + "/" + mBinaryCacheName; if (boost::filesystem::exists(file)) { mPlatform->deserializeShaders (file); @@ -305,19 +239,21 @@ namespace sh Factory::~Factory () { + mShaderSets.clear(); + if (mPlatform->supportsShaderSerialization () && mWriteMicrocodeCache) { - std::string file = mPlatform->getCacheFolder () + "/shShaderCache.txt"; + std::string file = mPlatform->getCacheFolder () + "/" + mBinaryCacheName; mPlatform->serializeShaders (file); } if (mReadSourceCache) { - // save the last modified time of shader sources + // save the last modified time of shader sources (as of when they were loaded) std::ofstream file; file.open(std::string(mPlatform->getCacheFolder () + "/lastModified.txt").c_str()); - for (LastModifiedMap::const_iterator it = mShadersLastModified.begin(); it != mShadersLastModified.end(); ++it) + for (LastModifiedMap::const_iterator it = mShadersLastModifiedNew.begin(); it != mShadersLastModifiedNew.end(); ++it) { file << it->first << "\n" << it->second << std::endl; } @@ -357,15 +293,16 @@ namespace sh while (i>0) { --i; - m->createForConfiguration (configuration, i); - - if (mListener) + if (m->createForConfiguration (configuration, i) && mListener) mListener->materialCreated (m, configuration, i); + else + return NULL; } - m->createForConfiguration (configuration, lodIndex); - if (mListener) + if (m->createForConfiguration (configuration, lodIndex) && mListener) mListener->materialCreated (m, configuration, lodIndex); + else + return NULL; } return m; } @@ -429,6 +366,12 @@ namespace sh ShaderSet* Factory::getShaderSet (const std::string& name) { + if (mShaderSets.find(name) == mShaderSets.end()) + { + std::stringstream msg; + msg << "Shader '" << name << "' not found"; + throw std::runtime_error(msg.str()); + } return &mShaderSets.find(name)->second; } @@ -456,6 +399,14 @@ namespace sh } } + void Factory::notifyConfigurationChanged() + { + for (MaterialMap::iterator it = mMaterials.begin(); it != mMaterials.end(); ++it) + { + it->second.destroyAll(); + } + } + MaterialInstance* Factory::getMaterialInstance (const std::string& name) { return findInstance(name); @@ -483,17 +434,21 @@ namespace sh return ""; } - PropertySetGet* Factory::getConfiguration (const std::string& name) + Configuration* Factory::getConfiguration (const std::string& name) { return &mConfigurations[name]; } - void Factory::registerConfiguration (const std::string& name, PropertySetGet configuration) + void Factory::createConfiguration (const std::string& name) { - mConfigurations[name] = configuration; mConfigurations[name].setParent (&mGlobalSettings); } + void Factory::destroyConfiguration(const std::string &name) + { + mConfigurations.erase(name); + } + void Factory::registerLodConfiguration (int index, PropertySetGet configuration) { mLodConfigurations[index] = configuration; @@ -561,17 +516,93 @@ namespace sh return p; } - void Factory::saveMaterials (const std::string& filename) + void Factory::saveAll () { - std::ofstream file; - file.open (filename.c_str ()); + std::map files; + for (MaterialMap::iterator it = mMaterials.begin(); it != mMaterials.end(); ++it) + { + if (it->second.getSourceFile().empty()) + continue; + if (files.find(it->second.getSourceFile()) == files.end()) + { + /// \todo check if this is actually the same file, since there can be different paths to the same file + std::ofstream* stream = new std::ofstream(); + stream->open (it->second.getSourceFile().c_str()); + + files[it->second.getSourceFile()] = stream; + } + it->second.save (*files[it->second.getSourceFile()]); + } + + for (std::map::iterator it = files.begin(); it != files.end(); ++it) + { + delete it->second; + } + files.clear(); + + for (ConfigurationMap::iterator it = mConfigurations.begin(); it != mConfigurations.end(); ++it) + { + if (it->second.getSourceFile().empty()) + continue; + if (files.find(it->second.getSourceFile()) == files.end()) + { + /// \todo check if this is actually the same file, since there can be different paths to the same file + std::ofstream* stream = new std::ofstream(); + stream->open (it->second.getSourceFile().c_str()); + + files[it->second.getSourceFile()] = stream; + } + it->second.save (it->first, *files[it->second.getSourceFile()]); + } + for (std::map::iterator it = files.begin(); it != files.end(); ++it) + { + delete it->second; + } + } + + void Factory::listMaterials(std::vector &out) + { for (MaterialMap::iterator it = mMaterials.begin(); it != mMaterials.end(); ++it) { - it->second.save(file); + out.push_back(it->first); + } + } + + void Factory::listGlobalSettings(std::map &out) + { + const PropertyMap& properties = mGlobalSettings.listProperties(); + + for (PropertyMap::const_iterator it = properties.begin(); it != properties.end(); ++it) + { + out[it->first] = retrieveValue(mGlobalSettings.getProperty(it->first), NULL).get(); + } + } + + void Factory::listConfigurationSettings(const std::string& name, std::map &out) + { + const PropertyMap& properties = mConfigurations[name].listProperties(); + + for (PropertyMap::const_iterator it = properties.begin(); it != properties.end(); ++it) + { + out[it->first] = retrieveValue(mConfigurations[name].getProperty(it->first), NULL).get(); + } + } + + void Factory::listConfigurationNames(std::vector &out) + { + for (ConfigurationMap::const_iterator it = mConfigurations.begin(); it != mConfigurations.end(); ++it) + { + out.push_back(it->first); } + } - file.close(); + void Factory::listShaderSets(std::vector &out) + { + for (ShaderSetMap::const_iterator it = mShaderSets.begin(); it != mShaderSets.end(); ++it) + { + out.push_back(it->first); + } } void Factory::_ensureMaterial(const std::string& name, const std::string& configuration) @@ -580,4 +611,187 @@ namespace sh assert(m); m->createForConfiguration (configuration, 0); } + + bool Factory::removeCache(const std::string& pattern) + { + bool ret = false; + if ( boost::filesystem::exists(mPlatform->getCacheFolder()) + && boost::filesystem::is_directory(mPlatform->getCacheFolder())) + { + boost::filesystem::directory_iterator end_iter; + for( boost::filesystem::directory_iterator dir_iter(mPlatform->getCacheFolder()) ; dir_iter != end_iter ; ++dir_iter) + { + if (boost::filesystem::is_regular_file(dir_iter->status()) ) + { + boost::filesystem::path file = dir_iter->path(); + + std::string pathname = file.filename().string(); + + // get first part of filename, e.g. main_fragment_546457654 -> main_fragment + // there is probably a better method for this... + std::vector tokens; + boost::split(tokens, pathname, boost::is_any_of("_")); + tokens.erase(--tokens.end()); + std::string shaderName; + for (std::vector::const_iterator vector_iter = tokens.begin(); vector_iter != tokens.end();) + { + shaderName += *(vector_iter++); + if (vector_iter != tokens.end()) + shaderName += "_"; + } + + if (shaderName == pattern) + { + boost::filesystem::remove(file); + ret = true; + std::cout << "Removing outdated shader: " << file << std::endl; + } + } + } + } + return ret; + } + + bool Factory::reloadShaders() + { + mShaderSets.clear(); + notifyConfigurationChanged(); + + bool removeBinaryCache = false; + ScriptLoader shaderSetLoader(".shaderset"); + ScriptLoader::loadAllFiles (&shaderSetLoader, mPlatform->getBasePath()); + std::map nodes = shaderSetLoader.getAllConfigScripts(); + for (std::map ::const_iterator it = nodes.begin(); + it != nodes.end(); ++it) + { + if (!(it->second->getName() == "shader_set")) + { + std::cerr << "sh::Factory: Warning: Unsupported root node type \"" << it->second->getName() << "\" for file type .shaderset" << std::endl; + break; + } + + if (!it->second->findChild("profiles_cg")) + throw std::runtime_error ("missing \"profiles_cg\" field for \"" + it->first + "\""); + if (!it->second->findChild("profiles_hlsl")) + throw std::runtime_error ("missing \"profiles_hlsl\" field for \"" + it->first + "\""); + if (!it->second->findChild("source")) + throw std::runtime_error ("missing \"source\" field for \"" + it->first + "\""); + if (!it->second->findChild("type")) + throw std::runtime_error ("missing \"type\" field for \"" + it->first + "\""); + + std::vector profiles_cg; + boost::split (profiles_cg, it->second->findChild("profiles_cg")->getValue(), boost::is_any_of(" ")); + std::string cg_profile; + for (std::vector::iterator it2 = profiles_cg.begin(); it2 != profiles_cg.end(); ++it2) + { + if (mPlatform->isProfileSupported(*it2)) + { + cg_profile = *it2; + break; + } + } + + std::vector profiles_hlsl; + boost::split (profiles_hlsl, it->second->findChild("profiles_hlsl")->getValue(), boost::is_any_of(" ")); + std::string hlsl_profile; + for (std::vector::iterator it2 = profiles_hlsl.begin(); it2 != profiles_hlsl.end(); ++it2) + { + if (mPlatform->isProfileSupported(*it2)) + { + hlsl_profile = *it2; + break; + } + } + + std::string sourceAbsolute = mPlatform->getBasePath() + "/" + it->second->findChild("source")->getValue(); + std::string sourceRelative = it->second->findChild("source")->getValue(); + + ShaderSet newSet (it->second->findChild("type")->getValue(), cg_profile, hlsl_profile, + sourceAbsolute, + mPlatform->getBasePath(), + it->first, + &mGlobalSettings); + + int lastModified = boost::filesystem::last_write_time (boost::filesystem::path(sourceAbsolute)); + mShadersLastModifiedNew[sourceRelative] = lastModified; + if (mShadersLastModified.find(sourceRelative) != mShadersLastModified.end()) + { + if (mShadersLastModified[sourceRelative] != lastModified) + { + // delete any outdated shaders based on this shader set + if (removeCache (it->first)) + removeBinaryCache = true; + } + } + else + { + // if we get here, this is either the first run or a new shader file was added + // in both cases we can safely delete + if (removeCache (it->first)) + removeBinaryCache = true; + } + mShaderSets.insert(std::make_pair(it->first, newSet)); + } + + // new is now current + mShadersLastModified = mShadersLastModifiedNew; + + return removeBinaryCache; + } + + void Factory::doMonitorShaderFiles() + { + bool reload=false; + ScriptLoader shaderSetLoader(".shaderset"); + ScriptLoader::loadAllFiles (&shaderSetLoader, mPlatform->getBasePath()); + std::map nodes = shaderSetLoader.getAllConfigScripts(); + for (std::map ::const_iterator it = nodes.begin(); + it != nodes.end(); ++it) + { + + std::string sourceAbsolute = mPlatform->getBasePath() + "/" + it->second->findChild("source")->getValue(); + std::string sourceRelative = it->second->findChild("source")->getValue(); + + int lastModified = boost::filesystem::last_write_time (boost::filesystem::path(sourceAbsolute)); + if (mShadersLastModified.find(sourceRelative) != mShadersLastModified.end()) + { + if (mShadersLastModified[sourceRelative] != lastModified) + { + reload=true; + break; + } + } + } + if (reload) + reloadShaders(); + } + + void Factory::logError(const std::string &msg) + { + mErrorLog << msg << '\n'; + } + + std::string Factory::getErrorLog() + { + std::string errors = mErrorLog.str(); + mErrorLog.str(""); + return errors; + } + + void Factory::unloadUnreferencedMaterials() + { + for (MaterialMap::iterator it = mMaterials.begin(); it != mMaterials.end(); ++it) + { + if (it->second.getMaterial()->isUnreferenced()) + it->second.destroyAll(); + } + } + + void Configuration::save(const std::string& name, std::ofstream &stream) + { + stream << "configuration " << name << '\n'; + stream << "{\n"; + PropertySetGet::save(stream, "\t"); + stream << "}\n"; + } } diff --git a/extern/shiny/Main/Factory.hpp b/extern/shiny/Main/Factory.hpp index 799dd71eb..97984609e 100644 --- a/extern/shiny/Main/Factory.hpp +++ b/extern/shiny/Main/Factory.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "MaterialInstance.hpp" #include "ShaderSet.hpp" @@ -12,9 +13,21 @@ namespace sh { class Platform; + class Configuration : public PropertySetGet + { + public: + void setSourceFile (const std::string& file) { mSourceFile = file ; } + std::string getSourceFile () { return mSourceFile; } + + void save(const std::string& name, std::ofstream &stream); + + private: + std::string mSourceFile; + }; + typedef std::map MaterialMap; typedef std::map ShaderSetMap; - typedef std::map ConfigurationMap; + typedef std::map ConfigurationMap; typedef std::map LodConfigurationMap; typedef std::map LastModifiedMap; @@ -81,8 +94,8 @@ namespace sh /// Get a MaterialInstance by name MaterialInstance* getMaterialInstance (const std::string& name); - /// Register a configuration, which can then be used by switching the active material scheme - void registerConfiguration (const std::string& name, PropertySetGet configuration); + /// Create a configuration, which can then be altered by using Factory::getConfiguration + void createConfiguration (const std::string& name); /// Register a lod configuration, which can then be used by setting up lod distance values for the material \n /// 0 refers to highest lod, so use 1 or higher as index parameter @@ -125,8 +138,48 @@ namespace sh /// \note The default is off (no cache reading) void setReadMicrocodeCache(bool read) { mReadMicrocodeCache = read; } - /// Saves all the materials that were initially loaded from the file with this name - void saveMaterials (const std::string& filename); + /// Lists all materials currently registered with the factory. Whether they are + /// loaded or not does not matter. + void listMaterials (std::vector& out); + + /// Lists current name & value of all global settings. + void listGlobalSettings (std::map& out); + + /// Lists configuration names. + void listConfigurationNames (std::vector& out); + + /// Lists current name & value of settings for a given configuration. + void listConfigurationSettings (const std::string& name, std::map& out); + + /// Lists shader sets. + void listShaderSets (std::vector& out); + + /// \note This only works if microcode caching is disabled, as there is currently no way to remove the cache + /// through the Ogre API. Luckily, this is already fixed in Ogre 1.9. + bool reloadShaders(); + + /// Calls reloadShaders() if shader files have been modified since the last reload. + /// \note This only works if microcode caching is disabled, as there is currently no way to remove the cache + /// through the Ogre API. Luckily, this is already fixed in Ogre 1.9. + void doMonitorShaderFiles(); + + /// Unloads all materials that are currently not referenced. This will not unload the textures themselves, + /// but it will let go of the SharedPtr's to the textures, so that you may unload them if you so desire. \n + /// A good time to call this would be after a new level has been loaded, but just calling it occasionally after a period + /// of time should work just fine too. + void unloadUnreferencedMaterials(); + + void destroyConfiguration (const std::string& name); + + void notifyConfigurationChanged(); + + /// Saves all materials and configurations, by default to the file they were loaded from. + /// If you wish to save them elsewhere, use setSourceFile first. + void saveAll (); + + /// Returns the error log as a string, then clears it. + /// Note: Errors are also written to the standard error output, or thrown if they are fatal. + std::string getErrorLog (); static Factory& getInstance(); ///< Return instance of this class. @@ -137,11 +190,13 @@ namespace sh /// You will probably never have to use this. void _ensureMaterial(const std::string& name, const std::string& configuration); + + Configuration* getConfiguration (const std::string& name); + private: MaterialInstance* requestMaterial (const std::string& name, const std::string& configuration, unsigned short lodIndex); ShaderSet* getShaderSet (const std::string& name); - PropertySetGet* getConfiguration (const std::string& name); Platform* getPlatform (); PropertySetGet* getCurrentGlobalSettings(); @@ -163,6 +218,8 @@ namespace sh std::map mTextureAliasInstances; + void logError (const std::string& msg); + friend class Platform; friend class MaterialInstance; friend class ShaderInstance; @@ -179,12 +236,14 @@ namespace sh bool mWriteMicrocodeCache; bool mReadSourceCache; bool mWriteSourceCache; + std::stringstream mErrorLog; MaterialMap mMaterials; ShaderSetMap mShaderSets; ConfigurationMap mConfigurations; LodConfigurationMap mLodConfigurations; LastModifiedMap mShadersLastModified; + LastModifiedMap mShadersLastModifiedNew; PropertySetGet mGlobalSettings; @@ -201,6 +260,11 @@ namespace sh MaterialInstance* findInstance (const std::string& name); MaterialInstance* searchInstance (const std::string& name); + + /// @return was anything removed? + bool removeCache (const std::string& pattern); + + static const std::string mBinaryCacheName; }; } diff --git a/extern/shiny/Main/Language.hpp b/extern/shiny/Main/Language.hpp index 20bf8ed61..6b271cb86 100644 --- a/extern/shiny/Main/Language.hpp +++ b/extern/shiny/Main/Language.hpp @@ -8,6 +8,7 @@ namespace sh Language_CG, Language_HLSL, Language_GLSL, + Language_GLSLES, Language_Count, Language_None }; diff --git a/extern/shiny/Main/MaterialInstance.cpp b/extern/shiny/Main/MaterialInstance.cpp index 0f8bcdda7..b8032d681 100644 --- a/extern/shiny/Main/MaterialInstance.cpp +++ b/extern/shiny/Main/MaterialInstance.cpp @@ -1,6 +1,7 @@ #include "MaterialInstance.hpp" #include +#include #include "Factory.hpp" #include "ShaderSet.hpp" @@ -12,6 +13,7 @@ namespace sh , mShadersEnabled(true) , mFactory(f) , mListener(NULL) + , mFailedToCreate(false) { } @@ -46,6 +48,7 @@ namespace sh return; mMaterial->removeAll(); mTexUnits.clear(); + mFailedToCreate = false; } void MaterialInstance::setProperty (const std::string& name, PropertyValuePtr value) @@ -54,118 +57,136 @@ namespace sh destroyAll(); // trigger updates } - void MaterialInstance::createForConfiguration (const std::string& configuration, unsigned short lodIndex) + bool MaterialInstance::createForConfiguration (const std::string& configuration, unsigned short lodIndex) { - bool res = mMaterial->createConfiguration(configuration, lodIndex); - if (!res) - return; // listener was false positive - - if (mListener) - mListener->requestedConfiguration (this, configuration); - - mFactory->setActiveConfiguration (configuration); - mFactory->setActiveLodLevel (lodIndex); - - bool allowFixedFunction = true; - if (!mShadersEnabled && hasProperty("allow_fixed_function")) - { - allowFixedFunction = retrieveValue(getProperty("allow_fixed_function"), NULL).get(); - } + if (mFailedToCreate) + return false; + try{ + mMaterial->ensureLoaded(); + bool res = mMaterial->createConfiguration(configuration, lodIndex); + if (!res) + return false; // listener was false positive + + if (mListener) + mListener->requestedConfiguration (this, configuration); + + mFactory->setActiveConfiguration (configuration); + mFactory->setActiveLodLevel (lodIndex); + + bool allowFixedFunction = true; + if (!mShadersEnabled && hasProperty("allow_fixed_function")) + { + allowFixedFunction = retrieveValue(getProperty("allow_fixed_function"), NULL).get(); + } - bool useShaders = mShadersEnabled || !allowFixedFunction; + bool useShaders = mShadersEnabled || !allowFixedFunction; - // get passes of the top-most parent - PassVector passes = getPasses(); - if (passes.size() == 0) - throw std::runtime_error ("material \"" + mName + "\" does not have any passes"); + // get passes of the top-most parent + PassVector* passes = getParentPasses(); + if (passes->empty()) + throw std::runtime_error ("material \"" + mName + "\" does not have any passes"); - for (PassVector::iterator it = passes.begin(); it != passes.end(); ++it) - { - boost::shared_ptr pass = mMaterial->createPass (configuration, lodIndex); - it->copyAll (pass.get(), this); + for (PassVector::iterator it = passes->begin(); it != passes->end(); ++it) + { + boost::shared_ptr pass = mMaterial->createPass (configuration, lodIndex); + it->copyAll (pass.get(), this); - // texture samplers used in the shaders - std::vector usedTextureSamplersVertex; - std::vector usedTextureSamplersFragment; + // texture samplers used in the shaders + std::vector usedTextureSamplersVertex; + std::vector usedTextureSamplersFragment; - PropertySetGet* context = this; + PropertySetGet* context = this; - // create or retrieve shaders - bool hasVertex = it->hasProperty("vertex_program"); - bool hasFragment = it->hasProperty("fragment_program"); - if (useShaders) - { - it->setContext(context); - it->mShaderProperties.setContext(context); - if (hasVertex) + // create or retrieve shaders + bool hasVertex = it->hasProperty("vertex_program") + && !retrieveValue(it->getProperty("vertex_program"), context).get().empty(); + bool hasFragment = it->hasProperty("fragment_program") + && !retrieveValue(it->getProperty("fragment_program"), context).get().empty(); + if (useShaders) { - ShaderSet* vertex = mFactory->getShaderSet(retrieveValue(it->getProperty("vertex_program"), context).get()); - ShaderInstance* v = vertex->getInstance(&it->mShaderProperties); - if (v) + it->setContext(context); + it->mShaderProperties.setContext(context); + if (hasVertex) { - pass->assignProgram (GPT_Vertex, v->getName()); - v->setUniformParameters (pass, &it->mShaderProperties); - - std::vector sharedParams = v->getSharedParameters (); - for (std::vector::iterator it = sharedParams.begin(); it != sharedParams.end(); ++it) + ShaderSet* vertex = mFactory->getShaderSet(retrieveValue(it->getProperty("vertex_program"), context).get()); + ShaderInstance* v = vertex->getInstance(&it->mShaderProperties); + if (v) { - pass->addSharedParameter (GPT_Vertex, *it); - } + pass->assignProgram (GPT_Vertex, v->getName()); + v->setUniformParameters (pass, &it->mShaderProperties); + + std::vector sharedParams = v->getSharedParameters (); + for (std::vector::iterator it2 = sharedParams.begin(); it2 != sharedParams.end(); ++it2) + { + pass->addSharedParameter (GPT_Vertex, *it2); + } - std::vector vector = v->getUsedSamplers (); - usedTextureSamplersVertex.insert(usedTextureSamplersVertex.end(), vector.begin(), vector.end()); + std::vector vector = v->getUsedSamplers (); + usedTextureSamplersVertex.insert(usedTextureSamplersVertex.end(), vector.begin(), vector.end()); + } } - } - if (hasFragment) - { - ShaderSet* fragment = mFactory->getShaderSet(retrieveValue(it->getProperty("fragment_program"), context).get()); - ShaderInstance* f = fragment->getInstance(&it->mShaderProperties); - if (f) + if (hasFragment) { - pass->assignProgram (GPT_Fragment, f->getName()); - f->setUniformParameters (pass, &it->mShaderProperties); - - std::vector sharedParams = f->getSharedParameters (); - for (std::vector::iterator it = sharedParams.begin(); it != sharedParams.end(); ++it) + ShaderSet* fragment = mFactory->getShaderSet(retrieveValue(it->getProperty("fragment_program"), context).get()); + ShaderInstance* f = fragment->getInstance(&it->mShaderProperties); + if (f) { - pass->addSharedParameter (GPT_Fragment, *it); - } + pass->assignProgram (GPT_Fragment, f->getName()); + f->setUniformParameters (pass, &it->mShaderProperties); - std::vector vector = f->getUsedSamplers (); - usedTextureSamplersFragment.insert(usedTextureSamplersFragment.end(), vector.begin(), vector.end()); + std::vector sharedParams = f->getSharedParameters (); + for (std::vector::iterator it2 = sharedParams.begin(); it2 != sharedParams.end(); ++it2) + { + pass->addSharedParameter (GPT_Fragment, *it2); + } + + std::vector vector = f->getUsedSamplers (); + usedTextureSamplersFragment.insert(usedTextureSamplersFragment.end(), vector.begin(), vector.end()); + } } } - } - // create texture units - std::vector texUnits = it->getTexUnits(); - int i=0; - for (std::vector::iterator texIt = texUnits.begin(); texIt != texUnits.end(); ++texIt ) - { - // only create those that are needed by the shader, OR those marked to be created in fixed function pipeline if shaders are disabled - bool foundVertex = std::find(usedTextureSamplersVertex.begin(), usedTextureSamplersVertex.end(), texIt->getName()) != usedTextureSamplersVertex.end(); - bool foundFragment = std::find(usedTextureSamplersFragment.begin(), usedTextureSamplersFragment.end(), texIt->getName()) != usedTextureSamplersFragment.end(); - if ( (foundVertex || foundFragment) - || (((!useShaders || (!hasVertex || !hasFragment)) && allowFixedFunction) && texIt->hasProperty("create_in_ffp") && retrieveValue(texIt->getProperty("create_in_ffp"), this).get())) + // create texture units + std::vector* texUnits = &it->mTexUnits; + int i=0; + for (std::vector::iterator texIt = texUnits->begin(); texIt != texUnits->end(); ++texIt ) { - boost::shared_ptr texUnit = pass->createTextureUnitState (); - texIt->copyAll (texUnit.get(), context); + // only create those that are needed by the shader, OR those marked to be created in fixed function pipeline if shaders are disabled + bool foundVertex = std::find(usedTextureSamplersVertex.begin(), usedTextureSamplersVertex.end(), texIt->getName()) != usedTextureSamplersVertex.end(); + bool foundFragment = std::find(usedTextureSamplersFragment.begin(), usedTextureSamplersFragment.end(), texIt->getName()) != usedTextureSamplersFragment.end(); + if ( (foundVertex || foundFragment) + || (((!useShaders || (!hasVertex || !hasFragment)) && allowFixedFunction) && texIt->hasProperty("create_in_ffp") && retrieveValue(texIt->getProperty("create_in_ffp"), this).get())) + { + boost::shared_ptr texUnit = pass->createTextureUnitState (texIt->getName()); + texIt->copyAll (texUnit.get(), context); - mTexUnits.push_back(texUnit); + mTexUnits.push_back(texUnit); - // set texture unit indices (required by GLSL) - if (useShaders && ((hasVertex && foundVertex) || (hasFragment && foundFragment)) && mFactory->getCurrentLanguage () == Language_GLSL) - { - pass->setTextureUnitIndex (foundVertex ? GPT_Vertex : GPT_Fragment, texIt->getName(), i); + // set texture unit indices (required by GLSL) + if (useShaders && ((hasVertex && foundVertex) || (hasFragment && foundFragment)) && mFactory->getCurrentLanguage () == Language_GLSL) + { + pass->setTextureUnitIndex (foundVertex ? GPT_Vertex : GPT_Fragment, texIt->getName(), i); - ++i; + ++i; + } } } } - } - if (mListener) - mListener->createdConfiguration (this, configuration); + if (mListener) + mListener->createdConfiguration (this, configuration); + return true; + + } catch (std::runtime_error& e) + { + destroyAll(); + mFailedToCreate = true; + std::stringstream msg; + msg << "Error while creating material " << mName << ": " << e.what(); + std::cerr << msg.str() << std::endl; + mFactory->logError(msg.str()); + return false; + } } Material* MaterialInstance::getMaterial () @@ -180,12 +201,23 @@ namespace sh return &mPasses.back(); } - PassVector MaterialInstance::getPasses() + void MaterialInstance::deletePass(unsigned int index) + { + assert(mPasses.size() > index); + mPasses.erase(mPasses.begin()+index); + } + + PassVector* MaterialInstance::getParentPasses() { if (mParent) - return static_cast(mParent)->getPasses(); + return static_cast(mParent)->getParentPasses(); else - return mPasses; + return &mPasses; + } + + PassVector* MaterialInstance::getPasses() + { + return &mPasses; } void MaterialInstance::setShadersEnabled (bool enabled) @@ -206,7 +238,7 @@ namespace sh if (mParent) { - stream << "\t" << static_cast(mParent)->getName() << "\n"; + stream << "\t" << "parent " << static_cast(mParent)->getName() << "\n"; } const PropertyMap& properties = listProperties (); @@ -215,6 +247,14 @@ namespace sh stream << "\t" << it->first << " " << retrieveValue(getProperty(it->first), NULL).get() << "\n"; } + for (PassVector::iterator it = mPasses.begin(); it != mPasses.end(); ++it) + { + stream << "\tpass" << '\n'; + stream << "\t{" << '\n'; + it->save(stream); + stream << "\t}" << '\n'; + } + stream << "}\n"; } } diff --git a/extern/shiny/Main/MaterialInstance.hpp b/extern/shiny/Main/MaterialInstance.hpp index 000f9d60c..72c78c7b7 100644 --- a/extern/shiny/Main/MaterialInstance.hpp +++ b/extern/shiny/Main/MaterialInstance.hpp @@ -25,6 +25,7 @@ namespace sh public: virtual void requestedConfiguration (MaterialInstance* m, const std::string& configuration) = 0; ///< called before creating virtual void createdConfiguration (MaterialInstance* m, const std::string& configuration) = 0; ///< called after creating + virtual ~MaterialInstanceListener(){} }; /** @@ -40,8 +41,12 @@ namespace sh MaterialInstance (const std::string& name, Factory* f); virtual ~MaterialInstance (); + PassVector* getParentPasses(); ///< gets the passes of the top-most parent + + PassVector* getPasses(); ///< get our passes (for derived materials, none) + MaterialInstancePass* createPass (); - PassVector getPasses(); ///< gets the passes of the top-most parent + void deletePass (unsigned int index); /// @attention Because the backend material passes are created on demand, the returned material here might not contain anything yet! /// The only place where you should use this method, is for the MaterialInstance given by the MaterialListener::materialCreated event! @@ -54,25 +59,25 @@ namespace sh virtual void setProperty (const std::string& name, PropertyValuePtr value); + void setSourceFile(const std::string& sourceFile) { mSourceFile = sourceFile; } + + std::string getSourceFile() { return mSourceFile; } + ///< get the name of the file this material was read from, or empty if it was created dynamically by code + private: void setParentInstance (const std::string& name); std::string getParentInstance (); void create (Platform* platform); - void createForConfiguration (const std::string& configuration, unsigned short lodIndex); + bool createForConfiguration (const std::string& configuration, unsigned short lodIndex); void destroyAll (); void setShadersEnabled (bool enabled); - void setSourceFile(const std::string& sourceFile) { mSourceFile = sourceFile; } - - std::string getSourceFile() { return mSourceFile; } - ///< get the name of the file this material was read from, or empty if it was created dynamically by code - void save (std::ofstream& stream); - ///< this will only save the properties, not the passes and texture units, and as such - /// is only intended to be used for derived materials + + bool mFailedToCreate; friend class Factory; diff --git a/extern/shiny/Main/MaterialInstancePass.cpp b/extern/shiny/Main/MaterialInstancePass.cpp index b14476f4e..a628cd64c 100644 --- a/extern/shiny/Main/MaterialInstancePass.cpp +++ b/extern/shiny/Main/MaterialInstancePass.cpp @@ -1,5 +1,7 @@ #include "MaterialInstancePass.hpp" +#include + namespace sh { @@ -9,8 +11,25 @@ namespace sh return &mTexUnits.back(); } - std::vector MaterialInstancePass::getTexUnits () + void MaterialInstancePass::save(std::ofstream &stream) { - return mTexUnits; + if (mShaderProperties.listProperties().size()) + { + stream << "\t\t" << "shader_properties" << '\n'; + stream << "\t\t{\n"; + mShaderProperties.save(stream, "\t\t\t"); + stream << "\t\t}\n"; + } + + PropertySetGet::save(stream, "\t\t"); + + for (std::vector ::iterator it = mTexUnits.begin(); + it != mTexUnits.end(); ++it) + { + stream << "\t\ttexture_unit " << it->getName() << '\n'; + stream << "\t\t{\n"; + it->save(stream, "\t\t\t"); + stream << "\t\t}\n"; + } } } diff --git a/extern/shiny/Main/MaterialInstancePass.hpp b/extern/shiny/Main/MaterialInstancePass.hpp index 7d7330f70..3d83d8fa3 100644 --- a/extern/shiny/Main/MaterialInstancePass.hpp +++ b/extern/shiny/Main/MaterialInstancePass.hpp @@ -18,10 +18,10 @@ namespace sh public: MaterialInstanceTextureUnit* createTextureUnit (const std::string& name); + void save (std::ofstream& stream); + PropertySetGet mShaderProperties; - std::vector getTexUnits (); - private: std::vector mTexUnits; }; } diff --git a/extern/shiny/Main/MaterialInstanceTextureUnit.hpp b/extern/shiny/Main/MaterialInstanceTextureUnit.hpp index ae9f54fd2..5ca400fd4 100644 --- a/extern/shiny/Main/MaterialInstanceTextureUnit.hpp +++ b/extern/shiny/Main/MaterialInstanceTextureUnit.hpp @@ -18,6 +18,7 @@ namespace sh public: MaterialInstanceTextureUnit (const std::string& name); std::string getName() const; + void setName (const std::string& name) { mName = name; } private: std::string mName; }; diff --git a/extern/shiny/Main/Platform.cpp b/extern/shiny/Main/Platform.cpp index 94b4f872a..3eb7f4ad3 100644 --- a/extern/shiny/Main/Platform.cpp +++ b/extern/shiny/Main/Platform.cpp @@ -9,7 +9,7 @@ namespace sh Platform::Platform (const std::string& basePath) : mBasePath(basePath) , mCacheFolder("./") - , mShaderCachingEnabled(false) + , mFactory(NULL) { } @@ -57,11 +57,6 @@ namespace sh mCacheFolder = folder; } - void Platform::setShaderCachingEnabled (bool enabled) - { - mShaderCachingEnabled = enabled; - } - std::string Platform::getCacheFolder() const { return mCacheFolder; diff --git a/extern/shiny/Main/Platform.hpp b/extern/shiny/Main/Platform.hpp index 1b095e957..24afea03b 100644 --- a/extern/shiny/Main/Platform.hpp +++ b/extern/shiny/Main/Platform.hpp @@ -24,6 +24,7 @@ namespace sh class GpuProgram { public: + virtual ~GpuProgram() {} virtual bool getSupported () = 0; ///< @return true if the compilation was successful /// @param name name of the uniform in the shader @@ -35,8 +36,7 @@ namespace sh class TextureUnitState : public PropertySet { public: - virtual ~TextureUnitState(); - + virtual ~TextureUnitState(); virtual void setTextureName (const std::string& textureName) = 0; protected: @@ -46,7 +46,7 @@ namespace sh class Pass : public PropertySet { public: - virtual boost::shared_ptr createTextureUnitState () = 0; + virtual boost::shared_ptr createTextureUnitState (const std::string& name) = 0; virtual void assignProgram (GpuProgramType type, const std::string& name) = 0; /// @param type gpu program type @@ -68,6 +68,9 @@ namespace sh virtual bool createConfiguration (const std::string& name, unsigned short lodIndex) = 0; ///< @return false if already exists virtual void removeAll () = 0; ///< remove all configurations + virtual bool isUnreferenced() = 0; + virtual void ensureLoaded() = 0; + virtual void setLodLevels (const std::string& lodLevels) = 0; virtual void setShadowCasterMaterial (const std::string& name) = 0; @@ -79,8 +82,6 @@ namespace sh Platform (const std::string& basePath); virtual ~Platform (); - void setShaderCachingEnabled (bool enabled); - /// set the folder to use for shader caching void setCacheFolder (const std::string& folder); @@ -93,6 +94,8 @@ namespace sh const std::string& name, const std::string& profile, const std::string& source, Language lang) = 0; + virtual void destroyGpuProgram (const std::string& name) = 0; + virtual void setSharedParameter (const std::string& name, PropertyValuePtr value) = 0; virtual bool isProfileSupported (const std::string& profile) = 0; @@ -105,6 +108,7 @@ namespace sh friend class Factory; friend class MaterialInstance; friend class ShaderInstance; + friend class ShaderSet; protected: /** @@ -131,9 +135,6 @@ namespace sh std::string mCacheFolder; Factory* mFactory; - protected: - bool mShaderCachingEnabled; - private: void setFactory (Factory* factory); diff --git a/extern/shiny/Main/PropertyBase.cpp b/extern/shiny/Main/PropertyBase.cpp index 0c39e5c1e..8592712fa 100644 --- a/extern/shiny/Main/PropertyBase.cpp +++ b/extern/shiny/Main/PropertyBase.cpp @@ -6,6 +6,8 @@ #include #include +#include + namespace sh { @@ -39,8 +41,9 @@ namespace sh mValue = false; else { - std::cerr << "sh::BooleanValue: Warning: Unrecognized value \"" << in << "\" for property value of type BooleanValue" << std::endl; - mValue = false; + std::stringstream msg; + msg << "sh::BooleanValue: Warning: Unrecognized value \"" << in << "\" for property value of type BooleanValue"; + throw std::runtime_error(msg.str()); } } @@ -183,12 +186,16 @@ namespace sh void PropertySet::setProperty (const std::string& name, PropertyValuePtr &value, PropertySetGet* context) { if (!setPropertyOverride (name, value, context)) - std::cerr << "sh::PropertySet: Warning: No match for property with name '" << name << "'" << std::endl; + { + std::stringstream msg; + msg << "sh::PropertySet: Warning: No match for property with name '" << name << "'"; + throw std::runtime_error(msg.str()); + } } bool PropertySet::setPropertyOverride (const std::string& name, PropertyValuePtr &value, PropertySetGet* context) { - // if we got here, none of the sub-classes was able to make use of the property + // if we got here, none of the sub-classes were able to make use of the property return false; } @@ -226,6 +233,11 @@ namespace sh mProperties [name] = value; } + void PropertySetGet::deleteProperty(const std::string &name) + { + mProperties.erase(name); + } + PropertyValuePtr& PropertySetGet::getProperty (const std::string& name) { bool found = (mProperties.find(name) != mProperties.end()); @@ -241,7 +253,7 @@ namespace sh return mProperties[name]; } - bool PropertySetGet::hasProperty (const std::string& name) + bool PropertySetGet::hasProperty (const std::string& name) const { bool found = (mProperties.find(name) != mProperties.end()); @@ -256,13 +268,35 @@ namespace sh return true; } - void PropertySetGet::copyAll (PropertySet* target, PropertySetGet* context) + void PropertySetGet::copyAll (PropertySet* target, PropertySetGet* context, bool copyParent) { - if (mParent) + if (mParent && copyParent) mParent->copyAll (target, context); for (PropertyMap::iterator it = mProperties.begin(); it != mProperties.end(); ++it) { target->setProperty(it->first, it->second, context); } } + + void PropertySetGet::copyAll (PropertySetGet* target, PropertySetGet* context, bool copyParent) + { + if (mParent && copyParent) + mParent->copyAll (target, context); + for (PropertyMap::iterator it = mProperties.begin(); it != mProperties.end(); ++it) + { + std::string val = retrieveValue(it->second, this).get(); + target->setProperty(it->first, sh::makeProperty(new sh::StringValue(val))); + } + } + + void PropertySetGet::save(std::ofstream &stream, const std::string& indentation) + { + for (PropertyMap::iterator it = mProperties.begin(); it != mProperties.end(); ++it) + { + if (typeid( *(it->second) ) == typeid(LinkedValue)) + stream << indentation << it->first << " " << "$" + static_cast(&*(it->second))->_getStringValue() << '\n'; + else + stream << indentation << it->first << " " << retrieveValue(it->second, this).get() << '\n'; + } + } } diff --git a/extern/shiny/Main/PropertyBase.hpp b/extern/shiny/Main/PropertyBase.hpp index 240acce81..13809443e 100644 --- a/extern/shiny/Main/PropertyBase.hpp +++ b/extern/shiny/Main/PropertyBase.hpp @@ -133,6 +133,7 @@ namespace sh class PropertySet { public: + virtual ~PropertySet() {} void setProperty (const std::string& name, PropertyValuePtr& value, PropertySetGet* context); protected: @@ -151,18 +152,26 @@ namespace sh virtual ~PropertySetGet() {} - void copyAll (PropertySet* target, PropertySetGet* context); ///< call setProperty for each property/value pair stored in \a this + void save (std::ofstream& stream, const std::string& indentation); + + void copyAll (PropertySet* target, PropertySetGet* context, bool copyParent=true); + ///< call setProperty for each property/value pair stored in \a this + void copyAll (PropertySetGet* target, PropertySetGet* context, bool copyParent=true); + ///< call setProperty for each property/value pair stored in \a this void setParent (PropertySetGet* parent); + PropertySetGet* getParent () { return mParent; } void setContext (PropertySetGet* context); PropertySetGet* getContext(); virtual void setProperty (const std::string& name, PropertyValuePtr value); PropertyValuePtr& getProperty (const std::string& name); + void deleteProperty (const std::string& name); + const PropertyMap& listProperties() { return mProperties; } - bool hasProperty (const std::string& name); + bool hasProperty (const std::string& name) const; private: PropertyMap mProperties; @@ -225,7 +234,7 @@ namespace sh template /// Create a property of any type - /// Example: sh::makeProperty\ (new sh::Vector4(1, 1, 1, 1)) + /// Example: sh::makeProperty (new sh::Vector4(1, 1, 1, 1)) inline PropertyValuePtr makeProperty (T* p) { return PropertyValuePtr ( static_cast(p) ); diff --git a/extern/shiny/Main/ScriptLoader.cpp b/extern/shiny/Main/ScriptLoader.cpp index 93d728b02..bafe07fc8 100644 --- a/extern/shiny/Main/ScriptLoader.cpp +++ b/extern/shiny/Main/ScriptLoader.cpp @@ -24,6 +24,10 @@ namespace sh } ScriptLoader::ScriptLoader(const std::string& fileEnding) + : mLoadOrder(0) + , mToken(TOKEN_NewLine) + , mLastToken(TOKEN_NewLine) + { mFileEnding = fileEnding; } @@ -36,7 +40,7 @@ namespace sh void ScriptLoader::clearScriptList() { std::map ::iterator i; - for (i = m_scriptList.begin(); i != m_scriptList.end(); i++) + for (i = m_scriptList.begin(); i != m_scriptList.end(); ++i) { delete i->second; } @@ -293,7 +297,7 @@ namespace sh { //Delete all children std::vector::iterator i; - for (i = mChildren.begin(); i != mChildren.end(); i++) + for (i = mChildren.begin(); i != mChildren.end(); ++i) { ScriptNode *node = *i; node->mRemoveSelf = false; @@ -323,15 +327,24 @@ namespace sh ScriptNode *ScriptNode::findChild(const std::string &name, bool recursive) { - int indx, prevC, nextC; + int indx; int childCount = (int)mChildren.size(); if (mLastChildFound != -1) { //If possible, try checking the nodes neighboring the last successful search //(often nodes searched for in sequence, so this will boost search speeds). - prevC = mLastChildFound-1; if (prevC < 0) prevC = 0; else if (prevC >= childCount) prevC = childCount-1; - nextC = mLastChildFound+1; if (nextC < 0) nextC = 0; else if (nextC >= childCount) nextC = childCount-1; + int prevC = mLastChildFound-1; + if (prevC < 0) + prevC = 0; + else if (prevC >= childCount) + prevC = childCount-1; + int nextC = mLastChildFound+1; + if (nextC < 0) + nextC = 0; + else if (nextC >= childCount) + nextC = childCount-1; + for (indx = prevC; indx <= nextC; ++indx) { ScriptNode *node = mChildren[indx]; diff --git a/extern/shiny/Main/ShaderInstance.cpp b/extern/shiny/Main/ShaderInstance.cpp index 07ef8dfe2..270aaba68 100644 --- a/extern/shiny/Main/ShaderInstance.cpp +++ b/extern/shiny/Main/ShaderInstance.cpp @@ -22,8 +22,11 @@ namespace return "SH_CG"; else if (lang == sh::Language_HLSL) return "SH_HLSL"; - else //if (lang == sh::Language_GLSL) + else if (lang == sh::Language_GLSL) return "SH_GLSL"; + else if (lang == sh::Language_GLSLES) + return "SH_GLSLES"; + throw std::runtime_error("invalid language"); } char getComponent(int num) @@ -194,13 +197,6 @@ namespace sh bool val = retrieveValue(value, properties->getContext()).get(); replaceValue = val ? "1" : "0"; } - else if (cmd == "shPropertyNotBool") // same as above, but inverts the result - { - std::string propertyName = args[0]; - PropertyValuePtr value = properties->getProperty(propertyName); - bool val = retrieveValue(value, properties->getContext()).get(); - replaceValue = val ? "0" : "1"; - } else if (cmd == "shPropertyString") { std::string propertyName = args[0]; @@ -214,6 +210,14 @@ namespace sh std::string value = retrieveValue(properties->getProperty(propertyName), properties->getContext()).get(); replaceValue = (value == comparedAgainst) ? "1" : "0"; } + else if (isCmd(source, pos, "@shPropertyHasValue")) + { + assert(args.size() == 1); + std::string propertyName = args[0]; + PropertyValuePtr value = properties->getProperty(propertyName); + std::string val = retrieveValue(value, properties->getContext()).get(); + replaceValue = (val.empty() ? "0" : "1"); + } else throw std::runtime_error ("unknown command \"" + cmd + "\""); source.replace(pos, (end+1)-pos, replaceValue); @@ -337,8 +341,7 @@ namespace sh size_t pos; bool readCache = Factory::getInstance ().getReadSourceCache () && boost::filesystem::exists( - Factory::getInstance ().getCacheFolder () + "/" + mName) - && !mParent->isDirty (); + Factory::getInstance ().getCacheFolder () + "/" + mName); bool writeCache = Factory::getInstance ().getWriteSourceCache (); @@ -363,12 +366,6 @@ namespace sh if (Factory::getInstance ().getShaderDebugOutputEnabled ()) writeDebugFile(source, name + ".pre"); - else - { - #ifdef SHINY_WRITE_SHADER_DEBUG - writeDebugFile(source, name + ".pre"); - #endif - } // why do we need our own preprocessor? there are several custom commands available in the shader files // (for example for binding uniforms to properties or auto constants) - more below. it is important that these @@ -648,12 +645,6 @@ namespace sh if (Factory::getInstance ().getShaderDebugOutputEnabled ()) writeDebugFile(source, name); - else - { -#ifdef SHINY_WRITE_SHADER_DEBUG - writeDebugFile(source, name); -#endif - } if (!mProgram->getSupported()) { diff --git a/extern/shiny/Main/ShaderSet.cpp b/extern/shiny/Main/ShaderSet.cpp index 2702ece19..8fb530d39 100644 --- a/extern/shiny/Main/ShaderSet.cpp +++ b/extern/shiny/Main/ShaderSet.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "Factory.hpp" @@ -17,7 +18,6 @@ namespace sh , mName(name) , mCgProfile(cgProfile) , mHlslProfile(hlslProfile) - , mIsDirty(false) { if (type == "vertex") mType = GPT_Vertex; @@ -27,12 +27,24 @@ namespace sh std::ifstream stream(sourceFile.c_str(), std::ifstream::in); std::stringstream buffer; + boost::filesystem::path p (sourceFile); + p = p.branch_path(); + mBasePath = p.string(); + buffer << stream.rdbuf(); stream.close(); mSource = buffer.str(); parse(); } + ShaderSet::~ShaderSet() + { + for (ShaderInstanceMap::iterator it = mInstances.begin(); it != mInstances.end(); ++it) + { + sh::Factory::getInstance().getPlatform()->destroyGpuProgram(it->second.getName()); + } + } + void ShaderSet::parse() { std::string currentToken; @@ -53,6 +65,12 @@ namespace sh size_t start = currentToken.find('(')+1; mGlobalSettings.push_back(currentToken.substr(start, currentToken.find(')')-start)); } + else if (boost::starts_with(currentToken, "@shPropertyHasValue")) + { + assert ((currentToken.find('(') != std::string::npos) && (currentToken.find(')') != std::string::npos)); + size_t start = currentToken.find('(')+1; + mPropertiesToExist.push_back(currentToken.substr(start, currentToken.find(')')-start)); + } else if (boost::starts_with(currentToken, "@shPropertyEqual")) { assert ((currentToken.find('(') != std::string::npos) && (currentToken.find(')') != std::string::npos) @@ -136,6 +154,11 @@ namespace sh { boost::hash_combine(seed, retrieveValue(currentGlobalSettings->getProperty(*it), NULL).get()); } + for (std::vector::iterator it = mPropertiesToExist.begin(); it != mPropertiesToExist.end(); ++it) + { + std::string v = retrieveValue(properties->getProperty(*it), properties->getContext()).get(); + boost::hash_combine(seed, static_cast(v != "")); + } boost::hash_combine(seed, static_cast(Factory::getInstance().getCurrentLanguage())); return seed; } diff --git a/extern/shiny/Main/ShaderSet.hpp b/extern/shiny/Main/ShaderSet.hpp index 776750598..988c6769f 100644 --- a/extern/shiny/Main/ShaderSet.hpp +++ b/extern/shiny/Main/ShaderSet.hpp @@ -21,6 +21,7 @@ namespace sh public: ShaderSet (const std::string& type, const std::string& cgProfile, const std::string& hlslProfile, const std::string& sourceFile, const std::string& basePath, const std::string& name, PropertySetGet* globalSettingsPtr); + ~ShaderSet(); /// Retrieve a shader instance for the given properties. \n /// If a \a ShaderInstance with the same properties exists already, simply returns this instance. \n @@ -30,9 +31,6 @@ namespace sh /// so it does not matter if you pass any extra properties that the shader does not care about. ShaderInstance* getInstance (PropertySetGet* properties); - void markDirty() { mIsDirty = true; } - ///< Signals that the cache is out of date, and thus should not be used this time - private: PropertySetGet* getCurrentGlobalSettings() const; std::string getBasePath() const; @@ -41,12 +39,8 @@ namespace sh std::string getHlslProfile() const; int getType() const; - bool isDirty() { return mIsDirty; } - friend class ShaderInstance; - bool mIsDirty; - private: GpuProgramType mType; std::string mSource; @@ -60,6 +54,10 @@ namespace sh std::vector mGlobalSettings; ///< names of the global settings that affect the shader source std::vector mProperties; ///< names of the per-material properties that affect the shader source + std::vector mPropertiesToExist; + ///< same as mProperties, however in this case, it is only relevant if the property is empty or not + /// (we don't care about the value) + ShaderInstanceMap mInstances; ///< maps permutation ID (generated from the properties) to \a ShaderInstance void parse(); ///< find out which properties and global settings affect the shader source diff --git a/extern/shiny/Platforms/Ogre/OgreGpuProgram.cpp b/extern/shiny/Platforms/Ogre/OgreGpuProgram.cpp index fe5aa2fe7..e71854019 100644 --- a/extern/shiny/Platforms/Ogre/OgreGpuProgram.cpp +++ b/extern/shiny/Platforms/Ogre/OgreGpuProgram.cpp @@ -28,9 +28,8 @@ namespace sh t = Ogre::GPT_FRAGMENT_PROGRAM; mProgram = mgr.createProgram(name, resourceGroup, lang, t); - if (lang != "glsl") + if (lang != "glsl" && lang != "glsles") mProgram->setParameter("entry_point", "main"); - if (lang == "hlsl") mProgram->setParameter("target", profile); else if (lang == "cg") diff --git a/extern/shiny/Platforms/Ogre/OgreMaterial.cpp b/extern/shiny/Platforms/Ogre/OgreMaterial.cpp index 4a550b8bf..77091fe03 100644 --- a/extern/shiny/Platforms/Ogre/OgreMaterial.cpp +++ b/extern/shiny/Platforms/Ogre/OgreMaterial.cpp @@ -15,6 +15,7 @@ namespace sh OgreMaterial::OgreMaterial (const std::string& name, const std::string& resourceGroup) : Material() { + mName = name; assert (Ogre::MaterialManager::getSingleton().getByName(name).isNull() && "Material already exists"); mMaterial = Ogre::MaterialManager::getSingleton().create (name, resourceGroup); mMaterial->removeAllTechniques(); @@ -22,9 +23,22 @@ namespace sh mMaterial->compile(); } + void OgreMaterial::ensureLoaded() + { + if (mMaterial.isNull()) + mMaterial = Ogre::MaterialManager::getSingleton().getByName(mName); + } + + bool OgreMaterial::isUnreferenced() + { + // Resource system internals hold 3 shared pointers, we hold one, so usecount of 4 means unused + return (!mMaterial.isNull() && mMaterial.useCount() <= Ogre::ResourceGroupManager::RESOURCE_SYSTEM_NUM_REFERENCE_COUNTS+1); + } + OgreMaterial::~OgreMaterial() { - Ogre::MaterialManager::getSingleton().remove(mMaterial->getName()); + if (!mMaterial.isNull()) + Ogre::MaterialManager::getSingleton().remove(mMaterial->getName()); } boost::shared_ptr OgreMaterial::createPass (const std::string& configuration, unsigned short lodIndex) @@ -34,6 +48,8 @@ namespace sh void OgreMaterial::removeAll () { + if (mMaterial.isNull()) + return; mMaterial->removeAllTechniques(); mMaterial->createTechnique()->setSchemeName (sDefaultTechniqueName); mMaterial->compile(); diff --git a/extern/shiny/Platforms/Ogre/OgreMaterial.hpp b/extern/shiny/Platforms/Ogre/OgreMaterial.hpp index bec23f0b6..bfa6e2c6f 100644 --- a/extern/shiny/Platforms/Ogre/OgreMaterial.hpp +++ b/extern/shiny/Platforms/Ogre/OgreMaterial.hpp @@ -18,6 +18,9 @@ namespace sh virtual boost::shared_ptr createPass (const std::string& configuration, unsigned short lodIndex); virtual bool createConfiguration (const std::string& name, unsigned short lodIndex); + virtual bool isUnreferenced(); + virtual void ensureLoaded(); + virtual void removeAll (); Ogre::MaterialPtr getOgreMaterial(); @@ -30,6 +33,7 @@ namespace sh private: Ogre::MaterialPtr mMaterial; + std::string mName; std::string mShadowCasterMaterial; }; diff --git a/extern/shiny/Platforms/Ogre/OgreMaterialSerializer.cpp b/extern/shiny/Platforms/Ogre/OgreMaterialSerializer.cpp index 9f57c7b44..f45e64155 100644 --- a/extern/shiny/Platforms/Ogre/OgreMaterialSerializer.cpp +++ b/extern/shiny/Platforms/Ogre/OgreMaterialSerializer.cpp @@ -1,5 +1,9 @@ #include "OgreMaterialSerializer.hpp" +#include + +#include + namespace sh { void OgreMaterialSerializer::reset() @@ -19,6 +23,13 @@ namespace sh bool OgreMaterialSerializer::setPassProperty (const std::string& param, std::string value, Ogre::Pass* pass) { + // workaround https://ogre3d.atlassian.net/browse/OGRE-158 + if (param == "transparent_sorting" && value == "force") + { + pass->setTransparentSortingForced(true); + return true; + } + reset(); mScriptContext.section = Ogre::MSS_PASS; @@ -35,6 +46,13 @@ namespace sh bool OgreMaterialSerializer::setTextureUnitProperty (const std::string& param, std::string value, Ogre::TextureUnitState* t) { + // quick access to automip setting, without having to use 'texture' which doesn't like spaces in filenames + if (param == "num_mipmaps") + { + t->setNumMipmaps(Ogre::StringConverter::parseInt(value)); + return true; + } + reset(); mScriptContext.section = Ogre::MSS_TEXTUREUNIT; diff --git a/extern/shiny/Platforms/Ogre/OgrePass.cpp b/extern/shiny/Platforms/Ogre/OgrePass.cpp index 8cfaae078..3a25c5c74 100644 --- a/extern/shiny/Platforms/Ogre/OgrePass.cpp +++ b/extern/shiny/Platforms/Ogre/OgrePass.cpp @@ -20,9 +20,9 @@ namespace sh mPass = t->createPass(); } - boost::shared_ptr OgrePass::createTextureUnitState () + boost::shared_ptr OgrePass::createTextureUnitState (const std::string& name) { - return boost::shared_ptr (new OgreTextureUnitState (this)); + return boost::shared_ptr (new OgreTextureUnitState (this, name)); } void OgrePass::assignProgram (GpuProgramType type, const std::string& name) @@ -50,13 +50,6 @@ namespace sh return true; // handled already else if (name == "fragment_program") return true; // handled already - else if (name == "ffp_vertex_colour_ambient") - { - bool enabled = retrieveValue(value, context).get(); - // fixed-function vertex colour tracking - mPass->setVertexColourTracking(enabled ? Ogre::TVC_AMBIENT : Ogre::TVC_NONE); - return true; - } else { OgreMaterialSerializer& s = OgrePlatform::getSerializer(); @@ -112,7 +105,17 @@ namespace sh else if (type == GPT_Fragment) params = mPass->getFragmentProgramParameters(); - params->addSharedParameters (name); + try + { + params->addSharedParameters (name); + } + catch (Ogre::Exception& e) + { + std::stringstream msg; + msg << "Could not create a shared parameter instance for '" + << name << "'. Make sure this shared parameter has a value set (via Factory::setSharedParameter)!"; + throw std::runtime_error(msg.str()); + } } void OgrePass::setTextureUnitIndex (int programType, const std::string& name, int index) diff --git a/extern/shiny/Platforms/Ogre/OgrePass.hpp b/extern/shiny/Platforms/Ogre/OgrePass.hpp index da67a1a2a..e7cfd4e33 100644 --- a/extern/shiny/Platforms/Ogre/OgrePass.hpp +++ b/extern/shiny/Platforms/Ogre/OgrePass.hpp @@ -14,7 +14,7 @@ namespace sh public: OgrePass (OgreMaterial* parent, const std::string& configuration, unsigned short lodIndex); - virtual boost::shared_ptr createTextureUnitState (); + virtual boost::shared_ptr createTextureUnitState (const std::string& name); virtual void assignProgram (GpuProgramType type, const std::string& name); Ogre::Pass* getOgrePass(); diff --git a/extern/shiny/Platforms/Ogre/OgrePlatform.cpp b/extern/shiny/Platforms/Ogre/OgrePlatform.cpp index 357949402..3725d5f35 100644 --- a/extern/shiny/Platforms/Ogre/OgrePlatform.cpp +++ b/extern/shiny/Platforms/Ogre/OgrePlatform.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include "OgreMaterial.hpp" @@ -23,7 +24,9 @@ namespace return "hlsl"; else if (lang == sh::Language_GLSL) return "glsl"; - throw std::runtime_error ("invalid language, valid are: cg, hlsl, glsl"); + else if (lang == sh::Language_GLSLES) + return "glsles"; + throw std::runtime_error ("invalid language, valid are: cg, hlsl, glsl, glsles"); } } @@ -76,6 +79,11 @@ namespace sh return boost::shared_ptr (material); } + void OgrePlatform::destroyGpuProgram(const std::string &name) + { + Ogre::HighLevelGpuProgramManager::getSingleton().remove(name); + } + boost::shared_ptr OgrePlatform::createGpuProgram ( GpuProgramType type, const std::string& compileArguments, @@ -122,6 +130,7 @@ namespace sh if (mSharedParameters.find(name) == mSharedParameters.end()) { params = Ogre::GpuProgramManager::getSingleton().createSharedParameters(name); + Ogre::GpuConstantType type; if (typeid(*value) == typeid(Vector4)) type = Ogre::GCT_FLOAT4; diff --git a/extern/shiny/Platforms/Ogre/OgrePlatform.hpp b/extern/shiny/Platforms/Ogre/OgrePlatform.hpp index d115c46bb..d3a1a8360 100644 --- a/extern/shiny/Platforms/Ogre/OgrePlatform.hpp +++ b/extern/shiny/Platforms/Ogre/OgrePlatform.hpp @@ -47,6 +47,8 @@ namespace sh const std::string& name, const std::string& profile, const std::string& source, Language lang); + virtual void destroyGpuProgram (const std::string& name); + virtual void setSharedParameter (const std::string& name, PropertyValuePtr value); friend class ShaderInstance; diff --git a/extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp b/extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp index 0938cf667..54dda3523 100644 --- a/extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp +++ b/extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp @@ -6,10 +6,11 @@ namespace sh { - OgreTextureUnitState::OgreTextureUnitState (OgrePass* parent) + OgreTextureUnitState::OgreTextureUnitState (OgrePass* parent, const std::string& name) : TextureUnitState() { mTextureUnitState = parent->getOgrePass()->createTextureUnitState(""); + mTextureUnitState->setName(name); } bool OgreTextureUnitState::setPropertyOverride (const std::string &name, PropertyValuePtr& value, PropertySetGet* context) diff --git a/extern/shiny/Platforms/Ogre/OgreTextureUnitState.hpp b/extern/shiny/Platforms/Ogre/OgreTextureUnitState.hpp index d36f4b945..0f1914f62 100644 --- a/extern/shiny/Platforms/Ogre/OgreTextureUnitState.hpp +++ b/extern/shiny/Platforms/Ogre/OgreTextureUnitState.hpp @@ -12,7 +12,7 @@ namespace sh class OgreTextureUnitState : public TextureUnitState { public: - OgreTextureUnitState (OgrePass* parent); + OgreTextureUnitState (OgrePass* parent, const std::string& name); virtual void setTextureName (const std::string& textureName); diff --git a/extern/shiny/Preprocessor/aq.cpp b/extern/shiny/Preprocessor/aq.cpp deleted file mode 100644 index b81d5b332..000000000 --- a/extern/shiny/Preprocessor/aq.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - http://www.boost.org/ - - Copyright (c) 2001 Daniel C. Nuffer. - Copyright (c) 2001-2011 Hartmut Kaiser. - 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) -=============================================================================*/ - -#define BOOST_WAVE_SOURCE 1 - -// disable stupid compiler warnings -#include - -#include -#include - -#include // configuration data -#include - -#include - -// this must occur after all of the includes and before any code appears -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_PREFIX -#endif - -/////////////////////////////////////////////////////////////////////////////// -namespace boost { -namespace wave { -namespace cpplexer { -namespace re2clex { - -int aq_grow(aq_queue q) -{ - using namespace std; // some systems have memcpy/realloc in std - std::size_t new_size = q->max_size << 1; - aq_stdelement* new_queue = (aq_stdelement*)realloc(q->queue, - new_size * sizeof(aq_stdelement)); - - BOOST_ASSERT(NULL != q); - BOOST_ASSERT(q->max_size < 100000); - BOOST_ASSERT(q->size <= q->max_size); - -#define ASSERT_SIZE BOOST_ASSERT( \ - ((q->tail + q->max_size + 1) - q->head) % q->max_size == \ - q->size % q->max_size) - - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - if (!new_queue) - { - BOOST_ASSERT(0); - return 0; - } - - q->queue = new_queue; - if (q->tail <= q->head) /* tail has wrapped around */ - { - /* move the tail from the beginning to the end */ - memcpy(q->queue + q->max_size, q->queue, - (q->tail + 1) * sizeof(aq_stdelement)); - q->tail += q->max_size; - } - q->max_size = new_size; - - BOOST_ASSERT(q->size <= q->max_size); - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - return 1; -} - -int aq_enqueue(aq_queue q, aq_stdelement e) -{ - BOOST_ASSERT(NULL != q); - BOOST_ASSERT(q->size <= q->max_size); - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - - if (AQ_FULL(q)) - if (!aq_grow(q)) - return 0; - - ++q->tail; - if (q->tail == q->max_size) - q->tail = 0; - - q->queue[q->tail] = e; - ++q->size; - - BOOST_ASSERT(q->size <= q->max_size); - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - return 1; -} - -int aq_enqueue_front(aq_queue q, aq_stdelement e) -{ - BOOST_ASSERT(NULL != q); - - BOOST_ASSERT(q->size <= q->max_size); - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - - if (AQ_FULL(q)) - if (!aq_grow(q)) - return 0; - - if (q->head == 0) - q->head = q->max_size - 1; - else - --q->head; - - q->queue[q->head] = e; - ++q->size; - - BOOST_ASSERT(q->size <= q->max_size); - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - return 1; -} - -int aq_serve(aq_queue q, aq_stdelement *e) -{ - - BOOST_ASSERT(NULL != q); - BOOST_ASSERT(q->size <= q->max_size); - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - - if (AQ_EMPTY(q)) - return 0; - - *e = q->queue[q->head]; - return aq_pop(q); -} - -int aq_pop(aq_queue q) -{ - - BOOST_ASSERT(NULL != q); - BOOST_ASSERT(q->size <= q->max_size); - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - - if (AQ_EMPTY(q)) - return 0; - - ++q->head; - if (q->head == q->max_size) - q->head = 0; - --q->size; - - BOOST_ASSERT(q->size <= q->max_size); - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - return 1; -} - -aq_queue aq_create(void) -{ - aq_queue q; - - using namespace std; // some systems have malloc in std - q = (aq_queue)malloc(sizeof(aq_queuetype)); - if (!q) - { - return 0; - } - - q->max_size = 8; /* initial size */ - q->queue = (aq_stdelement*)malloc( - sizeof(aq_stdelement) * q->max_size); - if (!q->queue) - { - free(q); - return 0; - } - - q->head = 0; - q->tail = q->max_size - 1; - q->size = 0; - - - BOOST_ASSERT(q->size <= q->max_size); - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - return q; -} - -void aq_terminate(aq_queue q) -{ - using namespace std; // some systems have free in std - - BOOST_ASSERT(NULL != q); - BOOST_ASSERT(q->size <= q->max_size); - ASSERT_SIZE; - BOOST_ASSERT(q->head <= q->max_size); - BOOST_ASSERT(q->tail <= q->max_size); - - free(q->queue); - free(q); -} - -/////////////////////////////////////////////////////////////////////////////// -} // namespace re2clex -} // namespace cpplexer -} // namespace wave -} // namespace boost - -// the suffix header occurs after all of the code -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_SUFFIX -#endif - diff --git a/extern/shiny/Preprocessor/cpp_re.cpp b/extern/shiny/Preprocessor/cpp_re.cpp deleted file mode 100644 index 69d77c372..000000000 --- a/extern/shiny/Preprocessor/cpp_re.cpp +++ /dev/null @@ -1,442 +0,0 @@ -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - - Copyright (c) 2001 Daniel C. Nuffer - Copyright (c) 2001-2011 Hartmut Kaiser. - 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) - - TODO: - It also may be necessary to add $ to identifiers, for asm. - handle errors better. - have some easier way to parse strings instead of files (done) -=============================================================================*/ - -#define BOOST_WAVE_SOURCE 1 - -// disable stupid compiler warnings -#include - -#include -#include -#include -#include -#include -#include -#include - -#include // configuration data - -#if defined(BOOST_HAS_UNISTD_H) -#include -#else -#include -#endif - -#include -#include - -#include -#include -#include -#include -#include - -// this must occur after all of the includes and before any code appears -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_PREFIX -#endif - -/////////////////////////////////////////////////////////////////////////////// -#if defined(BOOST_MSVC) -#pragma warning (disable: 4101) // 'foo' : unreferenced local variable -#pragma warning (disable: 4102) // 'foo' : unreferenced label -#endif - -/////////////////////////////////////////////////////////////////////////////// -#define BOOST_WAVE_BSIZE 196608 - -#define YYCTYPE uchar -#define YYCURSOR cursor -#define YYLIMIT limit -#define YYMARKER marker -#define YYFILL(n) \ - { \ - cursor = uchar_wrapper(fill(s, cursor), cursor.column); \ - limit = uchar_wrapper (s->lim); \ - } \ - /**/ - -#include - -/////////////////////////////////////////////////////////////////////////////// -#define BOOST_WAVE_UPDATE_CURSOR() \ - { \ - s->line += count_backslash_newlines(s, cursor); \ - s->curr_column = cursor.column; \ - s->cur = cursor; \ - s->lim = limit; \ - s->ptr = marker; \ - } \ - /**/ - -/////////////////////////////////////////////////////////////////////////////// -#define BOOST_WAVE_RET(i) \ - { \ - BOOST_WAVE_UPDATE_CURSOR() \ - if (s->cur > s->lim) \ - return T_EOF; /* may happen for empty files */ \ - return (i); \ - } \ - /**/ - -/////////////////////////////////////////////////////////////////////////////// -namespace boost { -namespace wave { -namespace cpplexer { -namespace re2clex { - -#define RE2C_ASSERT BOOST_ASSERT - -int get_one_char(Scanner *s) -{ - if (0 != s->act) { - RE2C_ASSERT(s->first != 0 && s->last != 0); - RE2C_ASSERT(s->first <= s->act && s->act <= s->last); - if (s->act < s->last) - return *(s->act)++; - } - return -1; -} - -std::ptrdiff_t rewind_stream (Scanner *s, int cnt) -{ - if (0 != s->act) { - RE2C_ASSERT(s->first != 0 && s->last != 0); - s->act += cnt; - RE2C_ASSERT(s->first <= s->act && s->act <= s->last); - return s->act - s->first; - } - return 0; -} - -std::size_t get_first_eol_offset(Scanner* s) -{ - if (!AQ_EMPTY(s->eol_offsets)) - { - return s->eol_offsets->queue[s->eol_offsets->head]; - } - else - { - return (unsigned int)-1; - } -} - -void adjust_eol_offsets(Scanner* s, std::size_t adjustment) -{ - aq_queue q; - std::size_t i; - - if (!s->eol_offsets) - s->eol_offsets = aq_create(); - - q = s->eol_offsets; - - if (AQ_EMPTY(q)) - return; - - i = q->head; - while (i != q->tail) - { - if (adjustment > q->queue[i]) - q->queue[i] = 0; - else - q->queue[i] -= adjustment; - ++i; - if (i == q->max_size) - i = 0; - } - if (adjustment > q->queue[i]) - q->queue[i] = 0; - else - q->queue[i] -= adjustment; -} - -int count_backslash_newlines(Scanner *s, uchar *cursor) -{ - std::size_t diff, offset; - int skipped = 0; - - /* figure out how many backslash-newlines skipped over unknowingly. */ - diff = cursor - s->bot; - offset = get_first_eol_offset(s); - while (offset <= diff && offset != (unsigned int)-1) - { - skipped++; - aq_pop(s->eol_offsets); - offset = get_first_eol_offset(s); - } - return skipped; -} - -bool is_backslash(uchar *p, uchar *end, int &len) -{ - if (*p == '\\') { - len = 1; - return true; - } - else if (*p == '?' && *(p+1) == '?' && (p+2 < end && *(p+2) == '/')) { - len = 3; - return true; - } - return false; -} - -uchar *fill(Scanner *s, uchar *cursor) -{ - using namespace std; // some systems have memcpy etc. in namespace std - if(!s->eof) - { - uchar* p; - std::ptrdiff_t cnt = s->tok - s->bot; - if(cnt) - { - if (NULL == s->lim) - s->lim = s->top; - memmove(s->bot, s->tok, s->lim - s->tok); - s->tok = s->cur = s->bot; - s->ptr -= cnt; - cursor -= cnt; - s->lim -= cnt; - adjust_eol_offsets(s, cnt); - } - - if((s->top - s->lim) < BOOST_WAVE_BSIZE) - { - uchar *buf = (uchar*) malloc(((s->lim - s->bot) + BOOST_WAVE_BSIZE)*sizeof(uchar)); - if (buf == 0) - { - using namespace std; // some systems have printf in std - if (0 != s->error_proc) { - (*s->error_proc)(s, lexing_exception::unexpected_error, - "Out of memory!"); - } - else - printf("Out of memory!\n"); - - /* get the scanner to stop */ - *cursor = 0; - return cursor; - } - - memmove(buf, s->tok, s->lim - s->tok); - s->tok = s->cur = buf; - s->ptr = &buf[s->ptr - s->bot]; - cursor = &buf[cursor - s->bot]; - s->lim = &buf[s->lim - s->bot]; - s->top = &s->lim[BOOST_WAVE_BSIZE]; - free(s->bot); - s->bot = buf; - } - - if (s->act != 0) { - cnt = s->last - s->act; - if (cnt > BOOST_WAVE_BSIZE) - cnt = BOOST_WAVE_BSIZE; - memmove(s->lim, s->act, cnt); - s->act += cnt; - if (cnt != BOOST_WAVE_BSIZE) - { - s->eof = &s->lim[cnt]; *(s->eof)++ = '\0'; - } - } - - /* backslash-newline erasing time */ - - /* first scan for backslash-newline and erase them */ - for (p = s->lim; p < s->lim + cnt - 2; ++p) - { - int len = 0; - if (is_backslash(p, s->lim + cnt, len)) - { - if (*(p+len) == '\n') - { - int offset = len + 1; - memmove(p, p + offset, s->lim + cnt - p - offset); - cnt -= offset; - --p; - aq_enqueue(s->eol_offsets, p - s->bot + 1); - } - else if (*(p+len) == '\r') - { - if (*(p+len+1) == '\n') - { - int offset = len + 2; - memmove(p, p + offset, s->lim + cnt - p - offset); - cnt -= offset; - --p; - } - else - { - int offset = len + 1; - memmove(p, p + offset, s->lim + cnt - p - offset); - cnt -= offset; - --p; - } - aq_enqueue(s->eol_offsets, p - s->bot + 1); - } - } - } - - /* FIXME: the following code should be fixed to recognize correctly the - trigraph backslash token */ - - /* check to see if what we just read ends in a backslash */ - if (cnt >= 2) - { - uchar last = s->lim[cnt-1]; - uchar last2 = s->lim[cnt-2]; - /* check \ EOB */ - if (last == '\\') - { - int next = get_one_char(s); - /* check for \ \n or \ \r or \ \r \n straddling the border */ - if (next == '\n') - { - --cnt; /* chop the final \, we've already read the \n. */ - aq_enqueue(s->eol_offsets, cnt + (s->lim - s->bot)); - } - else if (next == '\r') - { - int next2 = get_one_char(s); - if (next2 == '\n') - { - --cnt; /* skip the backslash */ - } - else - { - /* rewind one, and skip one char */ - rewind_stream(s, -1); - --cnt; - } - aq_enqueue(s->eol_offsets, cnt + (s->lim - s->bot)); - } - else if (next != -1) /* -1 means end of file */ - { - /* next was something else, so rewind the stream */ - rewind_stream(s, -1); - } - } - /* check \ \r EOB */ - else if (last == '\r' && last2 == '\\') - { - int next = get_one_char(s); - if (next == '\n') - { - cnt -= 2; /* skip the \ \r */ - } - else - { - /* rewind one, and skip two chars */ - rewind_stream(s, -1); - cnt -= 2; - } - aq_enqueue(s->eol_offsets, cnt + (s->lim - s->bot)); - } - /* check \ \n EOB */ - else if (last == '\n' && last2 == '\\') - { - cnt -= 2; - aq_enqueue(s->eol_offsets, cnt + (s->lim - s->bot)); - } - } - - s->lim += cnt; - if (s->eof) /* eof needs adjusting if we erased backslash-newlines */ - { - s->eof = s->lim; - *(s->eof)++ = '\0'; - } - } - return cursor; -} - -/////////////////////////////////////////////////////////////////////////////// -// Special wrapper class holding the current cursor position -struct uchar_wrapper -{ - uchar_wrapper (uchar *base_cursor, unsigned int column = 1) - : base_cursor(base_cursor), column(column) - {} - - uchar_wrapper& operator++() - { - ++base_cursor; - ++column; - return *this; - } - - uchar_wrapper& operator--() - { - --base_cursor; - --column; - return *this; - } - - uchar operator* () const - { - return *base_cursor; - } - - operator uchar *() const - { - return base_cursor; - } - - friend std::ptrdiff_t - operator- (uchar_wrapper const& lhs, uchar_wrapper const& rhs) - { - return lhs.base_cursor - rhs.base_cursor; - } - - uchar *base_cursor; - unsigned int column; -}; - -/////////////////////////////////////////////////////////////////////////////// -boost::wave::token_id scan(Scanner *s) -{ - BOOST_ASSERT(0 != s->error_proc); // error handler must be given - - uchar_wrapper cursor (s->tok = s->cur, s->column = s->curr_column); - uchar_wrapper marker (s->ptr); - uchar_wrapper limit (s->lim); - -// include the correct Re2C token definition rules -#if BOOST_WAVE_USE_STRICT_LEXER != 0 -#include "strict_cpp_re.inc" -#else -#include "cpp_re.inc" -#endif - -} /* end of scan */ - -/////////////////////////////////////////////////////////////////////////////// -} // namespace re2clex -} // namespace cpplexer -} // namespace wave -} // namespace boost - -#undef BOOST_WAVE_RET -#undef BOOST_WAVE_BSIZE -#undef YYCTYPE -#undef YYCURSOR -#undef YYLIMIT -#undef YYMARKER -#undef YYFILL - -// the suffix header occurs after all of the code -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_SUFFIX -#endif - diff --git a/extern/shiny/Preprocessor/cpp_re.inc b/extern/shiny/Preprocessor/cpp_re.inc deleted file mode 100644 index afb7fc189..000000000 --- a/extern/shiny/Preprocessor/cpp_re.inc +++ /dev/null @@ -1,9044 +0,0 @@ -/* Generated by re2c 0.13.5 on Sun Jan 09 15:38:23 2011 */ -#line 1 "cpp.re" -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - - Copyright (c) 2001 Daniel C. Nuffer - Copyright (c) 2001-2011 Hartmut Kaiser. - 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) - - This is a lexer conforming to the Standard with a few exceptions. - So it does allow the '$' to be part of identifiers. If you need strict - Standards conforming behaviour, please include the lexer definition - provided in the file strict_cpp.re. - - TODO: - handle errors better. -=============================================================================*/ - -#line 40 "cpp.re" - - - -#line 25 "cpp_re.inc" -{ - YYCTYPE yych; - unsigned int yyaccept = 0; - static const unsigned char yybm[] = { - /* table 1 .. 8: 0 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 60, 32, 60, 60, 64, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 60, 60, 52, 60, 60, 60, 60, 56, - 60, 60, 156, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 44, 57, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 58, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, - /* table 9 .. 12: 256 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 80, 0, 80, 80, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 80, 64, 0, 64, 96, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 224, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 64, 64, 64, 64, 64, 0, - 64, 224, 224, 224, 224, 224, 224, 96, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 64, 0, 64, 64, 96, - 64, 224, 224, 224, 224, 224, 224, 96, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - }; - - if ((YYLIMIT - YYCURSOR) < 17) YYFILL(17); - yych = *YYCURSOR; - switch (yych) { - case 0x00: goto yy90; - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x08: - case 0x0E: - case 0x0F: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1A: - case 0x1B: - case 0x1C: - case 0x1D: - case 0x1E: - case 0x1F: goto yy93; - case '\t': - case '\v': - case '\f': goto yy84; - case '\n': goto yy87; - case '\r': goto yy89; - case ' ': goto yy86; - case '!': goto yy68; - case '"': goto yy79; - case '#': goto yy45; - case '$': - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': - case 'G': - case 'H': - case 'I': - case 'J': - case 'K': - case 'M': - case 'N': - case 'O': - case 'P': - case 'Q': - case 'S': - case 'T': - case 'V': - case 'W': - case 'X': - case 'Y': - case 'Z': - case 'h': - case 'j': - case 'k': - case 'q': - case 'y': - case 'z': goto yy82; - case '%': goto yy37; - case '&': goto yy62; - case '\'': goto yy77; - case '(': goto yy47; - case ')': goto yy49; - case '*': goto yy57; - case '+': goto yy53; - case ',': goto yy74; - case '-': goto yy55; - case '.': goto yy4; - case '/': goto yy2; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': goto yy6; - case ':': goto yy43; - case ';': goto yy51; - case '<': goto yy33; - case '=': goto yy70; - case '>': goto yy72; - case '?': goto yy31; - case 'L': goto yy76; - case 'R': goto yy80; - case 'U': goto yy81; - case '[': goto yy39; - case '\\': goto yy83; - case ']': goto yy41; - case '^': goto yy59; - case '_': goto yy28; - case 'a': goto yy8; - case 'b': goto yy10; - case 'c': goto yy11; - case 'd': goto yy12; - case 'e': goto yy13; - case 'f': goto yy14; - case 'g': goto yy15; - case 'i': goto yy16; - case 'l': goto yy17; - case 'm': goto yy18; - case 'n': goto yy19; - case 'o': goto yy20; - case 'p': goto yy21; - case 'r': goto yy22; - case 's': goto yy23; - case 't': goto yy24; - case 'u': goto yy25; - case 'v': goto yy26; - case 'w': goto yy27; - case 'x': goto yy61; - case '{': goto yy29; - case '|': goto yy64; - case '}': goto yy35; - case '~': goto yy66; - default: goto yy92; - } -yy2: - ++YYCURSOR; - if ((yych = *YYCURSOR) <= '.') { - if (yych == '*') goto yy998; - } else { - if (yych <= '/') goto yy996; - if (yych == '=') goto yy994; - } -#line 188 "cpp.re" - { BOOST_WAVE_RET(T_DIVIDE); } -#line 238 "cpp_re.inc" -yy4: - yyaccept = 0; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '-') { - if (yych == '*') goto yy988; - } else { - if (yych <= '.') goto yy990; - if (yych <= '/') goto yy5; - if (yych <= '9') goto yy991; - } -yy5: -#line 174 "cpp.re" - { BOOST_WAVE_RET(T_DOT); } -#line 252 "cpp_re.inc" -yy6: - ++YYCURSOR; -yy7: -#line 45 "cpp.re" - { goto pp_number; } -#line 258 "cpp_re.inc" -yy8: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - switch (yych) { - case 'l': goto yy964; - case 'n': goto yy965; - case 's': goto yy966; - case 'u': goto yy967; - default: goto yy109; - } -yy9: -#line 290 "cpp.re" - { BOOST_WAVE_RET(T_IDENTIFIER); } -#line 272 "cpp_re.inc" -yy10: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'n') { - if (yych == 'i') goto yy946; - goto yy109; - } else { - if (yych <= 'o') goto yy947; - if (yych == 'r') goto yy948; - goto yy109; - } -yy11: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - switch (yych) { - case 'a': goto yy893; - case 'h': goto yy894; - case 'l': goto yy895; - case 'o': goto yy896; - default: goto yy109; - } -yy12: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'n') { - if (yych == 'e') goto yy855; - goto yy109; - } else { - if (yych <= 'o') goto yy856; - if (yych == 'y') goto yy858; - goto yy109; - } -yy13: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'm') { - if (yych == 'l') goto yy830; - goto yy109; - } else { - if (yych <= 'n') goto yy831; - if (yych == 'x') goto yy832; - goto yy109; - } -yy14: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - switch (yych) { - case 'a': goto yy811; - case 'l': goto yy812; - case 'o': goto yy813; - case 'r': goto yy814; - default: goto yy109; - } -yy15: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'o') goto yy807; - goto yy109; -yy16: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'l') { - if (yych == 'f') goto yy791; - goto yy109; - } else { - if (yych <= 'm') goto yy793; - if (yych <= 'n') goto yy794; - goto yy109; - } -yy17: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'o') goto yy787; - goto yy109; -yy18: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'u') goto yy780; - goto yy109; -yy19: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'e') { - if (yych == 'a') goto yy747; - if (yych <= 'd') goto yy109; - goto yy748; - } else { - if (yych <= 'o') { - if (yych <= 'n') goto yy109; - goto yy749; - } else { - if (yych == 'u') goto yy750; - goto yy109; - } - } -yy20: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'p') goto yy733; - if (yych == 'r') goto yy734; - goto yy109; -yy21: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'r') goto yy712; - if (yych == 'u') goto yy713; - goto yy109; -yy22: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'e') goto yy684; - goto yy109; -yy23: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 's') { - if (yych <= 'g') goto yy109; - if (yych <= 'h') goto yy638; - if (yych <= 'i') goto yy639; - goto yy109; - } else { - if (yych <= 't') goto yy640; - if (yych == 'w') goto yy641; - goto yy109; - } -yy24: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'h') { - if (yych == 'e') goto yy591; - if (yych <= 'g') goto yy109; - goto yy592; - } else { - if (yych <= 'r') { - if (yych <= 'q') goto yy109; - goto yy593; - } else { - if (yych == 'y') goto yy594; - goto yy109; - } - } -yy25: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '8') { - if (yych <= '&') { - if (yych == '"') goto yy129; - goto yy109; - } else { - if (yych <= '\'') goto yy131; - if (yych <= '7') goto yy109; - goto yy573; - } - } else { - if (yych <= 'm') { - if (yych == 'R') goto yy128; - goto yy109; - } else { - if (yych <= 'n') goto yy574; - if (yych == 's') goto yy575; - goto yy109; - } - } -yy26: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'i') goto yy555; - if (yych == 'o') goto yy556; - goto yy109; -yy27: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'c') goto yy543; - if (yych == 'h') goto yy544; - goto yy109; -yy28: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - switch (yych) { - case '_': goto yy454; - case 'a': goto yy455; - case 'b': goto yy456; - case 'c': goto yy457; - case 'd': goto yy458; - case 'f': goto yy459; - case 'i': goto yy460; - case 's': goto yy461; - default: goto yy109; - } -yy29: - ++YYCURSOR; -#line 138 "cpp.re" - { BOOST_WAVE_RET(T_LEFTBRACE); } -#line 466 "cpp_re.inc" -yy31: - yyaccept = 2; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '?') goto yy419; -yy32: -#line 163 "cpp.re" - { BOOST_WAVE_RET(T_QUESTION_MARK); } -#line 474 "cpp_re.inc" -yy33: - ++YYCURSOR; - if ((yych = *YYCURSOR) <= ':') { - if (yych == '%') goto yy415; - if (yych >= ':') goto yy413; - } else { - if (yych <= ';') goto yy34; - if (yych <= '<') goto yy411; - if (yych <= '=') goto yy409; - } -yy34: -#line 204 "cpp.re" - { BOOST_WAVE_RET(T_LESS); } -#line 488 "cpp_re.inc" -yy35: - ++YYCURSOR; -#line 141 "cpp.re" - { BOOST_WAVE_RET(T_RIGHTBRACE); } -#line 493 "cpp_re.inc" -yy37: - ++YYCURSOR; - if ((yych = *YYCURSOR) <= '<') { - if (yych == ':') goto yy400; - } else { - if (yych <= '=') goto yy402; - if (yych <= '>') goto yy404; - } -#line 189 "cpp.re" - { BOOST_WAVE_RET(T_PERCENT); } -#line 504 "cpp_re.inc" -yy39: - ++YYCURSOR; -#line 144 "cpp.re" - { BOOST_WAVE_RET(T_LEFTBRACKET); } -#line 509 "cpp_re.inc" -yy41: - ++YYCURSOR; -#line 147 "cpp.re" - { BOOST_WAVE_RET(T_RIGHTBRACKET); } -#line 514 "cpp_re.inc" -yy43: - ++YYCURSOR; - if ((yych = *YYCURSOR) == ':') goto yy396; - if (yych == '>') goto yy398; -#line 161 "cpp.re" - { BOOST_WAVE_RET(T_COLON); } -#line 521 "cpp_re.inc" -yy45: - yyaccept = 3; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'c') { - if (yych <= ' ') { - if (yych <= '\n') { - if (yych == '\t') goto yy273; - } else { - if (yych <= '\f') goto yy273; - if (yych >= ' ') goto yy273; - } - } else { - if (yych <= '.') { - if (yych == '#') goto yy284; - } else { - if (yych <= '/') goto yy273; - if (yych == '?') goto yy283; - } - } - } else { - if (yych <= 'p') { - if (yych <= 'i') { - if (yych <= 'e') goto yy273; - if (yych >= 'i') goto yy273; - } else { - if (yych == 'l') goto yy273; - if (yych >= 'p') goto yy273; - } - } else { - if (yych <= 't') { - if (yych == 'r') goto yy273; - } else { - if (yych == 'v') goto yy46; - if (yych <= 'w') goto yy273; - } - } - } -yy46: -#line 150 "cpp.re" - { BOOST_WAVE_RET(T_POUND); } -#line 562 "cpp_re.inc" -yy47: - ++YYCURSOR; -#line 158 "cpp.re" - { BOOST_WAVE_RET(T_LEFTPAREN); } -#line 567 "cpp_re.inc" -yy49: - ++YYCURSOR; -#line 159 "cpp.re" - { BOOST_WAVE_RET(T_RIGHTPAREN); } -#line 572 "cpp_re.inc" -yy51: - ++YYCURSOR; -#line 160 "cpp.re" - { BOOST_WAVE_RET(T_SEMICOLON); } -#line 577 "cpp_re.inc" -yy53: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '+') goto yy268; - if (yych == '=') goto yy270; -#line 185 "cpp.re" - { BOOST_WAVE_RET(T_PLUS); } -#line 584 "cpp_re.inc" -yy55: - ++YYCURSOR; - if ((yych = *YYCURSOR) <= '<') { - if (yych == '-') goto yy262; - } else { - if (yych <= '=') goto yy264; - if (yych <= '>') goto yy260; - } -#line 186 "cpp.re" - { BOOST_WAVE_RET(T_MINUS); } -#line 595 "cpp_re.inc" -yy57: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '=') goto yy258; -#line 187 "cpp.re" - { BOOST_WAVE_RET(T_STAR); } -#line 601 "cpp_re.inc" -yy59: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '=') goto yy256; -#line 190 "cpp.re" - { BOOST_WAVE_RET(T_XOR); } -#line 607 "cpp_re.inc" -yy61: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'o') goto yy249; - goto yy109; -yy62: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '&') goto yy245; - if (yych == '=') goto yy247; -#line 193 "cpp.re" - { BOOST_WAVE_RET(T_AND); } -#line 619 "cpp_re.inc" -yy64: - yyaccept = 4; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '>') { - if (yych == '=') goto yy240; - } else { - if (yych <= '?') goto yy237; - if (yych == '|') goto yy238; - } -yy65: -#line 195 "cpp.re" - { BOOST_WAVE_RET(T_OR); } -#line 632 "cpp_re.inc" -yy66: - ++YYCURSOR; -#line 198 "cpp.re" - { BOOST_WAVE_RET(T_COMPL); } -#line 637 "cpp_re.inc" -yy68: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '=') goto yy235; -#line 201 "cpp.re" - { BOOST_WAVE_RET(T_NOT); } -#line 643 "cpp_re.inc" -yy70: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '=') goto yy233; -#line 203 "cpp.re" - { BOOST_WAVE_RET(T_ASSIGN); } -#line 649 "cpp_re.inc" -yy72: - ++YYCURSOR; - if ((yych = *YYCURSOR) <= '<') goto yy73; - if (yych <= '=') goto yy227; - if (yych <= '>') goto yy229; -yy73: -#line 205 "cpp.re" - { BOOST_WAVE_RET(T_GREATER); } -#line 658 "cpp_re.inc" -yy74: - ++YYCURSOR; -#line 237 "cpp.re" - { BOOST_WAVE_RET(T_COMMA); } -#line 663 "cpp_re.inc" -yy76: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '?') { - if (yych <= '&') { - if (yych <= '"') { - if (yych <= '!') goto yy9; - goto yy137; - } else { - if (yych == '$') goto yy108; - goto yy9; - } - } else { - if (yych <= '/') { - if (yych <= '\'') goto yy226; - goto yy9; - } else { - if (yych <= '9') goto yy108; - if (yych <= '>') goto yy9; - goto yy111; - } - } - } else { - if (yych <= '[') { - if (yych <= 'Q') { - if (yych <= '@') goto yy9; - goto yy108; - } else { - if (yych <= 'R') goto yy225; - if (yych <= 'Z') goto yy108; - goto yy9; - } - } else { - if (yych <= '_') { - if (yych <= '\\') goto yy110; - if (yych <= '^') goto yy9; - goto yy108; - } else { - if (yych <= '`') goto yy9; - if (yych <= 'z') goto yy108; - goto yy9; - } - } - } -yy77: - yyaccept = 5; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '\f') { - if (yych == '\t') goto yy182; - if (yych >= '\v') goto yy182; - } else { - if (yych <= 0x1F) goto yy78; - if (yych != '\'') goto yy182; - } -yy78: -#line 339 "cpp.re" - { BOOST_WAVE_RET(TOKEN_FROM_ID(*s->tok, UnknownTokenType)); } -#line 721 "cpp_re.inc" -yy79: - yyaccept = 5; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '\n') { - if (yych == '\t') goto yy138; - goto yy78; - } else { - if (yych <= '\f') goto yy138; - if (yych <= 0x1F) goto yy78; - goto yy138; - } -yy80: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '"') goto yy135; - goto yy109; -yy81: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '&') { - if (yych == '"') goto yy129; - goto yy109; - } else { - if (yych <= '\'') goto yy131; - if (yych == 'R') goto yy128; - goto yy109; - } -yy82: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - goto yy109; -yy83: - yyaccept = 5; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'U') goto yy100; - if (yych == 'u') goto yy98; - goto yy78; -yy84: - ++YYCURSOR; - yych = *YYCURSOR; - goto yy97; -yy85: -#line 319 "cpp.re" - { BOOST_WAVE_RET(T_SPACE); } -#line 766 "cpp_re.inc" -yy86: - yych = *++YYCURSOR; - goto yy97; -yy87: - ++YYCURSOR; -yy88: -#line 322 "cpp.re" - { - s->line++; - cursor.column = 1; - BOOST_WAVE_RET(T_NEWLINE); - } -#line 779 "cpp_re.inc" -yy89: - yych = *++YYCURSOR; - if (yych == '\n') goto yy95; - goto yy88; -yy90: - ++YYCURSOR; -#line 329 "cpp.re" - { - if (s->eof && cursor != s->eof) - { - BOOST_WAVE_UPDATE_CURSOR(); // adjust the input cursor - (*s->error_proc)(s, lexing_exception::generic_lexing_error, - "invalid character '\\000' in input stream"); - } - BOOST_WAVE_RET(T_EOF); - } -#line 796 "cpp_re.inc" -yy92: - yych = *++YYCURSOR; - goto yy78; -yy93: - ++YYCURSOR; -#line 342 "cpp.re" - { - // flag the error - BOOST_WAVE_UPDATE_CURSOR(); // adjust the input cursor - (*s->error_proc)(s, lexing_exception::generic_lexing_error, - "invalid character '\\%03o' in input stream", *--YYCURSOR); - } -#line 809 "cpp_re.inc" -yy95: - yych = *++YYCURSOR; - goto yy88; -yy96: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; -yy97: - if (yybm[256+yych] & 16) { - goto yy96; - } - goto yy85; -yy98: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy125; - } else { - if (yych <= 'F') goto yy125; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy125; - } -yy99: - YYCURSOR = YYMARKER; - if (yyaccept <= 56) { - if (yyaccept <= 28) { - if (yyaccept <= 14) { - if (yyaccept <= 7) { - if (yyaccept <= 3) { - if (yyaccept <= 1) { - if (yyaccept <= 0) { - goto yy5; - } else { - goto yy9; - } - } else { - if (yyaccept <= 2) { - goto yy32; - } else { - goto yy46; - } - } - } else { - if (yyaccept <= 5) { - if (yyaccept <= 4) { - goto yy65; - } else { - goto yy78; - } - } else { - if (yyaccept <= 6) { - goto yy142; - } else { - goto yy192; - } - } - } - } else { - if (yyaccept <= 11) { - if (yyaccept <= 9) { - if (yyaccept <= 8) { - goto yy251; - } else { - goto yy255; - } - } else { - if (yyaccept <= 10) { - goto yy291; - } else { - goto yy306; - } - } - } else { - if (yyaccept <= 13) { - if (yyaccept <= 12) { - goto yy401; - } else { - goto yy429; - } - } else { - goto yy433; - } - } - } - } else { - if (yyaccept <= 21) { - if (yyaccept <= 18) { - if (yyaccept <= 16) { - if (yyaccept <= 15) { - goto yy437; - } else { - goto yy468; - } - } else { - if (yyaccept <= 17) { - goto yy474; - } else { - goto yy482; - } - } - } else { - if (yyaccept <= 20) { - if (yyaccept <= 19) { - goto yy490; - } else { - goto yy495; - } - } else { - goto yy500; - } - } - } else { - if (yyaccept <= 25) { - if (yyaccept <= 23) { - if (yyaccept <= 22) { - goto yy503; - } else { - goto yy513; - } - } else { - if (yyaccept <= 24) { - goto yy519; - } else { - goto yy522; - } - } - } else { - if (yyaccept <= 27) { - if (yyaccept <= 26) { - goto yy529; - } else { - goto yy536; - } - } else { - goto yy538; - } - } - } - } - } else { - if (yyaccept <= 42) { - if (yyaccept <= 35) { - if (yyaccept <= 32) { - if (yyaccept <= 30) { - if (yyaccept <= 29) { - goto yy540; - } else { - goto yy542; - } - } else { - if (yyaccept <= 31) { - goto yy548; - } else { - goto yy554; - } - } - } else { - if (yyaccept <= 34) { - if (yyaccept <= 33) { - goto yy564; - } else { - goto yy566; - } - } else { - goto yy572; - } - } - } else { - if (yyaccept <= 39) { - if (yyaccept <= 37) { - if (yyaccept <= 36) { - goto yy579; - } else { - goto yy587; - } - } else { - if (yyaccept <= 38) { - goto yy590; - } else { - goto yy603; - } - } - } else { - if (yyaccept <= 41) { - if (yyaccept <= 40) { - goto yy605; - } else { - goto yy608; - } - } else { - goto yy611; - } - } - } - } else { - if (yyaccept <= 49) { - if (yyaccept <= 46) { - if (yyaccept <= 44) { - if (yyaccept <= 43) { - goto yy613; - } else { - goto yy619; - } - } else { - if (yyaccept <= 45) { - goto yy628; - } else { - goto yy630; - } - } - } else { - if (yyaccept <= 48) { - if (yyaccept <= 47) { - goto yy637; - } else { - goto yy646; - } - } else { - goto yy652; - } - } - } else { - if (yyaccept <= 53) { - if (yyaccept <= 51) { - if (yyaccept <= 50) { - goto yy656; - } else { - goto yy663; - } - } else { - if (yyaccept <= 52) { - goto yy669; - } else { - goto yy675; - } - } - } else { - if (yyaccept <= 55) { - if (yyaccept <= 54) { - goto yy679; - } else { - goto yy683; - } - } else { - goto yy691; - } - } - } - } - } - } else { - if (yyaccept <= 85) { - if (yyaccept <= 71) { - if (yyaccept <= 64) { - if (yyaccept <= 60) { - if (yyaccept <= 58) { - if (yyaccept <= 57) { - goto yy705; - } else { - goto yy711; - } - } else { - if (yyaccept <= 59) { - goto yy718; - } else { - goto yy727; - } - } - } else { - if (yyaccept <= 62) { - if (yyaccept <= 61) { - goto yy732; - } else { - goto yy735; - } - } else { - if (yyaccept <= 63) { - goto yy739; - } else { - goto yy746; - } - } - } - } else { - if (yyaccept <= 68) { - if (yyaccept <= 66) { - if (yyaccept <= 65) { - goto yy756; - } else { - goto yy759; - } - } else { - if (yyaccept <= 67) { - goto yy763; - } else { - goto yy769; - } - } - } else { - if (yyaccept <= 70) { - if (yyaccept <= 69) { - goto yy771; - } else { - goto yy779; - } - } else { - goto yy786; - } - } - } - } else { - if (yyaccept <= 78) { - if (yyaccept <= 75) { - if (yyaccept <= 73) { - if (yyaccept <= 72) { - goto yy790; - } else { - goto yy792; - } - } else { - if (yyaccept <= 74) { - goto yy797; - } else { - goto yy801; - } - } - } else { - if (yyaccept <= 77) { - if (yyaccept <= 76) { - goto yy806; - } else { - goto yy810; - } - } else { - goto yy819; - } - } - } else { - if (yyaccept <= 82) { - if (yyaccept <= 80) { - if (yyaccept <= 79) { - goto yy821; - } else { - goto yy825; - } - } else { - if (yyaccept <= 81) { - goto yy829; - } else { - goto yy838; - } - } - } else { - if (yyaccept <= 84) { - if (yyaccept <= 83) { - goto yy843; - } else { - goto yy848; - } - } else { - goto yy851; - } - } - } - } - } else { - if (yyaccept <= 99) { - if (yyaccept <= 92) { - if (yyaccept <= 89) { - if (yyaccept <= 87) { - if (yyaccept <= 86) { - goto yy854; - } else { - goto yy857; - } - } else { - if (yyaccept <= 88) { - goto yy869; - } else { - goto yy874; - } - } - } else { - if (yyaccept <= 91) { - if (yyaccept <= 90) { - goto yy881; - } else { - goto yy886; - } - } else { - goto yy892; - } - } - } else { - if (yyaccept <= 96) { - if (yyaccept <= 94) { - if (yyaccept <= 93) { - goto yy901; - } else { - goto yy908; - } - } else { - if (yyaccept <= 95) { - goto yy910; - } else { - goto yy916; - } - } - } else { - if (yyaccept <= 98) { - if (yyaccept <= 97) { - goto yy921; - } else { - goto yy925; - } - } else { - goto yy928; - } - } - } - } else { - if (yyaccept <= 106) { - if (yyaccept <= 103) { - if (yyaccept <= 101) { - if (yyaccept <= 100) { - goto yy934; - } else { - goto yy938; - } - } else { - if (yyaccept <= 102) { - goto yy943; - } else { - goto yy945; - } - } - } else { - if (yyaccept <= 105) { - if (yyaccept <= 104) { - goto yy952; - } else { - goto yy955; - } - } else { - goto yy960; - } - } - } else { - if (yyaccept <= 110) { - if (yyaccept <= 108) { - if (yyaccept <= 107) { - goto yy963; - } else { - goto yy970; - } - } else { - if (yyaccept <= 109) { - goto yy972; - } else { - goto yy974; - } - } - } else { - if (yyaccept <= 112) { - if (yyaccept <= 111) { - goto yy978; - } else { - goto yy985; - } - } else { - goto yy987; - } - } - } - } - } - } -yy100: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy101; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy101: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy102; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy102: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy103; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy103: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy104; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy104: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy105; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy105: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy106; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy106: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy107; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy107: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy108; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy108: - yyaccept = 1; - YYMARKER = ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; -yy109: - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych != '\\') goto yy9; -yy110: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych == 'U') goto yy114; - if (yych == 'u') goto yy113; - goto yy99; -yy111: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych != '?') goto yy99; - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych == '/') goto yy110; - goto yy99; -yy113: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy122; - goto yy99; - } else { - if (yych <= 'F') goto yy122; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy122; - goto yy99; - } -yy114: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy115; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy115: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy116; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy116: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy117; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy117: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy118; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy118: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy119; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy119: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy120; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy120: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy121; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy121: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy108; - goto yy99; - } else { - if (yych <= 'F') goto yy108; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy108; - goto yy99; - } -yy122: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy123; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy123: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy124; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy124: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy108; - goto yy99; - } else { - if (yych <= 'F') goto yy108; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy108; - goto yy99; - } -yy125: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy126; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy126: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy127; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy127: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy108; - goto yy99; - } else { - if (yych <= 'F') goto yy108; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy108; - goto yy99; - } -yy128: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '"') goto yy133; - goto yy109; -yy129: - ++YYCURSOR; -#line 274 "cpp.re" - { - if (s->act_in_cpp0x_mode) - goto extstringlit; - --YYCURSOR; - BOOST_WAVE_RET(T_IDENTIFIER); - } -#line 1591 "cpp_re.inc" -yy131: - ++YYCURSOR; -#line 266 "cpp.re" - { - if (s->act_in_cpp0x_mode) - goto extcharlit; - --YYCURSOR; - BOOST_WAVE_RET(T_IDENTIFIER); - } -#line 1601 "cpp_re.inc" -yy133: - ++YYCURSOR; -#line 282 "cpp.re" - { - if (s->act_in_cpp0x_mode) - goto extrawstringlit; - --YYCURSOR; - BOOST_WAVE_RET(T_IDENTIFIER); - } -#line 1611 "cpp_re.inc" -yy135: - ++YYCURSOR; -#line 258 "cpp.re" - { - if (s->act_in_cpp0x_mode) - goto extrawstringlit; - --YYCURSOR; - BOOST_WAVE_RET(T_IDENTIFIER); - } -#line 1621 "cpp_re.inc" -yy137: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; -yy138: - if (yybm[256+yych] & 64) { - goto yy137; - } - if (yych <= '!') goto yy99; - if (yych <= '"') goto yy141; - if (yych >= '\\') goto yy140; -yy139: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[256+yych] & 64) { - goto yy137; - } - if (yych <= '!') goto yy99; - if (yych <= '"') goto yy141; - if (yych <= '[') goto yy152; -yy140: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '`') { - if (yych <= '7') { - if (yych <= '&') { - if (yych == '"') goto yy137; - goto yy99; - } else { - if (yych <= '\'') goto yy137; - if (yych <= '/') goto yy99; - goto yy147; - } - } else { - if (yych <= 'T') { - if (yych == '?') goto yy145; - goto yy99; - } else { - if (yych <= 'U') goto yy144; - if (yych == '\\') goto yy137; - goto yy99; - } - } - } else { - if (yych <= 'r') { - if (yych <= 'f') { - if (yych <= 'b') goto yy137; - if (yych <= 'e') goto yy99; - goto yy137; - } else { - if (yych == 'n') goto yy137; - if (yych <= 'q') goto yy99; - goto yy137; - } - } else { - if (yych <= 'u') { - if (yych <= 's') goto yy99; - if (yych <= 't') goto yy137; - goto yy143; - } else { - if (yych <= 'v') goto yy137; - if (yych == 'x') goto yy146; - goto yy99; - } - } - } -yy141: - ++YYCURSOR; -yy142: -#line 255 "cpp.re" - { BOOST_WAVE_RET(T_STRINGLIT); } -#line 1695 "cpp_re.inc" -yy143: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy178; - goto yy99; - } else { - if (yych <= 'F') goto yy178; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy178; - goto yy99; - } -yy144: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy171; - goto yy99; - } else { - if (yych <= 'F') goto yy171; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy171; - goto yy99; - } -yy145: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[256+yych] & 64) { - goto yy137; - } - if (yych <= '!') goto yy99; - if (yych <= '"') goto yy141; - if (yych <= '[') goto yy151; - goto yy140; -yy146: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[256+yych] & 128) { - goto yy149; - } - goto yy99; -yy147: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '"') { - if (yych <= '\n') { - if (yych == '\t') goto yy137; - goto yy99; - } else { - if (yych <= '\f') goto yy137; - if (yych <= 0x1F) goto yy99; - if (yych <= '!') goto yy137; - goto yy141; - } - } else { - if (yych <= '>') { - if (yych <= '/') goto yy137; - if (yych >= '8') goto yy137; - } else { - if (yych <= '?') goto yy139; - if (yych == '\\') goto yy140; - goto yy137; - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[256+yych] & 64) { - goto yy137; - } - if (yych <= '!') goto yy99; - if (yych <= '"') goto yy141; - if (yych <= '[') goto yy139; - goto yy140; -yy149: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[256+yych] & 128) { - goto yy149; - } - if (yych <= '!') { - if (yych <= '\n') { - if (yych == '\t') goto yy137; - goto yy99; - } else { - if (yych <= '\f') goto yy137; - if (yych <= 0x1F) goto yy99; - goto yy137; - } - } else { - if (yych <= '?') { - if (yych <= '"') goto yy141; - if (yych <= '>') goto yy137; - goto yy139; - } else { - if (yych == '\\') goto yy140; - goto yy137; - } - } -yy151: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[256+yych] & 64) { - goto yy137; - } - if (yych <= '!') goto yy99; - if (yych <= '"') goto yy141; - if (yych >= '\\') goto yy140; -yy152: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 1) { - goto yy152; - } - if (yych <= '!') { - if (yych <= '\n') { - if (yych == '\t') goto yy137; - goto yy99; - } else { - if (yych <= '\f') goto yy137; - if (yych <= 0x1F) goto yy99; - goto yy137; - } - } else { - if (yych <= '/') { - if (yych <= '"') goto yy141; - if (yych <= '.') goto yy137; - } else { - if (yych == '\\') goto yy140; - goto yy137; - } - } -yy154: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 2) { - goto yy154; - } - if (yych <= '7') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy158; - if (yych <= '/') goto yy137; - goto yy147; - } - } - } else { - if (yych <= 'U') { - if (yych == '?') goto yy159; - if (yych <= 'T') goto yy137; - goto yy157; - } else { - if (yych <= 'u') { - if (yych <= 't') goto yy137; - } else { - if (yych == 'x') goto yy149; - goto yy137; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy141; - if (yych <= '/') goto yy137; - goto yy168; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy139; - if (yych <= '@') goto yy137; - goto yy168; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy137; - goto yy140; - } else { - if (yych <= '`') goto yy137; - if (yych <= 'f') goto yy168; - goto yy137; - } - } - } -yy157: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy141; - if (yych <= '/') goto yy137; - goto yy161; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy139; - if (yych <= '@') goto yy137; - goto yy161; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy137; - goto yy140; - } else { - if (yych <= '`') goto yy137; - if (yych <= 'f') goto yy161; - goto yy137; - } - } - } -yy158: - yyaccept = 6; - YYMARKER = ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[256+yych] & 64) { - goto yy137; - } - if (yych <= '!') goto yy142; - if (yych <= '"') goto yy141; - if (yych <= '[') goto yy139; - goto yy140; -yy159: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[256+yych] & 64) { - goto yy137; - } - if (yych <= '!') goto yy99; - if (yych <= '"') goto yy141; - if (yych >= '\\') goto yy140; - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 1) { - goto yy152; - } - if (yych <= '!') { - if (yych <= '\n') { - if (yych == '\t') goto yy137; - goto yy99; - } else { - if (yych <= '\f') goto yy137; - if (yych <= 0x1F) goto yy99; - goto yy137; - } - } else { - if (yych <= '/') { - if (yych <= '"') goto yy141; - if (yych <= '.') goto yy137; - goto yy154; - } else { - if (yych == '\\') goto yy140; - goto yy137; - } - } -yy161: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy141; - if (yych <= '/') goto yy137; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy139; - if (yych <= '@') goto yy137; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy137; - goto yy140; - } else { - if (yych <= '`') goto yy137; - if (yych >= 'g') goto yy137; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy141; - if (yych <= '/') goto yy137; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy139; - if (yych <= '@') goto yy137; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy137; - goto yy140; - } else { - if (yych <= '`') goto yy137; - if (yych >= 'g') goto yy137; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy141; - if (yych <= '/') goto yy137; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy139; - if (yych <= '@') goto yy137; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy137; - goto yy140; - } else { - if (yych <= '`') goto yy137; - if (yych >= 'g') goto yy137; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy141; - if (yych <= '/') goto yy137; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy139; - if (yych <= '@') goto yy137; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy137; - goto yy140; - } else { - if (yych <= '`') goto yy137; - if (yych >= 'g') goto yy137; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy141; - if (yych <= '/') goto yy137; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy139; - if (yych <= '@') goto yy137; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy137; - goto yy140; - } else { - if (yych <= '`') goto yy137; - if (yych >= 'g') goto yy137; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy141; - if (yych <= '/') goto yy137; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy139; - if (yych <= '@') goto yy137; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy137; - goto yy140; - } else { - if (yych <= '`') goto yy137; - if (yych >= 'g') goto yy137; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[256+yych] & 64) { - goto yy137; - } - if (yych <= '!') goto yy99; - if (yych <= '"') goto yy141; - if (yych <= '[') goto yy139; - goto yy140; -yy168: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy141; - if (yych <= '/') goto yy137; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy139; - if (yych <= '@') goto yy137; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy137; - goto yy140; - } else { - if (yych <= '`') goto yy137; - if (yych >= 'g') goto yy137; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy137; - if (yych <= '\n') goto yy99; - goto yy137; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy99; - goto yy137; - } else { - if (yych <= '"') goto yy141; - if (yych <= '/') goto yy137; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy139; - if (yych <= '@') goto yy137; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy137; - goto yy140; - } else { - if (yych <= '`') goto yy137; - if (yych >= 'g') goto yy137; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[256+yych] & 64) { - goto yy137; - } - if (yych <= '!') goto yy99; - if (yych <= '"') goto yy141; - if (yych <= '[') goto yy139; - goto yy140; -yy171: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy172; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy172: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy173; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy173: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy174; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy174: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy175; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy175: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy176; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy176: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy177; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy177: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy137; - goto yy99; - } else { - if (yych <= 'F') goto yy137; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy137; - goto yy99; - } -yy178: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy179; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy179: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy180; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy180: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy137; - goto yy99; - } else { - if (yych <= 'F') goto yy137; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy137; - goto yy99; - } -yy181: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; -yy182: - if (yybm[0+yych] & 4) { - goto yy181; - } - if (yych <= '&') goto yy99; - if (yych <= '\'') goto yy191; - if (yych >= '\\') goto yy184; -yy183: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 4) { - goto yy181; - } - if (yych <= '&') goto yy99; - if (yych <= '\'') goto yy191; - if (yych <= '[') goto yy196; -yy184: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '`') { - if (yych <= '7') { - if (yych <= '&') { - if (yych == '"') goto yy181; - goto yy99; - } else { - if (yych <= '\'') goto yy181; - if (yych <= '/') goto yy99; - goto yy189; - } - } else { - if (yych <= 'T') { - if (yych == '?') goto yy187; - goto yy99; - } else { - if (yych <= 'U') goto yy186; - if (yych == '\\') goto yy181; - goto yy99; - } - } - } else { - if (yych <= 'r') { - if (yych <= 'f') { - if (yych <= 'b') goto yy181; - if (yych <= 'e') goto yy99; - goto yy181; - } else { - if (yych == 'n') goto yy181; - if (yych <= 'q') goto yy99; - goto yy181; - } - } else { - if (yych <= 'u') { - if (yych <= 's') goto yy99; - if (yych <= 't') goto yy181; - } else { - if (yych <= 'v') goto yy181; - if (yych == 'x') goto yy188; - goto yy99; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy222; - goto yy99; - } else { - if (yych <= 'F') goto yy222; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy222; - goto yy99; - } -yy186: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy215; - goto yy99; - } else { - if (yych <= 'F') goto yy215; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy215; - goto yy99; - } -yy187: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 4) { - goto yy181; - } - if (yych <= '&') goto yy99; - if (yych <= '\'') goto yy191; - if (yych <= '[') goto yy195; - goto yy184; -yy188: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy193; - goto yy99; - } else { - if (yych <= 'F') goto yy193; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy193; - goto yy99; - } -yy189: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '\'') { - if (yych <= '\n') { - if (yych == '\t') goto yy181; - goto yy99; - } else { - if (yych <= '\f') goto yy181; - if (yych <= 0x1F) goto yy99; - if (yych <= '&') goto yy181; - goto yy191; - } - } else { - if (yych <= '>') { - if (yych <= '/') goto yy181; - if (yych >= '8') goto yy181; - } else { - if (yych <= '?') goto yy183; - if (yych == '\\') goto yy184; - goto yy181; - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 4) { - goto yy181; - } - if (yych <= '&') goto yy99; - if (yych <= '\'') goto yy191; - if (yych <= '[') goto yy183; - goto yy184; -yy191: - ++YYCURSOR; -yy192: -#line 252 "cpp.re" - { BOOST_WAVE_RET(T_CHARLIT); } -#line 2542 "cpp_re.inc" -yy193: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - goto yy193; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - goto yy193; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych <= 'f') goto yy193; - goto yy181; - } - } - } -yy195: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 4) { - goto yy181; - } - if (yych <= '&') goto yy99; - if (yych <= '\'') goto yy191; - if (yych >= '\\') goto yy184; -yy196: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '\'') { - if (yych <= '\n') { - if (yych == '\t') goto yy181; - goto yy99; - } else { - if (yych <= '\f') goto yy181; - if (yych <= 0x1F) goto yy99; - if (yych <= '&') goto yy181; - goto yy191; - } - } else { - if (yych <= '>') { - if (yych != '/') goto yy181; - } else { - if (yych <= '?') goto yy196; - if (yych == '\\') goto yy184; - goto yy181; - } - } -yy198: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '>') { - if (yych <= 0x1F) { - if (yych <= '\t') { - if (yych <= 0x08) goto yy99; - goto yy181; - } else { - if (yych <= '\n') goto yy99; - if (yych <= '\f') goto yy181; - goto yy99; - } - } else { - if (yych <= '\'') { - if (yych <= '&') goto yy181; - goto yy202; - } else { - if (yych <= '/') goto yy181; - if (yych <= '7') goto yy189; - goto yy181; - } - } - } else { - if (yych <= '\\') { - if (yych <= 'T') { - if (yych <= '?') goto yy203; - goto yy181; - } else { - if (yych <= 'U') goto yy201; - if (yych <= '[') goto yy181; - goto yy198; - } - } else { - if (yych <= 'u') { - if (yych <= 't') goto yy181; - } else { - if (yych == 'x') goto yy193; - goto yy181; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - goto yy212; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - goto yy212; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych <= 'f') goto yy212; - goto yy181; - } - } - } -yy201: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - goto yy205; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - goto yy205; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych <= 'f') goto yy205; - goto yy181; - } - } - } -yy202: - yyaccept = 7; - YYMARKER = ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 4) { - goto yy181; - } - if (yych <= '&') goto yy192; - if (yych <= '\'') goto yy191; - if (yych <= '[') goto yy183; - goto yy184; -yy203: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 4) { - goto yy181; - } - if (yych <= '&') goto yy99; - if (yych <= '\'') goto yy191; - if (yych >= '\\') goto yy184; - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '\'') { - if (yych <= '\n') { - if (yych == '\t') goto yy181; - goto yy99; - } else { - if (yych <= '\f') goto yy181; - if (yych <= 0x1F) goto yy99; - if (yych <= '&') goto yy181; - goto yy191; - } - } else { - if (yych <= '>') { - if (yych == '/') goto yy198; - goto yy181; - } else { - if (yych <= '?') goto yy196; - if (yych == '\\') goto yy184; - goto yy181; - } - } -yy205: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych >= 'g') goto yy181; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych >= 'g') goto yy181; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych >= 'g') goto yy181; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych >= 'g') goto yy181; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych >= 'g') goto yy181; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych >= 'g') goto yy181; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 4) { - goto yy181; - } - if (yych <= '&') goto yy99; - if (yych <= '\'') goto yy191; - if (yych <= '[') goto yy183; - goto yy184; -yy212: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych >= 'g') goto yy181; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy181; - if (yych <= '\n') goto yy99; - goto yy181; - } else { - if (yych <= '&') { - if (yych <= 0x1F) goto yy99; - goto yy181; - } else { - if (yych <= '\'') goto yy191; - if (yych <= '/') goto yy181; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy183; - if (yych <= '@') goto yy181; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy181; - goto yy184; - } else { - if (yych <= '`') goto yy181; - if (yych >= 'g') goto yy181; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 4) { - goto yy181; - } - if (yych <= '&') goto yy99; - if (yych <= '\'') goto yy191; - if (yych <= '[') goto yy183; - goto yy184; -yy215: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy216; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy216: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy217; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy217: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy218; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy218: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy219; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy219: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy220; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy220: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy221; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy221: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy181; - goto yy99; - } else { - if (yych <= 'F') goto yy181; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy181; - goto yy99; - } -yy222: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy223; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy223: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych >= ':') goto yy99; - } else { - if (yych <= 'F') goto yy224; - if (yych <= '`') goto yy99; - if (yych >= 'g') goto yy99; - } -yy224: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy99; - if (yych <= '9') goto yy181; - goto yy99; - } else { - if (yych <= 'F') goto yy181; - if (yych <= '`') goto yy99; - if (yych <= 'f') goto yy181; - goto yy99; - } -yy225: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '"') goto yy135; - goto yy109; -yy226: - yych = *++YYCURSOR; - if (yych == '\'') goto yy99; - goto yy182; -yy227: - ++YYCURSOR; -#line 227 "cpp.re" - { BOOST_WAVE_RET(T_GREATEREQUAL); } -#line 3175 "cpp_re.inc" -yy229: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '=') goto yy231; -#line 220 "cpp.re" - { BOOST_WAVE_RET(T_SHIFTRIGHT); } -#line 3181 "cpp_re.inc" -yy231: - ++YYCURSOR; -#line 221 "cpp.re" - { BOOST_WAVE_RET(T_SHIFTRIGHTASSIGN); } -#line 3186 "cpp_re.inc" -yy233: - ++YYCURSOR; -#line 223 "cpp.re" - { BOOST_WAVE_RET(T_EQUAL); } -#line 3191 "cpp_re.inc" -yy235: - ++YYCURSOR; -#line 224 "cpp.re" - { BOOST_WAVE_RET(T_NOTEQUAL); } -#line 3196 "cpp_re.inc" -yy237: - yych = *++YYCURSOR; - if (yych == '?') goto yy242; - goto yy99; -yy238: - ++YYCURSOR; -#line 230 "cpp.re" - { BOOST_WAVE_RET(T_OROR); } -#line 3205 "cpp_re.inc" -yy240: - ++YYCURSOR; -#line 216 "cpp.re" - { BOOST_WAVE_RET(T_ORASSIGN); } -#line 3210 "cpp_re.inc" -yy242: - yych = *++YYCURSOR; - if (yych != '!') goto yy99; - ++YYCURSOR; -#line 232 "cpp.re" - { BOOST_WAVE_RET(T_OROR_TRIGRAPH); } -#line 3217 "cpp_re.inc" -yy245: - ++YYCURSOR; -#line 228 "cpp.re" - { BOOST_WAVE_RET(T_ANDAND); } -#line 3222 "cpp_re.inc" -yy247: - ++YYCURSOR; -#line 214 "cpp.re" - { BOOST_WAVE_RET(T_ANDASSIGN); } -#line 3227 "cpp_re.inc" -yy249: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 8; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '@') { - if (yych <= '/') { - if (yych == '$') goto yy108; - } else { - if (yych <= '9') goto yy108; - if (yych == '?') goto yy111; - } - } else { - if (yych <= '^') { - if (yych <= 'Z') goto yy108; - if (yych == '\\') goto yy110; - } else { - if (yych <= '_') goto yy252; - if (yych <= '`') goto yy251; - if (yych <= 'z') goto yy108; - } - } -yy251: -#line 192 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_XOR_ALT); } -#line 3254 "cpp_re.inc" -yy252: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'q') goto yy109; - yyaccept = 9; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy255: -#line 212 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_XORASSIGN_ALT); } -#line 3272 "cpp_re.inc" -yy256: - ++YYCURSOR; -#line 211 "cpp.re" - { BOOST_WAVE_RET(T_XORASSIGN); } -#line 3277 "cpp_re.inc" -yy258: - ++YYCURSOR; -#line 208 "cpp.re" - { BOOST_WAVE_RET(T_STARASSIGN); } -#line 3282 "cpp_re.inc" -yy260: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '*') goto yy266; -#line 248 "cpp.re" - { BOOST_WAVE_RET(T_ARROW); } -#line 3288 "cpp_re.inc" -yy262: - ++YYCURSOR; -#line 236 "cpp.re" - { BOOST_WAVE_RET(T_MINUSMINUS); } -#line 3293 "cpp_re.inc" -yy264: - ++YYCURSOR; -#line 207 "cpp.re" - { BOOST_WAVE_RET(T_MINUSASSIGN); } -#line 3298 "cpp_re.inc" -yy266: - ++YYCURSOR; -#line 239 "cpp.re" - { - if (s->act_in_c99_mode) { - --YYCURSOR; - BOOST_WAVE_RET(T_ARROW); - } - else { - BOOST_WAVE_RET(T_ARROWSTAR); - } - } -#line 3311 "cpp_re.inc" -yy268: - ++YYCURSOR; -#line 235 "cpp.re" - { BOOST_WAVE_RET(T_PLUSPLUS); } -#line 3316 "cpp_re.inc" -yy270: - ++YYCURSOR; -#line 206 "cpp.re" - { BOOST_WAVE_RET(T_PLUSASSIGN); } -#line 3321 "cpp_re.inc" -yy272: - ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 12) YYFILL(12); - yych = *YYCURSOR; -yy273: - if (yych <= 'h') { - if (yych <= ' ') { - if (yych <= '\n') { - if (yych == '\t') goto yy272; - goto yy99; - } else { - if (yych <= '\f') goto yy272; - if (yych <= 0x1F) goto yy99; - goto yy272; - } - } else { - if (yych <= 'c') { - if (yych != '/') goto yy99; - } else { - if (yych <= 'd') goto yy281; - if (yych <= 'e') goto yy275; - goto yy99; - } - } - } else { - if (yych <= 'q') { - if (yych <= 'l') { - if (yych <= 'i') goto yy282; - if (yych <= 'k') goto yy99; - goto yy279; - } else { - if (yych == 'p') goto yy278; - goto yy99; - } - } else { - if (yych <= 'u') { - if (yych <= 'r') goto yy276; - if (yych <= 't') goto yy99; - goto yy280; - } else { - if (yych == 'w') goto yy277; - goto yy99; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych == '*') goto yy389; - goto yy99; -yy275: - yych = *++YYCURSOR; - if (yych <= 'm') { - if (yych == 'l') goto yy365; - goto yy99; - } else { - if (yych <= 'n') goto yy366; - if (yych == 'r') goto yy367; - goto yy99; - } -yy276: - yych = *++YYCURSOR; - if (yych == 'e') goto yy359; - goto yy99; -yy277: - yych = *++YYCURSOR; - if (yych == 'a') goto yy352; - goto yy99; -yy278: - yych = *++YYCURSOR; - if (yych == 'r') goto yy346; - goto yy99; -yy279: - yych = *++YYCURSOR; - if (yych == 'i') goto yy342; - goto yy99; -yy280: - yych = *++YYCURSOR; - if (yych == 'n') goto yy337; - goto yy99; -yy281: - yych = *++YYCURSOR; - if (yych == 'e') goto yy331; - goto yy99; -yy282: - yych = *++YYCURSOR; - if (yych == 'f') goto yy290; - if (yych == 'n') goto yy289; - goto yy99; -yy283: - yych = *++YYCURSOR; - if (yych == '?') goto yy286; - goto yy99; -yy284: - ++YYCURSOR; -#line 153 "cpp.re" - { BOOST_WAVE_RET(T_POUND_POUND); } -#line 3419 "cpp_re.inc" -yy286: - yych = *++YYCURSOR; - if (yych != '=') goto yy99; - ++YYCURSOR; -#line 154 "cpp.re" - { BOOST_WAVE_RET(T_POUND_POUND_TRIGRAPH); } -#line 3426 "cpp_re.inc" -yy289: - yych = *++YYCURSOR; - if (yych == 'c') goto yy301; - goto yy99; -yy290: - yyaccept = 10; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'd') goto yy293; - if (yych == 'n') goto yy292; -yy291: -#line 301 "cpp.re" - { BOOST_WAVE_RET(T_PP_IF); } -#line 3439 "cpp_re.inc" -yy292: - yych = *++YYCURSOR; - if (yych == 'd') goto yy297; - goto yy99; -yy293: - yych = *++YYCURSOR; - if (yych != 'e') goto yy99; - yych = *++YYCURSOR; - if (yych != 'f') goto yy99; - ++YYCURSOR; -#line 302 "cpp.re" - { BOOST_WAVE_RET(T_PP_IFDEF); } -#line 3452 "cpp_re.inc" -yy297: - yych = *++YYCURSOR; - if (yych != 'e') goto yy99; - yych = *++YYCURSOR; - if (yych != 'f') goto yy99; - ++YYCURSOR; -#line 303 "cpp.re" - { BOOST_WAVE_RET(T_PP_IFNDEF); } -#line 3461 "cpp_re.inc" -yy301: - yych = *++YYCURSOR; - if (yych != 'l') goto yy99; - yych = *++YYCURSOR; - if (yych != 'u') goto yy99; - yych = *++YYCURSOR; - if (yych != 'd') goto yy99; - yych = *++YYCURSOR; - if (yych != 'e') goto yy99; - yyaccept = 11; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '_') goto yy307; - goto yy309; -yy306: -#line 299 "cpp.re" - { BOOST_WAVE_RET(T_PP_INCLUDE); } -#line 3478 "cpp_re.inc" -yy307: - yych = *++YYCURSOR; - if (yych == 'n') goto yy328; - goto yy99; -yy308: - yyaccept = 11; - YYMARKER = ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); - yych = *YYCURSOR; -yy309: - if (yych <= ' ') { - if (yych <= '\n') { - if (yych == '\t') goto yy308; - goto yy306; - } else { - if (yych <= '\f') goto yy308; - if (yych <= 0x1F) goto yy306; - goto yy308; - } - } else { - if (yych <= '.') { - if (yych == '"') goto yy312; - goto yy306; - } else { - if (yych <= '/') goto yy310; - if (yych == '<') goto yy311; - goto yy306; - } - } -yy310: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych == '*') goto yy321; - goto yy99; -yy311: - yych = *++YYCURSOR; - if (yych == '>') goto yy99; - goto yy318; -yy312: - yych = *++YYCURSOR; - if (yych == '"') goto yy99; - goto yy314; -yy313: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; -yy314: - if (yybm[0+yych] & 8) { - goto yy313; - } - if (yych <= '!') goto yy99; - ++YYCURSOR; -#line 296 "cpp.re" - { BOOST_WAVE_RET(T_PP_QHEADER); } -#line 3534 "cpp_re.inc" -yy317: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; -yy318: - if (yybm[0+yych] & 16) { - goto yy317; - } - if (yych <= '=') goto yy99; - ++YYCURSOR; -#line 293 "cpp.re" - { BOOST_WAVE_RET(T_PP_HHEADER); } -#line 3547 "cpp_re.inc" -yy321: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 32) { - goto yy321; - } - if (yych == '\r') goto yy323; - if (yych <= ')') goto yy99; - goto yy325; -yy323: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 32) { - goto yy321; - } - if (yych == '\r') goto yy323; - if (yych <= ')') goto yy99; -yy325: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 128) { - goto yy325; - } - if (yych <= '\r') { - if (yych <= 0x08) goto yy99; - if (yych <= '\f') goto yy321; - } else { - if (yych <= 0x1F) goto yy99; - if (yych == '/') goto yy308; - goto yy321; - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 32) { - goto yy321; - } - if (yych == '\r') goto yy323; - if (yych <= ')') goto yy99; - goto yy325; -yy328: - yych = *++YYCURSOR; - if (yych != 'e') goto yy99; - yych = *++YYCURSOR; - if (yych != 'x') goto yy99; - yych = *++YYCURSOR; - if (yych == 't') goto yy308; - goto yy99; -yy331: - yych = *++YYCURSOR; - if (yych != 'f') goto yy99; - yych = *++YYCURSOR; - if (yych != 'i') goto yy99; - yych = *++YYCURSOR; - if (yych != 'n') goto yy99; - yych = *++YYCURSOR; - if (yych != 'e') goto yy99; - ++YYCURSOR; -#line 307 "cpp.re" - { BOOST_WAVE_RET(T_PP_DEFINE); } -#line 3611 "cpp_re.inc" -yy337: - yych = *++YYCURSOR; - if (yych != 'd') goto yy99; - yych = *++YYCURSOR; - if (yych != 'e') goto yy99; - yych = *++YYCURSOR; - if (yych != 'f') goto yy99; - ++YYCURSOR; -#line 308 "cpp.re" - { BOOST_WAVE_RET(T_PP_UNDEF); } -#line 3622 "cpp_re.inc" -yy342: - yych = *++YYCURSOR; - if (yych != 'n') goto yy99; - yych = *++YYCURSOR; - if (yych != 'e') goto yy99; - ++YYCURSOR; -#line 309 "cpp.re" - { BOOST_WAVE_RET(T_PP_LINE); } -#line 3631 "cpp_re.inc" -yy346: - yych = *++YYCURSOR; - if (yych != 'a') goto yy99; - yych = *++YYCURSOR; - if (yych != 'g') goto yy99; - yych = *++YYCURSOR; - if (yych != 'm') goto yy99; - yych = *++YYCURSOR; - if (yych != 'a') goto yy99; - ++YYCURSOR; -#line 311 "cpp.re" - { BOOST_WAVE_RET(T_PP_PRAGMA); } -#line 3644 "cpp_re.inc" -yy352: - yych = *++YYCURSOR; - if (yych != 'r') goto yy99; - yych = *++YYCURSOR; - if (yych != 'n') goto yy99; - yych = *++YYCURSOR; - if (yych != 'i') goto yy99; - yych = *++YYCURSOR; - if (yych != 'n') goto yy99; - yych = *++YYCURSOR; - if (yych != 'g') goto yy99; - ++YYCURSOR; -#line 313 "cpp.re" - { BOOST_WAVE_RET(T_PP_WARNING); } -#line 3659 "cpp_re.inc" -yy359: - yych = *++YYCURSOR; - if (yych != 'g') goto yy99; - yych = *++YYCURSOR; - if (yych != 'i') goto yy99; - yych = *++YYCURSOR; - if (yych != 'o') goto yy99; - yych = *++YYCURSOR; - if (yych != 'n') goto yy99; - ++YYCURSOR; -#line 315 "cpp.re" - { BOOST_WAVE_RET(T_MSEXT_PP_REGION); } -#line 3672 "cpp_re.inc" -yy365: - yych = *++YYCURSOR; - if (yych == 'i') goto yy383; - if (yych == 's') goto yy384; - goto yy99; -yy366: - yych = *++YYCURSOR; - if (yych == 'd') goto yy372; - goto yy99; -yy367: - yych = *++YYCURSOR; - if (yych != 'r') goto yy99; - yych = *++YYCURSOR; - if (yych != 'o') goto yy99; - yych = *++YYCURSOR; - if (yych != 'r') goto yy99; - ++YYCURSOR; -#line 310 "cpp.re" - { BOOST_WAVE_RET(T_PP_ERROR); } -#line 3692 "cpp_re.inc" -yy372: - yych = *++YYCURSOR; - if (yych == 'i') goto yy373; - if (yych == 'r') goto yy374; - goto yy99; -yy373: - yych = *++YYCURSOR; - if (yych == 'f') goto yy381; - goto yy99; -yy374: - yych = *++YYCURSOR; - if (yych != 'e') goto yy99; - yych = *++YYCURSOR; - if (yych != 'g') goto yy99; - yych = *++YYCURSOR; - if (yych != 'i') goto yy99; - yych = *++YYCURSOR; - if (yych != 'o') goto yy99; - yych = *++YYCURSOR; - if (yych != 'n') goto yy99; - ++YYCURSOR; -#line 316 "cpp.re" - { BOOST_WAVE_RET(T_MSEXT_PP_ENDREGION); } -#line 3716 "cpp_re.inc" -yy381: - ++YYCURSOR; -#line 306 "cpp.re" - { BOOST_WAVE_RET(T_PP_ENDIF); } -#line 3721 "cpp_re.inc" -yy383: - yych = *++YYCURSOR; - if (yych == 'f') goto yy387; - goto yy99; -yy384: - yych = *++YYCURSOR; - if (yych != 'e') goto yy99; - ++YYCURSOR; -#line 304 "cpp.re" - { BOOST_WAVE_RET(T_PP_ELSE); } -#line 3732 "cpp_re.inc" -yy387: - ++YYCURSOR; -#line 305 "cpp.re" - { BOOST_WAVE_RET(T_PP_ELIF); } -#line 3737 "cpp_re.inc" -yy389: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '\r') { - if (yych <= 0x08) goto yy99; - if (yych <= '\f') goto yy389; - } else { - if (yych <= 0x1F) goto yy99; - if (yych == '*') goto yy393; - goto yy389; - } -yy391: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '\r') { - if (yych <= 0x08) goto yy99; - if (yych <= '\f') goto yy389; - goto yy391; - } else { - if (yych <= 0x1F) goto yy99; - if (yych != '*') goto yy389; - } -yy393: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= 0x1F) { - if (yych <= 0x08) goto yy99; - if (yych <= '\f') goto yy389; - if (yych >= 0x0E) goto yy99; - } else { - if (yych <= '*') { - if (yych <= ')') goto yy389; - goto yy393; - } else { - if (yych == '/') goto yy272; - goto yy389; - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '\r') { - if (yych <= 0x08) goto yy99; - if (yych <= '\f') goto yy389; - goto yy391; - } else { - if (yych <= 0x1F) goto yy99; - if (yych == '*') goto yy393; - goto yy389; - } -yy396: - ++YYCURSOR; -#line 165 "cpp.re" - { - if (s->act_in_c99_mode) { - --YYCURSOR; - BOOST_WAVE_RET(T_COLON); - } - else { - BOOST_WAVE_RET(T_COLON_COLON); - } - } -#line 3803 "cpp_re.inc" -yy398: - ++YYCURSOR; -#line 149 "cpp.re" - { BOOST_WAVE_RET(T_RIGHTBRACKET_ALT); } -#line 3808 "cpp_re.inc" -yy400: - yyaccept = 12; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'e') { - if (yych <= ' ') { - if (yych <= '\n') { - if (yych == '\t') goto yy273; - } else { - if (yych <= '\f') goto yy273; - if (yych >= ' ') goto yy273; - } - } else { - if (yych <= '.') { - if (yych == '%') goto yy406; - } else { - if (yych <= '/') goto yy273; - if (yych >= 'd') goto yy273; - } - } - } else { - if (yych <= 'p') { - if (yych <= 'k') { - if (yych == 'i') goto yy273; - } else { - if (yych <= 'l') goto yy273; - if (yych >= 'p') goto yy273; - } - } else { - if (yych <= 't') { - if (yych == 'r') goto yy273; - } else { - if (yych == 'v') goto yy401; - if (yych <= 'w') goto yy273; - } - } - } -yy401: -#line 151 "cpp.re" - { BOOST_WAVE_RET(T_POUND_ALT); } -#line 3848 "cpp_re.inc" -yy402: - ++YYCURSOR; -#line 210 "cpp.re" - { BOOST_WAVE_RET(T_PERCENTASSIGN); } -#line 3853 "cpp_re.inc" -yy404: - ++YYCURSOR; -#line 143 "cpp.re" - { BOOST_WAVE_RET(T_RIGHTBRACE_ALT); } -#line 3858 "cpp_re.inc" -yy406: - yych = *++YYCURSOR; - if (yych != ':') goto yy99; - ++YYCURSOR; -#line 157 "cpp.re" - { BOOST_WAVE_RET(T_POUND_POUND_ALT); } -#line 3865 "cpp_re.inc" -yy409: - ++YYCURSOR; -#line 226 "cpp.re" - { BOOST_WAVE_RET(T_LESSEQUAL); } -#line 3870 "cpp_re.inc" -yy411: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '=') goto yy417; -#line 219 "cpp.re" - { BOOST_WAVE_RET(T_SHIFTLEFT); } -#line 3876 "cpp_re.inc" -yy413: - ++YYCURSOR; -#line 146 "cpp.re" - { BOOST_WAVE_RET(T_LEFTBRACKET_ALT); } -#line 3881 "cpp_re.inc" -yy415: - ++YYCURSOR; -#line 140 "cpp.re" - { BOOST_WAVE_RET(T_LEFTBRACE_ALT); } -#line 3886 "cpp_re.inc" -yy417: - ++YYCURSOR; -#line 222 "cpp.re" - { BOOST_WAVE_RET(T_SHIFTLEFTASSIGN); } -#line 3891 "cpp_re.inc" -yy419: - yych = *++YYCURSOR; - switch (yych) { - case '!': goto yy432; - case '\'': goto yy430; - case '(': goto yy424; - case ')': goto yy426; - case '-': goto yy434; - case '/': goto yy436; - case '<': goto yy420; - case '=': goto yy428; - case '>': goto yy422; - default: goto yy99; - } -yy420: - ++YYCURSOR; -#line 139 "cpp.re" - { BOOST_WAVE_RET(T_LEFTBRACE_TRIGRAPH); } -#line 3910 "cpp_re.inc" -yy422: - ++YYCURSOR; -#line 142 "cpp.re" - { BOOST_WAVE_RET(T_RIGHTBRACE_TRIGRAPH); } -#line 3915 "cpp_re.inc" -yy424: - ++YYCURSOR; -#line 145 "cpp.re" - { BOOST_WAVE_RET(T_LEFTBRACKET_TRIGRAPH); } -#line 3920 "cpp_re.inc" -yy426: - ++YYCURSOR; -#line 148 "cpp.re" - { BOOST_WAVE_RET(T_RIGHTBRACKET_TRIGRAPH); } -#line 3925 "cpp_re.inc" -yy428: - yyaccept = 13; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'c') { - if (yych <= ' ') { - if (yych <= '\n') { - if (yych == '\t') goto yy273; - } else { - if (yych <= '\f') goto yy273; - if (yych >= ' ') goto yy273; - } - } else { - if (yych <= '.') { - if (yych == '#') goto yy449; - } else { - if (yych <= '/') goto yy273; - if (yych == '?') goto yy448; - } - } - } else { - if (yych <= 'p') { - if (yych <= 'i') { - if (yych <= 'e') goto yy273; - if (yych >= 'i') goto yy273; - } else { - if (yych == 'l') goto yy273; - if (yych >= 'p') goto yy273; - } - } else { - if (yych <= 't') { - if (yych == 'r') goto yy273; - } else { - if (yych == 'v') goto yy429; - if (yych <= 'w') goto yy273; - } - } - } -yy429: -#line 152 "cpp.re" - { BOOST_WAVE_RET(T_POUND_TRIGRAPH); } -#line 3966 "cpp_re.inc" -yy430: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '=') goto yy446; -#line 191 "cpp.re" - { BOOST_WAVE_RET(T_XOR_TRIGRAPH); } -#line 3972 "cpp_re.inc" -yy432: - yyaccept = 14; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '>') { - if (yych == '=') goto yy441; - } else { - if (yych <= '?') goto yy438; - if (yych == '|') goto yy439; - } -yy433: -#line 197 "cpp.re" - { BOOST_WAVE_RET(T_OR_TRIGRAPH); } -#line 3985 "cpp_re.inc" -yy434: - ++YYCURSOR; -#line 199 "cpp.re" - { BOOST_WAVE_RET(T_COMPL_TRIGRAPH); } -#line 3990 "cpp_re.inc" -yy436: - yyaccept = 15; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'U') goto yy100; - if (yych == 'u') goto yy98; -yy437: -#line 249 "cpp.re" - { BOOST_WAVE_RET(T_ANY_TRIGRAPH); } -#line 3999 "cpp_re.inc" -yy438: - yych = *++YYCURSOR; - if (yych == '?') goto yy443; - goto yy99; -yy439: - ++YYCURSOR; -#line 231 "cpp.re" - { BOOST_WAVE_RET(T_OROR_TRIGRAPH); } -#line 4008 "cpp_re.inc" -yy441: - ++YYCURSOR; -#line 218 "cpp.re" - { BOOST_WAVE_RET(T_ORASSIGN_TRIGRAPH); } -#line 4013 "cpp_re.inc" -yy443: - yych = *++YYCURSOR; - if (yych != '!') goto yy99; - ++YYCURSOR; -#line 234 "cpp.re" - { BOOST_WAVE_RET(T_OROR_TRIGRAPH); } -#line 4020 "cpp_re.inc" -yy446: - ++YYCURSOR; -#line 213 "cpp.re" - { BOOST_WAVE_RET(T_XORASSIGN_TRIGRAPH); } -#line 4025 "cpp_re.inc" -yy448: - yych = *++YYCURSOR; - if (yych == '?') goto yy451; - goto yy99; -yy449: - ++YYCURSOR; -#line 155 "cpp.re" - { BOOST_WAVE_RET(T_POUND_POUND_TRIGRAPH); } -#line 4034 "cpp_re.inc" -yy451: - yych = *++YYCURSOR; - if (yych != '=') goto yy99; - ++YYCURSOR; -#line 156 "cpp.re" - { BOOST_WAVE_RET(T_POUND_POUND_TRIGRAPH); } -#line 4041 "cpp_re.inc" -yy454: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - switch (yych) { - case 'a': goto yy455; - case 'b': goto yy456; - case 'c': goto yy457; - case 'd': goto yy458; - case 'e': goto yy507; - case 'f': goto yy505; - case 'i': goto yy504; - case 'l': goto yy508; - case 's': goto yy461; - case 't': goto yy506; - default: goto yy109; - } -yy455: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 's') goto yy501; - goto yy109; -yy456: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy496; - goto yy109; -yy457: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'd') goto yy491; - goto yy109; -yy458: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'e') goto yy483; - goto yy109; -yy459: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy475; - goto yy109; -yy460: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'n') goto yy469; - goto yy109; -yy461: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'd') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 16; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy468: -#line 130 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_STDCALL : T_IDENTIFIER); } -#line 4117 "cpp_re.inc" -yy469: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; -yy470: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 17; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy474: -#line 135 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_INLINE : T_IDENTIFIER); } -#line 4142 "cpp_re.inc" -yy475: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 18; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy482: -#line 129 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_FASTCALL : T_IDENTIFIER); } -#line 4172 "cpp_re.inc" -yy483: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 19; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy490: -#line 127 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_DECLSPEC : T_IDENTIFIER); } -#line 4202 "cpp_re.inc" -yy491: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 20; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy495: -#line 128 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_CDECL : T_IDENTIFIER); } -#line 4223 "cpp_re.inc" -yy496: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'd') goto yy109; - yyaccept = 21; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy500: -#line 126 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_BASED : T_IDENTIFIER); } -#line 4244 "cpp_re.inc" -yy501: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'm') goto yy109; - yyaccept = 22; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy503: -#line 136 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_ASM : T_IDENTIFIER); } -#line 4259 "cpp_re.inc" -yy504: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'n') goto yy530; - goto yy109; -yy505: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy475; - if (yych == 'i') goto yy523; - goto yy109; -yy506: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'r') goto yy520; - goto yy109; -yy507: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'x') goto yy514; - goto yy109; -yy508: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'v') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 23; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy513: -#line 134 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_LEAVE : T_IDENTIFIER); } -#line 4304 "cpp_re.inc" -yy514: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 24; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy519: -#line 132 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_EXCEPT : T_IDENTIFIER); } -#line 4328 "cpp_re.inc" -yy520: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'y') goto yy109; - yyaccept = 25; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy522: -#line 131 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_TRY : T_IDENTIFIER); } -#line 4343 "cpp_re.inc" -yy523: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'y') goto yy109; - yyaccept = 26; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy529: -#line 133 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_FINALLY : T_IDENTIFIER); } -#line 4370 "cpp_re.inc" -yy530: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'l') goto yy470; - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - switch (yych) { - case '1': goto yy532; - case '3': goto yy533; - case '6': goto yy534; - case '8': goto yy535; - default: goto yy109; - } -yy532: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '6') goto yy541; - goto yy109; -yy533: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '2') goto yy539; - goto yy109; -yy534: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '4') goto yy537; - goto yy109; -yy535: - yyaccept = 27; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy536: -#line 122 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_INT8 : T_IDENTIFIER); } -#line 4411 "cpp_re.inc" -yy537: - yyaccept = 28; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy538: -#line 125 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_INT64 : T_IDENTIFIER); } -#line 4423 "cpp_re.inc" -yy539: - yyaccept = 29; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy540: -#line 124 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_INT32 : T_IDENTIFIER); } -#line 4435 "cpp_re.inc" -yy541: - yyaccept = 30; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy542: -#line 123 "cpp.re" - { BOOST_WAVE_RET(s->enable_ms_extensions ? T_MSEXT_INT16 : T_IDENTIFIER); } -#line 4447 "cpp_re.inc" -yy543: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'h') goto yy549; - goto yy109; -yy544: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 31; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy548: -#line 120 "cpp.re" - { BOOST_WAVE_RET(T_WHILE); } -#line 4473 "cpp_re.inc" -yy549: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != '_') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 32; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy554: -#line 119 "cpp.re" - { BOOST_WAVE_RET(T_WCHART); } -#line 4497 "cpp_re.inc" -yy555: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'r') goto yy567; - goto yy109; -yy556: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'i') goto yy557; - if (yych == 'l') goto yy558; - goto yy109; -yy557: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'd') goto yy565; - goto yy109; -yy558: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 33; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy564: -#line 118 "cpp.re" - { BOOST_WAVE_RET(T_VOLATILE); } -#line 4540 "cpp_re.inc" -yy565: - yyaccept = 34; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy566: -#line 117 "cpp.re" - { BOOST_WAVE_RET(T_VOID); } -#line 4552 "cpp_re.inc" -yy567: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'u') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 35; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy572: -#line 116 "cpp.re" - { BOOST_WAVE_RET(T_VIRTUAL); } -#line 4576 "cpp_re.inc" -yy573: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '"') goto yy129; - if (yych == 'R') goto yy128; - goto yy109; -yy574: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'i') goto yy580; - if (yych == 's') goto yy581; - goto yy109; -yy575: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'g') goto yy109; - yyaccept = 36; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy579: -#line 115 "cpp.re" - { BOOST_WAVE_RET(T_USING); } -#line 4609 "cpp_re.inc" -yy580: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'o') goto yy588; - goto yy109; -yy581: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'g') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'd') goto yy109; - yyaccept = 37; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy587: -#line 114 "cpp.re" - { BOOST_WAVE_RET(T_UNSIGNED); } -#line 4641 "cpp_re.inc" -yy588: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 38; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy590: -#line 113 "cpp.re" - { BOOST_WAVE_RET(T_UNION); } -#line 4656 "cpp_re.inc" -yy591: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'm') goto yy631; - goto yy109; -yy592: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'i') goto yy614; - if (yych == 'r') goto yy615; - goto yy109; -yy593: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'u') goto yy609; - if (yych == 'y') goto yy610; - goto yy109; -yy594: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'h') { - if (yych != 'd') goto yy109; - } else { - if (yych <= 'i') goto yy598; - if (yych == 'n') goto yy599; - goto yy109; - } - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'e') goto yy606; - goto yy109; -yy598: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'd') goto yy604; - goto yy109; -yy599: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'm') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 39; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy603: -#line 112 "cpp.re" - { BOOST_WAVE_RET(T_TYPENAME); } -#line 4719 "cpp_re.inc" -yy604: - yyaccept = 40; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy605: -#line 111 "cpp.re" - { BOOST_WAVE_RET(T_TYPEID); } -#line 4731 "cpp_re.inc" -yy606: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'f') goto yy109; - yyaccept = 41; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy608: -#line 110 "cpp.re" - { BOOST_WAVE_RET(T_TYPEDEF); } -#line 4746 "cpp_re.inc" -yy609: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'e') goto yy612; - goto yy109; -yy610: - yyaccept = 42; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy611: -#line 109 "cpp.re" - { BOOST_WAVE_RET(T_TRY); } -#line 4763 "cpp_re.inc" -yy612: - yyaccept = 43; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy613: -#line 108 "cpp.re" - { BOOST_WAVE_RET(T_TRUE); } -#line 4775 "cpp_re.inc" -yy614: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 's') goto yy629; - goto yy109; -yy615: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'e') goto yy616; - if (yych == 'o') goto yy617; - goto yy109; -yy616: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy620; - goto yy109; -yy617: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'w') goto yy109; - yyaccept = 44; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy619: -#line 107 "cpp.re" - { BOOST_WAVE_RET(T_THROW); } -#line 4806 "cpp_re.inc" -yy620: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'd') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != '_') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'o') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 45; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy628: -#line 106 "cpp.re" - { BOOST_WAVE_RET(s->act_in_cpp0x_mode ? T_THREADLOCAL : T_IDENTIFIER); } -#line 4839 "cpp_re.inc" -yy629: - yyaccept = 46; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy630: -#line 105 "cpp.re" - { BOOST_WAVE_RET(T_THIS); } -#line 4851 "cpp_re.inc" -yy631: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 47; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy637: -#line 104 "cpp.re" - { BOOST_WAVE_RET(T_TEMPLATE); } -#line 4878 "cpp_re.inc" -yy638: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'o') goto yy680; - goto yy109; -yy639: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'g') goto yy670; - if (yych == 'z') goto yy671; - goto yy109; -yy640: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy647; - if (yych == 'r') goto yy648; - goto yy109; -yy641: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'h') goto yy109; - yyaccept = 48; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy646: -#line 103 "cpp.re" - { BOOST_WAVE_RET(T_SWITCH); } -#line 4919 "cpp_re.inc" -yy647: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 't') goto yy653; - goto yy109; -yy648: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'u') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 49; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy652: -#line 102 "cpp.re" - { BOOST_WAVE_RET(T_STRUCT); } -#line 4945 "cpp_re.inc" -yy653: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 50; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '@') { - if (yych <= '/') { - if (yych == '$') goto yy108; - } else { - if (yych <= '9') goto yy108; - if (yych == '?') goto yy111; - } - } else { - if (yych <= '^') { - if (yych <= 'Z') goto yy108; - if (yych == '\\') goto yy110; - } else { - if (yych <= '_') goto yy657; - if (yych <= '`') goto yy656; - if (yych <= 'z') goto yy108; - } - } -yy656: -#line 99 "cpp.re" - { BOOST_WAVE_RET(T_STATIC); } -#line 4975 "cpp_re.inc" -yy657: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy658; - if (yych == 'c') goto yy659; - goto yy109; -yy658: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 's') goto yy664; - goto yy109; -yy659: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 51; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy663: -#line 100 "cpp.re" - { BOOST_WAVE_RET(T_STATICCAST); } -#line 5007 "cpp_re.inc" -yy664: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 52; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy669: -#line 101 "cpp.re" - { BOOST_WAVE_RET(s->act_in_cpp0x_mode ? T_STATICASSERT : T_IDENTIFIER); } -#line 5031 "cpp_re.inc" -yy670: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'n') goto yy676; - goto yy109; -yy671: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'o') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'f') goto yy109; - yyaccept = 53; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy675: -#line 98 "cpp.re" - { BOOST_WAVE_RET(T_SIZEOF); } -#line 5057 "cpp_re.inc" -yy676: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'd') goto yy109; - yyaccept = 54; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy679: -#line 97 "cpp.re" - { BOOST_WAVE_RET(T_SIGNED); } -#line 5075 "cpp_re.inc" -yy680: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 55; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy683: -#line 96 "cpp.re" - { BOOST_WAVE_RET(T_SHORT); } -#line 5093 "cpp_re.inc" -yy684: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'h') { - if (yych != 'g') goto yy109; - } else { - if (yych <= 'i') goto yy686; - if (yych == 't') goto yy687; - goto yy109; - } - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'i') goto yy706; - goto yy109; -yy686: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'n') goto yy692; - goto yy109; -yy687: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'u') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 56; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy691: -#line 95 "cpp.re" - { BOOST_WAVE_RET(T_RETURN); } -#line 5133 "cpp_re.inc" -yy692: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != '_') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 57; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy705: -#line 94 "cpp.re" - { BOOST_WAVE_RET(T_REINTERPRETCAST); } -#line 5181 "cpp_re.inc" -yy706: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 58; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy711: -#line 93 "cpp.re" - { BOOST_WAVE_RET(T_REGISTER); } -#line 5205 "cpp_re.inc" -yy712: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'i') goto yy719; - if (yych == 'o') goto yy720; - goto yy109; -yy713: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'b') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 59; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy718: -#line 92 "cpp.re" - { BOOST_WAVE_RET(T_PUBLIC); } -#line 5235 "cpp_re.inc" -yy719: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'v') goto yy728; - goto yy109; -yy720: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'd') goto yy109; - yyaccept = 60; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy727: -#line 91 "cpp.re" - { BOOST_WAVE_RET(T_PROTECTED); } -#line 5270 "cpp_re.inc" -yy728: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 61; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy732: -#line 90 "cpp.re" - { BOOST_WAVE_RET(T_PRIVATE); } -#line 5291 "cpp_re.inc" -yy733: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'e') goto yy740; - goto yy109; -yy734: - yyaccept = 62; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '@') { - if (yych <= '/') { - if (yych == '$') goto yy108; - } else { - if (yych <= '9') goto yy108; - if (yych == '?') goto yy111; - } - } else { - if (yych <= '^') { - if (yych <= 'Z') goto yy108; - if (yych == '\\') goto yy110; - } else { - if (yych <= '_') goto yy736; - if (yych <= '`') goto yy735; - if (yych <= 'z') goto yy108; - } - } -yy735: -#line 233 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_OROR_ALT); } -#line 5320 "cpp_re.inc" -yy736: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'q') goto yy109; - yyaccept = 63; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy739: -#line 217 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_ORASSIGN_ALT); } -#line 5338 "cpp_re.inc" -yy740: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'o') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 64; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy746: -#line 89 "cpp.re" - { BOOST_WAVE_RET(T_OPERATOR); } -#line 5365 "cpp_re.inc" -yy747: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'm') goto yy772; - goto yy109; -yy748: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'w') goto yy770; - goto yy109; -yy749: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'e') goto yy757; - if (yych == 't') goto yy758; - goto yy109; -yy750: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 65; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy756: -#line 88 "cpp.re" - { BOOST_WAVE_RET(s->act_in_cpp0x_mode ? T_NULLPTR : T_IDENTIFIER); } -#line 5408 "cpp_re.inc" -yy757: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'x') goto yy764; - goto yy109; -yy758: - yyaccept = 66; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '@') { - if (yych <= '/') { - if (yych == '$') goto yy108; - } else { - if (yych <= '9') goto yy108; - if (yych == '?') goto yy111; - } - } else { - if (yych <= '^') { - if (yych <= 'Z') goto yy108; - if (yych == '\\') goto yy110; - } else { - if (yych <= '_') goto yy760; - if (yych <= '`') goto yy759; - if (yych <= 'z') goto yy108; - } - } -yy759: -#line 202 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_NOT_ALT); } -#line 5437 "cpp_re.inc" -yy760: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'q') goto yy109; - yyaccept = 67; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy763: -#line 225 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_NOTEQUAL_ALT); } -#line 5455 "cpp_re.inc" -yy764: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 68; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy769: -#line 87 "cpp.re" - { BOOST_WAVE_RET(s->act_in_cpp0x_mode ? T_NOEXCEPT : T_IDENTIFIER); } -#line 5479 "cpp_re.inc" -yy770: - yyaccept = 69; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy771: -#line 86 "cpp.re" - { BOOST_WAVE_RET(T_NEW); } -#line 5491 "cpp_re.inc" -yy772: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 70; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy779: -#line 85 "cpp.re" - { BOOST_WAVE_RET(T_NAMESPACE); } -#line 5521 "cpp_re.inc" -yy780: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'b') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 71; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy786: -#line 84 "cpp.re" - { BOOST_WAVE_RET(T_MUTABLE); } -#line 5548 "cpp_re.inc" -yy787: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'g') goto yy109; - yyaccept = 72; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy790: -#line 83 "cpp.re" - { BOOST_WAVE_RET(T_LONG); } -#line 5566 "cpp_re.inc" -yy791: - yyaccept = 73; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy792: -#line 79 "cpp.re" - { BOOST_WAVE_RET(T_IF); } -#line 5578 "cpp_re.inc" -yy793: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'p') goto yy802; - goto yy109; -yy794: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'l') goto yy795; - if (yych == 't') goto yy796; - goto yy109; -yy795: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'i') goto yy798; - goto yy109; -yy796: - yyaccept = 74; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy797: -#line 82 "cpp.re" - { BOOST_WAVE_RET(T_INT); } -#line 5606 "cpp_re.inc" -yy798: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 75; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy801: -#line 81 "cpp.re" - { BOOST_WAVE_RET(T_INLINE); } -#line 5624 "cpp_re.inc" -yy802: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'o') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 76; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy806: -#line 80 "cpp.re" - { BOOST_WAVE_RET(s->enable_import_keyword ? T_IMPORT : T_IDENTIFIER); } -#line 5645 "cpp_re.inc" -yy807: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'o') goto yy109; - yyaccept = 77; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy810: -#line 78 "cpp.re" - { BOOST_WAVE_RET(T_GOTO); } -#line 5663 "cpp_re.inc" -yy811: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'l') goto yy826; - goto yy109; -yy812: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'o') goto yy822; - goto yy109; -yy813: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'r') goto yy820; - goto yy109; -yy814: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'd') goto yy109; - yyaccept = 78; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy819: -#line 77 "cpp.re" - { BOOST_WAVE_RET(T_FRIEND); } -#line 5702 "cpp_re.inc" -yy820: - yyaccept = 79; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy821: -#line 76 "cpp.re" - { BOOST_WAVE_RET(T_FOR); } -#line 5714 "cpp_re.inc" -yy822: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 80; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy825: -#line 75 "cpp.re" - { BOOST_WAVE_RET(T_FLOAT); } -#line 5732 "cpp_re.inc" -yy826: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 81; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy829: -#line 74 "cpp.re" - { BOOST_WAVE_RET(T_FALSE); } -#line 5750 "cpp_re.inc" -yy830: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 's') goto yy852; - goto yy109; -yy831: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'u') goto yy849; - goto yy109; -yy832: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'p') goto yy833; - if (yych == 't') goto yy834; - goto yy109; -yy833: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'l') goto yy839; - if (yych == 'o') goto yy840; - goto yy109; -yy834: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 82; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy838: -#line 73 "cpp.re" - { BOOST_WAVE_RET(T_EXTERN); } -#line 5793 "cpp_re.inc" -yy839: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'i') goto yy844; - goto yy109; -yy840: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 83; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy843: -#line 72 "cpp.re" - { BOOST_WAVE_RET(T_EXPORT); } -#line 5816 "cpp_re.inc" -yy844: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 84; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy848: -#line 71 "cpp.re" - { BOOST_WAVE_RET(T_EXPLICIT); } -#line 5837 "cpp_re.inc" -yy849: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'm') goto yy109; - yyaccept = 85; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy851: -#line 70 "cpp.re" - { BOOST_WAVE_RET(T_ENUM); } -#line 5852 "cpp_re.inc" -yy852: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 86; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy854: -#line 69 "cpp.re" - { BOOST_WAVE_RET(T_ELSE); } -#line 5867 "cpp_re.inc" -yy855: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'e') { - if (yych == 'c') goto yy875; - goto yy109; - } else { - if (yych <= 'f') goto yy876; - if (yych == 'l') goto yy877; - goto yy109; - } -yy856: - yyaccept = 87; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'Z') { - if (yych <= '9') { - if (yych == '$') goto yy108; - if (yych >= '0') goto yy108; - } else { - if (yych == '?') goto yy111; - if (yych >= 'A') goto yy108; - } - } else { - if (yych <= '_') { - if (yych == '\\') goto yy110; - if (yych >= '_') goto yy108; - } else { - if (yych <= 't') { - if (yych >= 'a') goto yy108; - } else { - if (yych <= 'u') goto yy870; - if (yych <= 'z') goto yy108; - } - } - } -yy857: -#line 66 "cpp.re" - { BOOST_WAVE_RET(T_DO); } -#line 5906 "cpp_re.inc" -yy858: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'm') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != '_') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 88; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy869: -#line 68 "cpp.re" - { BOOST_WAVE_RET(T_DYNAMICCAST); } -#line 5948 "cpp_re.inc" -yy870: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'b') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 89; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy874: -#line 67 "cpp.re" - { BOOST_WAVE_RET(T_DOUBLE); } -#line 5969 "cpp_re.inc" -yy875: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'l') goto yy887; - goto yy109; -yy876: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy882; - goto yy109; -yy877: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 90; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy881: -#line 65 "cpp.re" - { BOOST_WAVE_RET(T_DELETE); } -#line 6000 "cpp_re.inc" -yy882: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'u') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 91; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy886: -#line 64 "cpp.re" - { BOOST_WAVE_RET(T_DEFAULT); } -#line 6021 "cpp_re.inc" -yy887: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'y') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 92; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy892: -#line 63 "cpp.re" - { BOOST_WAVE_RET(s->act_in_cpp0x_mode ? T_DECLTYPE : T_IDENTIFIER); } -#line 6045 "cpp_re.inc" -yy893: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'r') goto yy109; - if (yych <= 's') goto yy939; - if (yych <= 't') goto yy940; - goto yy109; -yy894: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy926; - goto yy109; -yy895: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy922; - goto yy109; -yy896: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'l') goto yy109; - if (yych <= 'm') goto yy898; - if (yych >= 'o') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'r') goto yy109; - if (yych <= 's') goto yy902; - if (yych <= 't') goto yy903; - goto yy109; -yy898: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 93; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy901: -#line 200 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_COMPL_ALT); } -#line 6092 "cpp_re.inc" -yy902: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 't') goto yy909; - goto yy109; -yy903: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'i') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'u') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 94; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy908: -#line 62 "cpp.re" - { BOOST_WAVE_RET(T_CONTINUE); } -#line 6121 "cpp_re.inc" -yy909: - yyaccept = 95; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= 'Z') { - if (yych <= '9') { - if (yych == '$') goto yy108; - if (yych >= '0') goto yy108; - } else { - if (yych == '?') goto yy111; - if (yych >= 'A') goto yy108; - } - } else { - if (yych <= '_') { - if (yych == '\\') goto yy110; - if (yych >= '_') goto yy911; - } else { - if (yych <= 'd') { - if (yych >= 'a') goto yy108; - } else { - if (yych <= 'e') goto yy912; - if (yych <= 'z') goto yy108; - } - } - } -yy910: -#line 59 "cpp.re" - { BOOST_WAVE_RET(T_CONST); } -#line 6149 "cpp_re.inc" -yy911: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'c') goto yy917; - goto yy109; -yy912: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'x') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'p') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 96; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy916: -#line 60 "cpp.re" - { BOOST_WAVE_RET(s->act_in_cpp0x_mode ? T_CONSTEXPR : T_IDENTIFIER); } -#line 6175 "cpp_re.inc" -yy917: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 97; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy921: -#line 61 "cpp.re" - { BOOST_WAVE_RET(T_CONSTCAST); } -#line 6196 "cpp_re.inc" -yy922: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 's') goto yy109; - yyaccept = 98; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy925: -#line 58 "cpp.re" - { BOOST_WAVE_RET(T_CLASS); } -#line 6214 "cpp_re.inc" -yy926: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 99; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '>') { - if (yych <= '0') { - if (yych == '$') goto yy108; - if (yych >= '0') goto yy108; - } else { - if (yych <= '2') { - if (yych <= '1') goto yy929; - goto yy108; - } else { - if (yych <= '3') goto yy930; - if (yych <= '9') goto yy108; - } - } - } else { - if (yych <= '\\') { - if (yych <= '@') { - if (yych <= '?') goto yy111; - } else { - if (yych <= 'Z') goto yy108; - if (yych >= '\\') goto yy110; - } - } else { - if (yych <= '_') { - if (yych >= '_') goto yy108; - } else { - if (yych <= '`') goto yy928; - if (yych <= 'z') goto yy108; - } - } - } -yy928: -#line 55 "cpp.re" - { BOOST_WAVE_RET(T_CHAR); } -#line 6254 "cpp_re.inc" -yy929: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '6') goto yy935; - goto yy109; -yy930: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != '2') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != '_') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 100; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy934: -#line 57 "cpp.re" - { BOOST_WAVE_RET(s->act_in_cpp0x_mode ? T_CHAR32_T : T_IDENTIFIER); } -#line 6280 "cpp_re.inc" -yy935: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != '_') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 101; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy938: -#line 56 "cpp.re" - { BOOST_WAVE_RET(s->act_in_cpp0x_mode ? T_CHAR16_T : T_IDENTIFIER); } -#line 6298 "cpp_re.inc" -yy939: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'e') goto yy944; - goto yy109; -yy940: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'c') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'h') goto yy109; - yyaccept = 102; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy943: -#line 54 "cpp.re" - { BOOST_WAVE_RET(T_CATCH); } -#line 6321 "cpp_re.inc" -yy944: - yyaccept = 103; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy945: -#line 53 "cpp.re" - { BOOST_WAVE_RET(T_CASE); } -#line 6333 "cpp_re.inc" -yy946: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 't') goto yy956; - goto yy109; -yy947: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'o') goto yy953; - goto yy109; -yy948: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'a') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'k') goto yy109; - yyaccept = 104; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy952: -#line 52 "cpp.re" - { BOOST_WAVE_RET(T_BREAK); } -#line 6364 "cpp_re.inc" -yy953: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'l') goto yy109; - yyaccept = 105; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy955: -#line 51 "cpp.re" - { BOOST_WAVE_RET(T_BOOL); } -#line 6379 "cpp_re.inc" -yy956: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy957; - if (yych == 'o') goto yy958; - goto yy109; -yy957: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'n') goto yy961; - goto yy109; -yy958: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'r') goto yy109; - yyaccept = 106; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy960: -#line 196 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_OR_ALT); } -#line 6405 "cpp_re.inc" -yy961: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'd') goto yy109; - yyaccept = 107; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy963: -#line 194 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_AND_ALT); } -#line 6420 "cpp_re.inc" -yy964: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'i') goto yy979; - goto yy109; -yy965: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'd') goto yy973; - goto yy109; -yy966: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'm') goto yy971; - goto yy109; -yy967: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 't') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'o') goto yy109; - yyaccept = 108; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy970: -#line 50 "cpp.re" - { BOOST_WAVE_RET(T_AUTO); } -#line 6453 "cpp_re.inc" -yy971: - yyaccept = 109; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy972: -#line 49 "cpp.re" - { BOOST_WAVE_RET(T_ASM); } -#line 6465 "cpp_re.inc" -yy973: - yyaccept = 110; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '@') { - if (yych <= '/') { - if (yych == '$') goto yy108; - } else { - if (yych <= '9') goto yy108; - if (yych == '?') goto yy111; - } - } else { - if (yych <= '^') { - if (yych <= 'Z') goto yy108; - if (yych == '\\') goto yy110; - } else { - if (yych <= '_') goto yy975; - if (yych <= '`') goto yy974; - if (yych <= 'z') goto yy108; - } - } -yy974: -#line 229 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_ANDAND_ALT); } -#line 6489 "cpp_re.inc" -yy975: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'e') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'q') goto yy109; - yyaccept = 111; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy978: -#line 215 "cpp.re" - { BOOST_WAVE_RET(s->act_in_c99_mode ? T_IDENTIFIER : T_ANDASSIGN_ALT); } -#line 6507 "cpp_re.inc" -yy979: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'g') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'n') goto yy109; - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 'a') goto yy982; - if (yych == 'o') goto yy983; - goto yy109; -yy982: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == 's') goto yy986; - goto yy109; -yy983: - yyaccept = 1; - yych = *(YYMARKER = ++YYCURSOR); - if (yych != 'f') goto yy109; - yyaccept = 112; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy985: -#line 48 "cpp.re" - { BOOST_WAVE_RET(s->act_in_cpp0x_mode ? T_ALIGNOF : T_IDENTIFIER); } -#line 6539 "cpp_re.inc" -yy986: - yyaccept = 113; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[256+yych] & 32) { - goto yy108; - } - if (yych == '?') goto yy111; - if (yych == '\\') goto yy110; -yy987: -#line 47 "cpp.re" - { BOOST_WAVE_RET(s->act_in_cpp0x_mode ? T_ALIGNAS : T_IDENTIFIER); } -#line 6551 "cpp_re.inc" -yy988: - ++YYCURSOR; -#line 176 "cpp.re" - { - if (s->act_in_c99_mode) { - --YYCURSOR; - BOOST_WAVE_RET(T_DOT); - } - else { - BOOST_WAVE_RET(T_DOTSTAR); - } - } -#line 6564 "cpp_re.inc" -yy990: - yych = *++YYCURSOR; - if (yych == '.') goto yy992; - goto yy99; -yy991: - yych = *++YYCURSOR; - goto yy7; -yy992: - ++YYCURSOR; -#line 162 "cpp.re" - { BOOST_WAVE_RET(T_ELLIPSIS); } -#line 6576 "cpp_re.inc" -yy994: - ++YYCURSOR; -#line 209 "cpp.re" - { BOOST_WAVE_RET(T_DIVIDEASSIGN); } -#line 6581 "cpp_re.inc" -yy996: - ++YYCURSOR; -#line 44 "cpp.re" - { goto cppcomment; } -#line 6586 "cpp_re.inc" -yy998: - ++YYCURSOR; -#line 43 "cpp.re" - { goto ccomment; } -#line 6591 "cpp_re.inc" -} -#line 348 "cpp.re" - - -ccomment: - -#line 6598 "cpp_re.inc" -{ - YYCTYPE yych; - if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); - yych = *YYCURSOR; - if (yych <= '\f') { - if (yych <= 0x08) { - if (yych <= 0x00) goto yy1009; - goto yy1011; - } else { - if (yych == '\n') goto yy1004; - goto yy1007; - } - } else { - if (yych <= 0x1F) { - if (yych <= '\r') goto yy1006; - goto yy1011; - } else { - if (yych != '*') goto yy1008; - } - } - ++YYCURSOR; - if ((yych = *YYCURSOR) == '/') goto yy1014; -yy1003: -#line 363 "cpp.re" - { goto ccomment; } -#line 6624 "cpp_re.inc" -yy1004: - ++YYCURSOR; -yy1005: -#line 355 "cpp.re" - { - /*if(cursor == s->eof) BOOST_WAVE_RET(T_EOF);*/ - /*s->tok = cursor; */ - s->line += count_backslash_newlines(s, cursor) +1; - cursor.column = 1; - goto ccomment; - } -#line 6636 "cpp_re.inc" -yy1006: - yych = *++YYCURSOR; - if (yych == '\n') goto yy1013; - goto yy1005; -yy1007: - yych = *++YYCURSOR; - goto yy1003; -yy1008: - yych = *++YYCURSOR; - goto yy1003; -yy1009: - ++YYCURSOR; -#line 366 "cpp.re" - { - if(cursor == s->eof) - { - BOOST_WAVE_UPDATE_CURSOR(); // adjust the input cursor - (*s->error_proc)(s, lexing_exception::generic_lexing_warning, - "Unterminated 'C' style comment"); - } - else - { - --YYCURSOR; // next call returns T_EOF - BOOST_WAVE_UPDATE_CURSOR(); // adjust the input cursor - (*s->error_proc)(s, lexing_exception::generic_lexing_error, - "invalid character: '\\000' in input stream"); - } - } -#line 6665 "cpp_re.inc" -yy1011: - ++YYCURSOR; -#line 383 "cpp.re" - { - // flag the error - BOOST_WAVE_UPDATE_CURSOR(); // adjust the input cursor - (*s->error_proc)(s, lexing_exception::generic_lexing_error, - "invalid character '\\%03o' in input stream", *--YYCURSOR); - } -#line 6675 "cpp_re.inc" -yy1013: - yych = *++YYCURSOR; - goto yy1005; -yy1014: - ++YYCURSOR; -#line 352 "cpp.re" - { BOOST_WAVE_RET(T_CCOMMENT); } -#line 6683 "cpp_re.inc" -} -#line 389 "cpp.re" - - -cppcomment: - -#line 6690 "cpp_re.inc" -{ - YYCTYPE yych; - if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); - yych = *YYCURSOR; - if (yych <= '\n') { - if (yych <= 0x00) goto yy1024; - if (yych <= 0x08) goto yy1026; - if (yych <= '\t') goto yy1021; - } else { - if (yych <= '\f') goto yy1021; - if (yych <= '\r') goto yy1020; - if (yych <= 0x1F) goto yy1026; - goto yy1023; - } - ++YYCURSOR; -yy1019: -#line 394 "cpp.re" - { - /*if(cursor == s->eof) BOOST_WAVE_RET(T_EOF); */ - /*s->tok = cursor; */ - s->line++; - cursor.column = 1; - BOOST_WAVE_RET(T_CPPCOMMENT); - } -#line 6715 "cpp_re.inc" -yy1020: - yych = *++YYCURSOR; - if (yych == '\n') goto yy1028; - goto yy1019; -yy1021: - ++YYCURSOR; -yy1022: -#line 402 "cpp.re" - { goto cppcomment; } -#line 6725 "cpp_re.inc" -yy1023: - yych = *++YYCURSOR; - goto yy1022; -yy1024: - ++YYCURSOR; -#line 405 "cpp.re" - { - if (s->eof && cursor != s->eof) - { - --YYCURSOR; // next call returns T_EOF - BOOST_WAVE_UPDATE_CURSOR(); // adjust the input cursor - (*s->error_proc)(s, lexing_exception::generic_lexing_error, - "invalid character '\\000' in input stream"); - } - - --YYCURSOR; // next call returns T_EOF - if (!s->single_line_only) - { - BOOST_WAVE_UPDATE_CURSOR(); // adjust the input cursor - (*s->error_proc)(s, lexing_exception::generic_lexing_warning, - "Unterminated 'C++' style comment"); - } - BOOST_WAVE_RET(T_CPPCOMMENT); - } -#line 6750 "cpp_re.inc" -yy1026: - ++YYCURSOR; -#line 425 "cpp.re" - { - // flag the error - BOOST_WAVE_UPDATE_CURSOR(); // adjust the input cursor - (*s->error_proc)(s, lexing_exception::generic_lexing_error, - "invalid character '\\%03o' in input stream", *--YYCURSOR); - } -#line 6760 "cpp_re.inc" -yy1028: - ++YYCURSOR; - yych = *YYCURSOR; - goto yy1019; -} -#line 431 "cpp.re" - - -/* this subscanner is called whenever a pp_number has been started */ -pp_number: -{ - cursor = uchar_wrapper(s->tok = s->cur, s->column = s->curr_column); - marker = uchar_wrapper(s->ptr); - limit = uchar_wrapper(s->lim); - - if (s->detect_pp_numbers) { - -#line 6778 "cpp_re.inc" -{ - YYCTYPE yych; - static const unsigned char yybm[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 64, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 64, 0, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 0, 0, 0, 0, 0, 0, - 0, 64, 64, 64, 64, 128, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 0, 0, 0, 0, 64, - 0, 64, 64, 64, 64, 128, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - }; - if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); - yych = *YYCURSOR; - if (yych == '.') goto yy1032; - if (yych <= '/') goto yy1031; - if (yych <= '9') goto yy1033; -yy1031: - YYCURSOR = YYMARKER; - goto yy1035; -yy1032: - yych = *++YYCURSOR; - if (yych <= '/') goto yy1031; - if (yych >= ':') goto yy1031; -yy1033: - YYMARKER = ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 64) { - goto yy1033; - } - if (yych <= 'Z') { - if (yych == '?') goto yy1039; - if (yych >= 'A') goto yy1036; - } else { - if (yych <= '\\') { - if (yych >= '\\') goto yy1038; - } else { - if (yych == 'e') goto yy1036; - } - } -yy1035: -#line 443 "cpp.re" - { BOOST_WAVE_RET(T_PP_NUMBER); } -#line 6847 "cpp_re.inc" -yy1036: - YYMARKER = ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 128) { - goto yy1036; - } - if (yych <= '>') { - if (yych <= '+') { - if (yych == '$') goto yy1033; - if (yych <= '*') goto yy1035; - goto yy1033; - } else { - if (yych <= '.') { - if (yych <= ',') goto yy1035; - goto yy1033; - } else { - if (yych <= '/') goto yy1035; - if (yych <= '9') goto yy1033; - goto yy1035; - } - } - } else { - if (yych <= '\\') { - if (yych <= '@') { - if (yych <= '?') goto yy1039; - goto yy1035; - } else { - if (yych <= 'Z') goto yy1033; - if (yych <= '[') goto yy1035; - } - } else { - if (yych <= '_') { - if (yych <= '^') goto yy1035; - goto yy1033; - } else { - if (yych <= '`') goto yy1035; - if (yych <= 'z') goto yy1033; - goto yy1035; - } - } - } -yy1038: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych == 'U') goto yy1042; - if (yych == 'u') goto yy1041; - goto yy1031; -yy1039: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych != '?') goto yy1031; - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych == '/') goto yy1038; - goto yy1031; -yy1041: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych <= '9') goto yy1050; - goto yy1031; - } else { - if (yych <= 'F') goto yy1050; - if (yych <= '`') goto yy1031; - if (yych <= 'f') goto yy1050; - goto yy1031; - } -yy1042: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych >= ':') goto yy1031; - } else { - if (yych <= 'F') goto yy1043; - if (yych <= '`') goto yy1031; - if (yych >= 'g') goto yy1031; - } -yy1043: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych >= ':') goto yy1031; - } else { - if (yych <= 'F') goto yy1044; - if (yych <= '`') goto yy1031; - if (yych >= 'g') goto yy1031; - } -yy1044: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych >= ':') goto yy1031; - } else { - if (yych <= 'F') goto yy1045; - if (yych <= '`') goto yy1031; - if (yych >= 'g') goto yy1031; - } -yy1045: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych >= ':') goto yy1031; - } else { - if (yych <= 'F') goto yy1046; - if (yych <= '`') goto yy1031; - if (yych >= 'g') goto yy1031; - } -yy1046: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych >= ':') goto yy1031; - } else { - if (yych <= 'F') goto yy1047; - if (yych <= '`') goto yy1031; - if (yych >= 'g') goto yy1031; - } -yy1047: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych >= ':') goto yy1031; - } else { - if (yych <= 'F') goto yy1048; - if (yych <= '`') goto yy1031; - if (yych >= 'g') goto yy1031; - } -yy1048: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych >= ':') goto yy1031; - } else { - if (yych <= 'F') goto yy1049; - if (yych <= '`') goto yy1031; - if (yych >= 'g') goto yy1031; - } -yy1049: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych <= '9') goto yy1033; - goto yy1031; - } else { - if (yych <= 'F') goto yy1033; - if (yych <= '`') goto yy1031; - if (yych <= 'f') goto yy1033; - goto yy1031; - } -yy1050: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych >= ':') goto yy1031; - } else { - if (yych <= 'F') goto yy1051; - if (yych <= '`') goto yy1031; - if (yych >= 'g') goto yy1031; - } -yy1051: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych >= ':') goto yy1031; - } else { - if (yych <= 'F') goto yy1052; - if (yych <= '`') goto yy1031; - if (yych >= 'g') goto yy1031; - } -yy1052: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1031; - if (yych <= '9') goto yy1033; - goto yy1031; - } else { - if (yych <= 'F') goto yy1033; - if (yych <= '`') goto yy1031; - if (yych <= 'f') goto yy1033; - goto yy1031; - } -} -#line 444 "cpp.re" - - } - else { - -#line 7063 "cpp_re.inc" -{ - YYCTYPE yych; - unsigned int yyaccept = 0; - static const unsigned char yybm[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 224, 224, 224, 224, 224, 224, 224, 224, - 160, 160, 0, 0, 0, 0, 0, 0, - 0, 128, 128, 128, 128, 128, 128, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 128, 128, 128, 128, 128, 128, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - }; - if ((YYLIMIT - YYCURSOR) < 4) YYFILL(4); - yych = *YYCURSOR; - if (yych <= '/') { - if (yych == '.') goto yy1060; - } else { - if (yych <= '0') goto yy1056; - if (yych <= '9') goto yy1058; - } -yy1055: - YYCURSOR = YYMARKER; - if (yyaccept <= 0) { - goto yy1057; - } else { - goto yy1063; - } -yy1056: - yyaccept = 0; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[0+yych] & 64) { - goto yy1075; - } - if (yych <= 'E') { - if (yych <= '/') { - if (yych == '.') goto yy1061; - } else { - if (yych <= '9') goto yy1078; - if (yych >= 'E') goto yy1071; - } - } else { - if (yych <= 'd') { - if (yych == 'X') goto yy1077; - } else { - if (yych <= 'e') goto yy1071; - if (yych == 'x') goto yy1077; - } - } -yy1057: -#line 451 "cpp.re" - { goto integer_suffix; } -#line 7140 "cpp_re.inc" -yy1058: - yyaccept = 0; - YYMARKER = ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 3) YYFILL(3); - yych = *YYCURSOR; - if (yybm[0+yych] & 32) { - goto yy1058; - } - if (yych <= 'D') { - if (yych == '.') goto yy1061; - goto yy1057; - } else { - if (yych <= 'E') goto yy1071; - if (yych == 'e') goto yy1071; - goto yy1057; - } -yy1060: - yych = *++YYCURSOR; - if (yych <= '/') goto yy1055; - if (yych >= ':') goto yy1055; -yy1061: - yyaccept = 1; - YYMARKER = ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 3) YYFILL(3); - yych = *YYCURSOR; - if (yych <= 'K') { - if (yych <= 'D') { - if (yych <= '/') goto yy1063; - if (yych <= '9') goto yy1061; - } else { - if (yych <= 'E') goto yy1064; - if (yych <= 'F') goto yy1065; - } - } else { - if (yych <= 'e') { - if (yych <= 'L') goto yy1066; - if (yych >= 'e') goto yy1064; - } else { - if (yych <= 'f') goto yy1065; - if (yych == 'l') goto yy1066; - } - } -yy1063: -#line 449 "cpp.re" - { BOOST_WAVE_RET(T_FLOATLIT); } -#line 7186 "cpp_re.inc" -yy1064: - yych = *++YYCURSOR; - if (yych <= ',') { - if (yych == '+') goto yy1068; - goto yy1055; - } else { - if (yych <= '-') goto yy1068; - if (yych <= '/') goto yy1055; - if (yych <= '9') goto yy1069; - goto yy1055; - } -yy1065: - yych = *++YYCURSOR; - if (yych == 'L') goto yy1067; - if (yych == 'l') goto yy1067; - goto yy1063; -yy1066: - yych = *++YYCURSOR; - if (yych == 'F') goto yy1067; - if (yych != 'f') goto yy1063; -yy1067: - yych = *++YYCURSOR; - goto yy1063; -yy1068: - yych = *++YYCURSOR; - if (yych <= '/') goto yy1055; - if (yych >= ':') goto yy1055; -yy1069: - ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); - yych = *YYCURSOR; - if (yych <= 'K') { - if (yych <= '9') { - if (yych <= '/') goto yy1063; - goto yy1069; - } else { - if (yych == 'F') goto yy1065; - goto yy1063; - } - } else { - if (yych <= 'f') { - if (yych <= 'L') goto yy1066; - if (yych <= 'e') goto yy1063; - goto yy1065; - } else { - if (yych == 'l') goto yy1066; - goto yy1063; - } - } -yy1071: - yych = *++YYCURSOR; - if (yych <= ',') { - if (yych != '+') goto yy1055; - } else { - if (yych <= '-') goto yy1072; - if (yych <= '/') goto yy1055; - if (yych <= '9') goto yy1073; - goto yy1055; - } -yy1072: - yych = *++YYCURSOR; - if (yych <= '/') goto yy1055; - if (yych >= ':') goto yy1055; -yy1073: - ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); - yych = *YYCURSOR; - if (yych <= 'K') { - if (yych <= '9') { - if (yych <= '/') goto yy1063; - goto yy1073; - } else { - if (yych == 'F') goto yy1065; - goto yy1063; - } - } else { - if (yych <= 'f') { - if (yych <= 'L') goto yy1066; - if (yych <= 'e') goto yy1063; - goto yy1065; - } else { - if (yych == 'l') goto yy1066; - goto yy1063; - } - } -yy1075: - yyaccept = 0; - YYMARKER = ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 3) YYFILL(3); - yych = *YYCURSOR; - if (yybm[0+yych] & 64) { - goto yy1075; - } - if (yych <= '9') { - if (yych == '.') goto yy1061; - if (yych <= '/') goto yy1057; - goto yy1078; - } else { - if (yych <= 'E') { - if (yych <= 'D') goto yy1057; - goto yy1071; - } else { - if (yych == 'e') goto yy1071; - goto yy1057; - } - } -yy1077: - yych = *++YYCURSOR; - if (yybm[0+yych] & 128) { - goto yy1080; - } - goto yy1055; -yy1078: - ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 3) YYFILL(3); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych == '.') goto yy1061; - if (yych <= '/') goto yy1055; - goto yy1078; - } else { - if (yych <= 'E') { - if (yych <= 'D') goto yy1055; - goto yy1071; - } else { - if (yych == 'e') goto yy1071; - goto yy1055; - } - } -yy1080: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 128) { - goto yy1080; - } - goto yy1057; -} -#line 452 "cpp.re" - - } -} - -/* this subscanner is called, whenever an Integer was recognized */ -integer_suffix: -{ - if (s->enable_ms_extensions) { - -#line 7335 "cpp_re.inc" -{ - YYCTYPE yych; - if ((YYLIMIT - YYCURSOR) < 3) YYFILL(3); - yych = *(YYMARKER = YYCURSOR); - if (yych <= 'h') { - if (yych <= 'L') { - if (yych >= 'L') goto yy1086; - } else { - if (yych == 'U') goto yy1085; - } - } else { - if (yych <= 'l') { - if (yych <= 'i') goto yy1087; - if (yych >= 'l') goto yy1086; - } else { - if (yych == 'u') goto yy1085; - } - } -yy1084: -#line 465 "cpp.re" - { BOOST_WAVE_RET(T_INTLIT); } -#line 7357 "cpp_re.inc" -yy1085: - yych = *++YYCURSOR; - if (yych == 'L') goto yy1094; - if (yych == 'l') goto yy1094; - goto yy1084; -yy1086: - yych = *++YYCURSOR; - if (yych <= 'U') { - if (yych == 'L') goto yy1093; - if (yych <= 'T') goto yy1084; - goto yy1092; - } else { - if (yych <= 'l') { - if (yych <= 'k') goto yy1084; - goto yy1093; - } else { - if (yych == 'u') goto yy1092; - goto yy1084; - } - } -yy1087: - yych = *++YYCURSOR; - if (yych == '6') goto yy1089; -yy1088: - YYCURSOR = YYMARKER; - goto yy1084; -yy1089: - yych = *++YYCURSOR; - if (yych != '4') goto yy1088; -yy1090: - ++YYCURSOR; -yy1091: -#line 462 "cpp.re" - { BOOST_WAVE_RET(T_LONGINTLIT); } -#line 7392 "cpp_re.inc" -yy1092: - yych = *++YYCURSOR; - goto yy1084; -yy1093: - yych = *++YYCURSOR; - if (yych == 'U') goto yy1090; - if (yych == 'u') goto yy1090; - goto yy1091; -yy1094: - ++YYCURSOR; - if ((yych = *YYCURSOR) == 'L') goto yy1090; - if (yych == 'l') goto yy1090; - goto yy1084; -} -#line 466 "cpp.re" - - } - else { - -#line 7412 "cpp_re.inc" -{ - YYCTYPE yych; - if ((YYLIMIT - YYCURSOR) < 3) YYFILL(3); - yych = *YYCURSOR; - if (yych <= 'U') { - if (yych == 'L') goto yy1099; - if (yych >= 'U') goto yy1098; - } else { - if (yych <= 'l') { - if (yych >= 'l') goto yy1099; - } else { - if (yych == 'u') goto yy1098; - } - } -yy1097: -#line 474 "cpp.re" - { BOOST_WAVE_RET(T_INTLIT); } -#line 7430 "cpp_re.inc" -yy1098: - yych = *++YYCURSOR; - if (yych == 'L') goto yy1104; - if (yych == 'l') goto yy1104; - goto yy1097; -yy1099: - yych = *++YYCURSOR; - if (yych <= 'U') { - if (yych == 'L') goto yy1101; - if (yych <= 'T') goto yy1097; - } else { - if (yych <= 'l') { - if (yych <= 'k') goto yy1097; - goto yy1101; - } else { - if (yych != 'u') goto yy1097; - } - } - yych = *++YYCURSOR; - goto yy1097; -yy1101: - ++YYCURSOR; - if ((yych = *YYCURSOR) == 'U') goto yy1103; - if (yych == 'u') goto yy1103; -yy1102: -#line 471 "cpp.re" - { BOOST_WAVE_RET(T_LONGINTLIT); } -#line 7458 "cpp_re.inc" -yy1103: - yych = *++YYCURSOR; - goto yy1102; -yy1104: - ++YYCURSOR; - if ((yych = *YYCURSOR) == 'L') goto yy1103; - if (yych == 'l') goto yy1103; - goto yy1097; -} -#line 475 "cpp.re" - - } -} - -/* this subscanner is invoked for C++0x extended character literals */ -extcharlit: -{ - -#line 7477 "cpp_re.inc" -{ - YYCTYPE yych; - static const unsigned char yybm[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 0, 0, 0, 0, 0, 0, - 0, 128, 128, 128, 128, 128, 128, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 128, 128, 128, 128, 128, 128, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - }; - if ((YYLIMIT - YYCURSOR) < 13) YYFILL(13); - yych = *YYCURSOR; - if (yych <= 0x1F) { - if (yych <= '\n') { - if (yych <= 0x08) goto yy1107; - if (yych <= '\t') goto yy1108; - goto yy1112; - } else { - if (yych <= '\f') goto yy1108; - if (yych <= '\r') goto yy1112; - } - } else { - if (yych <= '>') { - if (yych == '\'') goto yy1112; - goto yy1108; - } else { - if (yych <= '?') goto yy1110; - if (yych == '\\') goto yy1111; - goto yy1108; - } - } -yy1107: - YYCURSOR = YYMARKER; - goto yy1109; -yy1108: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '\'') goto yy1120; -yy1109: -#line 487 "cpp.re" - { BOOST_WAVE_RET(TOKEN_FROM_ID(*s->tok, UnknownTokenType)); } -#line 7544 "cpp_re.inc" -yy1110: - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '\'') goto yy1120; - if (yych == '?') goto yy1135; - goto yy1109; -yy1111: - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '`') { - if (yych <= '7') { - if (yych <= '&') { - if (yych == '"') goto yy1115; - goto yy1109; - } else { - if (yych <= '\'') goto yy1115; - if (yych <= '/') goto yy1109; - goto yy1118; - } - } else { - if (yych <= 'T') { - if (yych == '?') goto yy1116; - goto yy1109; - } else { - if (yych <= 'U') goto yy1114; - if (yych == '\\') goto yy1115; - goto yy1109; - } - } - } else { - if (yych <= 'r') { - if (yych <= 'f') { - if (yych <= 'b') goto yy1115; - if (yych <= 'e') goto yy1109; - goto yy1115; - } else { - if (yych == 'n') goto yy1115; - if (yych <= 'q') goto yy1109; - goto yy1115; - } - } else { - if (yych <= 'u') { - if (yych <= 's') goto yy1109; - if (yych <= 't') goto yy1115; - goto yy1113; - } else { - if (yych <= 'v') goto yy1115; - if (yych == 'x') goto yy1117; - goto yy1109; - } - } - } -yy1112: - yych = *++YYCURSOR; - goto yy1109; -yy1113: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych <= '9') goto yy1132; - goto yy1107; - } else { - if (yych <= 'F') goto yy1132; - if (yych <= '`') goto yy1107; - if (yych <= 'f') goto yy1132; - goto yy1107; - } -yy1114: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych <= '9') goto yy1125; - goto yy1107; - } else { - if (yych <= 'F') goto yy1125; - if (yych <= '`') goto yy1107; - if (yych <= 'f') goto yy1125; - goto yy1107; - } -yy1115: - yych = *++YYCURSOR; - if (yych == '\'') goto yy1120; - goto yy1107; -yy1116: - yych = *++YYCURSOR; - if (yych == '\'') goto yy1120; - if (yych == '?') goto yy1124; - goto yy1107; -yy1117: - yych = *++YYCURSOR; - if (yych == '\'') goto yy1107; - goto yy1123; -yy1118: - yych = *++YYCURSOR; - if (yych == '\'') goto yy1120; - if (yych <= '/') goto yy1107; - if (yych >= '8') goto yy1107; - yych = *++YYCURSOR; - if (yych == '\'') goto yy1120; - if (yych <= '/') goto yy1107; - if (yych <= '7') goto yy1115; - goto yy1107; -yy1120: - ++YYCURSOR; -#line 484 "cpp.re" - { BOOST_WAVE_RET(T_CHARLIT); } -#line 7649 "cpp_re.inc" -yy1122: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; -yy1123: - if (yybm[0+yych] & 128) { - goto yy1122; - } - if (yych == '\'') goto yy1120; - goto yy1107; -yy1124: - yych = *++YYCURSOR; - if (yych == '/') goto yy1115; - goto yy1107; -yy1125: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych >= ':') goto yy1107; - } else { - if (yych <= 'F') goto yy1126; - if (yych <= '`') goto yy1107; - if (yych >= 'g') goto yy1107; - } -yy1126: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych >= ':') goto yy1107; - } else { - if (yych <= 'F') goto yy1127; - if (yych <= '`') goto yy1107; - if (yych >= 'g') goto yy1107; - } -yy1127: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych >= ':') goto yy1107; - } else { - if (yych <= 'F') goto yy1128; - if (yych <= '`') goto yy1107; - if (yych >= 'g') goto yy1107; - } -yy1128: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych >= ':') goto yy1107; - } else { - if (yych <= 'F') goto yy1129; - if (yych <= '`') goto yy1107; - if (yych >= 'g') goto yy1107; - } -yy1129: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych >= ':') goto yy1107; - } else { - if (yych <= 'F') goto yy1130; - if (yych <= '`') goto yy1107; - if (yych >= 'g') goto yy1107; - } -yy1130: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych >= ':') goto yy1107; - } else { - if (yych <= 'F') goto yy1131; - if (yych <= '`') goto yy1107; - if (yych >= 'g') goto yy1107; - } -yy1131: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych <= '9') goto yy1115; - goto yy1107; - } else { - if (yych <= 'F') goto yy1115; - if (yych <= '`') goto yy1107; - if (yych <= 'f') goto yy1115; - goto yy1107; - } -yy1132: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych >= ':') goto yy1107; - } else { - if (yych <= 'F') goto yy1133; - if (yych <= '`') goto yy1107; - if (yych >= 'g') goto yy1107; - } -yy1133: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych >= ':') goto yy1107; - } else { - if (yych <= 'F') goto yy1134; - if (yych <= '`') goto yy1107; - if (yych >= 'g') goto yy1107; - } -yy1134: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1107; - if (yych <= '9') goto yy1115; - goto yy1107; - } else { - if (yych <= 'F') goto yy1115; - if (yych <= '`') goto yy1107; - if (yych <= 'f') goto yy1115; - goto yy1107; - } -yy1135: - yych = *++YYCURSOR; - if (yych != '/') goto yy1107; - ++YYCURSOR; - if ((yych = *YYCURSOR) <= '`') { - if (yych <= '7') { - if (yych <= '&') { - if (yych == '"') goto yy1115; - goto yy1107; - } else { - if (yych <= '\'') goto yy1115; - if (yych <= '/') goto yy1107; - goto yy1118; - } - } else { - if (yych <= 'T') { - if (yych == '?') goto yy1116; - goto yy1107; - } else { - if (yych <= 'U') goto yy1114; - if (yych == '\\') goto yy1115; - goto yy1107; - } - } - } else { - if (yych <= 'r') { - if (yych <= 'f') { - if (yych <= 'b') goto yy1115; - if (yych <= 'e') goto yy1107; - goto yy1115; - } else { - if (yych == 'n') goto yy1115; - if (yych <= 'q') goto yy1107; - goto yy1115; - } - } else { - if (yych <= 'u') { - if (yych <= 's') goto yy1107; - if (yych <= 't') goto yy1115; - goto yy1113; - } else { - if (yych <= 'v') goto yy1115; - if (yych == 'x') goto yy1117; - goto yy1107; - } - } - } -} -#line 488 "cpp.re" - -} - -/* this subscanner is invoked for C++0x extended character string literals */ -extstringlit: -{ - -#line 7824 "cpp_re.inc" -{ - YYCTYPE yych; - unsigned int yyaccept = 0; - static const unsigned char yybm[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 16, 0, 16, 16, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 16, 16, 0, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 16, 16, 16, 16, 16, 32, - 16, 144, 144, 144, 144, 144, 144, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 64, 16, 16, 16, - 16, 144, 144, 144, 144, 144, 144, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - }; - if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); - yych = *YYCURSOR; - if (yych <= 0x1F) { - if (yych <= '\n') { - if (yych <= 0x08) goto yy1139; - if (yych <= '\t') goto yy1140; - goto yy1146; - } else { - if (yych <= '\f') goto yy1140; - if (yych <= '\r') goto yy1146; - } - } else { - if (yych <= '>') { - if (yych == '"') goto yy1144; - goto yy1140; - } else { - if (yych <= '?') goto yy1142; - if (yych == '\\') goto yy1143; - goto yy1140; - } - } -yy1139: - YYCURSOR = YYMARKER; - if (yyaccept <= 0) { - goto yy1141; - } else { - goto yy1145; - } -yy1140: - yyaccept = 0; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '\n') { - if (yych == '\t') goto yy1150; - } else { - if (yych <= '\f') goto yy1150; - if (yych >= ' ') goto yy1150; - } -yy1141: -#line 499 "cpp.re" - { BOOST_WAVE_RET(TOKEN_FROM_ID(*s->tok, UnknownTokenType)); } -#line 7902 "cpp_re.inc" -yy1142: - yyaccept = 0; - yych = *(YYMARKER = ++YYCURSOR); - if (yybm[0+yych] & 32) { - goto yy1158; - } - if (yych <= '\n') { - if (yych == '\t') goto yy1150; - goto yy1141; - } else { - if (yych <= '\f') goto yy1150; - if (yych <= 0x1F) goto yy1141; - goto yy1150; - } -yy1143: - yyaccept = 0; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '`') { - if (yych <= '7') { - if (yych <= '&') { - if (yych == '"') goto yy1149; - goto yy1141; - } else { - if (yych <= '\'') goto yy1149; - if (yych <= '/') goto yy1141; - goto yy1153; - } - } else { - if (yych <= 'T') { - if (yych == '?') goto yy1151; - goto yy1141; - } else { - if (yych <= 'U') goto yy1148; - if (yych == '\\') goto yy1149; - goto yy1141; - } - } - } else { - if (yych <= 'r') { - if (yych <= 'f') { - if (yych <= 'b') goto yy1149; - if (yych <= 'e') goto yy1141; - goto yy1149; - } else { - if (yych == 'n') goto yy1149; - if (yych <= 'q') goto yy1141; - goto yy1149; - } - } else { - if (yych <= 'u') { - if (yych <= 's') goto yy1141; - if (yych <= 't') goto yy1149; - goto yy1147; - } else { - if (yych <= 'v') goto yy1149; - if (yych == 'x') goto yy1152; - goto yy1141; - } - } - } -yy1144: - ++YYCURSOR; -yy1145: -#line 496 "cpp.re" - { BOOST_WAVE_RET(T_STRINGLIT); } -#line 7968 "cpp_re.inc" -yy1146: - yych = *++YYCURSOR; - goto yy1141; -yy1147: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych <= '9') goto yy1187; - goto yy1139; - } else { - if (yych <= 'F') goto yy1187; - if (yych <= '`') goto yy1139; - if (yych <= 'f') goto yy1187; - goto yy1139; - } -yy1148: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych <= '9') goto yy1180; - goto yy1139; - } else { - if (yych <= 'F') goto yy1180; - if (yych <= '`') goto yy1139; - if (yych <= 'f') goto yy1180; - goto yy1139; - } -yy1149: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; -yy1150: - if (yybm[0+yych] & 16) { - goto yy1149; - } - if (yych <= '!') goto yy1139; - if (yych <= '"') goto yy1155; - if (yych <= '[') goto yy1156; - goto yy1157; -yy1151: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 16) { - goto yy1149; - } - if (yych <= '!') goto yy1139; - if (yych <= '"') goto yy1155; - if (yych <= '[') goto yy1179; - goto yy1157; -yy1152: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 128) { - goto yy1166; - } - goto yy1139; -yy1153: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '"') { - if (yych <= '\n') { - if (yych == '\t') goto yy1149; - goto yy1139; - } else { - if (yych <= '\f') goto yy1149; - if (yych <= 0x1F) goto yy1139; - if (yych <= '!') goto yy1149; - goto yy1155; - } - } else { - if (yych <= '>') { - if (yych <= '/') goto yy1149; - if (yych >= '8') goto yy1149; - } else { - if (yych <= '?') goto yy1156; - if (yych == '\\') goto yy1157; - goto yy1149; - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 16) { - goto yy1149; - } - if (yych <= '!') goto yy1139; - if (yych <= '"') goto yy1155; - if (yych <= '[') goto yy1156; - goto yy1157; -yy1155: - yych = *++YYCURSOR; - goto yy1145; -yy1156: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 16) { - goto yy1149; - } - if (yych <= '!') goto yy1139; - if (yych <= '"') goto yy1155; - if (yych <= '[') goto yy1158; -yy1157: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '`') { - if (yych <= '7') { - if (yych <= '&') { - if (yych == '"') goto yy1149; - goto yy1139; - } else { - if (yych <= '\'') goto yy1149; - if (yych <= '/') goto yy1139; - goto yy1153; - } - } else { - if (yych <= 'T') { - if (yych == '?') goto yy1151; - goto yy1139; - } else { - if (yych <= 'U') goto yy1148; - if (yych == '\\') goto yy1149; - goto yy1139; - } - } - } else { - if (yych <= 'r') { - if (yych <= 'f') { - if (yych <= 'b') goto yy1149; - if (yych <= 'e') goto yy1139; - goto yy1149; - } else { - if (yych == 'n') goto yy1149; - if (yych <= 'q') goto yy1139; - goto yy1149; - } - } else { - if (yych <= 'u') { - if (yych <= 's') goto yy1139; - if (yych <= 't') goto yy1149; - goto yy1147; - } else { - if (yych <= 'v') goto yy1149; - if (yych == 'x') goto yy1152; - goto yy1139; - } - } - } -yy1158: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 32) { - goto yy1158; - } - if (yych <= '!') { - if (yych <= '\n') { - if (yych == '\t') goto yy1149; - goto yy1139; - } else { - if (yych <= '\f') goto yy1149; - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } - } else { - if (yych <= '/') { - if (yych <= '"') goto yy1155; - if (yych <= '.') goto yy1149; - } else { - if (yych == '\\') goto yy1157; - goto yy1149; - } - } -yy1160: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 64) { - goto yy1160; - } - if (yych <= '7') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1164; - if (yych <= '/') goto yy1149; - goto yy1153; - } - } - } else { - if (yych <= 'U') { - if (yych == '?') goto yy1165; - if (yych <= 'T') goto yy1149; - goto yy1163; - } else { - if (yych <= 'u') { - if (yych <= 't') goto yy1149; - } else { - if (yych == 'x') goto yy1166; - goto yy1149; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1155; - if (yych <= '/') goto yy1149; - goto yy1176; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy1156; - if (yych <= '@') goto yy1149; - goto yy1176; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy1149; - goto yy1157; - } else { - if (yych <= '`') goto yy1149; - if (yych <= 'f') goto yy1176; - goto yy1149; - } - } - } -yy1163: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1155; - if (yych <= '/') goto yy1149; - goto yy1169; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy1156; - if (yych <= '@') goto yy1149; - goto yy1169; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy1149; - goto yy1157; - } else { - if (yych <= '`') goto yy1149; - if (yych <= 'f') goto yy1169; - goto yy1149; - } - } - } -yy1164: - yyaccept = 1; - YYMARKER = ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 16) { - goto yy1149; - } - if (yych <= '!') goto yy1145; - if (yych <= '"') goto yy1155; - if (yych <= '[') goto yy1156; - goto yy1157; -yy1165: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 16) { - goto yy1149; - } - if (yych <= '!') goto yy1139; - if (yych <= '"') goto yy1155; - if (yych <= '[') goto yy1168; - goto yy1157; -yy1166: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 128) { - goto yy1166; - } - if (yych <= '!') { - if (yych <= '\n') { - if (yych == '\t') goto yy1149; - goto yy1139; - } else { - if (yych <= '\f') goto yy1149; - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } - } else { - if (yych <= '?') { - if (yych <= '"') goto yy1155; - if (yych <= '>') goto yy1149; - goto yy1156; - } else { - if (yych == '\\') goto yy1157; - goto yy1149; - } - } -yy1168: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 32) { - goto yy1158; - } - if (yych <= '!') { - if (yych <= '\n') { - if (yych == '\t') goto yy1149; - goto yy1139; - } else { - if (yych <= '\f') goto yy1149; - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } - } else { - if (yych <= '/') { - if (yych <= '"') goto yy1155; - if (yych <= '.') goto yy1149; - goto yy1160; - } else { - if (yych == '\\') goto yy1157; - goto yy1149; - } - } -yy1169: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1155; - if (yych <= '/') goto yy1149; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy1156; - if (yych <= '@') goto yy1149; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy1149; - goto yy1157; - } else { - if (yych <= '`') goto yy1149; - if (yych >= 'g') goto yy1149; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1155; - if (yych <= '/') goto yy1149; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy1156; - if (yych <= '@') goto yy1149; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy1149; - goto yy1157; - } else { - if (yych <= '`') goto yy1149; - if (yych >= 'g') goto yy1149; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1155; - if (yych <= '/') goto yy1149; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy1156; - if (yych <= '@') goto yy1149; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy1149; - goto yy1157; - } else { - if (yych <= '`') goto yy1149; - if (yych >= 'g') goto yy1149; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1155; - if (yych <= '/') goto yy1149; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy1156; - if (yych <= '@') goto yy1149; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy1149; - goto yy1157; - } else { - if (yych <= '`') goto yy1149; - if (yych >= 'g') goto yy1149; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1155; - if (yych <= '/') goto yy1149; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy1156; - if (yych <= '@') goto yy1149; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy1149; - goto yy1157; - } else { - if (yych <= '`') goto yy1149; - if (yych >= 'g') goto yy1149; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1155; - if (yych <= '/') goto yy1149; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy1156; - if (yych <= '@') goto yy1149; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy1149; - goto yy1157; - } else { - if (yych <= '`') goto yy1149; - if (yych >= 'g') goto yy1149; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 16) { - goto yy1149; - } - if (yych <= '!') goto yy1139; - if (yych <= '"') goto yy1155; - if (yych <= '[') goto yy1156; - goto yy1157; -yy1176: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1155; - if (yych <= '/') goto yy1149; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy1156; - if (yych <= '@') goto yy1149; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy1149; - goto yy1157; - } else { - if (yych <= '`') goto yy1149; - if (yych >= 'g') goto yy1149; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '9') { - if (yych <= '\f') { - if (yych == '\t') goto yy1149; - if (yych <= '\n') goto yy1139; - goto yy1149; - } else { - if (yych <= '!') { - if (yych <= 0x1F) goto yy1139; - goto yy1149; - } else { - if (yych <= '"') goto yy1155; - if (yych <= '/') goto yy1149; - } - } - } else { - if (yych <= 'F') { - if (yych == '?') goto yy1156; - if (yych <= '@') goto yy1149; - } else { - if (yych <= '\\') { - if (yych <= '[') goto yy1149; - goto yy1157; - } else { - if (yych <= '`') goto yy1149; - if (yych >= 'g') goto yy1149; - } - } - } - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 16) { - goto yy1149; - } - if (yych <= '!') goto yy1139; - if (yych <= '"') goto yy1155; - if (yych <= '[') goto yy1156; - goto yy1157; -yy1179: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 16) { - goto yy1149; - } - if (yych <= '!') goto yy1139; - if (yych <= '"') goto yy1155; - if (yych <= '[') goto yy1158; - goto yy1157; -yy1180: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych >= ':') goto yy1139; - } else { - if (yych <= 'F') goto yy1181; - if (yych <= '`') goto yy1139; - if (yych >= 'g') goto yy1139; - } -yy1181: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych >= ':') goto yy1139; - } else { - if (yych <= 'F') goto yy1182; - if (yych <= '`') goto yy1139; - if (yych >= 'g') goto yy1139; - } -yy1182: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych >= ':') goto yy1139; - } else { - if (yych <= 'F') goto yy1183; - if (yych <= '`') goto yy1139; - if (yych >= 'g') goto yy1139; - } -yy1183: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych >= ':') goto yy1139; - } else { - if (yych <= 'F') goto yy1184; - if (yych <= '`') goto yy1139; - if (yych >= 'g') goto yy1139; - } -yy1184: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych >= ':') goto yy1139; - } else { - if (yych <= 'F') goto yy1185; - if (yych <= '`') goto yy1139; - if (yych >= 'g') goto yy1139; - } -yy1185: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych >= ':') goto yy1139; - } else { - if (yych <= 'F') goto yy1186; - if (yych <= '`') goto yy1139; - if (yych >= 'g') goto yy1139; - } -yy1186: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych <= '9') goto yy1149; - goto yy1139; - } else { - if (yych <= 'F') goto yy1149; - if (yych <= '`') goto yy1139; - if (yych <= 'f') goto yy1149; - goto yy1139; - } -yy1187: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych >= ':') goto yy1139; - } else { - if (yych <= 'F') goto yy1188; - if (yych <= '`') goto yy1139; - if (yych >= 'g') goto yy1139; - } -yy1188: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych >= ':') goto yy1139; - } else { - if (yych <= 'F') goto yy1189; - if (yych <= '`') goto yy1139; - if (yych >= 'g') goto yy1139; - } -yy1189: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1139; - if (yych <= '9') goto yy1149; - goto yy1139; - } else { - if (yych <= 'F') goto yy1149; - if (yych <= '`') goto yy1139; - if (yych <= 'f') goto yy1149; - goto yy1139; - } -} -#line 500 "cpp.re" - -} - -extrawstringlit: -{ - -#line 8743 "cpp_re.inc" -{ - YYCTYPE yych; - static const unsigned char yybm[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 0, 0, 0, 0, 0, 0, - 0, 128, 128, 128, 128, 128, 128, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 128, 128, 128, 128, 128, 128, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - }; - if ((YYLIMIT - YYCURSOR) < 12) YYFILL(12); - yych = *YYCURSOR; - if (yych <= 0x1F) { - if (yych <= '\n') { - if (yych <= 0x08) goto yy1192; - if (yych <= '\t') goto yy1193; - goto yy1197; - } else { - if (yych <= '\f') goto yy1193; - if (yych <= '\r') goto yy1199; - } - } else { - if (yych <= '>') { - if (yych == '"') goto yy1200; - goto yy1193; - } else { - if (yych <= '?') goto yy1195; - if (yych == '\\') goto yy1196; - goto yy1193; - } - } -yy1192: - YYCURSOR = YYMARKER; - goto yy1194; -yy1193: - ++YYCURSOR; -yy1194: -#line 507 "cpp.re" - { - goto extrawstringlit; - } -#line 8811 "cpp_re.inc" -yy1195: - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '?') goto yy1221; - goto yy1194; -yy1196: - yych = *++YYCURSOR; - if (yych <= '`') { - if (yych <= '7') { - if (yych <= '&') { - if (yych == '"') goto yy1193; - goto yy1192; - } else { - if (yych <= '\'') goto yy1193; - if (yych <= '/') goto yy1192; - goto yy1206; - } - } else { - if (yych <= 'T') { - if (yych == '?') goto yy1204; - goto yy1192; - } else { - if (yych <= 'U') goto yy1203; - if (yych == '\\') goto yy1193; - goto yy1192; - } - } - } else { - if (yych <= 'r') { - if (yych <= 'f') { - if (yych <= 'b') goto yy1193; - if (yych <= 'e') goto yy1192; - goto yy1193; - } else { - if (yych == 'n') goto yy1193; - if (yych <= 'q') goto yy1192; - goto yy1193; - } - } else { - if (yych <= 'u') { - if (yych <= 's') goto yy1192; - if (yych <= 't') goto yy1193; - goto yy1202; - } else { - if (yych <= 'v') goto yy1193; - if (yych == 'x') goto yy1205; - goto yy1192; - } - } - } -yy1197: - ++YYCURSOR; -yy1198: -#line 512 "cpp.re" - { - s->line += count_backslash_newlines(s, cursor) +1; - cursor.column = 1; - goto extrawstringlit; - } -#line 8870 "cpp_re.inc" -yy1199: - yych = *++YYCURSOR; - if (yych == '\n') goto yy1197; - goto yy1198; -yy1200: - ++YYCURSOR; -#line 518 "cpp.re" - { BOOST_WAVE_RET(T_RAWSTRINGLIT); } -#line 8879 "cpp_re.inc" -yy1202: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych <= '9') goto yy1218; - goto yy1192; - } else { - if (yych <= 'F') goto yy1218; - if (yych <= '`') goto yy1192; - if (yych <= 'f') goto yy1218; - goto yy1192; - } -yy1203: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych <= '9') goto yy1211; - goto yy1192; - } else { - if (yych <= 'F') goto yy1211; - if (yych <= '`') goto yy1192; - if (yych <= 'f') goto yy1211; - goto yy1192; - } -yy1204: - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '?') goto yy1210; - goto yy1194; -yy1205: - yych = *++YYCURSOR; - if (yybm[0+yych] & 128) { - goto yy1208; - } - goto yy1192; -yy1206: - yych = *++YYCURSOR; - if (yych <= '/') goto yy1194; - if (yych >= '8') goto yy1194; - yych = *++YYCURSOR; - if (yych <= '/') goto yy1194; - if (yych <= '7') goto yy1193; - goto yy1194; -yy1208: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 128) { - goto yy1208; - } - goto yy1194; -yy1210: - yych = *++YYCURSOR; - if (yych == '/') goto yy1193; - goto yy1192; -yy1211: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych >= ':') goto yy1192; - } else { - if (yych <= 'F') goto yy1212; - if (yych <= '`') goto yy1192; - if (yych >= 'g') goto yy1192; - } -yy1212: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych >= ':') goto yy1192; - } else { - if (yych <= 'F') goto yy1213; - if (yych <= '`') goto yy1192; - if (yych >= 'g') goto yy1192; - } -yy1213: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych >= ':') goto yy1192; - } else { - if (yych <= 'F') goto yy1214; - if (yych <= '`') goto yy1192; - if (yych >= 'g') goto yy1192; - } -yy1214: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych >= ':') goto yy1192; - } else { - if (yych <= 'F') goto yy1215; - if (yych <= '`') goto yy1192; - if (yych >= 'g') goto yy1192; - } -yy1215: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych >= ':') goto yy1192; - } else { - if (yych <= 'F') goto yy1216; - if (yych <= '`') goto yy1192; - if (yych >= 'g') goto yy1192; - } -yy1216: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych >= ':') goto yy1192; - } else { - if (yych <= 'F') goto yy1217; - if (yych <= '`') goto yy1192; - if (yych >= 'g') goto yy1192; - } -yy1217: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych <= '9') goto yy1193; - goto yy1192; - } else { - if (yych <= 'F') goto yy1193; - if (yych <= '`') goto yy1192; - if (yych <= 'f') goto yy1193; - goto yy1192; - } -yy1218: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych >= ':') goto yy1192; - } else { - if (yych <= 'F') goto yy1219; - if (yych <= '`') goto yy1192; - if (yych >= 'g') goto yy1192; - } -yy1219: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych >= ':') goto yy1192; - } else { - if (yych <= 'F') goto yy1220; - if (yych <= '`') goto yy1192; - if (yych >= 'g') goto yy1192; - } -yy1220: - yych = *++YYCURSOR; - if (yych <= '@') { - if (yych <= '/') goto yy1192; - if (yych <= '9') goto yy1193; - goto yy1192; - } else { - if (yych <= 'F') goto yy1193; - if (yych <= '`') goto yy1192; - if (yych <= 'f') goto yy1193; - goto yy1192; - } -yy1221: - ++YYCURSOR; - if ((yych = *YYCURSOR) == '/') goto yy1196; - goto yy1192; -} -#line 519 "cpp.re" - -} diff --git a/extern/shiny/Preprocessor/instantiate_cpp_exprgrammar.cpp b/extern/shiny/Preprocessor/instantiate_cpp_exprgrammar.cpp deleted file mode 100644 index 7318c29fa..000000000 --- a/extern/shiny/Preprocessor/instantiate_cpp_exprgrammar.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - http://www.boost.org/ - - Copyright (c) 2001-2011 Hartmut Kaiser. 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) -=============================================================================*/ - -#define BOOST_WAVE_SOURCE 1 - -// disable stupid compiler warnings -#include -#include - -#if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 - -#include -#include - -#include -#include - -#include - -// this must occur after all of the includes and before any code appears -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_PREFIX -#endif - -/////////////////////////////////////////////////////////////////////////////// -// -// Explicit instantiation of the expression_grammar_gen template with the -// correct lexer iterator type. This instantiates the corresponding parse -// function, which in turn instantiates the expression_grammar object (see -// wave/grammars/cpp_expression_grammar.hpp) -// -/////////////////////////////////////////////////////////////////////////////// - -// if you want to use your own token type the following line must be adjusted -typedef boost::wave::cpplexer::lex_token<> token_type; - -// no need to change anything below -template struct boost::wave::grammars::expression_grammar_gen; - -// the suffix header occurs after all of the code -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_SUFFIX -#endif - -#endif // #if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 - diff --git a/extern/shiny/Preprocessor/instantiate_cpp_grammar.cpp b/extern/shiny/Preprocessor/instantiate_cpp_grammar.cpp deleted file mode 100644 index 89cc3d7f3..000000000 --- a/extern/shiny/Preprocessor/instantiate_cpp_grammar.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - http://www.boost.org/ - - Copyright (c) 2001-2011 Hartmut Kaiser. 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) -=============================================================================*/ - -#define BOOST_WAVE_SOURCE 1 - -// disable stupid compiler warnings -#include -#include - -#if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 - -#include -#include - -#include -#include - -#include - -// this must occur after all of the includes and before any code appears -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_PREFIX -#endif - -/////////////////////////////////////////////////////////////////////////////// -// -// Explicit instantiation of the cpp_grammar_gen template with the correct -// token type. This instantiates the corresponding pt_parse function, which -// in turn instantiates the cpp_grammar object -// (see wave/grammars/cpp_grammar.hpp) -// -/////////////////////////////////////////////////////////////////////////////// - -// if you want to use your own token type the following line must be adjusted -typedef boost::wave::cpplexer::lex_token<> token_type; - -// no need to change anything below -typedef boost::wave::cpplexer::lex_iterator lexer_type; -typedef std::list > - token_sequence_type; - -template struct boost::wave::grammars::cpp_grammar_gen; - -// the suffix header occurs after all of the code -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_SUFFIX -#endif - -#endif // #if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 - diff --git a/extern/shiny/Preprocessor/instantiate_cpp_literalgrs.cpp b/extern/shiny/Preprocessor/instantiate_cpp_literalgrs.cpp deleted file mode 100644 index 4fbfb87f2..000000000 --- a/extern/shiny/Preprocessor/instantiate_cpp_literalgrs.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - http://www.boost.org/ - - Copyright (c) 2001-2011 Hartmut Kaiser. 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) -=============================================================================*/ - -#define BOOST_WAVE_SOURCE 1 - -// disable stupid compiler warnings -#include -#include - -#if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 - -#include - -#include -#include - -#include -#include -#include - -// this must occur after all of the includes and before any code appears -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_PREFIX -#endif - -/////////////////////////////////////////////////////////////////////////////// -// -// Explicit instantiation of the intlit_grammar_gen and chlit_grammar_gen -// templates with the correct token type. This instantiates the corresponding -// parse function, which in turn instantiates the corresponding parser object. -// -/////////////////////////////////////////////////////////////////////////////// - -typedef boost::wave::cpplexer::lex_token<> token_type; - -// no need to change anything below -template struct boost::wave::grammars::intlit_grammar_gen; -#if BOOST_WAVE_WCHAR_T_SIGNEDNESS == BOOST_WAVE_WCHAR_T_AUTOSELECT || \ - BOOST_WAVE_WCHAR_T_SIGNEDNESS == BOOST_WAVE_WCHAR_T_FORCE_SIGNED -template struct boost::wave::grammars::chlit_grammar_gen; -#endif -template struct boost::wave::grammars::chlit_grammar_gen; - -// the suffix header occurs after all of the code -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_SUFFIX -#endif - -#endif // #if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 - diff --git a/extern/shiny/Preprocessor/instantiate_defined_grammar.cpp b/extern/shiny/Preprocessor/instantiate_defined_grammar.cpp deleted file mode 100644 index b7afe3f1e..000000000 --- a/extern/shiny/Preprocessor/instantiate_defined_grammar.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - http://www.boost.org/ - - Copyright (c) 2001-2011 Hartmut Kaiser. 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) -=============================================================================*/ - -#define BOOST_WAVE_SOURCE 1 - -// disable stupid compiler warnings -#include -#include - -#if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 - -#include - -#include -#include - -#include - -// this must occur after all of the includes and before any code appears -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_PREFIX -#endif - -/////////////////////////////////////////////////////////////////////////////// -// -// Explicit instantiation of the defined_grammar_gen template -// with the correct token type. This instantiates the corresponding parse -// function, which in turn instantiates the defined_grammar -// object (see wave/grammars/cpp_defined_grammar.hpp) -// -/////////////////////////////////////////////////////////////////////////////// - -// if you want to use your own token type the following line must be adjusted -typedef boost::wave::cpplexer::lex_token<> token_type; - -// no need to change anything below -typedef boost::wave::cpplexer::lex_iterator lexer_type; -template struct boost::wave::grammars::defined_grammar_gen; - -// the suffix header occurs after all of the code -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_SUFFIX -#endif - -#endif // #if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 - diff --git a/extern/shiny/Preprocessor/instantiate_predef_macros.cpp b/extern/shiny/Preprocessor/instantiate_predef_macros.cpp deleted file mode 100644 index 758ad9734..000000000 --- a/extern/shiny/Preprocessor/instantiate_predef_macros.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - http://www.boost.org/ - - Copyright (c) 2001-2011 Hartmut Kaiser. 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) -=============================================================================*/ - -#define BOOST_WAVE_SOURCE 1 - -// disable stupid compiler warnings -#include -#include - -#if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 - -#include - -#include -#include - -#include - -// this must occur after all of the includes and before any code appears -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_PREFIX -#endif - -/////////////////////////////////////////////////////////////////////////////// -// -// Explicit instantiation of the predefined_macros_grammar_gen template -// with the correct token type. This instantiates the corresponding pt_parse -// function, which in turn instantiates the cpp_predefined_macros_grammar -// object (see wave/grammars/cpp_predef_macros_grammar.hpp) -// -/////////////////////////////////////////////////////////////////////////////// - -// if you want to use your own token type the following line must be adjusted -typedef boost::wave::cpplexer::lex_token<> token_type; - -// no need to change anything below -typedef boost::wave::cpplexer::lex_iterator lexer_type; -template struct boost::wave::grammars::predefined_macros_grammar_gen; - -// the suffix header occurs after all of the code -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_SUFFIX -#endif - -#endif // #if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 - diff --git a/extern/shiny/Preprocessor/instantiate_re2c_lexer.cpp b/extern/shiny/Preprocessor/instantiate_re2c_lexer.cpp deleted file mode 100644 index cd1b8898f..000000000 --- a/extern/shiny/Preprocessor/instantiate_re2c_lexer.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - Explicit instantiation of the lex_functor generation function - - http://www.boost.org/ - - Copyright (c) 2001-2011 Hartmut Kaiser. 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) -=============================================================================*/ - -#define BOOST_WAVE_SOURCE 1 - -// disable stupid compiler warnings -#include -#include // configuration data - -#if BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION != 0 - -#include - -#include -#include -#include - -/////////////////////////////////////////////////////////////////////////////// -// The following file needs to be included only once throughout the whole -// program. -#include - -// this must occur after all of the includes and before any code appears -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_PREFIX -#endif - -/////////////////////////////////////////////////////////////////////////////// -// -// This instantiates the correct 'new_lexer' function, which generates the -// C++ lexer used in this sample. You will have to instantiate the -// new_lexer_gen<> template with the same iterator type, as you have used for -// instantiating the boost::wave::context<> object. -// -// This is moved into a separate compilation unit to decouple the compilation -// of the C++ lexer from the compilation of the other modules, which helps to -// reduce compilation time. -// -// The template parameter(s) supplied should be identical to the first -// parameter supplied while instantiating the boost::wave::context<> template -// (see the file cpp.cpp). -// -/////////////////////////////////////////////////////////////////////////////// - -// if you want to use another iterator type for the underlying input stream -// a corresponding explicit template instantiation needs to be added below -template struct boost::wave::cpplexer::new_lexer_gen< - BOOST_WAVE_STRINGTYPE::iterator>; -template struct boost::wave::cpplexer::new_lexer_gen< - BOOST_WAVE_STRINGTYPE::const_iterator>; - -// the suffix header occurs after all of the code -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_SUFFIX -#endif - -#endif // BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION != 0 diff --git a/extern/shiny/Preprocessor/instantiate_re2c_lexer_str.cpp b/extern/shiny/Preprocessor/instantiate_re2c_lexer_str.cpp deleted file mode 100644 index 138ed6c3b..000000000 --- a/extern/shiny/Preprocessor/instantiate_re2c_lexer_str.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - Explicit instantiation of the lex_functor generation function - - http://www.boost.org/ - - Copyright (c) 2001-2011 Hartmut Kaiser. 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) -=============================================================================*/ - -#define BOOST_WAVE_SOURCE 1 - -// disable stupid compiler warnings -#include -#include // configuration data - -#if BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION != 0 - -#include - -#include -#include -#include - -/////////////////////////////////////////////////////////////////////////////// -// The following file needs to be included only once throughout the whole -// program. -#include - -// this must occur after all of the includes and before any code appears -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_PREFIX -#endif - -/////////////////////////////////////////////////////////////////////////////// -// -// If you've used another iterator type as std::string::iterator, you have to -// instantiate the new_lexer_gen<> template for this iterator type too. -// The reason is, that the library internally uses the new_lexer_gen<> -// template with a std::string::iterator. (You just have to undefine the -// following line.) -// -// This is moved into a separate compilation unit to decouple the compilation -// of the C++ lexer from the compilation of the other modules, which helps to -// reduce compilation time. -// -// The template parameter(s) supplied should be identical to the first -// parameter supplied while instantiating the boost::wave::context<> template -// (see the file cpp.cpp). -// -/////////////////////////////////////////////////////////////////////////////// - -#if !defined(BOOST_WAVE_STRINGTYPE_USE_STDSTRING) -template struct boost::wave::cpplexer::new_lexer_gen; -template struct boost::wave::cpplexer::new_lexer_gen; -#endif - -// the suffix header occurs after all of the code -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_SUFFIX -#endif - -#endif // BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION != 0 diff --git a/extern/shiny/Preprocessor/token_ids.cpp b/extern/shiny/Preprocessor/token_ids.cpp deleted file mode 100644 index 35e7725b4..000000000 --- a/extern/shiny/Preprocessor/token_ids.cpp +++ /dev/null @@ -1,447 +0,0 @@ -/*============================================================================= - Boost.Wave: A Standard compliant C++ preprocessor library - The definition of a default set of token identifiers and related - functions. - - http://www.boost.org/ - - Copyright (c) 2001-2011 Hartmut Kaiser. 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) -=============================================================================*/ - -#define BOOST_WAVE_SOURCE 1 - -// disable stupid compiler warnings -#include - -#include -#include -#include - -#include -#include - -// this must occur after all of the includes and before any code appears -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_PREFIX -#endif - -/////////////////////////////////////////////////////////////////////////////// -namespace boost { -namespace wave { - -/////////////////////////////////////////////////////////////////////////////// -// return a token name -BOOST_WAVE_STRINGTYPE -get_token_name(token_id tokid) -{ -// Table of token names -// -// Please note that the sequence of token names must match the sequence of -// token id's defined in then enum token_id above. -static char const *tok_names[] = { - /* 256 */ "AND", - /* 257 */ "ANDAND", - /* 258 */ "ASSIGN", - /* 259 */ "ANDASSIGN", - /* 260 */ "OR", - /* 261 */ "ORASSIGN", - /* 262 */ "XOR", - /* 263 */ "XORASSIGN", - /* 264 */ "COMMA", - /* 265 */ "COLON", - /* 266 */ "DIVIDE", - /* 267 */ "DIVIDEASSIGN", - /* 268 */ "DOT", - /* 269 */ "DOTSTAR", - /* 270 */ "ELLIPSIS", - /* 271 */ "EQUAL", - /* 272 */ "GREATER", - /* 273 */ "GREATEREQUAL", - /* 274 */ "LEFTBRACE", - /* 275 */ "LESS", - /* 276 */ "LESSEQUAL", - /* 277 */ "LEFTPAREN", - /* 278 */ "LEFTBRACKET", - /* 279 */ "MINUS", - /* 280 */ "MINUSASSIGN", - /* 281 */ "MINUSMINUS", - /* 282 */ "PERCENT", - /* 283 */ "PERCENTASSIGN", - /* 284 */ "NOT", - /* 285 */ "NOTEQUAL", - /* 286 */ "OROR", - /* 287 */ "PLUS", - /* 288 */ "PLUSASSIGN", - /* 289 */ "PLUSPLUS", - /* 290 */ "ARROW", - /* 291 */ "ARROWSTAR", - /* 292 */ "QUESTION_MARK", - /* 293 */ "RIGHTBRACE", - /* 294 */ "RIGHTPAREN", - /* 295 */ "RIGHTBRACKET", - /* 296 */ "COLON_COLON", - /* 297 */ "SEMICOLON", - /* 298 */ "SHIFTLEFT", - /* 299 */ "SHIFTLEFTASSIGN", - /* 300 */ "SHIFTRIGHT", - /* 301 */ "SHIFTRIGHTASSIGN", - /* 302 */ "STAR", - /* 303 */ "COMPL", - /* 304 */ "STARASSIGN", - /* 305 */ "ASM", - /* 306 */ "AUTO", - /* 307 */ "BOOL", - /* 308 */ "FALSE", - /* 309 */ "TRUE", - /* 310 */ "BREAK", - /* 311 */ "CASE", - /* 312 */ "CATCH", - /* 313 */ "CHAR", - /* 314 */ "CLASS", - /* 315 */ "CONST", - /* 316 */ "CONSTCAST", - /* 317 */ "CONTINUE", - /* 318 */ "DEFAULT", - /* 319 */ "DELETE", - /* 320 */ "DO", - /* 321 */ "DOUBLE", - /* 322 */ "DYNAMICCAST", - /* 323 */ "ELSE", - /* 324 */ "ENUM", - /* 325 */ "EXPLICIT", - /* 326 */ "EXPORT", - /* 327 */ "EXTERN", - /* 328 */ "FLOAT", - /* 329 */ "FOR", - /* 330 */ "FRIEND", - /* 331 */ "GOTO", - /* 332 */ "IF", - /* 333 */ "INLINE", - /* 334 */ "INT", - /* 335 */ "LONG", - /* 336 */ "MUTABLE", - /* 337 */ "NAMESPACE", - /* 338 */ "NEW", - /* 339 */ "OPERATOR", - /* 340 */ "PRIVATE", - /* 341 */ "PROTECTED", - /* 342 */ "PUBLIC", - /* 343 */ "REGISTER", - /* 344 */ "REINTERPRETCAST", - /* 345 */ "RETURN", - /* 346 */ "SHORT", - /* 347 */ "SIGNED", - /* 348 */ "SIZEOF", - /* 349 */ "STATIC", - /* 350 */ "STATICCAST", - /* 351 */ "STRUCT", - /* 352 */ "SWITCH", - /* 353 */ "TEMPLATE", - /* 354 */ "THIS", - /* 355 */ "THROW", - /* 356 */ "TRY", - /* 357 */ "TYPEDEF", - /* 358 */ "TYPEID", - /* 359 */ "TYPENAME", - /* 360 */ "UNION", - /* 361 */ "UNSIGNED", - /* 362 */ "USING", - /* 363 */ "VIRTUAL", - /* 364 */ "VOID", - /* 365 */ "VOLATILE", - /* 366 */ "WCHART", - /* 367 */ "WHILE", - /* 368 */ "PP_DEFINE", - /* 369 */ "PP_IF", - /* 370 */ "PP_IFDEF", - /* 371 */ "PP_IFNDEF", - /* 372 */ "PP_ELSE", - /* 373 */ "PP_ELIF", - /* 374 */ "PP_ENDIF", - /* 375 */ "PP_ERROR", - /* 376 */ "PP_LINE", - /* 377 */ "PP_PRAGMA", - /* 378 */ "PP_UNDEF", - /* 379 */ "PP_WARNING", - /* 380 */ "IDENTIFIER", - /* 381 */ "OCTALINT", - /* 382 */ "DECIMALINT", - /* 383 */ "HEXAINT", - /* 384 */ "INTLIT", - /* 385 */ "LONGINTLIT", - /* 386 */ "FLOATLIT", - /* 387 */ "CCOMMENT", - /* 388 */ "CPPCOMMENT", - /* 389 */ "CHARLIT", - /* 390 */ "STRINGLIT", - /* 391 */ "CONTLINE", - /* 392 */ "SPACE", - /* 393 */ "SPACE2", - /* 394 */ "NEWLINE", - /* 395 */ "POUND_POUND", - /* 396 */ "POUND", - /* 397 */ "ANY", - /* 398 */ "PP_INCLUDE", - /* 399 */ "PP_QHEADER", - /* 400 */ "PP_HHEADER", - /* 401 */ "EOF", - /* 402 */ "EOI", - /* 403 */ "PP_NUMBER", - - // MS extensions - /* 404 */ "MSEXT_INT8", - /* 405 */ "MSEXT_INT16", - /* 406 */ "MSEXT_INT32", - /* 407 */ "MSEXT_INT64", - /* 408 */ "MSEXT_BASED", - /* 409 */ "MSEXT_DECLSPEC", - /* 410 */ "MSEXT_CDECL", - /* 411 */ "MSEXT_FASTCALL", - /* 412 */ "MSEXT_STDCALL", - /* 413 */ "MSEXT_TRY", - /* 414 */ "MSEXT_EXCEPT", - /* 415 */ "MSEXT_FINALLY", - /* 416 */ "MSEXT_LEAVE", - /* 417 */ "MSEXT_INLINE", - /* 418 */ "MSEXT_ASM", - /* 419 */ "MSEXT_REGION", - /* 420 */ "MSEXT_ENDREGION", - - /* 421 */ "IMPORT", - - /* 422 */ "ALIGNAS", - /* 423 */ "ALIGNOF", - /* 424 */ "CHAR16_T", - /* 425 */ "CHAR32_T", - /* 426 */ "CONSTEXPR", - /* 427 */ "DECLTYPE", - /* 428 */ "NOEXCEPT", - /* 429 */ "NULLPTR", - /* 430 */ "STATIC_ASSERT", - /* 431 */ "THREADLOCAL", - /* 432 */ "RAWSTRINGLIT", - }; - - // make sure, I have not forgotten any commas (as I did more than once) - BOOST_STATIC_ASSERT( - sizeof(tok_names)/sizeof(tok_names[0]) == T_LAST_TOKEN-T_FIRST_TOKEN - ); - - unsigned int id = BASEID_FROM_TOKEN(tokid)-T_FIRST_TOKEN; - return (id < T_LAST_TOKEN-T_FIRST_TOKEN) ? tok_names[id] : ""; -} - -/////////////////////////////////////////////////////////////////////////////// -// return a token name -char const * -get_token_value(token_id tokid) -{ -// Table of token values -// -// Please note that the sequence of token names must match the sequence of -// token id's defined in then enum token_id above. -static char const *tok_values[] = { - /* 256 */ "&", - /* 257 */ "&&", - /* 258 */ "=", - /* 259 */ "&=", - /* 260 */ "|", - /* 261 */ "|=", - /* 262 */ "^", - /* 263 */ "^=", - /* 264 */ ",", - /* 265 */ ":", - /* 266 */ "/", - /* 267 */ "/=", - /* 268 */ ".", - /* 269 */ ".*", - /* 270 */ "...", - /* 271 */ "==", - /* 272 */ ">", - /* 273 */ ">=", - /* 274 */ "{", - /* 275 */ "<", - /* 276 */ "<=", - /* 277 */ "(", - /* 278 */ "[", - /* 279 */ "-", - /* 280 */ "-=", - /* 281 */ "--", - /* 282 */ "%", - /* 283 */ "%=", - /* 284 */ "!", - /* 285 */ "!=", - /* 286 */ "||", - /* 287 */ "+", - /* 288 */ "+=", - /* 289 */ "++", - /* 290 */ "->", - /* 291 */ "->*", - /* 292 */ "?", - /* 293 */ "}", - /* 294 */ ")", - /* 295 */ "]", - /* 296 */ "::", - /* 297 */ ";", - /* 298 */ "<<", - /* 299 */ "<<=", - /* 300 */ ">>", - /* 301 */ ">>=", - /* 302 */ "*", - /* 303 */ "~", - /* 304 */ "*=", - /* 305 */ "asm", - /* 306 */ "auto", - /* 307 */ "bool", - /* 308 */ "false", - /* 309 */ "true", - /* 310 */ "break", - /* 311 */ "case", - /* 312 */ "catch", - /* 313 */ "char", - /* 314 */ "class", - /* 315 */ "const", - /* 316 */ "const_cast", - /* 317 */ "continue", - /* 318 */ "default", - /* 319 */ "delete", - /* 320 */ "do", - /* 321 */ "double", - /* 322 */ "dynamic_cast", - /* 323 */ "else", - /* 324 */ "enum", - /* 325 */ "explicit", - /* 326 */ "export", - /* 327 */ "extern", - /* 328 */ "float", - /* 329 */ "for", - /* 330 */ "friend", - /* 331 */ "goto", - /* 332 */ "if", - /* 333 */ "inline", - /* 334 */ "int", - /* 335 */ "long", - /* 336 */ "mutable", - /* 337 */ "namespace", - /* 338 */ "new", - /* 339 */ "operator", - /* 340 */ "private", - /* 341 */ "protected", - /* 342 */ "public", - /* 343 */ "register", - /* 344 */ "reinterpret_cast", - /* 345 */ "return", - /* 346 */ "short", - /* 347 */ "signed", - /* 348 */ "sizeof", - /* 349 */ "static", - /* 350 */ "static_cast", - /* 351 */ "struct", - /* 352 */ "switch", - /* 353 */ "template", - /* 354 */ "this", - /* 355 */ "throw", - /* 356 */ "try", - /* 357 */ "typedef", - /* 358 */ "typeid", - /* 359 */ "typename", - /* 360 */ "union", - /* 361 */ "unsigned", - /* 362 */ "using", - /* 363 */ "virtual", - /* 364 */ "void", - /* 365 */ "volatile", - /* 366 */ "wchar_t", - /* 367 */ "while", - /* 368 */ "#define", - /* 369 */ "#if", - /* 370 */ "#ifdef", - /* 371 */ "#ifndef", - /* 372 */ "#else", - /* 373 */ "#elif", - /* 374 */ "#endif", - /* 375 */ "#error", - /* 376 */ "#line", - /* 377 */ "#pragma", - /* 378 */ "#undef", - /* 379 */ "#warning", - /* 380 */ "", // identifier - /* 381 */ "", // octalint - /* 382 */ "", // decimalint - /* 383 */ "", // hexlit - /* 384 */ "", // intlit - /* 385 */ "", // longintlit - /* 386 */ "", // floatlit - /* 387 */ "", // ccomment - /* 388 */ "", // cppcomment - /* 389 */ "", // charlit - /* 390 */ "", // stringlit - /* 391 */ "", // contline - /* 392 */ "", // space - /* 393 */ "", // space2 - /* 394 */ "\n", - /* 395 */ "##", - /* 396 */ "#", - /* 397 */ "", // any - /* 398 */ "#include", - /* 399 */ "#include", - /* 400 */ "#include", - /* 401 */ "", // eof - /* 402 */ "", // eoi - /* 403 */ "", // pp-number - - // MS extensions - /* 404 */ "__int8", - /* 405 */ "__int16", - /* 406 */ "__int32", - /* 407 */ "__int64", - /* 408 */ "__based", - /* 409 */ "__declspec", - /* 410 */ "__cdecl", - /* 411 */ "__fastcall", - /* 412 */ "__stdcall", - /* 413 */ "__try", - /* 414 */ "__except", - /* 415 */ "__finally", - /* 416 */ "__leave", - /* 417 */ "__inline", - /* 418 */ "__asm", - /* 419 */ "#region", - /* 420 */ "#endregion", - - /* 421 */ "import", - - /* 422 */ "alignas", - /* 423 */ "alignof", - /* 424 */ "char16_t", - /* 425 */ "char32_t", - /* 426 */ "constexpr", - /* 427 */ "decltype", - /* 428 */ "noexcept", - /* 429 */ "nullptr", - /* 430 */ "static_assert", - /* 431 */ "threadlocal", - /* 432 */ "", // extrawstringlit - }; - - // make sure, I have not forgotten any commas (as I did more than once) - BOOST_STATIC_ASSERT( - sizeof(tok_values)/sizeof(tok_values[0]) == T_LAST_TOKEN-T_FIRST_TOKEN - ); - - unsigned int id = BASEID_FROM_TOKEN(tokid)-T_FIRST_TOKEN; - return (id < T_LAST_TOKEN-T_FIRST_TOKEN) ? tok_values[id] : ""; -} - -/////////////////////////////////////////////////////////////////////////////// -} // namespace wave -} // namespace boost - -// the suffix header occurs after all of the code -#ifdef BOOST_HAS_ABI_HEADERS -#include BOOST_ABI_SUFFIX -#endif - - diff --git a/files/CMakeLists.txt b/files/CMakeLists.txt index e8426afb7..9b2325744 100644 --- a/files/CMakeLists.txt +++ b/files/CMakeLists.txt @@ -1,12 +1,8 @@ project(resources) set(WATER_FILES - underwater_dome.mesh water_nm.png -) - -set(GBUFFER_FILES - gbuffer.compositor + circle.png ) set(MATERIAL_FILES @@ -21,7 +17,6 @@ set(MATERIAL_FILES objects.shader objects.shaderset openmw.configuration - quad2.shader quad.mat quad.shader quad.shaderset @@ -43,10 +38,17 @@ set(MATERIAL_FILES selection.mat selection.shader selection.shaderset + watersim_heightmap.shader + watersim_addimpulse.shader + watersim_heighttonormal.shader + watersim_common.h + watersim.mat + watersim.shaderset + mygui.mat + mygui.shader + mygui.shaderset ) copy_all_files(${CMAKE_CURRENT_SOURCE_DIR}/water "${OpenMW_BINARY_DIR}/resources/water/" "${WATER_FILES}") -copy_all_files(${CMAKE_CURRENT_SOURCE_DIR}/gbuffer "${OpenMW_BINARY_DIR}/resources/gbuffer/" "${GBUFFER_FILES}") - copy_all_files(${CMAKE_CURRENT_SOURCE_DIR}/materials "${OpenMW_BINARY_DIR}/resources/materials/" "${MATERIAL_FILES}") diff --git a/files/gbuffer/gbuffer.compositor b/files/gbuffer/gbuffer.compositor deleted file mode 100644 index 0a0675fa0..000000000 --- a/files/gbuffer/gbuffer.compositor +++ /dev/null @@ -1,91 +0,0 @@ -// Compositor that just controls output to the MRT textures -compositor gbuffer -{ - technique - { - // MRT output. Currently this is a color texture plus a depth texture - texture mrt_output target_width target_height PF_FLOAT16_RGBA PF_FLOAT16_RGBA chain_scope depth_pool 2 - - target mrt_output - { - input none - - pass clear - { - colour_value 0 0 0 1 - } - pass render_quad - { - // this makes sure the depth for background is set to 1 - material openmw_viewport_init - } - pass render_scene - { - // Renders everything except water - first_render_queue 0 - last_render_queue 50 - } - - } - - target_output - { - input none - - pass render_quad - { - material quad - input 0 mrt_output 0 - } - } - } -} - -// Finalizer compositor to render objects that we don't want in the MRT textures (ex. water) -// NB the water has to be rendered in a seperate compositor anyway, because it -// accesses the MRT textures which can't be done while they are still being rendered to. -compositor gbufferFinalizer -{ - technique - { - texture no_mrt_output target_width target_height PF_R8G8B8A8 depth_pool 2 no_fsaa - texture previousscene target_width target_height PF_R8G8B8A8 - - target previousscene - { - input previous - } - target no_mrt_output - { - input none - shadows off - pass clear - { - buffers colour - colour_value 0 0 0 0 - } - pass render_quad - { - material quad_noDepthWrite - input 0 previousscene - } - pass render_scene - { - first_render_queue 51 - last_render_queue 105 - } - } - target_output - { - input none - pass clear - { - } - pass render_quad - { - material quad_noDepthWrite - input 0 no_mrt_output - } - } - } -} diff --git a/files/launcher.qss b/files/launcher.qss deleted file mode 100644 index 1eb056d4d..000000000 --- a/files/launcher.qss +++ /dev/null @@ -1,123 +0,0 @@ -#PlayGroup { - background-image: url(":/images/playpage-background.png"); - background-repeat: no-repeat; - background-position: top; - padding-left: 30px; - padding-right: 30px; -} - -#MastersWidget { - selection-background-color: palette(highlight); -} - -#PlayButton { - height: 50px; - margin-bottom: 30px; - - background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, - stop:0 rgba(255, 255, 255, 200), - stop:0.1 rgba(255, 255, 255, 15), - stop:0.49 rgba(255, 255, 255, 75), - stop:0.5 rgba(0, 0, 0, 0), - stop:0.9 rgba(0, 0, 0, 55), - stop:1 rgba(0, 0, 0, 100)); - - font-size: 26pt; - font-family: "EB Garamond", "EB Garamond 08"; - color: black; - - border-right: 1px solid rgba(0, 0, 0, 155); - border-left: 1px solid rgba(0, 0, 0, 55); - border-top: 1px solid rgba(0, 0, 0, 55); - border-bottom: 1px solid rgba(0, 0, 0, 155); - - border-radius: 5px; -} - -#PlayButton:hover { - border-bottom: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0)); - border-top: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0)); - border-right: qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0)); - border-left: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0)); - border-width: 2px; - border-style: solid; -} - -#PlayButton:pressed { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, - stop:0 rgba(0, 0, 0, 75), - stop:0.1 rgba(0, 0, 0, 15), - stop:0.2 rgba(255, 255, 255, 55) - stop:0.95 rgba(255, 255, 255, 55), - stop:1 rgba(255, 255, 255, 155)); - - border: 1px solid rgba(0, 0, 0, 55); -} - -#ProfileLabel { - font-size: 18pt; - font-family: "EB Garamond", "EB Garamond 08"; -} - -#ProfilesComboBox { - padding: 1px 18px 1px 3px; - - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 white, stop:0.2 rgba(0, 0, 0, 25), stop:1 white); - border-width: 1px; - border-color: rgba(0, 0, 0, 125); - border-style: solid; - border-radius: 2px; -} - -/*QComboBox gets the "on" state when the popup is open */ -#ProfilesComboBox:!editable:on, #ProfilesComboBox::drop-down:editable:on { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, - stop:0 rgba(0, 0, 0, 75), - stop:0.1 rgba(0, 0, 0, 15), - stop:0.2 rgba(255, 255, 255, 55)); - - border: 1px solid rgba(0, 0, 0, 55); -} - - -#ProfilesComboBox { /* shift the text when the popup opens */ - padding-top: 3px; - padding-left: 4px; - - font-size: 12pt; - font-family: "EB Garamond", "EB Garamond 08"; -} - -#ProfilesComboBox::drop-down { - subcontrol-origin: padding; - subcontrol-position: top right; - - border-width: 1px; - border-left-width: 1px; - border-left-color: darkgray; - border-left-style: solid; /* just a single line */ - border-top-right-radius: 3px; /* same radius as the QComboBox */ - border-bottom-right-radius: 3px; -} - -#ProfilesComboBox::down-arrow { - image: url(":/images/down.png"); -} - -#ProfilesComboBox::down-arrow:on { /* shift the arrow when popup is open */ - top: 1px; - left: 1px; -} - -#ProfilesComboBox QAbstractItemView { - border: 2px solid lightgray; - border-radius: 5px; -} - -#IconWidget { - background-image: url(":/images/openmw-header.png"); - background-color: white; - background-repeat: no-repeat; - background-attachment: scroll; - background-position: right; -} diff --git a/apps/launcher/resources/icons/tango/document-new.png b/files/launcher/icons/tango/document-new.png similarity index 100% rename from apps/launcher/resources/icons/tango/document-new.png rename to files/launcher/icons/tango/document-new.png diff --git a/apps/launcher/resources/icons/tango/edit-copy.png b/files/launcher/icons/tango/edit-copy.png similarity index 100% rename from apps/launcher/resources/icons/tango/edit-copy.png rename to files/launcher/icons/tango/edit-copy.png diff --git a/apps/launcher/resources/icons/tango/edit-delete.png b/files/launcher/icons/tango/edit-delete.png similarity index 100% rename from apps/launcher/resources/icons/tango/edit-delete.png rename to files/launcher/icons/tango/edit-delete.png diff --git a/apps/launcher/resources/icons/tango/go-bottom.png b/files/launcher/icons/tango/go-bottom.png similarity index 100% rename from apps/launcher/resources/icons/tango/go-bottom.png rename to files/launcher/icons/tango/go-bottom.png diff --git a/apps/launcher/resources/icons/tango/go-down.png b/files/launcher/icons/tango/go-down.png similarity index 100% rename from apps/launcher/resources/icons/tango/go-down.png rename to files/launcher/icons/tango/go-down.png diff --git a/apps/launcher/resources/icons/tango/go-top.png b/files/launcher/icons/tango/go-top.png similarity index 100% rename from apps/launcher/resources/icons/tango/go-top.png rename to files/launcher/icons/tango/go-top.png diff --git a/apps/launcher/resources/icons/tango/go-up.png b/files/launcher/icons/tango/go-up.png similarity index 100% rename from apps/launcher/resources/icons/tango/go-up.png rename to files/launcher/icons/tango/go-up.png diff --git a/apps/launcher/resources/icons/tango/index.theme b/files/launcher/icons/tango/index.theme similarity index 100% rename from apps/launcher/resources/icons/tango/index.theme rename to files/launcher/icons/tango/index.theme diff --git a/apps/launcher/resources/icons/tango/video-display.png b/files/launcher/icons/tango/video-display.png similarity index 100% rename from apps/launcher/resources/icons/tango/video-display.png rename to files/launcher/icons/tango/video-display.png diff --git a/files/launcher/images/clear.png b/files/launcher/images/clear.png new file mode 100644 index 000000000..0e72a65ea Binary files /dev/null and b/files/launcher/images/clear.png differ diff --git a/apps/launcher/resources/images/down.png b/files/launcher/images/down.png similarity index 100% rename from apps/launcher/resources/images/down.png rename to files/launcher/images/down.png diff --git a/apps/launcher/resources/images/openmw-header.png b/files/launcher/images/openmw-header.png similarity index 100% rename from apps/launcher/resources/images/openmw-header.png rename to files/launcher/images/openmw-header.png diff --git a/apps/launcher/resources/images/openmw-plugin.png b/files/launcher/images/openmw-plugin.png similarity index 100% rename from apps/launcher/resources/images/openmw-plugin.png rename to files/launcher/images/openmw-plugin.png diff --git a/apps/launcher/resources/images/openmw.ico b/files/launcher/images/openmw.ico similarity index 100% rename from apps/launcher/resources/images/openmw.ico rename to files/launcher/images/openmw.ico diff --git a/apps/launcher/resources/images/openmw.png b/files/launcher/images/openmw.png similarity index 100% rename from apps/launcher/resources/images/openmw.png rename to files/launcher/images/openmw.png diff --git a/apps/launcher/resources/images/playpage-background.png b/files/launcher/images/playpage-background.png similarity index 100% rename from apps/launcher/resources/images/playpage-background.png rename to files/launcher/images/playpage-background.png diff --git a/files/launcher/launcher.qrc b/files/launcher/launcher.qrc new file mode 100644 index 000000000..19b1c5a6f --- /dev/null +++ b/files/launcher/launcher.qrc @@ -0,0 +1,21 @@ + + + images/clear.png + images/down.png + images/openmw.png + images/openmw-plugin.png + images/openmw-header.png + images/playpage-background.png + + + icons/tango/index.theme + icons/tango/video-display.png + icons/tango/document-new.png + icons/tango/edit-copy.png + icons/tango/edit-delete.png + icons/tango/go-bottom.png + icons/tango/go-down.png + icons/tango/go-top.png + icons/tango/go-up.png + + diff --git a/files/launcher/launcher.rc b/files/launcher/launcher.rc new file mode 100644 index 000000000..df2121f46 --- /dev/null +++ b/files/launcher/launcher.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "images/openmw.ico" diff --git a/files/materials/atmosphere.shader b/files/materials/atmosphere.shader index 295fa9376..f02c5766f 100644 --- a/files/materials/atmosphere.shader +++ b/files/materials/atmosphere.shader @@ -1,37 +1,39 @@ #include "core.h" -#define MRT @shGlobalSettingBool(mrt_output) - #ifdef SH_VERTEX_SHADER SH_BEGIN_PROGRAM - shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shUniform(float4x4, view) @shAutoConstant(view, view_matrix) + shUniform(float4x4, projection) @shAutoConstant(projection, projection_matrix) - shColourInput(float4) - shOutput(float4, colourPassthrough) + shOutput(float, alphaFade) SH_START_PROGRAM { - shOutputPosition = shMatrixMult(wvp, shInputPosition); - colourPassthrough = colour; + float4x4 viewFixed = view; +#if !SH_GLSL + viewFixed[0][3] = 0; + viewFixed[1][3] = 0; + viewFixed[2][3] = 0; +#else + viewFixed[3][0] = 0; + viewFixed[3][1] = 0; + viewFixed[3][2] = 0; +#endif + shOutputPosition = shMatrixMult(projection, shMatrixMult(viewFixed, shInputPosition)); + alphaFade = shInputPosition.z < 150.0 ? 0.0 : 1.0; } #else SH_BEGIN_PROGRAM - shInput(float4, colourPassthrough) -#if MRT - shDeclareMrtOutput(1) -#endif + shInput(float, alphaFade) shUniform(float4, atmosphereColour) @shSharedParameter(atmosphereColour) + shUniform(float4, horizonColour) @shSharedParameter(horizonColour, horizonColour) SH_START_PROGRAM { - shOutputColour(0) = colourPassthrough * atmosphereColour; - -#if MRT - shOutputColour(1) = float4(1,1,1,1); -#endif + shOutputColour(0) = alphaFade * atmosphereColour + (1.f - alphaFade) * horizonColour; } #endif diff --git a/files/materials/clouds.shader b/files/materials/clouds.shader index f4258bf5d..b76f2ded7 100644 --- a/files/materials/clouds.shader +++ b/files/materials/clouds.shader @@ -1,31 +1,36 @@ #include "core.h" -#define MRT @shGlobalSettingBool(mrt_output) - #ifdef SH_VERTEX_SHADER SH_BEGIN_PROGRAM - shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shUniform(float4x4, view) @shAutoConstant(view, view_matrix) +shUniform(float4x4, projection) @shAutoConstant(projection, projection_matrix) shVertexInput(float2, uv0) shOutput(float2, UV) - shColourInput(float4) - shOutput(float4, colourPassthrough) + shOutput(float, alphaFade) SH_START_PROGRAM { - colourPassthrough = colour; - shOutputPosition = shMatrixMult(wvp, shInputPosition); - UV = uv0; + float4x4 viewFixed = view; +#if !SH_GLSL + viewFixed[0][3] = 0; + viewFixed[1][3] = 0; + viewFixed[2][3] = 0; +#else + viewFixed[3][0] = 0; + viewFixed[3][1] = 0; + viewFixed[3][2] = 0; +#endif + shOutputPosition = shMatrixMult(projection, shMatrixMult(viewFixed, shInputPosition)); + UV = uv0; + alphaFade = (shInputPosition.z <= 200.f) ? ((shInputPosition.z <= 100.f) ? 0.0 : 0.25) : 1.0; } #else SH_BEGIN_PROGRAM shInput(float2, UV) - shInput(float4, colourPassthrough) -#if MRT - shDeclareMrtOutput(1) -#endif + shInput(float, alphaFade) shSampler2D(diffuseMap1) shSampler2D(diffuseMap2) @@ -42,11 +47,7 @@ float4 albedo = shSample(diffuseMap1, scrolledUV) * (1-cloudBlendFactor) + shSample(diffuseMap2, scrolledUV) * cloudBlendFactor; - shOutputColour(0) = colourPassthrough * float4(cloudColour, 1) * albedo * float4(1,1,1, cloudOpacity); - -#if MRT - shOutputColour(1) = float4(1,1,1,1); -#endif + shOutputColour(0) = float4(cloudColour, 1) * albedo * float4(1,1,1, cloudOpacity * alphaFade); } #endif diff --git a/files/materials/core.h b/files/materials/core.h index 1c9ea1d1d..6f8179c08 100644 --- a/files/materials/core.h +++ b/files/materials/core.h @@ -1,8 +1,3 @@ -#define gammaCorrectRead(v) pow(max(v, 0.00001f), float3(gammaCorrection,gammaCorrection,gammaCorrection)) -#define gammaCorrectOutput(v) pow(max(v, 0.00001f), float3(1.f/gammaCorrection,1.f/gammaCorrection,1.f/gammaCorrection)) - - - #if SH_HLSL == 1 || SH_CG == 1 #define shTexture2D sampler2D @@ -27,6 +22,8 @@ #define shNormalInput(type) , in type normal : NORMAL #define shColourInput(type) , in type colour : COLOR + + #define shFract(val) frac(val) #ifdef SH_VERTEX_SHADER @@ -61,9 +58,20 @@ #endif -#if SH_GLSL == 1 +#if SH_GLSL == 1 || SH_GLSLES == 1 + #define shFract(val) fract(val) + +#if SH_GLSLES == 1 + @version 100 +#else @version 120 +#endif + +#if SH_GLSLES == 1 && SH_FRAGMENT_SHADER +precision mediump int; +precision mediump float; +#endif #define float2 vec2 #define float3 vec3 diff --git a/files/materials/moon.shader b/files/materials/moon.shader index 02f3d8001..a7d183d10 100644 --- a/files/materials/moon.shader +++ b/files/materials/moon.shader @@ -1,19 +1,28 @@ #include "core.h" -#define MRT @shGlobalSettingBool(mrt_output) - - #ifdef SH_VERTEX_SHADER SH_BEGIN_PROGRAM - shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shUniform(float4x4, world) @shAutoConstant(world, world_matrix) + shUniform(float4x4, view) @shAutoConstant(view, view_matrix) +shUniform(float4x4, projection) @shAutoConstant(projection, projection_matrix) shVertexInput(float2, uv0) shOutput(float2, UV) SH_START_PROGRAM { - shOutputPosition = shMatrixMult(wvp, shInputPosition); - UV = uv0; + float4x4 viewFixed = view; +#if !SH_GLSL + viewFixed[0][3] = 0; + viewFixed[1][3] = 0; + viewFixed[2][3] = 0; +#else + viewFixed[3][0] = 0; + viewFixed[3][1] = 0; + viewFixed[3][2] = 0; +#endif + shOutputPosition = shMatrixMult(projection, shMatrixMult(viewFixed, shMatrixMult(world, shInputPosition))); + UV = uv0; } #else @@ -22,9 +31,6 @@ shSampler2D(diffuseMap) shSampler2D(alphaMap) shInput(float2, UV) -#if MRT - shDeclareMrtOutput(1) -#endif shUniform(float4, materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) shUniform(float4, materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) @@ -42,10 +48,6 @@ shOutputColour(0).rgb += (1-tex.a) * shOutputColour(0).a * atmosphereColour.rgb; //fill dark side of moon with atmosphereColour shOutputColour(0).rgb += (1-materialDiffuse.a) * atmosphereColour.rgb; //fade bump -#if MRT - shOutputColour(1) = float4(1,1,1,1); -#endif - } #endif diff --git a/files/materials/mygui.mat b/files/materials/mygui.mat new file mode 100644 index 000000000..78cba3f89 --- /dev/null +++ b/files/materials/mygui.mat @@ -0,0 +1,25 @@ +material MyGUI/NoTexture +{ + pass + { + vertex_program mygui_vertex + fragment_program mygui_fragment + shader_properties + { + has_texture false + } + } +} + +material MyGUI/OneTexture +{ + pass + { + vertex_program mygui_vertex + fragment_program mygui_fragment + shader_properties + { + has_texture true + } + } +} diff --git a/files/materials/mygui.shader b/files/materials/mygui.shader new file mode 100644 index 000000000..014558d97 --- /dev/null +++ b/files/materials/mygui.shader @@ -0,0 +1,45 @@ +#include "core.h" + +#define TEXTURE @shPropertyBool(has_texture) + +#ifdef SH_VERTEX_SHADER + + SH_BEGIN_PROGRAM +#if TEXTURE + shVertexInput(float2, uv0) + shOutput(float2, UV) +#endif + shColourInput(float4) + shOutput(float4, colourPassthrough) + + SH_START_PROGRAM + { + shOutputPosition = float4(shInputPosition.xyz, 1.f); +#if TEXTURE + UV.xy = uv0; +#endif + colourPassthrough = colour; + } + +#else + + + SH_BEGIN_PROGRAM + +#if TEXTURE + shSampler2D(diffuseMap) + shInput(float2, UV) +#endif + + shInput(float4, colourPassthrough) + + SH_START_PROGRAM + { +#if TEXTURE + shOutputColour(0) = shSample(diffuseMap, UV.xy) * colourPassthrough; +#else + shOutputColour(0) = colourPassthrough; +#endif + } + +#endif diff --git a/files/materials/mygui.shaderset b/files/materials/mygui.shaderset new file mode 100644 index 000000000..980cd4caf --- /dev/null +++ b/files/materials/mygui.shaderset @@ -0,0 +1,15 @@ +shader_set mygui_vertex +{ + source mygui.shader + type vertex + profiles_cg vs_2_0 vp40 arbvp1 + profiles_hlsl vs_3_0 vs_2_0 +} + +shader_set mygui_fragment +{ + source mygui.shader + type fragment + profiles_cg ps_3_0 ps_2_x ps_2_0 fp40 arbfp1 + profiles_hlsl ps_3_0 ps_2_0 +} diff --git a/files/materials/objects.mat b/files/materials/objects.mat index f1198b4a2..8f8734d62 100644 --- a/files/materials/objects.mat +++ b/files/materials/objects.mat @@ -1,16 +1,24 @@ material openmw_objects_base { diffuse 1.0 1.0 1.0 1.0 - specular 0.4 0.4 0.4 32 + specular 0 0 0 0 ambient 1.0 1.0 1.0 emissive 0.0 0.0 0.0 - has_vertex_colour false + vertmode 0 diffuseMap black.png + normalMap + emissiveMap + use_emissive_map false + use_detail_map false + emissiveMapUVSet 0 + detailMapUVSet 0 - is_transparent false // real transparency, alpha rejection doesn't count here scene_blend default depth_write default + depth_check default alpha_rejection default + transparent_sorting default + polygon_mode default pass { @@ -19,8 +27,12 @@ material openmw_objects_base shader_properties { - has_vertex_colour $has_vertex_colour - is_transparent $is_transparent + vertexcolor_mode $vertmode + normalMap $normalMap + emissiveMapUVSet $emissiveMapUVSet + detailMapUVSet $detailMapUVSet + emissiveMap $emissiveMap + detailMap $detailMap } diffuse $diffuse @@ -30,14 +42,38 @@ material openmw_objects_base scene_blend $scene_blend alpha_rejection $alpha_rejection depth_write $depth_write - - ffp_vertex_colour_ambient $has_vertex_colour - + depth_check $depth_check + transparent_sorting $transparent_sorting + polygon_mode $polygon_mode texture_unit diffuseMap { direct_texture $diffuseMap create_in_ffp true + tex_coord_set $emissiveMapUVSet + } + + texture_unit normalMap + { + direct_texture $normalMap + // force automips here for now + num_mipmaps 4 + } + + texture_unit emissiveMap + { + create_in_ffp $use_emissive_map + colour_op add + direct_texture $emissiveMap + tex_coord_set $emissiveMapUVSet + } + + texture_unit detailMap + { + create_in_ffp $use_detail_map + colour_op_ex modulate_x2 src_current src_texture + direct_texture $detailMap + tex_coord_set $detailMapUVSet } texture_unit shadowMap0 @@ -58,10 +94,5 @@ material openmw_objects_base tex_address_mode clamp filtering none } - - texture_unit causticMap - { - direct_texture water_nm.png - } } } diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 25624351c..0c869d2cb 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -2,25 +2,33 @@ #define FOG @shGlobalSettingBool(fog) -#define MRT @shPropertyNotBool(is_transparent) && @shGlobalSettingBool(mrt_output) -#define LIGHTING @shGlobalSettingBool(lighting) -#define SHADOWS_PSSM LIGHTING && @shGlobalSettingBool(shadows_pssm) -#define SHADOWS LIGHTING && @shGlobalSettingBool(shadows) +#define SHADOWS_PSSM @shGlobalSettingBool(shadows_pssm) +#define SHADOWS @shGlobalSettingBool(shadows) #if SHADOWS || SHADOWS_PSSM #include "shadows.h" #endif -#if FOG || MRT || SHADOWS_PSSM +#if FOG || SHADOWS_PSSM #define NEED_DEPTH #endif +#define NORMAL_MAP @shPropertyHasValue(normalMap) +#define EMISSIVE_MAP @shPropertyHasValue(emissiveMap) +#define DETAIL_MAP @shPropertyHasValue(detailMap) -#define UNDERWATER @shGlobalSettingBool(underwater_effects) && LIGHTING +// 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)) +// if normal mapping is enabled, we force pixel lighting +#define VERTEX_LIGHTING (!@shPropertyHasValue(normalMap)) -#define HAS_VERTEXCOLOR @shPropertyBool(has_vertex_colour) +#define UNDERWATER @shGlobalSettingBool(render_refraction) + +#define VERTEXCOLOR_MODE @shPropertyString(vertexcolor_mode) + +#define VIEWPROJ_FIX @shGlobalSettingBool(viewproj_fix) #ifdef SH_VERTEX_SHADER @@ -28,27 +36,71 @@ SH_BEGIN_PROGRAM shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) + + shUniform(float4x4, textureMatrix0) @shAutoConstant(textureMatrix0, texture_matrix, 0) + +#if (VIEWPROJ_FIX) || (SHADOWS) + shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) +#endif + +#if VIEWPROJ_FIX + shUniform(float4, vpRow2Fix) @shSharedParameter(vpRow2Fix, vpRow2Fix) + shUniform(float4x4, vpMatrix) @shAutoConstant(vpMatrix, viewproj_matrix) +#endif + shVertexInput(float2, uv0) - shOutput(float2, UV) +#if SECOND_UV_SET + shVertexInput(float2, uv1) +#endif + shOutput(float4, UV) + shNormalInput(float4) + +#if NORMAL_MAP + shTangentInput(float4) + shOutput(float3, tangentPassthrough) +#endif + +#if !VERTEX_LIGHTING + shOutput(float3, normalPassthrough) +#endif + #ifdef NEED_DEPTH shOutput(float, depthPassthrough) #endif -#if LIGHTING - shOutput(float3, normalPassthrough) shOutput(float3, objSpacePositionPassthrough) -#endif -#if HAS_VERTEXCOLOR +#if VERTEXCOLOR_MODE != 0 shColourInput(float4) +#endif + +#if VERTEXCOLOR_MODE != 0 && !VERTEX_LIGHTING shOutput(float4, colourPassthrough) #endif +#if VERTEX_LIGHTING + shUniform(float, lightCount) @shAutoConstant(lightCount, light_count) + shUniform(float4, lightPosition[@shGlobalSettingString(num_lights)]) @shAutoConstant(lightPosition, light_position_view_space_array, @shGlobalSettingString(num_lights)) + shUniform(float4, lightDiffuse[@shGlobalSettingString(num_lights)]) @shAutoConstant(lightDiffuse, light_diffuse_colour_array, @shGlobalSettingString(num_lights)) + shUniform(float4, lightAttenuation[@shGlobalSettingString(num_lights)]) @shAutoConstant(lightAttenuation, light_attenuation_array, @shGlobalSettingString(num_lights)) + shUniform(float4, lightAmbient) @shAutoConstant(lightAmbient, ambient_light_colour) + shUniform(float4x4, worldView) @shAutoConstant(worldView, worldview_matrix) +#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 + +#endif + #if SHADOWS shOutput(float4, lightSpacePos0) shUniform(float4x4, texViewProjMatrix0) @shAutoConstant(texViewProjMatrix0, texture_viewproj_matrix) - shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) #endif #if SHADOWS_PSSM @@ -56,28 +108,59 @@ shOutput(float4, lightSpacePos@shIterator) shUniform(float4x4, texViewProjMatrix@shIterator) @shAutoConstant(texViewProjMatrix@shIterator, texture_viewproj_matrix, @shIterator) @shEndForeach - shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) +#if !VIEWPROJ_FIX + shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) +#endif +#endif + +#if VERTEX_LIGHTING + shOutput(float4, lightResult) + shOutput(float3, directionalResult) #endif SH_START_PROGRAM { shOutputPosition = shMatrixMult(wvp, shInputPosition); - UV = uv0; -#if LIGHTING + + UV.xy = shMatrixMult (textureMatrix0, float4(uv0,0,1)).xy; +#if SECOND_UV_SET + UV.zw = uv1; +#endif + +#if NORMAL_MAP + tangentPassthrough = tangent.xyz; +#endif +#if !VERTEX_LIGHTING normalPassthrough = normal.xyz; #endif +#if VERTEXCOLOR_MODE != 0 && !VERTEX_LIGHTING + colourPassthrough = colour; +#endif #ifdef NEED_DEPTH - depthPassthrough = shOutputPosition.z; + + +#if VIEWPROJ_FIX + float4x4 vpFixed = vpMatrix; +#if !SH_GLSL + vpFixed[2] = vpRow2Fix; +#else + vpFixed[0][2] = vpRow2Fix.x; + vpFixed[1][2] = vpRow2Fix.y; + vpFixed[2][2] = vpRow2Fix.z; + vpFixed[3][2] = vpRow2Fix.w; #endif -#if LIGHTING - objSpacePositionPassthrough = shInputPosition.xyz; + float4x4 fixedWVP = shMatrixMult(vpFixed, worldMatrix); + + depthPassthrough = shMatrixMult(fixedWVP, shInputPosition).z; +#else + depthPassthrough = shOutputPosition.z; #endif -#if HAS_VERTEXCOLOR - colourPassthrough = colour; #endif + objSpacePositionPassthrough = shInputPosition.xyz; + #if SHADOWS lightSpacePos0 = shMatrixMult(texViewProjMatrix0, shMatrixMult(worldMatrix, shInputPosition)); #endif @@ -86,6 +169,54 @@ @shForeach(3) lightSpacePos@shIterator = shMatrixMult(texViewProjMatrix@shIterator, wPos); @shEndForeach +#endif + + +#if VERTEX_LIGHTING + float3 viewPos = shMatrixMult(worldView, shInputPosition).xyz; + float3 viewNormal = normalize(shMatrixMult(worldView, float4(normal.xyz, 0)).xyz); + + float3 lightDir; + float d; + lightResult = float4(0,0,0,1); + @shForeach(@shGlobalSettingString(num_lights)) + lightDir = lightPosition[@shIterator].xyz - (viewPos * lightPosition[@shIterator].w); + d = length(lightDir); + lightDir = normalize(lightDir); + + +#if VERTEXCOLOR_MODE == 2 + lightResult.xyz += colour.xyz * lightDiffuse[@shIterator].xyz + * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) + * max(dot(viewNormal.xyz, lightDir), 0); +#else + lightResult.xyz += materialDiffuse.xyz * lightDiffuse[@shIterator].xyz + * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) + * max(dot(viewNormal.xyz, lightDir), 0); +#endif + +#if @shIterator == 0 + directionalResult = lightResult.xyz; +#endif + + @shEndForeach + + +#if VERTEXCOLOR_MODE == 2 + lightResult.xyz += lightAmbient.xyz * colour.xyz + materialEmissive.xyz; + lightResult.a *= colour.a; +#endif +#if VERTEXCOLOR_MODE == 1 + lightResult.xyz += lightAmbient.xyz * materialAmbient.xyz + colour.xyz; +#endif +#if VERTEXCOLOR_MODE == 0 + lightResult.xyz += lightAmbient.xyz * materialAmbient.xyz + materialEmissive.xyz; +#endif + +#if VERTEXCOLOR_MODE != 2 + lightResult.a *= materialDiffuse.a; +#endif + #endif } @@ -98,47 +229,44 @@ #endif SH_BEGIN_PROGRAM - shSampler2D(diffuseMap) - shInput(float2, UV) -#if MRT - shDeclareMrtOutput(1) + shSampler2D(diffuseMap) + +#if NORMAL_MAP + shSampler2D(normalMap) #endif -#ifdef NEED_DEPTH - shInput(float, depthPassthrough) +#if EMISSIVE_MAP + shSampler2D(emissiveMap) #endif -#if MRT - shUniform(float, far) @shAutoConstant(far, far_clip_distance) +#if DETAIL_MAP + shSampler2D(detailMap) #endif - shUniform(float, gammaCorrection) @shSharedParameter(gammaCorrection, gammaCorrection) + shInput(float4, UV) -#if LIGHTING +#if NORMAL_MAP + shInput(float3, tangentPassthrough) +#endif +#if !VERTEX_LIGHTING shInput(float3, normalPassthrough) +#endif + +#ifdef NEED_DEPTH + shInput(float, depthPassthrough) +#endif + shInput(float3, objSpacePositionPassthrough) - shUniform(float4, lightAmbient) @shAutoConstant(lightAmbient, ambient_light_colour) - #if !HAS_VERTEXCOLOR - shUniform(float4, materialAmbient) @shAutoConstant(materialAmbient, surface_ambient_colour) - #endif - shUniform(float4, materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) - shUniform(float4, materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) - @shForeach(@shGlobalSettingString(num_lights)) - shUniform(float4, lightPosObjSpace@shIterator) @shAutoConstant(lightPosObjSpace@shIterator, light_position_object_space, @shIterator) - shUniform(float4, lightAttenuation@shIterator) @shAutoConstant(lightAttenuation@shIterator, light_attenuation, @shIterator) - shUniform(float4, lightDiffuse@shIterator) @shAutoConstant(lightDiffuse@shIterator, light_diffuse_colour, @shIterator) - @shEndForeach + +#if VERTEXCOLOR_MODE != 0 && !VERTEX_LIGHTING + shInput(float4, colourPassthrough) #endif - + #if FOG shUniform(float3, fogColour) @shAutoConstant(fogColour, fog_colour) shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params) #endif -#if HAS_VERTEXCOLOR - shInput(float4, colourPassthrough) -#endif - #if SHADOWS shInput(float4, lightSpacePos0) shSampler2D(shadowMap0) @@ -159,42 +287,106 @@ #if (UNDERWATER) || (FOG) shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) - shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position) + shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position) #endif #if UNDERWATER - shUniform(float, waterLevel) @shSharedParameter(waterLevel) - - shUniform(float4, lightDirectionWS0) @shAutoConstant(lightDirectionWS0, light_position, 0) - - shSampler2D(causticMap) - - shUniform(float, waterTimer) @shSharedParameter(waterTimer) - shUniform(float2, waterSunFade_sunHeight) @shSharedParameter(waterSunFade_sunHeight) shUniform(float, waterEnabled) @shSharedParameter(waterEnabled) - - shUniform(float3, windDir_windSpeed) @shSharedParameter(windDir_windSpeed) +#endif + +#if VERTEX_LIGHTING + shInput(float4, lightResult) + shInput(float3, directionalResult) +#else + shUniform(float, lightCount) @shAutoConstant(lightCount, light_count) + shUniform(float4, lightPosition[@shGlobalSettingString(num_lights)]) @shAutoConstant(lightPosition, light_position_view_space_array, @shGlobalSettingString(num_lights)) + shUniform(float4, lightDiffuse[@shGlobalSettingString(num_lights)]) @shAutoConstant(lightDiffuse, light_diffuse_colour_array, @shGlobalSettingString(num_lights)) + shUniform(float4, lightAttenuation[@shGlobalSettingString(num_lights)]) @shAutoConstant(lightAttenuation, light_attenuation_array, @shGlobalSettingString(num_lights)) + shUniform(float4, lightAmbient) @shAutoConstant(lightAmbient, ambient_light_colour) + shUniform(float4x4, worldView) @shAutoConstant(worldView, worldview_matrix) + #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 #endif SH_START_PROGRAM { - shOutputColour(0) = shSample(diffuseMap, UV); - shOutputColour(0).xyz = gammaCorrectRead(shOutputColour(0).xyz); - -#if LIGHTING - float3 normal = normalize(normalPassthrough); + shOutputColour(0) = shSample(diffuseMap, UV.xy); + +#if DETAIL_MAP +#if @shPropertyString(detailMapUVSet) + shOutputColour(0) *= shSample(detailMap, UV.zw)*2; +#else + shOutputColour(0) *= shSample(detailMap, UV.xy)*2; +#endif +#endif + +#if NORMAL_MAP + float3 normal = normalPassthrough; + float3 binormal = cross(tangentPassthrough.xyz, normal.xyz); + float3x3 tbn = float3x3(tangentPassthrough.xyz, binormal, normal.xyz); + + #if SH_GLSL + tbn = transpose(tbn); + #endif + + float3 TSnormal = shSample(normalMap, UV.xy).xyz * 2 - 1; + + normal = normalize (shMatrixMult( transpose(tbn), TSnormal )); +#endif + +#if !VERTEX_LIGHTING + float3 viewPos = shMatrixMult(worldView, float4(objSpacePositionPassthrough,1)).xyz; + float3 viewNormal = normalize(shMatrixMult(worldView, float4(normal.xyz, 0)).xyz); + float3 lightDir; - float3 diffuse = float3(0,0,0); float d; - -#if HAS_VERTEXCOLOR - // ambient vertex colour tracking, FFP behaviour - float3 ambient = colourPassthrough.xyz * lightAmbient.xyz; + float4 lightResult = float4(0,0,0,1); + @shForeach(@shGlobalSettingString(num_lights)) + lightDir = lightPosition[@shIterator].xyz - (viewPos * lightPosition[@shIterator].w); + d = length(lightDir); + lightDir = normalize(lightDir); + +#if VERTEXCOLOR_MODE == 2 + lightResult.xyz += colourPassthrough.xyz * lightDiffuse[@shIterator].xyz + * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) + * max(dot(viewNormal.xyz, lightDir), 0); #else - float3 ambient = materialAmbient.xyz * lightAmbient.xyz; + lightResult.xyz += materialDiffuse.xyz * lightDiffuse[@shIterator].xyz + * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) + * max(dot(viewNormal.xyz, lightDir), 0); +#endif + +#if @shIterator == 0 + float3 directionalResult = lightResult.xyz; +#endif + + @shEndForeach + + +#if VERTEXCOLOR_MODE == 2 + lightResult.xyz += lightAmbient.xyz * colourPassthrough.xyz + materialEmissive.xyz; + lightResult.a *= colourPassthrough.a; +#endif +#if VERTEXCOLOR_MODE == 1 + lightResult.xyz += lightAmbient.xyz * materialAmbient.xyz + colourPassthrough.xyz; +#endif +#if VERTEXCOLOR_MODE == 0 + 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 #if SHADOWS float shadow = depthShadowPCF (shadowMap0, lightSpacePos0, invShadowmapSize0); @@ -215,100 +407,42 @@ - float3 caustics = float3(1,1,1); - #if (UNDERWATER) || (FOG) float3 worldPos = shMatrixMult(worldMatrix, float4(objSpacePositionPassthrough,1)).xyz; #endif #if UNDERWATER - float3 waterEyePos = float3(1,1,1); - // NOTE: this calculation would be wrong for non-uniform scaling - float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); - waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,1,0), waterLevel); - caustics = getCaustics(causticMap, worldPos, waterEyePos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); - if (worldPos.y >= waterLevel || waterEnabled != 1.f) - caustics = float3(1,1,1); -#endif - - - @shForeach(@shGlobalSettingString(num_lights)) - - /// \todo use the array auto params for lights, and use a real for-loop with auto param "light_count" iterations - lightDir = lightPosObjSpace@shIterator.xyz - (objSpacePositionPassthrough.xyz * lightPosObjSpace@shIterator.w); - d = length(lightDir); - - lightDir = normalize(lightDir); - -#if @shIterator == 0 + float3 waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,0,1), waterLevel); +#endif - #if (SHADOWS || SHADOWS_PSSM) - diffuse += materialDiffuse.xyz * lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0) * shadow * caustics; - - #else - diffuse += materialDiffuse.xyz * lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0) * caustics; - - #endif - +#if SHADOWS || SHADOWS_PSSM + shOutputColour(0) *= (lightResult - float4(directionalResult * (1.0-shadow),0)); #else - diffuse += materialDiffuse.xyz * lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0); + shOutputColour(0) *= lightResult; #endif - @shEndForeach +#if FOG + float fogValue = shSaturate((depthPassthrough - fogParams.y) * fogParams.w); - shOutputColour(0).xyz *= (ambient + diffuse + materialEmissive.xyz); -#endif +#if UNDERWATER + shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, UNDERWATER_COLOUR, shSaturate(length(waterEyePos-worldPos) / VISIBILITY)); +#else + shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColour, fogValue); +#endif -#if HAS_VERTEXCOLOR && !LIGHTING - shOutputColour(0).xyz *= colourPassthrough.xyz; #endif -#if FOG - float fogValue = shSaturate((length(cameraPos.xyz-worldPos) - fogParams.y) * fogParams.w); - - #if UNDERWATER - // regular fog only if fragment is above water - if (worldPos.y > waterLevel || waterEnabled != 1.f) +#if EMISSIVE_MAP + #if @shPropertyString(emissiveMapUVSet) + shOutputColour(0).xyz += shSample(emissiveMap, UV.zw).xyz; + #else + shOutputColour(0).xyz += shSample(emissiveMap, UV.xy).xyz; #endif - shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, gammaCorrectRead(fogColour), fogValue); #endif // prevent negative colour output (for example with negative lights) shOutputColour(0).xyz = max(shOutputColour(0).xyz, float3(0,0,0)); - -#if UNDERWATER - float fogAmount = (cameraPos.y > waterLevel) - ? shSaturate(length(waterEyePos-worldPos) / VISIBILITY) - : shSaturate(length(cameraPos.xyz-worldPos)/ VISIBILITY); - - float3 eyeVec = normalize(cameraPos.xyz-worldPos); - - float waterSunGradient = dot(eyeVec, -normalize(lightDirectionWS0.xyz)); - waterSunGradient = shSaturate(pow(waterSunGradient*0.7+0.3,2.0)); - float3 waterSunColour = gammaCorrectRead(float3(0.0,1.0,0.85)) *waterSunGradient * 0.5; - - float waterGradient = dot(eyeVec, float3(0.0,-1.0,0.0)); - waterGradient = clamp((waterGradient*0.5+0.5),0.2,1.0); - float3 watercolour = ( gammaCorrectRead(float3(0.0078, 0.5176, 0.700))+waterSunColour)*waterGradient*2.0; - watercolour = shLerp(watercolour*0.3*waterSunFade_sunHeight.x, watercolour, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); - watercolour = (cameraPos.y <= waterLevel) ? watercolour : watercolour*0.3; - - - float darkness = VISIBILITY*2.0; - darkness = clamp((waterEyePos.y - waterLevel + darkness)/darkness,0.2,1.0); - watercolour *= darkness; - - float isUnderwater = (worldPos.y < waterLevel) ? 1.0 : 0.0; - shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, watercolour, fogAmount * isUnderwater * waterEnabled); -#endif - - shOutputColour(0).xyz = gammaCorrectOutput(shOutputColour(0).xyz); - -#if MRT - shOutputColour(1) = float4(depthPassthrough / far,1,1,1); -#endif - } #endif diff --git a/files/materials/openmw.configuration b/files/materials/openmw.configuration index 2f84680f0..b953a9131 100644 --- a/files/materials/openmw.configuration +++ b/files/materials/openmw.configuration @@ -2,14 +2,18 @@ configuration water_reflection { shadows false shadows_pssm false - mrt_output false + viewproj_fix true +} + +configuration water_refraction +{ + viewproj_fix true + render_refraction true } configuration local_map { fog false - mrt_output false - lighting false shadows false shadows_pssm false simple_water true diff --git a/files/materials/quad.mat b/files/materials/quad.mat index afb7f5111..77a2c0c34 100644 --- a/files/materials/quad.mat +++ b/files/materials/quad.mat @@ -4,7 +4,7 @@ material quad pass { - vertex_program quad_vertex + vertex_program transform_vertex fragment_program quad_fragment depth_write $depth_write @@ -20,16 +20,3 @@ material quad_noDepthWrite parent quad depth_write off } - -material openmw_viewport_init -{ - pass - { - vertex_program viewport_init_vertex - fragment_program viewport_init_fragment - - depth_write off - depth_check off - scene_blend add - } -} diff --git a/files/materials/quad.shaderset b/files/materials/quad.shaderset index c61497503..71fd82da4 100644 --- a/files/materials/quad.shaderset +++ b/files/materials/quad.shaderset @@ -1,4 +1,4 @@ -shader_set quad_vertex +shader_set transform_vertex { source quad.shader type vertex @@ -13,19 +13,3 @@ shader_set quad_fragment profiles_cg ps_2_x ps_2_0 ps fp40 arbfp1 profiles_hlsl ps_2_0 } - -shader_set viewport_init_vertex -{ - source quad2.shader - type vertex - profiles_cg vs_2_0 vp40 arbvp1 - profiles_hlsl vs_2_0 -} - -shader_set viewport_init_fragment -{ - source quad2.shader - type fragment - profiles_cg ps_2_x ps_2_0 ps fp40 arbfp1 - profiles_hlsl ps_2_0 -} diff --git a/files/materials/quad2.shader b/files/materials/quad2.shader deleted file mode 100644 index e54d83ef4..000000000 --- a/files/materials/quad2.shader +++ /dev/null @@ -1,23 +0,0 @@ -#include "core.h" - -#ifdef SH_VERTEX_SHADER - - SH_BEGIN_PROGRAM - shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) - SH_START_PROGRAM - { - shOutputPosition = shMatrixMult(wvp, shInputPosition); - } - -#else - - SH_BEGIN_PROGRAM - shUniform(float3, viewportBackground) @shSharedParameter(viewportBackground) - shDeclareMrtOutput(1) - SH_START_PROGRAM - { - shOutputColour(0) = float4(viewportBackground, 1); - shOutputColour(1) = float4(1,1,1,1); - } - -#endif diff --git a/files/materials/selection.mat b/files/materials/selection.mat index a76dd7179..2cb92f884 100644 --- a/files/materials/selection.mat +++ b/files/materials/selection.mat @@ -1,5 +1,6 @@ material SelectionColour { + allow_fixed_function false pass { vertex_program selection_vertex diff --git a/files/materials/shadowcaster.shader b/files/materials/shadowcaster.shader index b312d414c..b992366a7 100644 --- a/files/materials/shadowcaster.shader +++ b/files/materials/shadowcaster.shader @@ -45,8 +45,7 @@ // use alpha channel of the first texture float alpha = shSample(texture1, UV).a; - // discard if alpha is less than 0.5 - if (alpha < 1.0) + if (alpha < 0.5) discard; #endif diff --git a/files/materials/sky.mat b/files/materials/sky.mat index 4af90a170..ccf2a8053 100644 --- a/files/materials/sky.mat +++ b/files/materials/sky.mat @@ -1,3 +1,34 @@ +material QueryTotalPixels +{ + allow_fixed_function false + pass + { + vertex_program sun_vertex + fragment_program sun_fragment + cull_hardware none + polygon_mode_overrideable off + depth_check off + depth_write off + colour_write off + } +} + +material QueryVisiblePixels +{ + allow_fixed_function false + pass + { + vertex_program sun_vertex + fragment_program sun_fragment + cull_hardware none + cull_software none + polygon_mode_overrideable off + depth_check on + depth_write off + colour_write off + } +} + material openmw_moon { allow_fixed_function false @@ -5,7 +36,8 @@ material openmw_moon { vertex_program moon_vertex fragment_program moon_fragment - + cull_hardware none + polygon_mode_overrideable off depth_write off depth_check off @@ -59,7 +91,6 @@ material openmw_atmosphere polygon_mode_overrideable off - scene_blend alpha_blend depth_write off } } @@ -93,6 +124,7 @@ material openmw_sun { vertex_program sun_vertex fragment_program sun_fragment + cull_hardware none polygon_mode_overrideable off diff --git a/files/materials/stars.shader b/files/materials/stars.shader index 5a55d171e..33f22bd14 100644 --- a/files/materials/stars.shader +++ b/files/materials/stars.shader @@ -1,19 +1,28 @@ #include "core.h" -#define MRT @shGlobalSettingBool(mrt_output) - #ifdef SH_VERTEX_SHADER SH_BEGIN_PROGRAM - shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) - + shUniform(float4x4, view) @shAutoConstant(view, view_matrix) +shUniform(float4x4, projection) @shAutoConstant(projection, projection_matrix) + shVertexInput(float2, uv0) shOutput(float2, UV) shOutput(float, fade) SH_START_PROGRAM { - shOutputPosition = shMatrixMult(wvp, shInputPosition); + float4x4 viewFixed = view; +#if !SH_GLSL + viewFixed[0][3] = 0; + viewFixed[1][3] = 0; + viewFixed[2][3] = 0; +#else + viewFixed[3][0] = 0; + viewFixed[3][1] = 0; + viewFixed[3][2] = 0; +#endif + shOutputPosition = shMatrixMult(projection, shMatrixMult(viewFixed, shInputPosition)); UV = uv0; fade = (shInputPosition.z > 50) ? 1 : 0; @@ -22,9 +31,6 @@ #else SH_BEGIN_PROGRAM -#if MRT - shDeclareMrtOutput(1) -#endif shInput(float2, UV) shInput(float, fade) @@ -36,11 +42,6 @@ SH_START_PROGRAM { shOutputColour(0) = shSample(diffuseMap, UV) * float4(1,1,1, nightFade * fade); - - -#if MRT - shOutputColour(1) = float4(1,1,1,1); -#endif } #endif diff --git a/files/materials/sun.shader b/files/materials/sun.shader index 45cd2f24b..abe4c97f1 100644 --- a/files/materials/sun.shader +++ b/files/materials/sun.shader @@ -1,19 +1,28 @@ #include "core.h" -#define MRT @shGlobalSettingBool(mrt_output) - - #ifdef SH_VERTEX_SHADER SH_BEGIN_PROGRAM - shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shUniform(float4x4, world) @shAutoConstant(world, world_matrix) + shUniform(float4x4, view) @shAutoConstant(view, view_matrix) +shUniform(float4x4, projection) @shAutoConstant(projection, projection_matrix) shVertexInput(float2, uv0) shOutput(float2, UV) SH_START_PROGRAM { - shOutputPosition = shMatrixMult(wvp, shInputPosition); - UV = uv0; + float4x4 viewFixed = view; +#if !SH_GLSL + viewFixed[0][3] = 0; + viewFixed[1][3] = 0; + viewFixed[2][3] = 0; +#else + viewFixed[3][0] = 0; + viewFixed[3][1] = 0; + viewFixed[3][2] = 0; +#endif + shOutputPosition = shMatrixMult(projection, shMatrixMult(viewFixed, shMatrixMult(world, shInputPosition))); + UV = uv0; } #else @@ -21,19 +30,12 @@ SH_BEGIN_PROGRAM shSampler2D(diffuseMap) shInput(float2, UV) -#if MRT - shDeclareMrtOutput(1) -#endif shUniform(float4, materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) //shUniform(float4, materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) SH_START_PROGRAM { shOutputColour(0) = float4(1,1,1,materialDiffuse.a) * shSample(diffuseMap, UV); - -#if MRT - shOutputColour(1) = float4(1,1,1,1); -#endif } #endif diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index dee733263..58146118e 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -1,14 +1,11 @@ #include "core.h" -#define IS_FIRST_PASS 1 +#define IS_FIRST_PASS (@shPropertyString(pass_index) == 0) #define FOG @shGlobalSettingBool(fog) -#define MRT @shGlobalSettingBool(mrt_output) -#define LIGHTING @shGlobalSettingBool(lighting) - -#define SHADOWS_PSSM LIGHTING && @shGlobalSettingBool(shadows_pssm) -#define SHADOWS LIGHTING && @shGlobalSettingBool(shadows) +#define SHADOWS_PSSM @shGlobalSettingBool(shadows_pssm) +#define SHADOWS @shGlobalSettingBool(shadows) #if SHADOWS || SHADOWS_PSSM #include "shadows.h" @@ -18,12 +15,17 @@ #define NUM_LAYERS @shPropertyString(num_layers) -#if MRT || FOG || SHADOWS_PSSM +#if FOG || SHADOWS_PSSM #define NEED_DEPTH 1 #endif -#define UNDERWATER @shGlobalSettingBool(underwater_effects) && LIGHTING +#define UNDERWATER @shGlobalSettingBool(render_refraction) + +#define VIEWPROJ_FIX @shGlobalSettingBool(viewproj_fix) +#if !IS_FIRST_PASS +// This is not the first pass. +#endif #if NEED_DEPTH @shAllocatePassthrough(1, depth) @@ -31,9 +33,7 @@ @shAllocatePassthrough(2, UV) -#if LIGHTING -@shAllocatePassthrough(3, objSpacePosition) -#endif +@shAllocatePassthrough(3, worldPos) #if SHADOWS @shAllocatePassthrough(4, lightSpacePos0) @@ -52,6 +52,10 @@ shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) shUniform(float4x4, viewProjMatrix) @shAutoConstant(viewProjMatrix, viewproj_matrix) +#if VIEWPROJ_FIX + shUniform(float4, vpRow2Fix) @shSharedParameter(vpRow2Fix, vpRow2Fix) +#endif + shUniform(float2, lodMorph) @shAutoConstant(lodMorph, custom, 1001) shVertexInput(float2, uv0) @@ -88,21 +92,37 @@ float toMorph = -min(0, sign(uv1.y - lodMorph.y)); // morph - // this assumes XZ terrain alignment - worldPos.y += uv1.x * toMorph * lodMorph.x; + // this assumes XY terrain alignment + worldPos.z += uv1.x * toMorph * lodMorph.x; shOutputPosition = shMatrixMult(viewProjMatrix, worldPos); #if NEED_DEPTH +#if VIEWPROJ_FIX + float4x4 vpFixed = viewProjMatrix; +#if !SH_GLSL + vpFixed[2] = vpRow2Fix; +#else + vpFixed[0][2] = vpRow2Fix.x; + vpFixed[1][2] = vpRow2Fix.y; + vpFixed[2][2] = vpRow2Fix.z; + vpFixed[3][2] = vpRow2Fix.w; +#endif + + float4x4 fixedWVP = shMatrixMult(vpFixed, worldMatrix); + + float depth = shMatrixMult(fixedWVP, shInputPosition).z; + @shPassthroughAssign(depth, depth); +#else @shPassthroughAssign(depth, shOutputPosition.z); +#endif + #endif @shPassthroughAssign(UV, uv0); -#if LIGHTING - @shPassthroughAssign(objSpacePosition, shInputPosition.xyz); -#endif + @shPassthroughAssign(worldPos, worldPos.xyz); #if SHADOWS float4 lightSpacePos = shMatrixMult(texViewProjMatrix0, shMatrixMult(worldMatrix, shInputPosition)); @@ -137,8 +157,6 @@ shSampler2D(normalMap) // global normal map - shUniform(float, gammaCorrection) @shSharedParameter(gammaCorrection, gammaCorrection) - @shForeach(@shPropertyString(num_blendmaps)) shSampler2D(blendMap@shIterator) @@ -154,22 +172,13 @@ #endif @shPassthroughFragmentInputs - -#if MRT - shDeclareMrtOutput(1) - shUniform(float, far) @shAutoConstant(far, far_clip_distance) -#endif - -#if LIGHTING shUniform(float4, lightAmbient) @shAutoConstant(lightAmbient, ambient_light_colour) @shForeach(@shGlobalSettingString(terrain_num_lights)) - shUniform(float4, lightPosObjSpace@shIterator) @shAutoConstant(lightPosObjSpace@shIterator, light_position_object_space, @shIterator) + shUniform(float4, lightPosObjSpace@shIterator) @shAutoConstant(lightPosObjSpace@shIterator, light_position, @shIterator) shUniform(float4, lightAttenuation@shIterator) @shAutoConstant(lightAttenuation@shIterator, light_attenuation, @shIterator) shUniform(float4, lightDiffuse@shIterator) @shAutoConstant(lightDiffuse@shIterator, light_diffuse_colour, @shIterator) @shEndForeach -#endif - #if SHADOWS shSampler2D(shadowMap0) @@ -194,14 +203,6 @@ #if UNDERWATER shUniform(float, waterLevel) @shSharedParameter(waterLevel) - shUniform(float4, lightDirectionWS0) @shAutoConstant(lightDirectionWS0, light_position, 0) - - shSampler2D(causticMap) - - shUniform(float, waterTimer) @shSharedParameter(waterTimer) - shUniform(float2, waterSunFade_sunHeight) @shSharedParameter(waterSunFade_sunHeight) - - shUniform(float3, windDir_windSpeed) @shSharedParameter(windDir_windSpeed) #endif @@ -214,35 +215,22 @@ float2 UV = @shPassthroughReceive(UV); -#if LIGHTING - float3 objSpacePosition = @shPassthroughReceive(objSpacePosition); + float3 worldPos = @shPassthroughReceive(worldPos); float3 normal = shSample(normalMap, UV).rgb * 2 - 1; normal = normalize(normal); -#endif - - float3 caustics = float3(1,1,1); -#if (UNDERWATER) || (FOG) - float3 worldPos = shMatrixMult(worldMatrix, float4(objSpacePosition,1)).xyz; -#endif - #if UNDERWATER - - float3 waterEyePos = float3(1,1,1); - // NOTE: this calculation would be wrong for non-uniform scaling - float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); - waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,1,0), waterLevel); - caustics = getCaustics(causticMap, worldPos, waterEyePos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); - if (worldPos.y >= waterLevel) - caustics = float3(1,1,1); + float3 waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,0,1), waterLevel); +#endif - +#if !IS_FIRST_PASS +// Opacity the previous passes should have, i.e. 1 - (opacity of this pass) +float previousAlpha = 1.f; #endif - - + // Layer calculations @shForeach(@shPropertyString(num_blendmaps)) float4 blendValues@shIterator = shSample(blendMap@shIterator, UV); @@ -252,12 +240,20 @@ @shForeach(@shPropertyString(num_layers)) -#if IS_FIRST_PASS == 1 && @shIterator == 0 - // first layer of first pass doesn't need a blend map - albedo = gammaCorrectRead(shSample(diffuseMap0, UV * 10).rgb); +#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, UV * 10).rgb; + #else + albedo = shLerp(albedo, shSample(diffuseMap@shIterator, UV * 10).rgb, blendValues@shPropertyString(blendmap_component_@shIterator)); + #endif #else - albedo = shLerp(albedo, gammaCorrectRead(shSample(diffuseMap@shIterator, UV * 10).rgb), blendValues@shPropertyString(blendmap_component_@shIterator)); - + #if @shIterator == 0 + albedo = shSample(diffuseMap@shIterator, UV * 10).rgb, blendValues@shPropertyString(blendmap_component_@shIterator); + #else + albedo = shLerp(albedo, shSample(diffuseMap@shIterator, UV * 10).rgb, blendValues@shPropertyString(blendmap_component_@shIterator)); + #endif + previousAlpha *= 1.f-blendValues@shPropertyString(blendmap_component_@shIterator); #endif @shEndForeach @@ -276,7 +272,6 @@ // Lighting -#if LIGHTING // shadows only for the first (directional) light #if SHADOWS float4 lightSpacePos0 = @shPassthroughReceive(lightSpacePos0); @@ -308,7 +303,7 @@ @shForeach(@shGlobalSettingString(terrain_num_lights)) - lightDir = lightPosObjSpace@shIterator.xyz - (objSpacePosition.xyz * lightPosObjSpace@shIterator.w); + lightDir = lightPosObjSpace@shIterator.xyz - (worldPos.xyz * lightPosObjSpace@shIterator.w); d = length(lightDir); @@ -317,10 +312,10 @@ #if @shIterator == 0 #if (SHADOWS || SHADOWS_PSSM) - diffuse += lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0) * shadow * caustics; + diffuse += lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0) * shadow; #else - diffuse += lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0) * caustics; + diffuse += lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0); #endif @@ -330,56 +325,27 @@ @shEndForeach - shOutputColour(0).xyz *= (lightAmbient.xyz + diffuse); -#endif - + shOutputColour(0).xyz *= (lightAmbient.xyz + diffuse); #if FOG - float fogValue = shSaturate((length(cameraPos.xyz-worldPos) - fogParams.y) * fogParams.w); + float fogValue = shSaturate((depth - fogParams.y) * fogParams.w); #if UNDERWATER - // regular fog only if fragment is above water - if (worldPos.y > waterLevel) + shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, UNDERWATER_COLOUR, shSaturate(length(waterEyePos-worldPos) / VISIBILITY)); + #else + shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColour, fogValue); #endif - shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, gammaCorrectRead(fogColour), fogValue); #endif // prevent negative colour output (for example with negative lights) shOutputColour(0).xyz = max(shOutputColour(0).xyz, float3(0,0,0)); - -#if UNDERWATER - float fogAmount = (cameraPos.y > waterLevel) - ? shSaturate(length(waterEyePos-worldPos) / VISIBILITY) - : shSaturate(length(cameraPos.xyz-worldPos)/ VISIBILITY); - - float3 eyeVec = normalize(cameraPos.xyz-worldPos); - - float waterSunGradient = dot(eyeVec, -normalize(lightDirectionWS0.xyz)); - waterSunGradient = shSaturate(pow(waterSunGradient*0.7+0.3,2.0)); - float3 waterSunColour = gammaCorrectRead(float3(0.0,1.0,0.85))*waterSunGradient * 0.5; - - float waterGradient = dot(eyeVec, float3(0.0,-1.0,0.0)); - waterGradient = clamp((waterGradient*0.5+0.5),0.2,1.0); - float3 watercolour = (gammaCorrectRead(float3(0.0078, 0.5176, 0.700))+waterSunColour)*waterGradient*2.0; - float3 waterext = gammaCorrectRead(float3(0.6, 0.9, 1.0));//water extinction - watercolour = shLerp(watercolour*0.3*waterSunFade_sunHeight.x, watercolour, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); - watercolour = (cameraPos.y <= waterLevel) ? watercolour : watercolour*0.3; - - - float darkness = VISIBILITY*2.0; - darkness = clamp((waterEyePos.y - waterLevel + darkness)/darkness,0.2,1.0); - watercolour *= darkness; - - float isUnderwater = (worldPos.y < waterLevel) ? 1.0 : 0.0; - shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, watercolour, fogAmount * isUnderwater); -#endif - - shOutputColour(0).xyz = gammaCorrectOutput(shOutputColour(0).xyz); -#if MRT - shOutputColour(1) = float4(depth / far,1,1,1); +#if IS_FIRST_PASS + shOutputColour(0).a = 1; +#else + shOutputColour(0).a = 1.f-previousAlpha; #endif } diff --git a/files/materials/underwater.h b/files/materials/underwater.h index 18052a98d..8474f299d 100644 --- a/files/materials/underwater.h +++ b/files/materials/underwater.h @@ -1,4 +1,6 @@ -#define VISIBILITY 1500.0 // how far you can look through water +#define UNDERWATER_COLOUR float3(0.18039, 0.23137, 0.25490) + +#define VISIBILITY 1000.0 // how far you can look through water #define BIG_WAVES_X 0.3 // strength of big waves #define BIG_WAVES_Y 0.3 @@ -79,9 +81,9 @@ float3 perturb(shTexture2D tex, float2 coords, float bend, float2 windDir, float float3 getCaustics (shTexture2D causticMap, float3 worldPos, float3 waterEyePos, float3 worldNormal, float3 lightDirectionWS0, float waterLevel, float waterTimer, float3 windDir_windSpeed) { - float waterDepth = shSaturate((waterEyePos.y - worldPos.y) / 50.0); + float waterDepth = shSaturate((waterEyePos.z - worldPos.z) / 50.0); - float3 causticPos = intercept(worldPos.xyz, lightDirectionWS0.xyz, float3(0,1,0), waterLevel); + float3 causticPos = intercept(worldPos.xyz, lightDirectionWS0.xyz, float3(0,0,1), waterLevel); ///\ todo clean this up float causticdepth = length(causticPos-worldPos.xyz); @@ -91,20 +93,21 @@ float3 getCaustics (shTexture2D causticMap, float3 worldPos, float3 waterEyePos, // NOTE: the original shader calculated a tangent space basis here, // but using only the world normal is cheaper and i couldn't see a visual difference // also, if this effect gets moved to screen-space some day, it's unlikely to have tangent information - float3 causticNorm = worldNormal.xyz * perturb(causticMap, causticPos.xz, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).xzy * 2 - 1; + float3 causticNorm = worldNormal.xyz * perturb(causticMap, causticPos.xy, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).xyz * 2 - 1; + causticNorm = float3(causticNorm.x, causticNorm.y, -causticNorm.z); //float fresnel = pow(clamp(dot(LV,causticnorm),0.0,1.0),2.0); float NdotL = max(dot(worldNormal.xyz, lightDirectionWS0.xyz),0.0); - float causticR = 1.0-perturb(causticMap, causticPos.xz, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; + float causticR = 1.0-perturb(causticMap, causticPos.xy, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; /// \todo sunFade // float3 caustics = clamp(pow(float3(causticR)*5.5,float3(5.5*causticdepth)),0.0,1.0)*NdotL*sunFade*causticdepth; float3 caustics = clamp(pow(float3(causticR,causticR,causticR)*5.5,float3(5.5*causticdepth,5.5*causticdepth,5.5*causticdepth)),0.0,1.0)*NdotL*causticdepth; - float causticG = 1.0-perturb(causticMap,causticPos.xz+(1.0-causticdepth)*ABBERATION, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; - float causticB = 1.0-perturb(causticMap,causticPos.xz+(1.0-causticdepth)*ABBERATION*2.0, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; + float causticG = 1.0-perturb(causticMap,causticPos.xy+(1.0-causticdepth)*ABBERATION, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; + float causticB = 1.0-perturb(causticMap,causticPos.xy+(1.0-causticdepth)*ABBERATION*2.0, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; //caustics = shSaturate(pow(float3(causticR,causticG,causticB)*5.5,float3(5.5*causticdepth)))*NdotL*sunFade*causticdepth; caustics = shSaturate(pow(float3(causticR,causticG,causticB)*5.5,float3(5.5*causticdepth,5.5*causticdepth,5.5*causticdepth)))*NdotL*causticdepth; diff --git a/files/materials/water.mat b/files/materials/water.mat index dcea5a0d0..1e5f8c8e0 100644 --- a/files/materials/water.mat +++ b/files/materials/water.mat @@ -1,8 +1,10 @@ material Water { + allow_fixed_function false + pass { - emissive 0.6 0.7 1.0 + emissive 1.0 1.0 1.0 ambient 0 0 0 diffuse 0 0 0 1 specular 0 0 0 32 @@ -11,6 +13,9 @@ material Water fragment_program water_fragment cull_hardware none + + scene_blend alpha_blend + depth_write off texture_unit reflectionMap { @@ -20,7 +25,7 @@ material Water texture_unit refractionMap { - texture_alias WaterRefraction + direct_texture WaterRefraction tex_address_mode clamp } @@ -32,10 +37,16 @@ material Water texture_unit normalMap { - direct_texture water_nm.png + texture water_nm.png 5 } - - + + texture_unit rippleNormalMap + { + direct_texture RippleNormal + tex_address_mode border + tex_border_colour 0.5 0.5 1.0 + } + // for simple_water texture_unit animatedTexture { @@ -43,13 +54,24 @@ material Water scale 0.1 0.1 alpha_op_ex source1 src_manual src_current 0.7 } - } -} - -material Underwater_Dome -{ - parent openmw_objects_base - - depth_write off + texture_unit shadowMap0 + { + content_type shadow + tex_address_mode clamp + filtering none + } + texture_unit shadowMap1 + { + content_type shadow + tex_address_mode clamp + filtering none + } + texture_unit shadowMap2 + { + content_type shadow + tex_address_mode clamp + filtering none + } + } } diff --git a/files/materials/water.shader b/files/materials/water.shader index 6bd277eab..a6d0a47e6 100644 --- a/files/materials/water.shader +++ b/files/materials/water.shader @@ -5,11 +5,7 @@ #if SIMPLE_WATER - // --------------------------------------- SIMPLE WATER --------------------------------------------------- - - - #define MRT @shGlobalSettingBool(mrt_output) - + // --------------------------------------- SIMPLE WATER --------------------------------------------------- #ifdef SH_VERTEX_SHADER @@ -32,9 +28,6 @@ shSampler2D(animatedTexture) shInput(float2, UV) shInput(float, depth) -#if MRT - shDeclareMrtOutput(1) -#endif shUniform(float3, fogColor) @shAutoConstant(fogColor, fog_colour) shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params) @@ -42,15 +35,11 @@ SH_START_PROGRAM { - shOutputColour(0).xyz = shSample(animatedTexture, UV * 15).xyz * float3(0.6, 0.7, 1.0); + shOutputColour(0).xyz = shSample(animatedTexture, UV * 15).xyz * float3(1.0, 1.0, 1.0); shOutputColour(0).w = 0.7; float fogValue = shSaturate((depth - fogParams.y) * fogParams.w); shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue); - -#if MRT - shOutputColour(1) = float4(1,1,1,1); -#endif } #endif @@ -61,7 +50,15 @@ // Inspired by Blender GLSL Water by martinsh ( http://devlog-martinsh.blogspot.de/2012/07/waterundewater-shader-wip.html ) +#define SHADOWS_PSSM @shGlobalSettingBool(shadows_pssm) +#define SHADOWS @shGlobalSettingBool(shadows) +#if SHADOWS || SHADOWS_PSSM + #include "shadows.h" +#endif + +#define RIPPLES 1 +#define REFRACTION @shGlobalSettingBool(refraction) #ifdef SH_VERTEX_SHADER @@ -74,6 +71,23 @@ shOutput(float4, position) shOutput(float, depthPassthrough) + +#if SHADOWS + shOutput(float4, lightSpacePos0) + shUniform(float4x4, texViewProjMatrix0) @shAutoConstant(texViewProjMatrix0, texture_viewproj_matrix) +#endif + +#if SHADOWS_PSSM + @shForeach(3) + shOutput(float4, lightSpacePos@shIterator) + shUniform(float4x4, texViewProjMatrix@shIterator) @shAutoConstant(texViewProjMatrix@shIterator, texture_viewproj_matrix, @shIterator) + @shEndForeach +#endif + +#if SHADOWS || SHADOWS_PSSM + shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) +#endif + SH_START_PROGRAM { shOutputPosition = shMatrixMult(wvp, shInputPosition); @@ -98,6 +112,17 @@ position = shInputPosition; depthPassthrough = shOutputPosition.z; + + +#if SHADOWS + lightSpacePos0 = shMatrixMult(texViewProjMatrix0, shMatrixMult(worldMatrix, shInputPosition)); +#endif +#if SHADOWS_PSSM + float4 wPos = shMatrixMult(worldMatrix, shInputPosition); + @shForeach(3) + lightSpacePos@shIterator = shMatrixMult(texViewProjMatrix@shIterator, wPos); + @shEndForeach +#endif } #else @@ -122,10 +147,10 @@ #define REFL_BUMP 0.08 // reflection distortion amount #define REFR_BUMP 0.06 // refraction distortion amount - #define SCATTER_AMOUNT 3.0 // amount of sunlight scattering - #define SCATTER_COLOUR gammaCorrectRead(float3(0.0,1.0,0.95)) // colour of sunlight scattering + #define SCATTER_AMOUNT 0.3 // amount of sunlight scattering + #define SCATTER_COLOUR float3(0.0,1.0,0.95) // colour of sunlight scattering - #define SUN_EXT gammaCorrectRead(float3(0.45, 0.55, 0.68)) //sunlight extinction + #define SUN_EXT float3(0.45, 0.55, 0.68) //sunlight extinction #define SPEC_HARDNESS 256 // specular highlights hardness @@ -159,13 +184,25 @@ shInput(float3, screenCoordsPassthrough) shInput(float4, position) shInput(float, depthPassthrough) + + #if RIPPLES + shUniform(float3, rippleCenter) @shSharedParameter(rippleCenter, rippleCenter) + shUniform(float, rippleAreaLength) @shSharedParameter(rippleAreaLength, rippleAreaLength) + #endif shUniform(float, far) @shAutoConstant(far, far_clip_distance) shSampler2D(reflectionMap) +#if REFRACTION shSampler2D(refractionMap) +#endif shSampler2D(depthMap) shSampler2D(normalMap) + + #if RIPPLES + shSampler2D(rippleNormalMap) + shUniform(float4x4, wMat) @shAutoConstant(wMat, world_matrix) + #endif shUniform(float3, windDir_windSpeed) @shSharedParameter(windDir_windSpeed) #define WIND_SPEED windDir_windSpeed.z @@ -176,9 +213,6 @@ shUniform(float4, sunPosition) @shAutoConstant(sunPosition, light_position, 0) shUniform(float4, sunSpecular) @shAutoConstant(sunSpecular, light_specular_colour, 0) - - shUniform(float, gammaCorrection) @shSharedParameter(gammaCorrection, gammaCorrection) - shUniform(float, renderTargetFlipping) @shAutoConstant(renderTargetFlipping, render_target_flipping) @@ -187,17 +221,50 @@ shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params) shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position_object_space) + + +#if SHADOWS + shInput(float4, lightSpacePos0) + shSampler2D(shadowMap0) + shUniform(float2, invShadowmapSize0) @shAutoConstant(invShadowmapSize0, inverse_texture_size, 1) +#endif +#if SHADOWS_PSSM + @shForeach(3) + shInput(float4, lightSpacePos@shIterator) + shSampler2D(shadowMap@shIterator) + shUniform(float2, invShadowmapSize@shIterator) @shAutoConstant(invShadowmapSize@shIterator, inverse_texture_size, @shIterator(1)) + @shEndForeach + shUniform(float3, pssmSplitPoints) @shSharedParameter(pssmSplitPoints) +#endif + +#if SHADOWS || SHADOWS_PSSM + shUniform(float4, shadowFar_fadeStart) @shSharedParameter(shadowFar_fadeStart) +#endif SH_START_PROGRAM { +#if SHADOWS + float shadow = depthShadowPCF (shadowMap0, lightSpacePos0, invShadowmapSize0); +#endif +#if SHADOWS_PSSM + float shadow = pssmDepthShadow (lightSpacePos0, invShadowmapSize0, shadowMap0, lightSpacePos1, invShadowmapSize1, shadowMap1, lightSpacePos2, invShadowmapSize2, shadowMap2, depthPassthrough, pssmSplitPoints); +#endif + +#if SHADOWS || SHADOWS_PSSM + float fadeRange = shadowFar_fadeStart.x - shadowFar_fadeStart.y; + float fade = 1-((depthPassthrough - shadowFar_fadeStart.y) / fadeRange); + shadow = (depthPassthrough > shadowFar_fadeStart.x) ? 1.0 : ((depthPassthrough > shadowFar_fadeStart.y) ? 1.0-((1.0-shadow)*fade) : shadow); +#endif + +#if !SHADOWS && !SHADOWS_PSSM + float shadow = 1.0; +#endif + float2 screenCoords = screenCoordsPassthrough.xy / screenCoordsPassthrough.z; screenCoords.y = (1-shSaturate(renderTargetFlipping))+renderTargetFlipping*screenCoords.y; - float depth = shSample(depthMap, screenCoords).x * far - depthPassthrough; - float shoreFade = shSaturate(depth / 50.0); - float2 nCoord = float2(0,0); nCoord = UV * (WAVE_SCALE * 0.05) + WIND_DIR * waterTimer * (WIND_SPEED*0.04); @@ -219,92 +286,75 @@ float3 normal = (normal0 * BIG_WAVES_X + normal1 * BIG_WAVES_Y + normal2 * MID_WAVES_X + normal3 * MID_WAVES_Y + - normal4 * SMALL_WAVES_X + normal5 * SMALL_WAVES_Y).xzy; - - normal = normalize(float3(normal.x * BUMP, normal.y, normal.z * BUMP)); - + normal4 * SMALL_WAVES_X + normal5 * SMALL_WAVES_Y); + + float4 worldPosition = shMatrixMult(wMat, float4(position.xyz, 1)); + float2 relPos = (worldPosition.xy - rippleCenter.xy) / rippleAreaLength + 0.5; + float3 normal_ripple = normalize(shSample(rippleNormalMap, relPos.xy).xyz * 2 - 1); + + //normal = normalize(normal + normal_ripple); + normal = normalize(float3(normal.x * BUMP + normal_ripple.x, normal.y * BUMP + normal_ripple.y, normal.z)); + normal = float3(normal.x, normal.y, -normal.z); + // normal for sunlight scattering float3 lNormal = (normal0 * BIG_WAVES_X*0.5 + normal1 * BIG_WAVES_Y*0.5 + normal2 * MID_WAVES_X*0.2 + normal3 * MID_WAVES_Y*0.2 + - normal4 * SMALL_WAVES_X*0.1 + normal5 * SMALL_WAVES_Y*0.1).xzy; - lNormal = normalize(float3(lNormal.x * BUMP, lNormal.y, lNormal.z * BUMP)); - + normal4 * SMALL_WAVES_X*0.1 + normal5 * SMALL_WAVES_Y*0.1).xyz; + lNormal = normalize(float3(lNormal.x * BUMP, lNormal.y * BUMP, lNormal.z)); + lNormal = float3(lNormal.x, lNormal.y, -lNormal.z); + float3 lVec = normalize(sunPosition.xyz); float3 vVec = normalize(position.xyz - cameraPos.xyz); - float isUnderwater = (cameraPos.y > 0) ? 0.0 : 1.0; + float isUnderwater = (cameraPos.z > 0) ? 0.0 : 1.0; // sunlight scattering - float3 pNormal = float3(0,1,0); + float3 pNormal = float3(0,0,1); float3 lR = reflect(lVec, lNormal); float3 llR = reflect(lVec, pNormal); float s = shSaturate(dot(lR, vVec)*2.0-1.2); - float lightScatter = shSaturate(dot(-lVec,lNormal)*0.7+0.3) * s * SCATTER_AMOUNT * waterSunFade_sunHeight.x * shSaturate(1.0-exp(-waterSunFade_sunHeight.y)); - float3 scatterColour = shLerp(float3(SCATTER_COLOUR)*gammaCorrectRead(float3(1.0,0.4,0.0)), SCATTER_COLOUR, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); + float lightScatter = shadow * shSaturate(dot(-lVec,lNormal)*0.7+0.3) * s * SCATTER_AMOUNT * waterSunFade_sunHeight.x * shSaturate(1.0-exp(-waterSunFade_sunHeight.y)); + float3 scatterColour = shLerp(float3(SCATTER_COLOUR)*float3(1.0,0.4,0.0), SCATTER_COLOUR, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); // fresnel - float ior = (cameraPos.y>0)?(1.333/1.0):(1.0/1.333); //air to water; water to air + float ior = (cameraPos.z>0)?(1.333/1.0):(1.0/1.333); //air to water; water to air float fresnel = fresnel_dielectric(-vVec, normal, ior); fresnel = shSaturate(fresnel); // reflection - float3 reflection = gammaCorrectRead(shSample(reflectionMap, screenCoords+(normal.xz*REFL_BUMP)).rgb); + float3 reflection = shSample(reflectionMap, screenCoords+(normal.xy*REFL_BUMP)).rgb; // refraction float3 R = reflect(vVec, normal); - - // check the depth at the refracted coords, and don't do any normal distortion for the refraction if the object to refract - // is actually above the water (objectDepth < waterDepth) - // this solves silhouettes around objects above the water - float refractDepth = shSample(depthMap, screenCoords-(shoreFade * normal.xz*REFR_BUMP)).x * far - depthPassthrough; - float doRefraction = (refractDepth < 0) ? 0.f : 1.f; - - float3 refraction = gammaCorrectRead(shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP * doRefraction))*1.0).rgb); + +#if REFRACTION + float3 refraction = shSample(refractionMap, (screenCoords-(normal.xy*REFR_BUMP))*1.0).rgb; // brighten up the refraction underwater - refraction = (cameraPos.y < 0) ? shSaturate(refraction * 1.5) : refraction; - + refraction = (cameraPos.z < 0) ? shSaturate(refraction * 1.5) : refraction; +#endif + // specular - float specular = pow(max(dot(R, lVec), 0.0),SPEC_HARDNESS); + float specular = pow(max(dot(R, lVec), 0.0),SPEC_HARDNESS) * shadow; +#if REFRACTION shOutputColour(0).xyz = shLerp( shLerp(refraction, scatterColour, lightScatter), reflection, fresnel) + specular * sunSpecular.xyz; - - // smooth transition to shore (above water only) - shOutputColour(0).xyz = shLerp(shOutputColour(0).xyz, refraction, (1-shoreFade) * (1-isUnderwater)); - +#else + shOutputColour(0).xyz = shLerp(reflection, float3(0.18039, 0.23137, 0.25490), (1.0-fresnel)*0.5) + specular * sunSpecular.xyz; +#endif // fog - if (isUnderwater == 1) - { - float waterSunGradient = dot(-vVec, -lVec); - waterSunGradient = shSaturate(pow(waterSunGradient*0.7+0.3,2.0)); - float3 waterSunColour = gammaCorrectRead(float3(0.0,1.0,0.85))*waterSunGradient * 0.5; - - float waterGradient = dot(-vVec, float3(0.0,-1.0,0.0)); - waterGradient = clamp((waterGradient*0.5+0.5),0.2,1.0); - float3 watercolour = (gammaCorrectRead(float3(0.0078, 0.5176, 0.700))+waterSunColour)*waterGradient*2.0; - float3 waterext = gammaCorrectRead(float3(0.6, 0.9, 1.0));//water extinction - watercolour = shLerp(watercolour*0.3*waterSunFade_sunHeight.x, watercolour, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); - - float darkness = VISIBILITY*2.0; - darkness = clamp((cameraPos.y+darkness)/darkness,0.2,1.0); - - - float fog = shSaturate(length(cameraPos.xyz-position.xyz) / VISIBILITY); - shOutputColour(0).xyz = shLerp(shOutputColour(0).xyz, watercolour * darkness, shSaturate(fog / waterext)); - } - else - { - float fogValue = shSaturate((length(cameraPos.xyz-position.xyz) - fogParams.y) * fogParams.w); - shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, gammaCorrectRead(fogColor), fogValue); - } - - shOutputColour(0).xyz = gammaCorrectOutput(shOutputColour(0).xyz); + float fogValue = shSaturate((depthPassthrough - fogParams.y) * fogParams.w); + shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue); - shOutputColour(0).w = 1; +#if REFRACTION + shOutputColour(0).w = 1; +#else + shOutputColour(0).w = shSaturate(fresnel*2 + specular); +#endif } #endif diff --git a/files/materials/watersim.mat b/files/materials/watersim.mat new file mode 100644 index 000000000..b58b1a851 --- /dev/null +++ b/files/materials/watersim.mat @@ -0,0 +1,59 @@ +material HeightmapSimulation +{ + allow_fixed_function false + pass + { + depth_check off + depth_write off + vertex_program transform_vertex + fragment_program watersim_fragment + + texture_unit heightPrevSampler + { + tex_address_mode border + tex_border_colour 0 0 0 + texture_alias Heightmap0 + } + texture_unit heightCurrentSampler + { + tex_address_mode border + tex_border_colour 0 0 0 + texture_alias Heightmap1 + } + } +} + +material HeightToNormalMap +{ + allow_fixed_function false + pass + { + depth_check off + depth_write off + vertex_program transform_vertex + fragment_program height_to_normal_fragment + + texture_unit heightCurrentSampler + { + texture_alias Heightmap2 + } + } +} + +material AddImpulse +{ + allow_fixed_function false + pass + { + depth_check off + depth_write off + scene_blend alpha_blend + vertex_program transform_vertex + fragment_program add_impulse_fragment + + texture_unit alphaMap + { + texture circle.png + } + } +} diff --git a/files/materials/watersim.shaderset b/files/materials/watersim.shaderset new file mode 100644 index 000000000..ea512e25f --- /dev/null +++ b/files/materials/watersim.shaderset @@ -0,0 +1,31 @@ +shader_set transform_vertex +{ + source quad.shader + type vertex + profiles_cg vs_2_0 vp40 arbvp1 + profiles_hlsl vs_2_0 +} + +shader_set watersim_fragment +{ + source watersim_heightmap.shader + type fragment + profiles_cg ps_3_0 ps_2_x ps_2_0 fp40 arbfp1 + profiles_hlsl ps_3_0 ps_2_0 +} + +shader_set height_to_normal_fragment +{ + source watersim_heighttonormal.shader + type fragment + profiles_cg ps_3_0 ps_2_x ps_2_0 fp40 arbfp1 + profiles_hlsl ps_3_0 ps_2_0 +} + +shader_set add_impulse_fragment +{ + source watersim_addimpulse.shader + type fragment + profiles_cg ps_3_0 ps_2_x ps_2_0 fp40 arbfp1 + profiles_hlsl ps_3_0 ps_2_0 +} diff --git a/files/materials/watersim_addimpulse.shader b/files/materials/watersim_addimpulse.shader new file mode 100644 index 000000000..3ca4192cd --- /dev/null +++ b/files/materials/watersim_addimpulse.shader @@ -0,0 +1,12 @@ +#include "core.h" +#include "watersim_common.h" + + SH_BEGIN_PROGRAM + shInput(float2, UV) + shSampler2D(alphaMap) + + SH_START_PROGRAM + { + shOutputColour(0) = EncodeHeightmap(1.0); + shOutputColour(0).a = shSample (alphaMap, UV.xy).a; + } diff --git a/files/materials/watersim_common.h b/files/materials/watersim_common.h new file mode 100644 index 000000000..aa7a636a0 --- /dev/null +++ b/files/materials/watersim_common.h @@ -0,0 +1,25 @@ +float DecodeHeightmap(float4 heightmap) +{ + float4 table = float4(1.0, -1.0, 0.0, 0.0); + return dot(heightmap, table); +} + +float DecodeHeightmap(shTexture2D HeightmapSampler, float2 texcoord) +{ + float4 heightmap = shSample(HeightmapSampler, texcoord); + return DecodeHeightmap(heightmap); +} + +float4 EncodeHeightmap(float fHeight) +{ + float h = fHeight; + float positive = fHeight > 0.0 ? fHeight : 0.0; + float negative = fHeight < 0.0 ? -fHeight : 0.0; + + float4 color = float4(0,0,0,0); + + color.r = positive; + color.g = negative; + + return color; +} diff --git a/files/materials/watersim_heightmap.shader b/files/materials/watersim_heightmap.shader new file mode 100644 index 000000000..ec8ae4174 --- /dev/null +++ b/files/materials/watersim_heightmap.shader @@ -0,0 +1,51 @@ +#include "core.h" + +#define DAMPING 0.95 + +#include "watersim_common.h" + + SH_BEGIN_PROGRAM + shInput(float2, UV) + shSampler2D(heightPrevSampler) + shSampler2D(heightCurrentSampler) + shUniform(float3, previousFrameOffset) @shSharedParameter(previousFrameOffset, previousFrameOffset) + shUniform(float3, currentFrameOffset) @shSharedParameter(currentFrameOffset, currentFrameOffset) + shUniform(float4, rippleTextureSize) @shSharedParameter(rippleTextureSize, rippleTextureSize) + + SH_START_PROGRAM + { +#if !SH_HLSL + const float3 offset[4] = float3[4]( + float3(-1.0, 0.0, 0.25), + float3( 1.0, 0.0, 0.25), + float3( 0.0,-1.0, 0.25), + float3( 0.0, 1.0, 0.25) + ); +#else + const float3 offset[4] = { + float3(-1.0, 0.0, 0.25), + float3( 1.0, 0.0, 0.25), + float3( 0.0,-1.0, 0.25), + float3( 0.0, 1.0, 0.25) + }; +#endif + + float fHeightPrev = DecodeHeightmap(heightPrevSampler, UV.xy + previousFrameOffset.xy + currentFrameOffset.xy); + + float fNeighCurrent = 0; + for ( int i=0; i<4; i++ ) + { + float2 vTexcoord = UV + currentFrameOffset.xy + offset[i].xy * rippleTextureSize.xy; + fNeighCurrent += (DecodeHeightmap(heightCurrentSampler, vTexcoord) * offset[i].z); + } + + float fHeight = fNeighCurrent * 2.0 - fHeightPrev; + + fHeight *= DAMPING; + + shOutputColour(0) = EncodeHeightmap(fHeight); + } + + + + diff --git a/files/materials/watersim_heighttonormal.shader b/files/materials/watersim_heighttonormal.shader new file mode 100644 index 000000000..eaa4af498 --- /dev/null +++ b/files/materials/watersim_heighttonormal.shader @@ -0,0 +1,36 @@ +#include "core.h" +#include "watersim_common.h" + + SH_BEGIN_PROGRAM + shInput(float2, UV) + shSampler2D(heightCurrentSampler) + shUniform(float4, rippleTextureSize) @shSharedParameter(rippleTextureSize, rippleTextureSize) + + SH_START_PROGRAM + { +#if !SH_HLSL + float2 offset[4] = float2[4] ( + float2(-1.0, 0.0), + float2( 1.0, 0.0), + float2( 0.0,-1.0), + float2( 0.0, 1.0) + ); +#else + float2 offset[4] = { + float2(-1.0, 0.0), + float2( 1.0, 0.0), + float2( 0.0,-1.0), + float2( 0.0, 1.0) + }; +#endif + + float fHeightL = DecodeHeightmap(heightCurrentSampler, UV.xy + offset[0]*rippleTextureSize.xy); + float fHeightR = DecodeHeightmap(heightCurrentSampler, UV.xy + offset[1]*rippleTextureSize.xy); + float fHeightT = DecodeHeightmap(heightCurrentSampler, UV.xy + offset[2]*rippleTextureSize.xy); + float fHeightB = DecodeHeightmap(heightCurrentSampler, UV.xy + offset[3]*rippleTextureSize.xy); + + float3 n = float3(fHeightB - fHeightT, fHeightR - fHeightL, 1.0); + float3 normal = (n + 1.0) * 0.5; + + shOutputColour(0) = float4(normal.rgb, 1.0); + } diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 871be93d1..af695ac6c 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -9,7 +9,6 @@ set(MYGUI_FILES core.skin core.xml EBGaramond-Regular.ttf - mwgui.png Obliviontt.zip openmw_alchemy_window.layout openmw_book.layout @@ -79,8 +78,11 @@ set(MYGUI_FILES openmw_trainingwindow.layout openmw_travel_window.layout openmw_persuasion_dialog.layout + openmw_merchantrepair.layout + openmw_repair.layout + openmw_companion_window.layout smallbars.png - VeraMono.ttf + DejaVuLGCSansMono.ttf markers.png ) diff --git a/files/mygui/DejaVuLGCSansMono.ttf b/files/mygui/DejaVuLGCSansMono.ttf new file mode 100644 index 000000000..80c45b73e Binary files /dev/null and b/files/mygui/DejaVuLGCSansMono.ttf differ diff --git a/files/mygui/VeraMono.ttf b/files/mygui/VeraMono.ttf deleted file mode 100644 index 139f0b431..000000000 Binary files a/files/mygui/VeraMono.ttf and /dev/null differ diff --git a/files/mygui/core.skin b/files/mygui/core.skin index e52080fe0..ea3e2debc 100644 --- a/files/mygui/core.skin +++ b/files/mygui/core.skin @@ -2,7 +2,6 @@ - diff --git a/files/mygui/mwgui.png b/files/mygui/mwgui.png deleted file mode 100644 index 318f16e41..000000000 Binary files a/files/mygui/mwgui.png and /dev/null differ diff --git a/files/mygui/openmw_alchemy_window.layout b/files/mygui/openmw_alchemy_window.layout index 8471f69df..076a9293a 100644 --- a/files/mygui/openmw_alchemy_window.layout +++ b/files/mygui/openmw_alchemy_window.layout @@ -59,11 +59,7 @@ - - - - - + diff --git a/files/mygui/openmw_book.layout b/files/mygui/openmw_book.layout index 894c1dbeb..d9ac2f43a 100644 --- a/files/mygui/openmw_book.layout +++ b/files/mygui/openmw_book.layout @@ -2,41 +2,47 @@ - + - + + - - - - + + + + + + - - - - + + + + + + - + + - + - + - + - - + + diff --git a/files/mygui/openmw_chargen_class_description.layout b/files/mygui/openmw_chargen_class_description.layout index 11031eb4e..8823e1e76 100644 --- a/files/mygui/openmw_chargen_class_description.layout +++ b/files/mygui/openmw_chargen_class_description.layout @@ -4,6 +4,9 @@ + + + diff --git a/files/mygui/openmw_chargen_race.layout b/files/mygui/openmw_chargen_race.layout index a9ec9905d..c569abb86 100644 --- a/files/mygui/openmw_chargen_race.layout +++ b/files/mygui/openmw_chargen_race.layout @@ -8,33 +8,52 @@ - + - + - - + + + + + + + + + - - - + + + + + + + + + - - - + + + + + + + + + - + @@ -42,11 +61,11 @@ - + - + diff --git a/files/mygui/openmw_companion_window.layout b/files/mygui/openmw_companion_window.layout new file mode 100644 index 000000000..87aa01fd4 --- /dev/null +++ b/files/mygui/openmw_companion_window.layout @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_container_window.layout b/files/mygui/openmw_container_window.layout index 94e9458a5..5c9327b1d 100644 --- a/files/mygui/openmw_container_window.layout +++ b/files/mygui/openmw_container_window.layout @@ -1,20 +1,20 @@ - + - - - - - + + + + + diff --git a/files/mygui/openmw_dialogue_window.layout b/files/mygui/openmw_dialogue_window.layout index 1271a287b..46090d000 100644 --- a/files/mygui/openmw_dialogue_window.layout +++ b/files/mygui/openmw_dialogue_window.layout @@ -3,14 +3,15 @@ - - - - - - - - + + + + + + + + + diff --git a/files/mygui/openmw_edit.skin.xml b/files/mygui/openmw_edit.skin.xml index 02fee4b17..da21385e2 100644 --- a/files/mygui/openmw_edit.skin.xml +++ b/files/mygui/openmw_edit.skin.xml @@ -1,55 +1,52 @@ + + + + + + - + + + + + - - - - - - - - - - - - - + + + + + + + - + + + - - - - - - - - - - - - - + + + + diff --git a/files/mygui/openmw_edit_effect.layout b/files/mygui/openmw_edit_effect.layout index 45ecb63ed..cad22c064 100644 --- a/files/mygui/openmw_edit_effect.layout +++ b/files/mygui/openmw_edit_effect.layout @@ -31,7 +31,7 @@ - + @@ -39,7 +39,7 @@ - + @@ -56,7 +56,7 @@ - + @@ -72,7 +72,7 @@ - + diff --git a/files/mygui/openmw_enchanting_dialog.layout b/files/mygui/openmw_enchanting_dialog.layout index a19c56925..f64d21dea 100644 --- a/files/mygui/openmw_enchanting_dialog.layout +++ b/files/mygui/openmw_enchanting_dialog.layout @@ -26,14 +26,18 @@ - + + + - + + + @@ -66,18 +70,18 @@ - + - + - + - - + + @@ -85,18 +89,23 @@ - + + + + + - + - + + diff --git a/files/mygui/openmw_font.xml b/files/mygui/openmw_font.xml index cb5dd648f..726bfb281 100644 --- a/files/mygui/openmw_font.xml +++ b/files/mygui/openmw_font.xml @@ -9,8 +9,10 @@ + + @@ -21,34 +23,21 @@ - - - - - + + + + - + + + + + + + - - - - - - - - - - - - - - - - - - diff --git a/files/mygui/openmw_hud.layout b/files/mygui/openmw_hud.layout index e4c6f0765..67d0632f8 100644 --- a/files/mygui/openmw_hud.layout +++ b/files/mygui/openmw_hud.layout @@ -61,10 +61,7 @@ - - + @@ -107,24 +104,24 @@ - - + - + - + - + - + - + diff --git a/files/mygui/openmw_hud_box.skin.xml b/files/mygui/openmw_hud_box.skin.xml index ce231e5bb..f847ca51a 100644 --- a/files/mygui/openmw_hud_box.skin.xml +++ b/files/mygui/openmw_hud_box.skin.xml @@ -1,78 +1,35 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/files/mygui/openmw_inventory_window.layout b/files/mygui/openmw_inventory_window.layout index 3a60916f7..0f315b239 100644 --- a/files/mygui/openmw_inventory_window.layout +++ b/files/mygui/openmw_inventory_window.layout @@ -1,13 +1,12 @@ - + - - + @@ -17,7 +16,6 @@ - @@ -27,11 +25,7 @@ - - - - - + diff --git a/files/mygui/openmw_itemselection_dialog.layout b/files/mygui/openmw_itemselection_dialog.layout index 39c048303..003eb1c6a 100644 --- a/files/mygui/openmw_itemselection_dialog.layout +++ b/files/mygui/openmw_itemselection_dialog.layout @@ -4,11 +4,7 @@ - - - - - + diff --git a/files/mygui/openmw_journal.layout b/files/mygui/openmw_journal.layout index fdf82e4de..58d2c2690 100644 --- a/files/mygui/openmw_journal.layout +++ b/files/mygui/openmw_journal.layout @@ -2,24 +2,108 @@ - + + + + + - - - - - - - - - + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_journal_skin.xml b/files/mygui/openmw_journal_skin.xml index 9f82e907f..44448225c 100644 --- a/files/mygui/openmw_journal_skin.xml +++ b/files/mygui/openmw_journal_skin.xml @@ -10,8 +10,6 @@ - - - + diff --git a/files/mygui/openmw_layers.xml b/files/mygui/openmw_layers.xml index 33a3ca40c..84ec6f7c5 100644 --- a/files/mygui/openmw_layers.xml +++ b/files/mygui/openmw_layers.xml @@ -7,7 +7,7 @@ - + diff --git a/files/mygui/openmw_levelup_dialog.layout b/files/mygui/openmw_levelup_dialog.layout index 86e65e99a..765bf88a8 100644 --- a/files/mygui/openmw_levelup_dialog.layout +++ b/files/mygui/openmw_levelup_dialog.layout @@ -1,17 +1,36 @@ - + + + + + + - + - + + + + + + + + + + + + + + + @@ -127,12 +146,17 @@ - - - - + + + + + + + + + diff --git a/files/mygui/openmw_list.skin.xml b/files/mygui/openmw_list.skin.xml index 64435451a..a5065c7ca 100644 --- a/files/mygui/openmw_list.skin.xml +++ b/files/mygui/openmw_list.skin.xml @@ -1,228 +1,107 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + - - + - - - - - + + + - - - - - - - - - - - + - - - - - - + - - + + - - + + - - - - - + + - - - - - + - - + - - + - - - + + + + + + + + + - - + + - - - - + + + + + + - + - - - - + + + + + @@ -234,81 +113,94 @@ - - - - + + + + - - + - - + + + - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - - - - + + + + - - + - - + - + - - - - - + + + + + - + - - + - - - - - - - - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_loading_screen.layout b/files/mygui/openmw_loading_screen.layout index 6862702ca..1e4bba5ed 100644 --- a/files/mygui/openmw_loading_screen.layout +++ b/files/mygui/openmw_loading_screen.layout @@ -6,12 +6,13 @@ - + - + + - + diff --git a/files/mygui/openmw_map_window.layout b/files/mygui/openmw_map_window.layout index b5479b676..d4b87e60d 100644 --- a/files/mygui/openmw_map_window.layout +++ b/files/mygui/openmw_map_window.layout @@ -1,7 +1,7 @@ - + diff --git a/files/mygui/openmw_map_window_skin.xml b/files/mygui/openmw_map_window_skin.xml index 0c6050969..13f18c6d3 100644 --- a/files/mygui/openmw_map_window_skin.xml +++ b/files/mygui/openmw_map_window_skin.xml @@ -1,7 +1,7 @@ - + diff --git a/files/mygui/openmw_merchantrepair.layout b/files/mygui/openmw_merchantrepair.layout new file mode 100644 index 000000000..360f5f4f0 --- /dev/null +++ b/files/mygui/openmw_merchantrepair.layout @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_pointer.xml b/files/mygui/openmw_pointer.xml index 42ee5d435..cf21037f8 100644 --- a/files/mygui/openmw_pointer.xml +++ b/files/mygui/openmw_pointer.xml @@ -5,26 +5,31 @@ + + - + + - + + - + + diff --git a/files/mygui/openmw_progress.skin.xml b/files/mygui/openmw_progress.skin.xml index c4b94e28e..4666be221 100644 --- a/files/mygui/openmw_progress.skin.xml +++ b/files/mygui/openmw_progress.skin.xml @@ -17,6 +17,11 @@ + + + + + @@ -51,4 +56,12 @@ + + + + + + + + diff --git a/files/mygui/openmw_repair.layout b/files/mygui/openmw_repair.layout new file mode 100644 index 000000000..2881a5853 --- /dev/null +++ b/files/mygui/openmw_repair.layout @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_resources.xml b/files/mygui/openmw_resources.xml index 5a695515d..e47ff6386 100644 --- a/files/mygui/openmw_resources.xml +++ b/files/mygui/openmw_resources.xml @@ -17,27 +17,6 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/files/mygui/openmw_scroll.layout b/files/mygui/openmw_scroll.layout index 6315c0241..194700f36 100644 --- a/files/mygui/openmw_scroll.layout +++ b/files/mygui/openmw_scroll.layout @@ -13,7 +13,7 @@ - + diff --git a/files/mygui/openmw_scroll_skin.xml b/files/mygui/openmw_scroll_skin.xml index 70fad3f4b..1b94f0c29 100644 --- a/files/mygui/openmw_scroll_skin.xml +++ b/files/mygui/openmw_scroll_skin.xml @@ -2,12 +2,12 @@ - + - + - + diff --git a/files/mygui/openmw_settings.xml b/files/mygui/openmw_settings.xml index c63f962fb..37d235968 100644 --- a/files/mygui/openmw_settings.xml +++ b/files/mygui/openmw_settings.xml @@ -1,7 +1,7 @@ - + diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 2f9b5a67f..77d14d0f6 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -15,8 +15,8 @@ - - + + @@ -30,8 +30,8 @@ - - + + @@ -64,36 +64,36 @@ - - + + - - + + - - + + - - + + - - + + @@ -117,8 +117,8 @@ - - + + @@ -133,8 +133,8 @@ - - + + @@ -157,59 +157,49 @@ - + - + - + - - + + - + - - + + - + - - - - - - - - - - - - - - + + + + + - - - + + @@ -233,16 +223,16 @@ - - + + - - + + @@ -290,9 +280,9 @@ - + - + diff --git a/files/mygui/openmw_spell_buying_window.layout b/files/mygui/openmw_spell_buying_window.layout index 1e18fda23..c55cd0e22 100644 --- a/files/mygui/openmw_spell_buying_window.layout +++ b/files/mygui/openmw_spell_buying_window.layout @@ -5,13 +5,13 @@ - - + + - - - + + + diff --git a/files/mygui/openmw_spell_window.layout b/files/mygui/openmw_spell_window.layout index d489f41b8..18a5af352 100644 --- a/files/mygui/openmw_spell_window.layout +++ b/files/mygui/openmw_spell_window.layout @@ -1,11 +1,11 @@ - + - - + + diff --git a/files/mygui/openmw_stats_window.layout b/files/mygui/openmw_stats_window.layout index c2c8a5dae..55ee7b9da 100644 --- a/files/mygui/openmw_stats_window.layout +++ b/files/mygui/openmw_stats_window.layout @@ -1,7 +1,7 @@ - + diff --git a/files/mygui/openmw_text.skin.xml b/files/mygui/openmw_text.skin.xml index 13b22ff93..b2a15b503 100644 --- a/files/mygui/openmw_text.skin.xml +++ b/files/mygui/openmw_text.skin.xml @@ -154,8 +154,13 @@ - - + + + + + + + diff --git a/files/mygui/openmw_tooltips.layout b/files/mygui/openmw_tooltips.layout index 514d1a25b..7291f2ca2 100644 --- a/files/mygui/openmw_tooltips.layout +++ b/files/mygui/openmw_tooltips.layout @@ -5,124 +5,136 @@ - - - - + + + + - + + + + + + + + + + + - - - - + + + - + - - + - - - - + + + - + - - + - - - + - - - - - + + + + + + + + + + + - + - - - - + + + - + + + - - - + + + - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - + - - - - + @@ -135,37 +147,38 @@ - - + + + - + + - - - - + - - - + + + + + + + + + + + - + - - - - - - @@ -181,43 +194,49 @@ - - + + + - + - - - - + + + - + + - - - - + + + + + + + + + + + + + + - - - - + - - diff --git a/files/mygui/openmw_trade_window.layout b/files/mygui/openmw_trade_window.layout index d38377f98..a540c387e 100644 --- a/files/mygui/openmw_trade_window.layout +++ b/files/mygui/openmw_trade_window.layout @@ -24,11 +24,7 @@ - - - - - + @@ -51,10 +47,6 @@ - - - - @@ -66,6 +58,10 @@ + + + + diff --git a/files/mygui/openmw_windows.skin.xml b/files/mygui/openmw_windows.skin.xml index a9eb23d68..73f68e80d 100644 --- a/files/mygui/openmw_windows.skin.xml +++ b/files/mygui/openmw_windows.skin.xml @@ -8,6 +8,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -473,9 +585,7 @@ - - - + diff --git a/files/mygui/smallbars.png b/files/mygui/smallbars.png index a9ed572ef..f938412c2 100644 Binary files a/files/mygui/smallbars.png and b/files/mygui/smallbars.png differ diff --git a/files/opencs.desktop b/files/opencs.desktop new file mode 100644 index 000000000..f6aad5b09 --- /dev/null +++ b/files/opencs.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Name=OpenMW Content Editor +GenericName=Content Editor +Comment=A replacement for the Morrowind Construction Set. +TryExec=opencs +Exec=opencs +Icon=opencs +Categories=Game;RolePlaying; diff --git a/files/opencs/opencs.png b/files/opencs/opencs.png new file mode 100644 index 000000000..dddf220a3 Binary files /dev/null and b/files/opencs/opencs.png differ diff --git a/files/opencs/resources.qrc b/files/opencs/resources.qrc new file mode 100644 index 000000000..ecfab44a2 --- /dev/null +++ b/files/opencs/resources.qrc @@ -0,0 +1,5 @@ + + + opencs.png + + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 1768b2f5e..69aa20883 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -28,8 +28,6 @@ vsync = false # PBuffer, FBO, Copy opengl rtt mode = FBO -gamma = 2.2 - [GUI] # 1 is fully opaque menu transparency = 0.84 @@ -54,7 +52,8 @@ texture filtering = anisotropic anisotropy = 4 # Number of texture mipmaps to generate -num mipmaps = 5 +# This setting is currently ignored due to mipmap generation problems on Intel/AMD +#num mipmaps = 5 shader mode = @@ -129,11 +128,10 @@ fog end factor = 1.0 num lights = 8 [Water] -# Enable this to get fancy-looking water with reflections and refractions -# Only available if object shaders are on -# All the settings below have no effect if this is false shader = true +refraction = true + rtt size = 512 reflect terrain = true reflect statics = false @@ -141,9 +139,6 @@ reflect small statics = false reflect actors = false reflect misc = false -# Enable underwater effect. It is not resource intensive, so only disable it if you have problems. -underwater effect = true - [Sound] # Device name. Blank means default device = diff --git a/files/transparency-overrides.cfg b/files/transparency-overrides.cfg index 299792be1..65f9b477a 100644 --- a/files/transparency-overrides.cfg +++ b/files/transparency-overrides.cfg @@ -572,3 +572,52 @@ [textures\tx_velothi_glyph00.dds] alphaRejectValue = 128 + + + +# Bloodmoon + +[textures\tx_bm_holly_01.dds] + alphaRejectValue = 128 + +[textures\tx_bm_holly_snow_01.dds] + alphaRejectValue = 128 + +[textures\tx_bm_pine_04a.dds] + alphaRejectValue = 128 + +[textures\tx_bm_pine_03a.dds] + alphaRejectValue = 128 + +[textures\tx_bm_pine_02a.dds] + alphaRejectValue = 128 + +[textures\tx_bm_pine_01a.dds] + alphaRejectValue = 128 + +[textures\tx_bm_shrub_02.dds] + alphaRejectValue = 128 + +[textures\tx_bm_shrub_01.dds] + alphaRejectValue = 128 + +[textures\tx_bm_snow_pine_01a.dds] + alphaRejectValue = 128 + +[textures\tx_bm_snow_pine_02a.dds] + alphaRejectValue = 128 + +[textures\tx_bm_snow_pine_03a.dds] + alphaRejectValue = 128 + +[textures\tx_bm_snow_pine_04a.dds] + alphaRejectValue = 128 + +[textures\tx_bm_deadpine_01.dds] + alphaRejectValue = 128 + +[textures\tx_bm_shrub_snow_02.dds] + alphaRejectValue = 128 + +[textures\tx_bm_s_deadpine_01.dds] + alphaRejectValue = 128 diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui new file mode 100644 index 000000000..041a9576d --- /dev/null +++ b/files/ui/datafilespage.ui @@ -0,0 +1,167 @@ + + + DataFilesPage + + + + 0 + 0 + 518 + 304 + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Filter: + + + + + + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + false + + + + + + + + + + + Current Profile: + + + + + + + true + + + + 0 + 0 + + + + + + + + New Profile + + + &New Profile + + + true + + + + + + + Delete Profile + + + Delete Profile + + + Ctrl+D + + + true + + + + + + + + + + + + + + New Profile + + + New Profile + + + Ctrl+N + + + + + + + + + + Delete Profile + + + Delete Profile + + + + + Check Selection + + + + + Uncheck Selection + + + + + + LineEdit + QLineEdit +
components/fileorderlist/utils/lineedit.hpp
+
+ + ProfilesComboBox + QComboBox +
components/fileorderlist/utils/profilescombobox.hpp
+
+
+ + +
diff --git a/files/ui/graphicspage.ui b/files/ui/graphicspage.ui new file mode 100644 index 000000000..5c330cebd --- /dev/null +++ b/files/ui/graphicspage.ui @@ -0,0 +1,142 @@ + + + GraphicsPage + + + + 0 + 0 + 332 + 297 + + + + + + + Render System + + + + + + Rendering Subsystem: + + + + + + + + + + + + + Display + + + + + + Vertical Sync + + + + + + + Full Screen + + + + + + + Anti-aliasing: + + + + + + + Resolution: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + + + + + + + 800 + + + + + + + x + + + + + + + 600 + + + + + + + + + Custom: + + + + + + + Standard: + + + true + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 61 + + + + + + + + + diff --git a/files/ui/mainwindow.ui b/files/ui/mainwindow.ui new file mode 100644 index 000000000..a1dfb172b --- /dev/null +++ b/files/ui/mainwindow.ui @@ -0,0 +1,72 @@ + + + MainWindow + + + + 575 + 535 + + + + OpenMW Launcher + + + + :/images/openmw.png:/images/openmw.png + + + + + + + + 400 + 80 + + + + + 16777215 + 80 + + + + #iconWidget { + background-image: url(":/images/openmw-header.png"); + background-color: palette(base); + background-repeat: no-repeat; + background-attachment: scroll; + background-position: right; +} + + + + + + + + + + + + + + + + + + + + QDialogButtonBox::Close + + + + + + + + + + + diff --git a/files/ui/playpage.ui b/files/ui/playpage.ui new file mode 100644 index 000000000..c0320de1e --- /dev/null +++ b/files/ui/playpage.ui @@ -0,0 +1,193 @@ + + + PlayPage + + + + + + #Scroll { + background-image: url(":/images/playpage-background.png"); + background-repeat: no-repeat; + background-position: top; +} + + + + QFrame::StyledPanel + + + QFrame::Plain + + + 0 + + + + 30 + + + 100 + + + 30 + + + + + #profilesComboBox { + padding: 1px 18px 1px 3px; + + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 white, stop:0.2 rgba(0, 0, 0, 25), stop:1 white); + border-width: 1px; + border-color: rgba(0, 0, 0, 125); + border-style: solid; + border-radius: 2px; + + font-size: 12pt; + font-family: "EB Garamond", "EB Garamond 08"; + color: black; +} + +/*QComboBox gets the "on" state when the popup is open */ +#profilesComboBox:!editable:on { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 rgba(0, 0, 0, 75), + stop:0.1 rgba(0, 0, 0, 15), + stop:0.2 rgba(255, 255, 255, 55)); + + border: 1px solid rgba(0, 0, 0, 55); +} + +#profilesComboBox:on { /* shift the text when the popup opens */ + padding-top: 3px; + padding-left: 4px; +} + +#profilesComboBox::drop-down { + subcontrol-origin: padding; + subcontrol-position: top right; + + border-width: 1px; + border-left-width: 1px; + border-left-color: darkgray; + border-left-style: solid; /* just a single line */ + border-top-right-radius: 3px; /* same radius as the QComboBox */ + border-bottom-right-radius: 3px; +} + +#profilesComboBox::down-arrow { + image: url(":/images/down.png"); +} + +#profilesComboBox::down-arrow:on { /* shift the arrow when popup is open */ + top: 1px; + left: 1px; +} + +#profilesComboBox QAbstractItemView { + border: 0px; +} + + + + + + + #profileLabel { + font-size: 18pt; + font-family: "EB Garamond", "EB Garamond 08"; + color: black; +} + + + + Current Profile: + + + + + + + + 200 + 85 + + + + + 200 + 85 + + + + #playButton { + height: 50px; + margin-bottom: 30px; + + background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, + stop:0 rgba(255, 255, 255, 200), + stop:0.1 rgba(255, 255, 255, 15), + stop:0.49 rgba(255, 255, 255, 75), + stop:0.5 rgba(0, 0, 0, 0), + stop:0.9 rgba(0, 0, 0, 55), + stop:1 rgba(0, 0, 0, 100)); + + font-size: 26pt; + font-family: "EB Garamond", "EB Garamond 08"; + color: black; + + border-right: 1px solid rgba(0, 0, 0, 155); + border-left: 1px solid rgba(0, 0, 0, 55); + border-top: 1px solid rgba(0, 0, 0, 55); + border-bottom: 1px solid rgba(0, 0, 0, 155); + + border-radius: 5px; +} + +#playButton:hover { + border-bottom: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0)); + border-top: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0)); + border-right: qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0)); + border-left: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0)); + border-width: 2px; + border-style: solid; +} + +#playButton:pressed { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 rgba(0, 0, 0, 75), + stop:0.1 rgba(0, 0, 0, 15), + stop:0.2 rgba(255, 255, 255, 55) + stop:0.95 rgba(255, 255, 255, 55), + stop:1 rgba(255, 255, 255, 155)); + + border: 1px solid rgba(0, 0, 0, 55); +} + + + Play + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + diff --git a/files/water/circle.png b/files/water/circle.png new file mode 100644 index 000000000..9a1cf268c Binary files /dev/null and b/files/water/circle.png differ diff --git a/files/water/underwater_dome.mesh b/files/water/underwater_dome.mesh deleted file mode 100644 index 64ca569c2..000000000 Binary files a/files/water/underwater_dome.mesh and /dev/null differ diff --git a/libs/openengine/bullet/BtOgreExtras.h b/libs/openengine/bullet/BtOgreExtras.h index 423924eda..b20a3ff98 100644 --- a/libs/openengine/bullet/BtOgreExtras.h +++ b/libs/openengine/bullet/BtOgreExtras.h @@ -207,7 +207,7 @@ public: mLineDrawer->setMaterial("BtOgre/DebugLines"); - mLineDrawer->setVisibilityFlags (1024); + //mLineDrawer->setVisibilityFlags (1024); } ~DebugDrawer() diff --git a/libs/openengine/bullet/BulletShapeLoader.cpp b/libs/openengine/bullet/BulletShapeLoader.cpp index 071a5ee8a..431d2b91b 100644 --- a/libs/openengine/bullet/BulletShapeLoader.cpp +++ b/libs/openengine/bullet/BulletShapeLoader.cpp @@ -17,15 +17,17 @@ Ogre::Resource(creator, name, handle, group, isManual, loader) list all the options that need to be set before loading, of which we have none as such. Full details can be set through scripts. */ - Shape = NULL; + mCollisionShape = NULL; + mRaycastingShape = NULL; + mHasCollisionNode = false; mCollide = true; - mIgnore = false; createParamDictionary("BulletShape"); } BulletShape::~BulletShape() { - deleteShape(Shape); + deleteShape(mCollisionShape); + deleteShape(mRaycastingShape); } // farm out to BulletShapeLoader @@ -34,27 +36,28 @@ void BulletShape::loadImpl() mLoader->loadResource(this); } -void BulletShape::deleteShape(btCollisionShape* mShape) +void BulletShape::deleteShape(btCollisionShape* shape) { - if(mShape!=NULL) + if(shape!=NULL) { - if(mShape->isCompound()) + if(shape->isCompound()) { - btCompoundShape* ms = static_cast(Shape); + btCompoundShape* ms = static_cast(mCollisionShape); int a = ms->getNumChildShapes(); for(int i=0; i getChildShape(i)); } } - delete mShape; + delete shape; } - mShape = NULL; + shape = NULL; } void BulletShape::unloadImpl() { - deleteShape(Shape); + deleteShape(mCollisionShape); + deleteShape(mRaycastingShape); } //TODO:change this? diff --git a/libs/openengine/bullet/BulletShapeLoader.h b/libs/openengine/bullet/BulletShapeLoader.h index d9bd5879a..a6591010a 100644 --- a/libs/openengine/bullet/BulletShapeLoader.h +++ b/libs/openengine/bullet/BulletShapeLoader.h @@ -22,7 +22,7 @@ protected: void unloadImpl(); size_t calculateSize() const; - void deleteShape(btCollisionShape* mShape); + void deleteShape(btCollisionShape* shape); public: @@ -32,13 +32,17 @@ public: virtual ~BulletShape(); - btCollisionShape* Shape; - Ogre::Vector3 boxTranslation; - Ogre::Quaternion boxRotation; + btCollisionShape* mCollisionShape; + btCollisionShape* mRaycastingShape; + + // Whether or not a NiRootCollisionNode was present in the .nif. If there is none, the collision behaviour + // depends on object type, so we need to expose this variable. + bool mHasCollisionNode; + + Ogre::Vector3 mBoxTranslation; + Ogre::Quaternion mBoxRotation; //this flag indicate if the shape is used for collision or if it's for raycasting only. bool mCollide; - - bool mIgnore; }; /** diff --git a/libs/openengine/bullet/CMotionState.cpp b/libs/openengine/bullet/CMotionState.cpp index 6be615dfb..c20415884 100644 --- a/libs/openengine/bullet/CMotionState.cpp +++ b/libs/openengine/bullet/CMotionState.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include namespace OEngine { namespace Physic diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index b39ba53a2..bbe633847 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -2,8 +2,7 @@ #include #include #include -#include "pmove.h" -#include +#include #include "CMotionState.h" #include "OgreRoot.h" #include "btKinematicCharacterController.h" @@ -14,106 +13,71 @@ #include #include -#define BIT(x) (1<<(x)) - namespace OEngine { namespace Physic { - enum collisiontypes { - COL_NOTHING = 0, //createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation); + mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation, true); Ogre::Quaternion inverse = mBoxRotation.Inverse(); mBoxRotationInverse = btQuaternion(inverse.x, inverse.y, inverse.z,inverse.w); - mEngine->addRigidBody(mBody, false); //Add rigid body to dynamics world, but do not add to object map - pmove = new playerMove; - pmove->mEngine = mEngine; - btBoxShape* box = static_cast (mBody->getCollisionShape()); - if(box != NULL){ - btVector3 size = box->getHalfExtentsWithMargin(); - Ogre::Vector3 halfExtents = Ogre::Vector3(size.getX(), size.getY(), size.getZ()); - pmove->ps.halfExtents = halfExtents; - } + mEngine->addRigidBody(mBody, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map } PhysicActor::~PhysicActor() { - if(mBody){ + if(mBody) + { mEngine->dynamicsWorld->removeRigidBody(mBody); delete mBody; } - delete pmove; - } - - void PhysicActor::setCurrentWater(bool hasWater, int waterHeight){ - pmove->hasWater = hasWater; - if(hasWater){ - pmove->waterHeight = waterHeight; + if(mRaycastingBody) + { + mEngine->dynamicsWorld->removeRigidBody(mRaycastingBody); + delete mRaycastingBody; } } - void PhysicActor::setGravity(float gravity) - { - pmove->ps.gravity = gravity; - } - - void PhysicActor::setSpeed(float speed) - { - pmove->ps.speed = speed; - } - void PhysicActor::enableCollisions(bool collision) { + assert(mBody); + if(collision && !collisionMode) mBody->translate(btVector3(0,0,-1000)); + if(!collision && collisionMode) mBody->translate(btVector3(0,0,1000)); collisionMode = collision; - if(collisionMode) - pmove->ps.move_type=PM_NORMAL; - else - pmove->ps.move_type=PM_NOCLIP; - } - - void PhysicActor::setJumpVelocity(float velocity) - { - pmove->ps.jump_velocity = velocity; } - bool PhysicActor::getCollisionMode() - { - return collisionMode; - } - void PhysicActor::setMovement(signed char rightmove, signed char forwardmove, signed char upmove) + void PhysicActor::setPosition(const Ogre::Vector3 &pos) { - playerMove::playercmd& pm_ref = pmove->cmd; - pm_ref.rightmove = rightmove; - pm_ref.forwardmove = forwardmove; - pm_ref.upmove = upmove; - } - - void PhysicActor::setPmoveViewAngles(float pitch, float yaw, float roll){ - pmove->ps.viewangles.x = pitch; - pmove->ps.viewangles.y = yaw; - pmove->ps.viewangles.z = roll; + assert(mBody); + if(pos != getPosition()) + { + mEngine->adjustRigidBody(mBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation); + mEngine->adjustRigidBody(mRaycastingBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation); + } } - - - void PhysicActor::setRotation(const Ogre::Quaternion quat) + void PhysicActor::setRotation(const Ogre::Quaternion &quat) { + assert(mBody); if(!quat.equals(getRotation(), Ogre::Radian(0))){ mEngine->adjustRigidBody(mBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation); + mEngine->adjustRigidBody(mRaycastingBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation); + } } + Ogre::Vector3 PhysicActor::getPosition() { + assert(mBody); btVector3 vec = mBody->getWorldTransform().getOrigin(); Ogre::Quaternion rotation = Ogre::Quaternion(mBody->getWorldTransform().getRotation().getW(), mBody->getWorldTransform().getRotation().getX(), mBody->getWorldTransform().getRotation().getY(), mBody->getWorldTransform().getRotation().getZ()); @@ -124,42 +88,62 @@ namespace Physic Ogre::Quaternion PhysicActor::getRotation() { + assert(mBody); btQuaternion quat = mBody->getWorldTransform().getRotation() * mBoxRotationInverse; return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ()); } - void PhysicActor::setPosition(const Ogre::Vector3 pos) - { - mEngine->adjustRigidBody(mBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation); - btVector3 vec = mBody->getWorldTransform().getOrigin(); - pmove->ps.origin = Ogre::Vector3(vec.getX(), vec.getY(), vec.getZ()); - } - void PhysicActor::setScale(float scale){ - Ogre::Vector3 position = getPosition(); - Ogre::Quaternion rotation = getRotation(); //We only need to change the scaled box translation, box rotations remain the same. + assert(mBody); mBoxScaledTranslation = mBoxScaledTranslation / mBody->getCollisionShape()->getLocalScaling().getX(); mBoxScaledTranslation *= scale; + Ogre::Vector3 pos = getPosition(); + Ogre::Quaternion rot = getRotation(); if(mBody){ mEngine->dynamicsWorld->removeRigidBody(mBody); + mEngine->dynamicsWorld->removeRigidBody(mRaycastingBody); delete mBody; + delete mRaycastingBody; } //Create the newly scaled rigid body - mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation); - mEngine->addRigidBody(mBody, false); //Add rigid body to dynamics world, but do not add to object map - btBoxShape* box = static_cast (mBody->getCollisionShape()); - if(box != NULL){ - btVector3 size = box->getHalfExtentsWithMargin(); - Ogre::Vector3 halfExtents = Ogre::Vector3(size.getX(), size.getY(), size.getZ()); - pmove->ps.halfExtents = halfExtents; + mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot); + mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot, 0, 0, true); + mEngine->addRigidBody(mBody, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map + } + + Ogre::Vector3 PhysicActor::getHalfExtents() const + { + if(mBody) + { + btBoxShape *box = static_cast(mBody->getCollisionShape()); + if(box != NULL) + { + btVector3 size = box->getHalfExtentsWithMargin(); + return Ogre::Vector3(size.getX(), size.getY(), size.getZ()); + } } + return Ogre::Vector3(0.0f); + } + + void PhysicActor::setVerticalForce(float force) + { + verticalForce = force; } - void PhysicActor::runPmove(){ - Pmove(pmove); - Ogre::Vector3 newpos = pmove->ps.origin; - mBody->getWorldTransform().setOrigin(btVector3(newpos.x, newpos.y, newpos.z)); + float PhysicActor::getVerticalForce() const + { + return verticalForce; + } + + void PhysicActor::setOnGround(bool grounded) + { + onGround = grounded; + } + + bool PhysicActor::getOnGround() const + { + return collisionMode && onGround; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -169,7 +153,7 @@ namespace Physic RigidBody::RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name) : btRigidBody(CI) , mName(name) - , mIgnore(false) + , mPlaceable(false) { } @@ -221,11 +205,7 @@ namespace Physic { if(!isDebugCreated) { - Ogre::SceneManagerEnumerator::SceneManagerIterator iter = Ogre::Root::getSingleton().getSceneManagerIterator(); - iter.begin(); - Ogre::SceneManager* scn = iter.getNext(); - Ogre::SceneNode* node = scn->getRootSceneNode()->createChildSceneNode(); - node->pitch(Ogre::Degree(-90)); + Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode(); mDebugDrawer = new BtOgre::DebugDrawer(node, dynamicsWorld); dynamicsWorld->setDebugDrawer(mDebugDrawer); isDebugCreated = true; @@ -249,6 +229,11 @@ namespace Physic return mDebugActive; } + void PhysicEngine::setSceneManager(Ogre::SceneManager* sceneMgr) + { + mSceneMgr = sceneMgr; + } + PhysicEngine::~PhysicEngine() { HeightFieldContainer::iterator hf_it = mHeightFieldMap.begin(); @@ -259,8 +244,19 @@ namespace Physic delete hf_it->second.mBody; } - RigidBodyContainer::iterator rb_it = ObjectMap.begin(); - for (; rb_it != ObjectMap.end(); ++rb_it) + RigidBodyContainer::iterator rb_it = mCollisionObjectMap.begin(); + for (; rb_it != mCollisionObjectMap.end(); ++rb_it) + { + if (rb_it->second != NULL) + { + dynamicsWorld->removeRigidBody(rb_it->second); + + delete rb_it->second; + rb_it->second = NULL; + } + } + rb_it = mRaycastingObjectMap.begin(); + for (; rb_it != mRaycastingObjectMap.end(); ++rb_it) { if (rb_it->second != NULL) { @@ -271,13 +267,11 @@ namespace Physic } } - PhysicActorContainer::iterator pa_it = PhysicActorMap.begin(); - for (; pa_it != PhysicActorMap.end(); ++pa_it) + PhysicActorContainer::iterator pa_it = mActorMap.begin(); + for (; pa_it != mActorMap.end(); ++pa_it) { if (pa_it->second != NULL) { - - delete pa_it->second; pa_it->second = NULL; } @@ -329,7 +323,6 @@ namespace Physic btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,newMotionState,hfShape); RigidBody* body = new RigidBody(CI,name); - body->mCollide = true; body->getWorldTransform().setOrigin(btVector3( (x+0.5)*triSize*(sqrtVerts-1), (y+0.5)*triSize*(sqrtVerts-1), (maxh+minh)/2.f)); HeightField hf; @@ -338,7 +331,8 @@ namespace Physic mHeightFieldMap [name] = hf; - dynamicsWorld->addRigidBody(body,COL_WORLD,COL_WORLD|COL_ACTOR_INTERNAL|COL_ACTOR_EXTERNAL); + dynamicsWorld->addRigidBody(body,CollisionType_HeightMap|CollisionType_Raycasting, + CollisionType_World|CollisionType_Actor|CollisionType_Raycasting); } void PhysicEngine::removeHeightField(int x, int y) @@ -356,18 +350,21 @@ namespace Physic mHeightFieldMap.erase(name); } - void PhysicEngine::adjustRigidBody(RigidBody* body, Ogre::Vector3 position, Ogre::Quaternion rotation, - Ogre::Vector3 scaledBoxTranslation, Ogre::Quaternion boxRotation){ + void PhysicEngine::adjustRigidBody(RigidBody* body, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, + const Ogre::Vector3 &scaledBoxTranslation, const Ogre::Quaternion &boxRotation) + { btTransform tr; - rotation = rotation * boxRotation; - Ogre::Vector3 transrot = rotation * scaledBoxTranslation; + Ogre::Quaternion boxrot = rotation * boxRotation; + Ogre::Vector3 transrot = boxrot * scaledBoxTranslation; Ogre::Vector3 newPosition = transrot + position; - + tr.setOrigin(btVector3(newPosition.x, newPosition.y, newPosition.z)); - tr.setRotation(btQuaternion(rotation.x,rotation.y,rotation.z,rotation.w)); + tr.setRotation(btQuaternion(boxrot.x,boxrot.y,boxrot.z,boxrot.w)); body->setWorldTransform(tr); } - void PhysicEngine::boxAdjustExternal(std::string mesh, RigidBody* body, float scale, Ogre::Vector3 position, Ogre::Quaternion rotation){ + void PhysicEngine::boxAdjustExternal(const std::string &mesh, RigidBody* body, + float scale, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation) + { std::string sid = (boost::format("%07.3f") % scale).str(); std::string outputstring = mesh + sid; //std::cout << "The string" << outputstring << "\n"; @@ -377,11 +374,12 @@ namespace Physic BulletShapeManager::getSingletonPtr()->load(outputstring,"General"); BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General"); - adjustRigidBody(body, position, rotation, shape->boxTranslation * scale, shape->boxRotation); + adjustRigidBody(body, position, rotation, shape->mBoxTranslation * scale, shape->mBoxRotation); } - RigidBody* PhysicEngine::createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation, - Ogre::Vector3* scaledBoxTranslation, Ogre::Quaternion* boxRotation) + RigidBody* PhysicEngine::createAndAdjustRigidBody(const std::string &mesh, const std::string &name, + float scale, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, + Ogre::Vector3* scaledBoxTranslation, Ogre::Quaternion* boxRotation, bool raycasting, bool placeable) { std::string sid = (boost::format("%07.3f") % scale).str(); std::string outputstring = mesh + sid; @@ -390,113 +388,169 @@ namespace Physic mShapeLoader->load(outputstring,"General"); BulletShapeManager::getSingletonPtr()->load(outputstring,"General"); BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General"); - shape->Shape->setLocalScaling( btVector3(scale,scale,scale)); + if (placeable && !raycasting && shape->mCollisionShape && !shape->mHasCollisionNode) + return NULL; + + if (!shape->mCollisionShape && !raycasting) + return NULL; + if (!shape->mRaycastingShape && raycasting) + return NULL; + + if (!raycasting) + shape->mCollisionShape->setLocalScaling( btVector3(scale,scale,scale)); + 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,shape->Shape); + btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo + (0,newMotionState, raycasting ? shape->mRaycastingShape : shape->mCollisionShape); RigidBody* body = new RigidBody(CI,name); - body->mCollide = shape->mCollide; - body->mIgnore = shape->mIgnore; + body->mPlaceable = placeable; if(scaledBoxTranslation != 0) - *scaledBoxTranslation = shape->boxTranslation * scale; + *scaledBoxTranslation = shape->mBoxTranslation * scale; if(boxRotation != 0) - *boxRotation = shape->boxRotation; + *boxRotation = shape->mBoxRotation; - adjustRigidBody(body, position, rotation, shape->boxTranslation * scale, shape->boxRotation); + adjustRigidBody(body, position, rotation, shape->mBoxTranslation * scale, shape->mBoxRotation); return body; } - void PhysicEngine::addRigidBody(RigidBody* body, bool addToMap) + void PhysicEngine::addRigidBody(RigidBody* body, bool addToMap, RigidBody* raycastingBody,bool actor) { - if(body) - { - if (body->mIgnore) - return; - if(body->mCollide) - { - dynamicsWorld->addRigidBody(body,COL_WORLD,COL_WORLD|COL_ACTOR_INTERNAL|COL_ACTOR_EXTERNAL); - } - else - { - dynamicsWorld->addRigidBody(body,COL_RAYCASTING,COL_RAYCASTING|COL_WORLD); - } - body->setActivationState(DISABLE_DEACTIVATION); - if(addToMap){ - RigidBody* oldBody = ObjectMap[body->mName]; - if (oldBody != NULL) - { - dynamicsWorld->removeRigidBody(oldBody); - delete oldBody; - } - - ObjectMap[body->mName] = body; - } + if(!body && !raycastingBody) + return; // nothing to do + + const std::string& name = (body ? body->mName : raycastingBody->mName); + + if (body){ + if(actor) dynamicsWorld->addRigidBody(body,CollisionType_Actor,CollisionType_World|CollisionType_HeightMap); + else dynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_World|CollisionType_Actor|CollisionType_HeightMap); + } + + if (raycastingBody) + dynamicsWorld->addRigidBody(raycastingBody,CollisionType_Raycasting,CollisionType_Raycasting|CollisionType_World); + + if(addToMap){ + removeRigidBody(name); + deleteRigidBody(name); + + if (body) + mCollisionObjectMap[name] = body; + if (raycastingBody) + mRaycastingObjectMap[name] = raycastingBody; } } - void PhysicEngine::removeRigidBody(std::string name) + void PhysicEngine::removeRigidBody(const std::string &name) { - RigidBodyContainer::iterator it = ObjectMap.find(name); - if (it != ObjectMap.end() ) + RigidBodyContainer::iterator it = mCollisionObjectMap.find(name); + if (it != mCollisionObjectMap.end() ) + { + RigidBody* body = it->second; + if(body != NULL) + { + dynamicsWorld->removeRigidBody(body); + } + } + it = mRaycastingObjectMap.find(name); + if (it != mRaycastingObjectMap.end() ) { RigidBody* body = it->second; if(body != NULL) { - // broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); - /*PhysicActorContainer::iterator it2 = PhysicActorMap.begin(); - for(;it2!=PhysicActorMap.end();it++) - { - it2->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); - it2->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); - }*/ dynamicsWorld->removeRigidBody(body); } } } - void PhysicEngine::deleteRigidBody(std::string name) + void PhysicEngine::deleteRigidBody(const std::string &name) { - RigidBodyContainer::iterator it = ObjectMap.find(name); - if (it != ObjectMap.end() ) + RigidBodyContainer::iterator it = mCollisionObjectMap.find(name); + if (it != mCollisionObjectMap.end() ) { RigidBody* body = it->second; - //btScaledBvhTriangleMeshShape* scaled = dynamic_cast (body->getCollisionShape()); if(body != NULL) { delete body; } - /*if(scaled != NULL) + mCollisionObjectMap.erase(it); + } + it = mRaycastingObjectMap.find(name); + if (it != mRaycastingObjectMap.end() ) + { + RigidBody* body = it->second; + + if(body != NULL) { - delete scaled; - }*/ - ObjectMap.erase(it); + delete body; + } + mRaycastingObjectMap.erase(it); } } - RigidBody* PhysicEngine::getRigidBody(std::string name) + RigidBody* PhysicEngine::getRigidBody(const std::string &name, bool raycasting) { - RigidBodyContainer::iterator it = ObjectMap.find(name); - if (it != ObjectMap.end() ) + RigidBodyContainer* map = raycasting ? &mRaycastingObjectMap : &mCollisionObjectMap; + RigidBodyContainer::iterator it = map->find(name); + if (it != map->end() ) { - RigidBody* body = ObjectMap[name]; + RigidBody* body = (*map)[name]; return body; } else { - return 0; + return NULL; } } + class ContactTestResultCallback : public btCollisionWorld::ContactResultCallback + { + public: + std::vector mResult; + + // added in bullet 2.81 + // this is just a quick hack, as there does not seem to be a BULLET_VERSION macro? +#if defined(BT_COLLISION_OBJECT_WRAPPER_H) + virtual btScalar addSingleResult(btManifoldPoint& cp, + const btCollisionObjectWrapper* colObj0Wrap,int partId0,int index0, + const btCollisionObjectWrapper* colObj1Wrap,int partId1,int index1) + { + const RigidBody* body = dynamic_cast(colObj0Wrap->m_collisionObject); + if (body) + mResult.push_back(body->mName); + return 0.f; + } +#else + virtual btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObject* col0, int partId0, int index0, + const btCollisionObject* col1, int partId1, int index1) + { + const RigidBody* body = dynamic_cast(col0); + if (body) + mResult.push_back(body->mName); + return 0.f; + } +#endif + }; + + std::vector PhysicEngine::getCollisions(const std::string& name) + { + RigidBody* body = getRigidBody(name); + ContactTestResultCallback callback; + dynamicsWorld->contactTest(body, callback); + return callback.mResult; + } + void PhysicEngine::stepSimulation(double deltaT) { + // This seems to be needed for character controller objects dynamicsWorld->stepSimulation(deltaT,10, 1/60.0); if(isDebugCreated) { @@ -504,8 +558,8 @@ namespace Physic } } - void PhysicEngine::addCharacter(std::string name, std::string mesh, - Ogre::Vector3 position, float scale, Ogre::Quaternion rotation) + void PhysicEngine::addCharacter(const std::string &name, const std::string &mesh, + const Ogre::Vector3 &position, float scale, const Ogre::Quaternion &rotation) { // Remove character with given name, so we don't make memory // leak when character would be added twice @@ -515,14 +569,13 @@ namespace Physic //dynamicsWorld->addAction( newActor->mCharacter ); - PhysicActorMap[name] = newActor; + mActorMap[name] = newActor; } - void PhysicEngine::removeCharacter(std::string name) + void PhysicEngine::removeCharacter(const std::string &name) { - //std::cout << "remove"; - PhysicActorContainer::iterator it = PhysicActorMap.find(name); - if (it != PhysicActorMap.end() ) + PhysicActorContainer::iterator it = mActorMap.find(name); + if (it != mActorMap.end() ) { PhysicActor* act = it->second; if(act != NULL) @@ -530,16 +583,16 @@ namespace Physic delete act; } - PhysicActorMap.erase(it); + mActorMap.erase(it); } } - PhysicActor* PhysicEngine::getCharacter(std::string name) + PhysicActor* PhysicEngine::getCharacter(const std::string &name) { - PhysicActorContainer::iterator it = PhysicActorMap.find(name); - if (it != PhysicActorMap.end() ) + PhysicActorContainer::iterator it = mActorMap.find(name); + if (it != mActorMap.end() ) { - PhysicActor* act = PhysicActorMap[name]; + PhysicActor* act = mActorMap[name]; return act; } else @@ -552,14 +605,20 @@ namespace Physic { } - std::pair PhysicEngine::rayTest(btVector3& from,btVector3& to) + std::pair PhysicEngine::rayTest(btVector3& from,btVector3& to,bool raycastingObjectOnly,bool ignoreHeightMap) { std::string name = ""; float d = -1; float d1 = 10000.; btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to); - resultCallback1.m_collisionFilterMask = COL_WORLD|COL_RAYCASTING; + if(raycastingObjectOnly) + resultCallback1.m_collisionFilterMask = CollisionType_Raycasting; + else + resultCallback1.m_collisionFilterMask = CollisionType_World; + + if(!ignoreHeightMap) + resultCallback1.m_collisionFilterMask = resultCallback1.m_collisionFilterMask | CollisionType_HeightMap; dynamicsWorld->rayTest(from, to, resultCallback1); if (resultCallback1.hasHit()) { @@ -568,35 +627,51 @@ namespace Physic d = d1; } - btCollisionWorld::ClosestRayResultCallback resultCallback2(from, to); - resultCallback2.m_collisionFilterMask = COL_ACTOR_INTERNAL|COL_ACTOR_EXTERNAL; - dynamicsWorld->rayTest(from, to, resultCallback2); - float d2 = 10000.; - if (resultCallback2.hasHit()) + return std::pair(name,d); + } + + // callback that ignores player in results + struct OurClosestConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback + { + public: + OurClosestConvexResultCallback(const btVector3& convexFromWorld,const btVector3& convexToWorld) + : btCollisionWorld::ClosestConvexResultCallback(convexFromWorld, convexToWorld) {} + + virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace) { - d2 = resultCallback1.m_closestHitFraction; - if(d2<=d1) - { - name = static_cast(*resultCallback2.m_collisionObject).mName; - d = d2; - } + if (const RigidBody* body = dynamic_cast(convexResult.m_hitCollisionObject)) + if (body->mName == "player") + return 0; + return btCollisionWorld::ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace); } + }; - return std::pair(name,d); + std::pair PhysicEngine::sphereCast (float radius, btVector3& from, btVector3& to) + { + OurClosestConvexResultCallback callback(from, to); + callback.m_collisionFilterMask = OEngine::Physic::CollisionType_World|OEngine::Physic::CollisionType_HeightMap; + + btSphereShape shape(radius); + const btQuaternion btrot(0.0f, 0.0f, 0.0f); + + btTransform from_ (btrot, from); + btTransform to_ (btrot, to); + + dynamicsWorld->convexSweepTest(&shape, from_, to_, callback); + + if (callback.hasHit()) + return std::make_pair(true, callback.m_closestHitFraction); + else + return std::make_pair(false, 1); } std::vector< std::pair > PhysicEngine::rayTest2(btVector3& from, btVector3& to) { MyRayResultCallback resultCallback1; - resultCallback1.m_collisionFilterMask = COL_WORLD|COL_RAYCASTING; + resultCallback1.m_collisionFilterMask = CollisionType_Raycasting; dynamicsWorld->rayTest(from, to, resultCallback1); std::vector< std::pair > results = resultCallback1.results; - MyRayResultCallback resultCallback2; - resultCallback2.m_collisionFilterMask = COL_ACTOR_INTERNAL|COL_ACTOR_EXTERNAL; - dynamicsWorld->rayTest(from, to, resultCallback2); - std::vector< std::pair > actorResults = resultCallback2.results; - std::vector< std::pair > results2; for (std::vector< std::pair >::iterator it=results.begin(); @@ -605,12 +680,6 @@ namespace Physic results2.push_back( std::make_pair( (*it).first, static_cast(*(*it).second).mName ) ); } - for (std::vector< std::pair >::iterator it=actorResults.begin(); - it != actorResults.end(); ++it) - { - results2.push_back( std::make_pair( (*it).first, static_cast(*(*it).second).mName ) ); - } - std::sort(results2.begin(), results2.end(), MyRayResultCallback::cmp); return results2; @@ -629,6 +698,32 @@ namespace Physic btTransform trans; trans.setIdentity(); - shape->Shape->getAabb(trans, min, max); + if (shape->mRaycastingShape) + shape->mRaycastingShape->getAabb(trans, min, max); + else if (shape->mCollisionShape) + shape->mCollisionShape->getAabb(trans, min, max); + else + { + min = btVector3(0,0,0); + max = btVector3(0,0,0); + } + } + + bool PhysicEngine::isAnyActorStandingOn (const std::string& objectName) + { + for (PhysicActorContainer::iterator it = mActorMap.begin(); it != mActorMap.end(); ++it) + { + if (!it->second->getOnGround()) + continue; + Ogre::Vector3 pos = it->second->getPosition(); + btVector3 from (pos.x, pos.y, pos.z); + btVector3 to = from - btVector3(0,0,5); + std::pair result = rayTest(from, to); + if (result.first == objectName) + return true; + } + return false; } -}} + +} +} diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 76bdb491d..80c681fe5 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -18,13 +18,17 @@ class btSequentialImpulseConstraintSolver; class btCollisionDispatcher; class btDiscreteDynamicsWorld; class btHeightfieldTerrainShape; -struct playerMove; namespace BtOgre { class DebugDrawer; } +namespace Ogre +{ + class SceneManager; +} + namespace MWWorld { class World; @@ -39,6 +43,14 @@ namespace Physic class PhysicEngine; class RigidBody; + enum CollisionType { + CollisionType_Nothing = 0, // rayTest(btVector3& from,btVector3& to); + std::pair rayTest(btVector3& from,btVector3& to,bool raycastingObjectOnly = true,bool ignoreHeightMap = false); /** * Return all objects hit by a ray. */ std::vector< std::pair > rayTest2(btVector3& from, btVector3& to); + std::pair sphereCast (float radius, btVector3& from, btVector3& to); + ///< @return (hit, relative distance) + + std::vector getCollisions(const std::string& name); + //event list of non player object std::list NPEventList; @@ -310,16 +326,19 @@ namespace Physic HeightFieldContainer mHeightFieldMap; typedef std::map RigidBodyContainer; - RigidBodyContainer ObjectMap; + RigidBodyContainer mCollisionObjectMap; + + RigidBodyContainer mRaycastingObjectMap; typedef std::map PhysicActorContainer; - PhysicActorContainer PhysicActorMap; + PhysicActorContainer mActorMap; + + Ogre::SceneManager* mSceneMgr; //debug rendering BtOgre::DebugDrawer* mDebugDrawer; bool isDebugCreated; bool mDebugActive; - }; diff --git a/libs/openengine/bullet/pmove.cpp b/libs/openengine/bullet/pmove.cpp deleted file mode 100644 index 8d98a482e..000000000 --- a/libs/openengine/bullet/pmove.cpp +++ /dev/null @@ -1,2115 +0,0 @@ -/* -This source file is a *modified* version of bg_pmove.c from the Quake 3 Arena source code, -which was released under the GNU GPL (v2) in 2005. -Quake 3 Arena is copyright (C) 1999-2005 Id Software, Inc. -*/ - - -#include "pmove.h" - - - -//#include "bprintf.h" - -//#include "..\..\ESMParser\ESMParser\CELL.h" - -//#include "GameTime.h" - -//#include "Sound.h" - -//#include "..\..\ESMParser\ESMParser\SNDG.h" -//#include "..\..\ESMParser\ESMParser\SOUN.h" - -#include - -//SceneInstance* global_lastscene = NULL; - -// Forward declaration: -void PM_AirMove(); - -static playerMove* pm = NULL; - -//extern std::map ExtCellLookup; - -static struct playermoveLocal -{ - playermoveLocal() : frametime(1.0f / 20.0f), groundPlane(true), walking(true), msec(50) - { - forward = Ogre::Vector3(0.0f, 0.0f, 0.0f); - right = Ogre::Vector3(0.0f, 0.0f, 0.0f); - up = Ogre::Vector3(0.0f, 0.0f, 0.0f); - - previous_origin = Ogre::Vector3(0.0f, 0.0f, 0.0f); - previous_velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f); - } - - traceResults groundTrace; - - //SceneInstance* scene; - - float frametime; // in seconds (usually something like 0.01f) - float impactSpeed; - - Ogre::Vector3 forward; - Ogre::Vector3 right; - Ogre::Vector3 up; - - int msec; - - Ogre::Vector3 previous_origin, previous_velocity; - - int previous_waterlevel; // the waterlevel before this pmove - - bool groundPlane; // if we're standing on a groundplane this frame - - bool walking; - int waterHeight; - bool hasWater; - bool isInterior; - -} pml; - -static inline void PM_ClipVelocity(const Ogre::Vector3& in, const Ogre::Vector3& normal, Ogre::Vector3& out, const float overbounce) -{ - float backoff; - //float change; - //int i; - - // backoff = in dot normal - //backoff = DotProduct (in, normal); - backoff = in.dotProduct(normal); - - if ( backoff < 0 ) - backoff *= overbounce; - else - backoff /= overbounce; - - // change = normal * backoff - // out = in - change - /*for ( i=0 ; i<3 ; i++ ) - { - change = normal[i]*backoff; - out[i] = in[i] - change; - - }*/ - float changex = normal.x * backoff; - out.x = in.x - changex; - float changey = normal.y * backoff; - out.y = in.y - changey; - float changez = normal.z * backoff; - out.z = in.z - changez; -} - -float VectorNormalize2( const Ogre::Vector3& v, Ogre::Vector3& out) -{ - float length, ilength; - - length = v.x * v.x+ v.y * v.y + v.z * v.z; - length = sqrt(length); - - if (length) - { -#ifndef Q3_VM // bk0101022 - FPE related -// assert( ((Q_fabs(v[0])!=0.0f) || (Q_fabs(v[1])!=0.0f) || (Q_fabs(v[2])!=0.0f)) ); -#endif - ilength = 1 / length; - out.x= v.x * ilength; - out.y = v.y * ilength; - out.z = v.z * ilength; - } else - { -#ifndef Q3_VM // bk0101022 - FPE related -// assert( ((Q_fabs(v[0])==0.0f) && (Q_fabs(v[1])==0.0f) && (Q_fabs(v[2])==0.0f)) ); -#endif - //VectorClear( out ); - out.x = 0; out.y = 0; out.z = 0; - } - - return length; - -} - - -float VectorNormalize(Ogre::Vector3& out) -{ - float length, ilength; - - length = out.x * out.x + out.y * out.y + out.z * out.z; - length = sqrt(length); - - if (length) - { -#ifndef Q3_VM // bk0101022 - FPE related -// assert( ((Q_fabs(v[0])!=0.0f) || (Q_fabs(v[1])!=0.0f) || (Q_fabs(v[2])!=0.0f)) ); -#endif - ilength = 1 / length; - out.x = out.x * ilength; - out.y = out.y * ilength; - out.z = out.z * ilength; - } - - return length; - -} - -/* -================== -PM_SlideMove - -Returns qtrue if the velocity was clipped in some way -================== -*/ - -bool PM_SlideMove( bool gravity ) -{ - int bumpcount, numbumps; - Ogre::Vector3 dir; - float d; - int numplanes; - Ogre::Vector3 planes[MAX_CLIP_PLANES]; - Ogre::Vector3 primal_velocity; - Ogre::Vector3 clipVelocity; - int i, j, k; - struct traceResults trace; - Ogre::Vector3 end(0,0,0); - float time_left; - float into; - Ogre::Vector3 endVelocity(0,0,0); - Ogre::Vector3 endClipVelocity(0,0,0); - - numbumps = 4; - - // primal_velocity = pm->ps->velocity - //VectorCopy (pm->ps->velocity, primal_velocity); - primal_velocity = pm->ps.velocity; - - if ( gravity ) - { - // endVelocity = pm->ps->velocity - vec3(0, 0, pm->ps->gravity * pml.frametime) - //VectorCopy( pm->ps->velocity, endVelocity ); - endVelocity = pm->ps.velocity; - //endVelocity[2] -= pm->ps->gravity * pml.frametime; - endVelocity.z -= pm->ps.gravity * pml.frametime; - - // pm->ps->velocity = avg(pm->ps->velocity.z, endVelocity.z) - //pm->ps->velocity[2] = ( pm->ps->velocity[2] + endVelocity[2] ) * 0.5; - pm->ps.velocity.z= (pm->ps.velocity.z + endVelocity.z) * 0.5f; - - //primal_velocity[2] = endVelocity[2]; - primal_velocity.z = endVelocity.z; - - if ( pml.groundPlane ) - // slide along the ground plane - //PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); - PM_ClipVelocity(pm->ps.velocity, pml.groundTrace.planenormal, pm->ps.velocity, OVERCLIP); - } - - time_left = pml.frametime; - - // never turn against the ground plane - if ( pml.groundPlane ) - { - numplanes = 1; - - // planes[0] = pml.groundTrace.plane.normal - //VectorCopy( pml.groundTrace.plane.normal, planes[0] ); - planes[0] = pml.groundTrace.planenormal; - } else - numplanes = 0; - - // never turn against original velocity - VectorNormalize2( pm->ps.velocity, planes[numplanes] ); - numplanes++; - - for ( bumpcount = 0; bumpcount < numbumps; bumpcount++ ) - { - - // calculate position we are trying to move to - //VectorMA( pm->ps->origin, time_left, pm->ps->velocity, end ); - end = pm->ps.origin + pm->ps.velocity * time_left; - - // see if we can make it there - //pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemaskg); - //tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&(end), *(const D3DXVECTOR3* const)&(pm->ps.velocity), 0, pml.traceObj); - newtrace(&trace, pm->ps.origin, end, pm->ps.halfExtents, Ogre::Math::DegreesToRadians (pm->ps.viewangles.y), pm->isInterior, pm->mEngine); - - if (trace.allsolid) - { - // entity is completely trapped in another solid - //pm->ps->velocity[2] = 0; // don't build up falling damage, but allow sideways acceleration - pm->ps.velocity = Ogre::Vector3(0,0,0); - return true; - } - - if (trace.fraction > 0) - // actually covered some distance - //VectorCopy (trace.endpos, pm->ps->origin); - pm->ps.origin = trace.endpos; - - if (trace.fraction == 1) - break; // moved the entire distance - - // save entity for contact8 - //PM_AddTouchEnt( trace.entityNum ); - - time_left -= time_left * trace.fraction; - - if (numplanes >= MAX_CLIP_PLANES) - { - // this shouldn't really happen - //VectorClear( pm->ps->velocity ); - pm->ps.velocity = Ogre::Vector3(0,0,0); - return true; - } - - // - // if this is the same plane we hit before, nudge velocity - // out along it, which fixes some epsilon issues with - // non-axial planes - // - for ( i = 0 ; i < numplanes ; i++ ) - { - if (trace.planenormal.dotProduct(planes[i]) > 0.99) //OGRE::VECTOR3 ? - //if ( DotProduct( trace.plane.normal, planes[i] ) > 0.99 ) - { - // pm->ps->velocity += (trace.plane.normal + pm->ps->velocity) - //VectorAdd( trace.plane.normal, pm->ps->velocity, pm->ps->velocity ); - pm->ps.velocity = trace.planenormal + pm->ps.velocity; - break; - } - } - - if ( i < numplanes ) - continue; - - //VectorCopy (trace.plane.normal, planes[numplanes]); - planes[numplanes] = trace.planenormal; - numplanes++; - - // - // modify velocity so it parallels all of the clip planes - // - - // find a plane that it enters - for ( i = 0 ; i < numplanes ; i++ ) - { - //into = DotProduct( pm->ps->velocity, planes[i] ); - into = pm->ps.velocity.dotProduct(planes[i]); - if ( into >= 0.1 ) - continue; // move doesn't interact with the plane - - - if(planes[i].x >= .70) - { - pm->ps.velocity.z = 0; - return true; - } - // see how hard we are hitting things - if ( -into > pml.impactSpeed ) - pml.impactSpeed = -into; - - // slide along the plane - //PM_ClipVelocity (pm->ps->velocity, planes[i], clipVelocity, OVERCLIP ); - PM_ClipVelocity(pm->ps.velocity, planes[i], clipVelocity, OVERCLIP); - - // slide along the plane - PM_ClipVelocity (endVelocity, planes[i], endClipVelocity, OVERCLIP ); - - // see if there is a second plane that the new move enters - for ( j = 0 ; j < numplanes ; j++ ) - { - if ( j == i ) - continue; - - if (clipVelocity.dotProduct(planes[j]) >= 0.1) - //if ( DotProduct( clipVelocity, planes[j] ) >= 0.1 ) - continue; // move doesn't interact with the plane - - - - - //pm->ps.velocity = Ogre::Vector3(0,0,0); - //return true; - - - // try clipping the move to the plane - PM_ClipVelocity( clipVelocity, planes[j], clipVelocity, OVERCLIP ); - PM_ClipVelocity( endClipVelocity, planes[j], endClipVelocity, OVERCLIP ); - - // see if it goes back into the first clip plane - if (clipVelocity.dotProduct(planes[i]) >= 0) - //if ( DotProduct( clipVelocity, planes[i] ) >= 0 ) - continue; - - - // slide the original velocity along the crease - //dProduct (planes[i], planes[j], dir); - dir = planes[i].crossProduct(planes[j]) ; - - //VectorNormalize( dir ); - //D3DXVec3Normalize( (D3DXVECTOR3* const)&dir, (const D3DXVECTOR3* const)&dir); - VectorNormalize(dir); - - //d = DotProduct( dir, pm->ps->velocity ); - d = dir.dotProduct(pm->ps.velocity); - - //VectorScale( dir, d, clipVelocity ); - clipVelocity = dir * d; - - //CrossProduct (planes[i], planes[j], dir); - dir = planes[i].crossProduct(planes[j]) ; - - - //VectorNormalize( dir ); - //D3DXVec3Normalize( (D3DXVECTOR3* const)&dir, (const D3DXVECTOR3* const)&dir); - VectorNormalize(dir); - - //d = DotProduct( dir, endVelocity ); - d = dir.dotProduct(endVelocity); - - //VectorScale( dir, d, endClipVelocity ); - endClipVelocity = dir * d; - - // see if there is a third plane the the new move enters - for ( k = 0 ; k < numplanes ; k++ ) - { - - if ( k == i || k == j ) - continue; - - if (clipVelocity.dotProduct(planes[k]) >= 0.1) - //if ( DotProduct( clipVelocity, planes[k] ) >= 0.1 ) - continue; // move doesn't interact with the plane - - // stop dead at a tripple plane interaction - //VectorClear( pm->ps->velocity ); - //printf("Stop dead at a triple plane interaction\n"); - pm->ps.velocity = Ogre::Vector3(0,0,0); - return true; - } - } - - // if we have fixed all interactions, try another move - //VectorCopy( clipVelocity, pm->ps->velocity ); - pm->ps.velocity = clipVelocity; - - //VectorCopy( endClipVelocity, endVelocity ); - endVelocity = endClipVelocity; - break; - } - } - - if ( gravity ) - //VectorCopy( endVelocity, pm->ps->velocity ); - pm->ps.velocity = endVelocity; - - // don't change velocity if in a timer (FIXME: is this correct?) - if ( pm->ps.pm_time ) - //VectorCopy( primal_velocity, pm->ps->velocity ); - pm->ps.velocity = primal_velocity; - - //return ( (qboolean)(bumpcount != 0) ); - return bumpcount != 0; -} - -/* -================== -PM_StepSlideMove - -================== -*/ -int PM_StepSlideMove( bool gravity ) -{ - Ogre::Vector3 start_o, start_v; - Ogre::Vector3 down_o, down_v; - traceResults trace; -// float down_dist, up_dist; -// vec3_t delta, delta2; - Ogre::Vector3 up, down; - float stepSize; - - //std::cout << "StepSlideMove\n"; - // start_o = pm->ps->origin - //VectorCopy (pm->ps->origin, start_o); - start_o = pm->ps.origin; - - // start_v = pm->ps->velocity - //VectorCopy (pm->ps->velocity, start_v); - start_v = pm->ps.velocity; - - if ( PM_SlideMove( gravity ) == false ) - return 1; // we got exactly where we wanted to go first try - - - // down = start_o - vec3(0, 0, STEPSIZE) - //VectorCopy(start_o, down); - down = start_o; - down.z -= STEPSIZE; - - //pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); - //tracefunc(&trace, start_o, down, , 0, pml.scene); - //tracefunc(&trace, *(const D3DXVECTOR3* const)&start_o, *(const D3DXVECTOR3* const)&down, D3DXVECTOR3(0.0f, -STEPSIZE, 0.0f), 0, pml.traceObj); - newtrace(&trace, down, start_o, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); - - // up = vec3(0, 0, 1) - //VectorSet(up, 0, 0, 1); - up = Ogre::Vector3(0.0f, 0.0f, 1.0f); - - // never step up when you still have up velocity - //if ( pm->ps->velocity[2] > 0 && (trace.fraction == 1.0 || DotProduct(trace.plane.normal, up) < 0.7)) - if (pm->ps.velocity.z > 0 && ( - trace.fraction == 1.0 || trace.planenormal.dotProduct(up) < 0.7 - ) ) - return 2; - - // down_o = pm->ps->origin - //VectorCopy (pm->ps->origin, down_o); - down_o = pm->ps.origin; - - // down_v = pm->ps->velocity - //VectorCopy (pm->ps->velocity, down_v); - down_v = pm->ps.velocity; - - // up = start_o + vec3(0, 0, STEPSIZE) - //VectorCopy (start_o, up); - up = start_o; - //up[2] += STEPSIZE; - up.z += STEPSIZE; - - // test the player position if they were a stepheight higher - //pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask); - //tracefunc(&trace, *(const D3DXVECTOR3* const)&start_o, *(const D3DXVECTOR3* const)&up, D3DXVECTOR3(0.0f, STEPSIZE, 0.0f), 0, pml.traceObj); - newtrace(&trace, start_o, up, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); - if ( trace.allsolid ) - { - //if ( pm->debugLevel ) - //Com_Printf("%i:bend can't step\n", c_pmove); - //bprintf("bend can't step\n"); - return 3; // can't step up - } - - //stepSize = trace.endpos[2] - start_o[2]; - stepSize = trace.endpos.z - start_o.z; - - // try slidemove from this position - //VectorCopy (trace.endpos, pm->ps->origin); // pm->ps->origin = trace.endpos - pm->ps.origin = trace.endpos; - //VectorCopy (start_v, pm->ps->velocity); // pm->ps->velocity = start_v - pm->ps.velocity = start_v; - - PM_SlideMove( gravity ); - - // push down the final amount - - // down = pm->ps->origin - vec3(0, 0, stepSize) - //VectorCopy (pm->ps->origin, down); - down = pm->ps.origin; - //down[2] -= stepSize; - down.z -= stepSize; - - - //pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); - //tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&down, D3DXVECTOR3(0.0f, -STEPSIZE, 0.0f), 0, pml.traceObj); - newtrace(&trace, pm->ps.origin, down, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); - if ( !trace.allsolid ) - //VectorCopy (trace.endpos, pm->ps->origin); - pm->ps.origin = trace.endpos; - - if ( trace.fraction < 1.0 ) - //PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); - PM_ClipVelocity(pm->ps.velocity, trace.planenormal, pm->ps.velocity, OVERCLIP); - - { - // use the step move - float delta; - - //delta = pm->ps->origin[2] - start_o[2]; - delta = pm->ps.origin.z - start_o.z; - if ( delta > 2 ) - { - pm->ps.counter = 10; - - /* - if (gravity) - printf("g on: %f ", delta); - else - printf("g off: %f ", delta); - - if ( delta < 7 ) - printf("stepped 3 < x < 7\n"); - //PM_AddEvent( EV_STEP_4 ); - else if ( delta < 11 ) - printf("stepped 7 < x < 11\n"); - //PM_AddEvent( EV_STEP_8 ); - else if ( delta < 15 ) - printf("stepped 11 < x < 15\n"); - //PM_AddEvent( EV_STEP_12 ); - else - printf("stepped 15+\n"); - //PM_AddEvent( EV_STEP_16 ); - */ - } - /*if ( pm->debugLevel ) - Com_Printf("%i:stepped\n", c_pmove);*/ - } - - return 4; -} - -void PM_Friction(void) -{ - - Ogre::Vector3 vec; - float* vel; - float speed, newspeed, control; - float drop; - - vel = &(pm->ps.velocity.x); - - // vec = vel - //VectorCopy( vel, vec ); - vec = pm->ps.velocity; - - if ( pml.walking ) - //vec[2] = 0; // ignore slope movement - vec.z = 0; - - //speed = VectorLength(vec); - speed = vec.length(); - if (speed < 1) - { - vel[0] = 0; - vel[1] = 0; // allow sinking underwater - // FIXME: still have z friction underwater? - //bprintf("Static friction (vec = [%f, %f, %f]) (vec.length = %f)\n", vec.x, vec.y, vec.z, speed); - return; - } - - drop = 0; - - // apply ground friction - if ( pm->ps.waterlevel <= 1 ) - { - if ( pml.walking )//&& !(pml.groundTrace.surfaceFlags & SURF_SLICK) ) - { - // if getting knocked back, no friction - //if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) - { - control = (speed < pm_stopspeed) ? pm_stopspeed : speed; - drop += control * pm_friction * pml.frametime; - } - } - } - - // apply water friction even if just wading - if ( pm->ps.waterlevel ) - drop += speed * pm_waterfriction * pm->ps.waterlevel * pml.frametime; - - // apply flying friction - /*if ( pm->ps->powerups[PW_FLIGHT]) - drop += speed * pm_flightfriction * pml.frametime; - - if ( pm->ps->pm_type == PM_SPECTATOR) - drop += speed * pm_spectatorfriction * pml.frametime;*/ - if (pm->ps.move_type == PM_SPECTATOR) - drop += speed * pm_flightfriction * pml.frametime; - - // scale the velocity - newspeed = speed - drop; - if (newspeed < 0) - newspeed = 0; - - newspeed /= speed; - - // vel *= newspeed - vel[0] = vel[0] * newspeed; - vel[1] = vel[1] * newspeed; - vel[2] = vel[2] * newspeed; -} - -float PM_CmdScale(playerMove::playercmd* const cmd) -{ - int max; - float total; - float scale; - - max = abs( cmd->forwardmove ); - if ( abs( cmd->rightmove ) > max ) - max = abs( cmd->rightmove ); - - if ( abs( cmd->upmove ) > max ) - max = abs( cmd->upmove ); - - if ( !max ) - return 0; - - total = sqrtf( (const float)(cmd->forwardmove * cmd->forwardmove - + cmd->rightmove * cmd->rightmove + cmd->upmove * cmd->upmove) ); - scale = (float)pm->ps.speed * max / ( 127.0f * total ); - if(pm->ps.move_type == PM_NOCLIP) - scale *= 10; - - return scale; -} - -static void PM_Accelerate( Ogre::Vector3& wishdir, float wishspeed, float accel ) -{ -// int i; - float addspeed, accelspeed, currentspeed; - - - // currentspeed = pm->ps->velocity dot wishdir - //currentspeed = DotProduct (pm->ps->velocity, wishdir); - currentspeed = pm->ps.velocity.dotProduct(wishdir); - - addspeed = wishspeed - currentspeed; - if (addspeed <= 0) - return; - - accelspeed = accel * pml.frametime * wishspeed; - - // Clamp accelspeed at addspeed - if (accelspeed > addspeed) - accelspeed = addspeed; - - // pm->ps->velocity += accelspeed * wishdir - //for (i=0 ; i<3 ; i++) - //pm->ps->velocity[i] += accelspeed * wishdir[i]; - pm->ps.velocity += (wishdir * accelspeed); - //pm->ps.velocity = wishdir * wishspeed; //New, for instant acceleration - -} - -static bool PM_CheckJump(void) -{ - //if ( pm->ps->pm_flags & PMF_RESPAWNED ) - //return qfalse; // don't allow jump until all buttons are up - - if ( pm->cmd.upmove < 10 ) - // not holding jump - return false; - - pm->cmd.upmove = 0; - - // must wait for jump to be released - /*if ( pm->ps->pm_flags & PMF_JUMP_HELD ) - { - // clear upmove so cmdscale doesn't lower running speed - pm->cmd.upmove = 0; - return false; - }*/ - - pml.groundPlane = false; // jumping away - pml.walking = false; - //pm->ps->pm_flags |= PMF_JUMP_HELD; - - pm->ps.groundEntityNum = ENTITYNUM_NONE; - pm->ps.velocity.z = pm->ps.jump_velocity; - pm->ps.bSnap = false; - //PM_AddEvent( EV_JUMP ); - - /*if ( pm->cmd.forwardmove >= 0 ) - { - PM_ForceLegsAnim( LEGS_JUMP ); - pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; - } - else - { - PM_ForceLegsAnim( LEGS_JUMPB ); - pm->ps->pm_flags |= PMF_BACKWARDS_JUMP; - }*/ - - return true; -} - -static void PM_WaterMove( playerMove* const pm ) -{ - //int i; - //vec3_t wishvel; - Ogre::Vector3 wishvel; - float wishspeed; - //vec3_t wishdir; - Ogre::Vector3 wishdir; - float scale; - float vel; - - pm->ps.bSnap = false; - - /*if ( PM_CheckWaterJump() ) - { - PM_WaterJumpMove(); - return; - }*/ -#if 0 - // jump = head for surface - if ( pm->cmd.upmove >= 10 ) { - if (pm->ps->velocity[2] > -300) { - if ( pm->watertype == CONTENTS_WATER ) { - pm->ps->velocity[2] = 100; - } else if (pm->watertype == CONTENTS_SLIME) { - pm->ps->velocity[2] = 80; - } else { - pm->ps->velocity[2] = 50; - } - } - } -#endif - PM_Friction (); - - if (pm->cmd.forwardmove || pm->cmd.rightmove) - { - //NEEDS TO BE REWRITTEN FOR OGRE TIME--------------------------------------------------- - /* - static const TimeTicks footstep_duration = GetTimeFreq(); // make each splash last 1.0s - static TimeTicks lastStepTime = 0; - const TimeTicks thisStepTime = GetTimeQPC(); - static bool lastWasLeft = false; - if (thisStepTime > lastStepTime) - { - if (pm->cmd.ducking) - lastStepTime = thisStepTime + footstep_duration * 2; // splashes while ducking are twice as slow - else - lastStepTime = thisStepTime + footstep_duration; - - lastWasLeft = !lastWasLeft; - */ - //-----------------jhooks1 - - /* - namestruct defaultCreature; - const SNDG* const sndg = SNDG::GetFromMap(defaultCreature, lastWasLeft ? SNDG::r_swim : SNDG::l_swim); - if (sndg) - { - const namestruct& SOUNID = sndg->soundID; - const SOUN* const soun = SOUN::GetSound(SOUNID); - if (soun) - { - PlaySound2D(soun->soundFilename, soun->soundData->GetVolumeFloat() ); - } - }*/ - //Sound, ignore for now -- jhooks1 - //} - } - - scale = PM_CmdScale( &pm->cmd ); - // - // user intentions - // - if ( !scale ) - { - /*wishvel[0] = 0; - wishvel[1] = 0; - wishvel[2] = -60; // sink towards bottom - */ - wishvel.x = 0; - wishvel.z = -60; - wishvel.y = 0; - } - else - { - /*for (i=0 ; i<3 ; i++) - wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;*/ - wishvel = pml.forward * scale * pm->cmd.forwardmove + pml.right * scale * pm->cmd.rightmove; - - //wishvel[2] += scale * pm->cmd.upmove; - wishvel.z += pm->cmd.upmove * scale; - } - - //VectorCopy (wishvel, wishdir); - wishdir = wishvel; - wishspeed = VectorNormalize(wishdir); - - if ( wishspeed > pm->ps.speed * pm_swimScale ) - wishspeed = pm->ps.speed * pm_swimScale; - - PM_Accelerate (wishdir, wishspeed, pm_wateraccelerate); - - // make sure we can go up slopes easily under water - //if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) - if (pml.groundPlane && pm->ps.velocity.dotProduct(pml.groundTrace.planenormal) < 0.0f) - { - //vel = VectorLength(pm->ps->velocity); - vel = pm->ps.velocity.length(); - - // slide along the ground plane - //PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); - PM_ClipVelocity(pm->ps.velocity, pml.groundTrace.planenormal, pm->ps.velocity, OVERCLIP); - - VectorNormalize(pm->ps.velocity); - //VectorScale(pm->ps->velocity, vel, pm->ps->velocity); - pm->ps.velocity = pm->ps.velocity * vel; - } - - PM_SlideMove( false ); -} - -/* -=================== -PM_WalkMove - -=================== -*/ -static void PM_WalkMove( playerMove* const pmove ) -{ -// int i; - Ogre::Vector3 wishvel; - float fmove, smove; - Ogre::Vector3 wishdir; - float wishspeed; - float scale; - playerMove::playercmd cmd; - float accelerate; - float vel; - //pm->ps.gravity = 4000; - - //std::cout << "Player is walking\n"; - - if ( pm->ps.waterlevel > 2 && //DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) - pml.forward.dotProduct(pml.groundTrace.planenormal) > 0.0f) - { - // begin swimming - PM_WaterMove(pmove); - return; - } - - - if ( PM_CheckJump () ) - { - - // jumped away - if ( pm->ps.waterlevel > 1 ) - PM_WaterMove(pmove); - else - PM_AirMove(); - //printf("Jumped away\n"); - return; - } - - // Footsteps time - if (pmove->cmd.forwardmove || pmove->cmd.rightmove) - { - bool step_underwater = false; - //if (pmove->traceObj) - //{ - - - //jhooks1 - Water handling, deal with later - - - - if (pmove->hasWater) - { - if (pmove->hasWater ) - { - const float waterHeight = pmove->waterHeight; - const float waterSoundStepHeight = waterHeight + pm->ps.halfExtents.y; - if (pmove->ps.origin.y < waterSoundStepHeight) - step_underwater = true; - } - } - //} - - /* - static const TimeTicks footstep_duration = GetTimeFreq() / 2; // make each footstep last 500ms - static TimeTicks lastStepTime = 0; - const TimeTicks thisStepTime = GetTimeQPC(); - static bool lastWasLeft = false; - if (thisStepTime > lastStepTime) - { - if (pmove->cmd.ducking) - lastStepTime = thisStepTime + footstep_duration * 2; // footsteps while ducking are twice as slow - else - lastStepTime = thisStepTime + footstep_duration; - - lastWasLeft = !lastWasLeft; - */ - - if (step_underwater) - { - /* - const namestruct ns(lastWasLeft ? "FootWaterRight" : "FootWaterLeft"); - const SOUN* const soun = SOUN::GetSound(ns); - if (soun) - { - PlaySound2D(soun->soundFilename, soun->soundData->GetVolumeFloat() ); - }*/ - } - else - { - /* - namestruct defaultCreature; - const SNDG* const sndg = SNDG::GetFromMap(defaultCreature, lastWasLeft ? SNDG::r_foot : SNDG::l_foot); - if (sndg) - { - const namestruct& SOUNID = sndg->soundID; - const SOUN* const soun = SOUN::GetSound(SOUNID); - if (soun) - { - PlaySound2D(soun->soundFilename, soun->soundData->GetVolumeFloat() ); - } - }*/ - } - } - - - PM_Friction (); - - - //bprintf("vel: (%f, %f, %f)\n", pm->ps.velocity.x, pm->ps.velocity.y, pm->ps.velocity.z); - - fmove = pm->cmd.forwardmove; - smove = pm->cmd.rightmove; - - - cmd = pm->cmd; - scale = PM_CmdScale( &cmd ); - - // set the movementDir so clients can rotate the legs for strafing - //PM_SetMovementDir(); - - // project moves down to flat plane - //pml.forward[2] = 0; - pml.forward.z = 0; - - //pml.right[2] = 0; - pml.right.z = 0; - //std::cout << "Further down" << pm->ps.velocity << "\n"; - - - // project the forward and right directions onto the ground plane - PM_ClipVelocity (pml.forward, pml.groundTrace.planenormal, pml.forward, OVERCLIP ); - PM_ClipVelocity (pml.right, pml.groundTrace.planenormal, pml.right, OVERCLIP ); - //std::cout << "Clip velocity" << pm->ps.velocity << "\n"; - // - - VectorNormalize (pml.forward); - VectorNormalize (pml.right); - //pml.forward = pml.forward.normalise(); - //pml.right = pml.right.normalise(); - //std::cout << "forward2" << pml.forward << "\n"; - //std::cout << "right2" << pml.right << "\n"; - - - // wishvel = (pml.forward * fmove) + (pml.right * smove); - //for ( i = 0 ; i < 3 ; i++ ) - //wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; - wishvel = pml.forward * fmove + pml.right * smove; - - - //bprintf("f: (%f, %f, %f), s: (%f, %f, %f)\n", fmove, smove); - - - // when going up or down slopes the wish velocity should Not be zero -// wishvel[2] = 0; - - // wishdir = wishvel - //VectorCopy (wishvel, wishdir); - //wishvel = wishdir; - wishdir = wishvel; - - wishspeed = VectorNormalize(wishdir); - //std::cout << "Wishspeed: " << wishspeed << "\n"; - wishspeed *= scale; - //std::cout << "Wishspeed scaled:" << wishspeed << "\n"; - - // clamp the speed lower if ducking - if ( pm->cmd.ducking ) - if ( wishspeed > pm->ps.speed * pm_duckScale ) - wishspeed = pm->ps.speed * pm_duckScale; - - // clamp the speed lower if wading or walking on the bottom - if ( pm->ps.waterlevel ) - { - float waterScale; - - waterScale = pm->ps.waterlevel / 3.0f; - waterScale = 1.0f - ( 1.0f - pm_swimScale ) * waterScale; - if ( wishspeed > pm->ps.speed * waterScale ) - wishspeed = pm->ps.speed * waterScale; - } - - // when a player gets hit, they temporarily lose - // full control, which allows them to be moved a bit - //if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) - //accelerate = pm_airaccelerate; - //else - accelerate = pm_accelerate; - - - PM_Accelerate (wishdir, wishspeed, accelerate); - //std::cout << "Velocityafter: " << pm->ps.velocity << "\n"; - - //Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]); - //Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity)); - - //if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) - //pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime; - //else - //{ - // don't reset the z velocity for slopes -// pm->ps->velocity[2] = 0; - //} - - //vel = VectorLength(pm->ps->velocity); - vel = pm->ps.velocity.length(); - //std::cout << "The length" << vel << "\n"; - - // slide along the ground plane - PM_ClipVelocity (pm->ps.velocity, pml.groundTrace.planenormal, - pm->ps.velocity, OVERCLIP ); - //std::cout << "Velocity clipped" << pm->ps.velocity << "\n"; - - // don't decrease velocity when going up or down a slope - VectorNormalize(pm->ps.velocity); - //pm->ps.velocity = pm->ps.velocity.normalise(); - - //std::cout << "Final:" << pm->ps.velocity << "\n"; - //VectorScale(pm->ps->velocity, vel, pm->ps->velocity); - pm->ps.velocity = pm->ps.velocity * vel; - - // don't do anything if standing still - //if (!pm->ps->velocity[0] && !pm->ps->velocity[1]) - if (!pm->ps.velocity.x && !pm->ps.velocity.z) - return; - - PM_StepSlideMove( false ); - - //Com_Printf("velocity2 = %1.1f\n", VectorLength(pm->ps->velocity)); - - -} - -void PM_UpdateViewAngles( playerMove::playerStruct* const ps, playerMove::playercmd* const cmd ) -{ - short temp; - int i; - - //while(1); - - //if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) - //return; // no view changes at all - - //if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) - //return; // no view changes at all - - // circularly clamp the angles with deltas - //bprintf("View angles: %i, %i, %i\n", cmd->angles[0], cmd->angles[1], cmd->angles[2]); - for (i = 0 ; i < 3 ; i++) - { - temp = cmd->angles[i];// + ps->delta_angles[i]; - //if ( i == PITCH ) - { - // don't let the player look up or down more than 90 degrees - /*if ( temp > 16000 ) - { - ps->delta_angles[i] = 16000 - cmd->angles[i]; - temp = 16000; - } - else if ( temp < -16000 ) - { - ps->delta_angles[i] = -16000 - cmd->angles[i]; - temp = -16000; - }*/ - } - (&(ps->viewangles.x) )[i] = SHORT2ANGLE(temp); - //cmd->angles[i] += ps->delta_angles[i]; - } - //ps->delta_angles[0] = ps->delta_angles[1] = ps->delta_angles[2] = 0; - -} - -void AngleVectors( const Ogre::Vector3& angles, Ogre::Vector3* const forward, Ogre::Vector3* const right, Ogre::Vector3* const up) -{ - float angle; - static float sr, sp, sy, cr, cp, cy; - // static to help MS compiler fp bugs - - //angle = angles[YAW] * (M_PI*2 / 360); - angle = angles.x * (M_PI * 2.0f / 360.0f); - sp = sinf(angle); - cp = cosf(angle); - - //angle = angles[PITCH] * (M_PI*2 / 360); - angle = angles.y * (-M_PI * 2.0f / 360.0f); - sy = sinf(angle); - cy = cosf(angle); - - //angle = angles[ROLL] * (M_PI*2 / 360); - angle = angles.z * (M_PI * 2.0f / 360.0f); - sr = sinf(angle); - cr = cosf(angle); - - if (forward) - { - forward->x = cp * cy; - forward->y = cp * sy; - forward->z = -sp; - } - if (right) - { - right->x = (-1 * sr * sp * cy + -1 * cr * -sy); - right->y = (-1 * sr * sp * sy + -1 * cr * cy); - right->z = 0; - } - if (up) - { - up->x =(cr * sp * cy + -sr * -sy); - up->y=(cr * sp * sy + -sr * cy); - up->z = cr * cp; - } - -} - -void PM_GroundTraceMissed() -{ - traceResults trace; - Ogre::Vector3 point; - //We should not have significant upwards velocity when in the air, unless we jumped. - //This code protects against flying into the air when moving at high speeds. - //Z velocity is set to 50, instead of 0, to help move up certain steps. - - //std::cout << "Ground trace missed\n"; - // we just transitioned into freefall - //if ( pm->debugLevel ) - //Com_Printf("%i:lift\n", c_pmove); - - // if they aren't in a jumping animation and the ground is a ways away, force into it - // if we didn't do the trace, the player would be backflipping down staircases - //VectorCopy( pm->ps->origin, point ); - point = pm->ps.origin; - //point[2] -= 64; - point.z -= 32; - - //pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); - //tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&point, D3DXVECTOR3(0.0f, -64.0f, 0.0f), 0, pml.traceObj); - newtrace(&trace, pm->ps.origin, point, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); - //It hit the ground below - if ( trace.fraction < 1.0 && pm->ps.origin.z > trace.endpos.z) - { - pm->ps.origin = trace.endpos; - pml.walking = true; - pml.groundPlane = true; - pm->ps.groundEntityNum = trace.entityNum; - - } - else{ - pm->ps.groundEntityNum = ENTITYNUM_NONE; - pml.groundPlane = false; - pml.walking = false; - pm->ps.bSnap = false; - } - - -} - -static bool PM_CorrectAllSolid(traceResults* const trace) -{ - int i, j, k; - Ogre::Vector3 point; - - //if ( pm->debugLevel ) - //Com_Printf("%i:allsolid\n", c_pmove); - //bprintf("allsolid\n"); - - // jitter around - for (i = -1; i <= 1; i++) - { - for (j = -1; j <= 1; j++) - { - for (k = -1; k <= 1; k++) - { - //VectorCopy(pm->ps->origin, point); - point = pm->ps.origin; - - /*point[0] += (float) i; - point[1] += (float) j; - point[2] += (float) k;*/ - point += Ogre::Vector3( (const float)i, (const float)j, (const float)k); - - //pm->trace (trace, point, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); - //tracefunc(trace, *(const D3DXVECTOR3* const)&point, *(const D3DXVECTOR3* const)&point, D3DXVECTOR3(0.0f, 0.0f, 0.0f), 0, pml.traceObj); - newtrace(trace, point, point, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); - - if ( !trace->allsolid ) - { - /*point[0] = pm->ps->origin[0]; - point[1] = pm->ps->origin[1]; - point[2] = pm->ps->origin[2] - 0.25;*/ - point = pm->ps.origin; - point.z -= 0.25f; - - //pm->trace (trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); - //tracefunc(trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&point, D3DXVECTOR3(0.0f, -0.25f, 0.0f), 0, pml.traceObj); - newtrace(trace, pm->ps.origin, point, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); - pml.groundTrace = *trace; - return true; - } - } - } - } - - //pm->ps->groundEntityNum = ENTITYNUM_NONE; - pm->ps.groundEntityNum = ENTITYNUM_NONE; - pml.groundPlane = false; - pml.walking = false; - - return false; -} - -static void PM_CrashLand( void ) -{ - float delta; - float dist ; - float vel, acc; - float t; - float a, b, c, den; - - // decide which landing animation to use - /*if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP ) - PM_ForceLegsAnim( LEGS_LANDB ); - else - PM_ForceLegsAnim( LEGS_LAND ); - - pm->ps->legsTimer = TIMER_LAND;*/ - - // calculate the exact velocity on landing - //dist = pm->ps->origin[2] - pml.previous_origin[2]; - - dist = pm->ps.origin.z - pml.previous_origin.z; - - //vel = pml.previous_velocity[2]; - vel = pml.previous_velocity.z; - - //acc = -pm->ps->gravity; - acc = -pm->ps.gravity; - - a = acc / 2; - b = vel; - c = -dist; - - den = b * b - 4 * a * c; - if ( den < 0 ) - return; - - t = (-b - sqrtf( den ) ) / ( 2 * a ); - - delta = vel + t * acc; - delta = delta * delta * 0.0001f; - - // ducking while falling doubles damage - /*if ( pm->ps->pm_flags & PMF_DUCKED ) - delta *= 2;*/ - if (pm->cmd.upmove < -20) - delta *= 2; - - // never take falling damage if completely underwater - if ( pm->ps.waterlevel == 3 ) - return; - - // reduce falling damage if there is standing water - if ( pm->ps.waterlevel == 2 ) - delta *= 0.25; - if ( pm->ps.waterlevel == 1 ) - delta *= 0.5; - - if ( delta < 1 ) - return; -/* - if (delta > 60) - printf("Far crashland: %f\n", delta); - else if (delta > 40) - printf("Medium crashland: %f\n", delta); - else if (delta > 4) - printf("Short crashland: %f\n", delta); -*/ - if (delta > 60) - { - /* - static const namestruct healthDamage("Health Damage"); - const SOUN* const soun = SOUN::GetSound(healthDamage); - if (soun) - { - PlaySound2D(soun->soundFilename, soun->soundData->GetVolumeFloat() ); - }*/ - } - - if (delta > 3) // We need at least a short crashland to proc the sound effects: - { - bool splashSound = false; - - if (pm->hasWater) - { - - const float waterHeight = pm->waterHeight; - const float waterHeightSplash = waterHeight + pm->ps.halfExtents.y; - if (pm->ps.origin.z < waterHeightSplash) - { - splashSound = true; - } - - } - - - if (splashSound) - { - //Change this later----------------------------------- - /* - const namestruct ns("DefaultLandWater"); - const SOUN* const soun = SOUN::GetSound(ns); - if (soun) - { - PlaySound2D(soun->soundFilename, soun->soundDatga->GetVolumeFloat() ); - }*/ - } - else - { - //Change this later--------------------------------- - /* - namestruct defaultCreature; - const SNDG* const sndg = SNDG::GetFromMap(defaultCreature, SNDG::land); - if (sndg) - { - const namestruct& SOUNID = sndg->soundID; - const SOUN* const soun = SOUN::GetSound(SOUNID); - if (soun) - { - PlaySound2D(soun->soundFilename, soun->soundData->GetVolumeFloat() ); - } - }*/ - } - } - - // create a local entity event to play the sound - - // SURF_NODAMAGE is used for bounce pads where you don't ever - // want to take damage or play a crunch sound - //if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) ) - { - /*if ( delta > 60 ) - PM_AddEvent( EV_FALL_FAR ); - else if ( delta > 40 ) - { - // this is a pain grunt, so don't play it if dead - if ( pm->ps->stats[STAT_HEALTH] > 0 ) - PM_AddEvent( EV_FALL_MEDIUM ); - } - else if ( delta > 7 ) - PM_AddEvent( EV_FALL_SHORT ); - else - PM_AddEvent( PM_FootstepForSurface() );*/ - } - - // start footstep cycle over - //pm->ps->bobCycle = 0; -} - -static void PM_GroundTrace( void ) -{ - Ogre::Vector3 point; - traceResults trace; - - /*point[0] = pm->ps->origin[0]; - point[1] = pm->ps->origin[1]; - point[2] = pm->ps->origin[2] - 0.25;*/ - point = pm->ps.origin; - point.z -= 0.25f; - - //pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); - //tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&point, D3DXVECTOR3(0.0f, -0.25f, 0.0f), 0, pml.traceObj); - newtrace(&trace, pm->ps.origin, point, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); - pml.groundTrace = trace; - - // do something corrective if the trace starts in a solid... - if ( trace.allsolid ) { - //std::cout << "ALL SOLID\n"; - if ( !PM_CorrectAllSolid(&trace) ){ - //std::cout << "Returning after correct all solid\n"; - return; - } - } - // if the trace didn't hit anything, we are in free fall - if ( trace.fraction == 1.0) - { - if(pm->ps.velocity.z > 50.0f && pm->ps.bSnap && pm->ps.speed > 1000.0f) - pm->ps.velocity.z = 50.0f; - if(pm->ps.snappingImplemented){ - if(pm->ps.bSnap && pm->ps.counter <= 0) - PM_GroundTraceMissed(); - } - - - - return; - } - else - { - //It hit something, so we are on the ground - pm->ps.bSnap = true; - - } - // check if getting thrown off the ground - //if ( pm->ps->velocity[2] > 0 && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 ) - if (pm->ps.velocity.z > 0 && pm->ps.velocity.dotProduct(trace.planenormal) > 10.0f ) - { - //if ( pm->debugLevel ) - //Com_Printf("%i:kickoff\n", c_pmove); - - // go into jump animation - /*if ( pm->cmd.forwardmove >= 0 ) - { - PM_ForceLegsAnim( LEGS_JUMP ); - pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; - } - else - { - PM_ForceLegsAnim( LEGS_JUMPB ); - pm->ps->pm_flags |= PMF_BACKWARDS_JUMP; - }*/ - if(!pm->ps.bSnap){ - pm->ps.groundEntityNum = ENTITYNUM_NONE; - pml.groundPlane = false; - pml.walking = false; - } - else - { - pml.groundPlane = true; - pml.walking = true; - } - return; - } - - - - - // slopes that are too steep will not be considered onground - //if ( trace.plane.normal[2] < MIN_WALK_NORMAL ) - //std::cout << "MinWalkNormal" << trace.planenormal.z; - if (trace.planenormal.z < MIN_WALK_NORMAL) - { - //if ( pm->debugLevel ) - //Com_Printf("%i:steep\n", c_pmove); - - // FIXME: if they can't slide down the slope, let them - // walk (sharp crevices) - pm->ps.groundEntityNum = ENTITYNUM_NONE; - pml.groundPlane = true; - pml.walking = false; - return; - } - - pml.groundPlane = true; - pml.walking = true; - - // hitting solid ground will end a waterjump - /*if (pm->ps.pm_flags & PMF_TIME_WATERJUMP) - { - pm->ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND); - pm->ps->pm_time = 0; - }*/ - - if ( pm->ps.groundEntityNum == ENTITYNUM_NONE ) - { - // just hit the ground - /*if ( pm->debugLevel ) - Com_Printf("%i:Land\n", c_pmove);*/ - //bprintf("Land\n"); - - PM_CrashLand(); - - // don't do landing time if we were just going down a slope - //if ( pml.previous_velocity[2] < -200 ) - if (pml.previous_velocity.z < -200) - { - // don't allow another jump for a little while - //pm->ps->pm_flags |= PMF_TIME_LAND; - pm->ps.pm_time = 250; - } - } - - pm->ps.groundEntityNum = trace.entityNum; - - // don't reset the z velocity for slopes -// pm->ps->velocity[2] = 0; - - //PM_AddTouchEnt( trace.entityNum ); -} - -void PM_AirMove() -{ - //int i; - Ogre::Vector3 wishvel; - float fmove, smove; - Ogre::Vector3 wishdir; - float wishspeed; - float scale; - playerMove::playercmd cmd; - //pm->ps.gravity = 800; - PM_Friction(); - - fmove = pm->cmd.forwardmove; - smove = pm->cmd.rightmove; - - cmd = pm->cmd; - scale = PM_CmdScale( &cmd ); - // set the movementDir so clients can rotate the legs for strafing - //PM_SetMovementDir(); - - // project moves down to flat plane - //pml.forward[2] = 0; - pml.forward.z = 0; //Z or Y? - //pml.right[2] = 0; - pml.right.z = 0; - //VectorNormalize (pml.forward); - VectorNormalize(pml.forward); - VectorNormalize(pml.right); - //VectorNormalize (pml.right); - - //for ( i = 0 ; i < 2 ; i++ ) - //wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; - wishvel = pml.forward * fmove + pml.right * smove; - - //wishvel[2] = 0; - wishvel.z = 0; - - //VectorCopy (wishvel, wishdir); - wishdir = wishvel; - //wishspeed = VectorNormalize(wishdir); - wishspeed = VectorNormalize(wishdir); - - wishspeed *= scale; - - // not on ground, so little effect on velocity - PM_Accelerate (wishdir, wishspeed, pm_airaccelerate); - - // we may have a ground plane that is very steep, even - // though we don't have a groundentity - // slide along the steep plane - if ( pml.groundPlane ) - PM_ClipVelocity (pm->ps.velocity, pml.groundTrace.planenormal, pm->ps.velocity, OVERCLIP ); - -/*#if 0 - //ZOID: If we are on the grapple, try stair-stepping - //this allows a player to use the grapple to pull himself - //over a ledge - if (pm->ps->pm_flags & PMF_GRAPPLE_PULL) - PM_StepSlideMove ( qtrue ); - else - PM_SlideMove ( qtrue ); -#endif*/ - //std::cout << "Moving in the air" << pm->ps.velocity << "\n"; - - /*bprintf("%i ", */PM_StepSlideMove ( true )/* )*/; - - -} - -static void PM_NoclipMove( void ) -{ - float speed, drop, friction, control, newspeed; -// int i; - Ogre::Vector3 wishvel; - float fmove, smove; - Ogre::Vector3 wishdir; - float wishspeed; - float scale; - - //pm->ps->viewheight = DEFAULT_VIEWHEIGHT; - - // friction - - //speed = VectorLength (pm->ps->velocity); - speed = pm->ps.velocity.length(); - if (speed < 1) - //VectorCopy (vec3_origin, pm->ps->velocity); - pm->ps.velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f); - else - { - drop = 0; - - friction = pm_friction * 1.5f; // extra friction - control = speed < pm_stopspeed ? pm_stopspeed : speed; - drop += control * friction * pml.frametime; - - // scale the velocity - newspeed = speed - drop; - if (newspeed < 0) - newspeed = 0; - newspeed /= speed; - - //VectorScale (pm->ps->velocity, newspeed, pm->ps->velocity); - pm->ps.velocity = pm->ps.velocity * newspeed; - } - - // accelerate - scale = PM_CmdScale( &pm->cmd ); - - fmove = pm->cmd.forwardmove; - smove = pm->cmd.rightmove; - - //for (i=0 ; i<3 ; i++) - //wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; - - wishvel = pml.forward * fmove + pml.right * smove; - //wishvel[2] += pm->cmd.upmove; - wishvel.z += pm->cmd.upmove; - - //VectorCopy (wishvel, wishdir); - wishdir = wishvel; - wishspeed = VectorNormalize(wishdir); - wishspeed *= scale; - - - PM_Accelerate( wishdir, wishspeed, pm_accelerate ); - - // move - //VectorMA (pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin); - pm->ps.origin = pm->ps.origin + pm->ps.velocity * pml.frametime; -} - -static void PM_DropTimers( void ) -{ - // drop misc timing counter - if ( pm->ps.pm_time ) - { - if ( pml.msec >= pm->ps.pm_time ) - { - //pm->ps->pm_flags &= ~PMF_ALL_TIMES; - pm->ps.pm_time = 0; - } - else - pm->ps.pm_time -= pml.msec; - } - - //bprintf("Time: %i\n", pm->ps.pm_time); - - // drop animation counter - /*if ( pm->ps->legsTimer > 0 ) - { - pm->ps->legsTimer -= pml.msec; - if ( pm->ps->legsTimer < 0 ) - pm->ps->legsTimer = 0; - } - - if ( pm->ps->torsoTimer > 0 ) - { - pm->ps->torsoTimer -= pml.msec; - if ( pm->ps->torsoTimer < 0 ) - pm->ps->torsoTimer = 0; - }*/ -} - -static void PM_FlyMove( void ) -{ - //int i; - Ogre::Vector3 wishvel; - float wishspeed; - Ogre::Vector3 wishdir; - float scale; - - // normal slowdown - PM_Friction (); - - scale = PM_CmdScale( &pm->cmd ); - // - // user intentions - // - if ( !scale ) - { - /*wishvel[0] = 0; - wishvel[1] = 0; - wishvel[2] = 0;*/ - wishvel = Ogre::Vector3(0,0,0); - } - else - { - //for (i=0 ; i<3 ; i++) - //wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove; - wishvel = pml.forward * scale * pm->cmd.forwardmove + pml.right * scale * pm->cmd.rightmove; - - //wishvel[2] += scale * pm->cmd.upmove; - wishvel.z += /*6.35f * */pm->cmd.upmove * scale; - } - - //VectorCopy (wishvel, wishdir); - wishdir = wishvel; - - //wishspeed = VectorNormalize(wishdir); - wishspeed = VectorNormalize(wishdir); - - PM_Accelerate (wishdir, wishspeed, pm_flyaccelerate); - - PM_StepSlideMove( false ); -} - - -void PM_SetWaterLevel( playerMove* const pm ) -{ - Ogre::Vector3 point; - //int cont; - int sample1; - int sample2; - - // - // get waterlevel, accounting for ducking - // - - pm->ps.waterlevel = WL_DRYLAND; - pm->ps.watertype = 0; - - /*point[0] = pm->ps->origin[0]; - point[1] = pm->ps->origin[1]; - point[2] = pm->ps->origin[2] + MINS_Z + 1; */ - point.x = pm->ps.origin.x; - point.y = pm->ps.origin.y; - point.z = pm->ps.origin.z + MINS_Z + 1; - - //cont = pm->pointcontents( point, pm->ps->clientNum ); - bool checkWater = (pml.hasWater && pml.waterHeight > point.z); - //if ( cont & MASK_WATER ) - if ( checkWater) - { - sample2 = /*pm->ps.viewheight*/DEFAULT_VIEWHEIGHT - MINS_Z; - sample1 = sample2 / 2; - - pm->ps.watertype = CONTENTS_WATER;//cont; - pm->ps.waterlevel = WL_ANKLE; - //point[2] = pm->ps->origin[2] + MINS_Z + sample1; - point.z = pm->ps.origin.z + MINS_Z + sample1; - checkWater = (pml.hasWater && pml.waterHeight > point.z); - //cont = pm->pointcontents (point, pm->ps->clientNum ); - //if ( cont & MASK_WATER ) - if (checkWater) - { - pm->ps.waterlevel = WL_WAIST; - //point[2] = pm->ps->origin[2] + MINS_Z + sample2; - point.z = pm->ps.origin.z + MINS_Z + sample2; - //cont = pm->pointcontents (point, pm->ps->clientNum ); - //if ( cont & MASK_WATER ) - checkWater = (pml.hasWater && pml.waterHeight > point.z); - if (checkWater ) - pm->ps.waterlevel = WL_UNDERWATER; - } - } -} - -void PmoveSingle (playerMove* const pmove) -{ - pmove->ps.counter--; - //pm = pmove; - - // Aedra doesn't support Q3-style VM traps D: //while(1); - - // this counter lets us debug movement problems with a journal - // by setting a conditional breakpoint fot the previous frame - //c_pmove++; - - // clear results - //pm->numtouch = 0; - pm->ps.watertype = 0; - pm->ps.waterlevel = WL_DRYLAND; - - //if ( pm->ps->stats[STAT_HEALTH] <= 0 ) - //pm->tracemask &= ~CONTENTS_BODY; // corpses can fly through bodies - - - // make sure walking button is clear if they are running, to avoid - // proxy no-footsteps cheats - //if ( abs( pm->cmd.forwardmove ) > 64 || abs( pm->cmd.rightmove ) > 64 ) - //pm->cmd.buttons &= ~BUTTON_WALKING; - - - // set the talk balloon flag - //if ( pm->cmd.buttons & BUTTON_TALK ) - //pm->ps->eFlags |= EF_TALK; - //else - //pm->ps->eFlags &= ~EF_TALK; - - // set the firing flag for continuous beam weapons - /*if ( !(pm->ps->pm_flags & PMF_RESPAWNED) && pm->ps->pm_type != PM_INTERMISSION - && ( pm->cmd.buttons & BUTTON_ATTACK ) && pm->ps->ammo[ pm->ps->weapon ] ) - pm->ps->eFlags |= EF_FIRING; - else - pm->ps->eFlags &= ~EF_FIRING;*/ - - // clear the respawned flag if attack and use are cleared - /*if ( pm->ps->stats[STAT_HEALTH] > 0 && - !( pm->cmd.buttons & (BUTTON_ATTACK | BUTTON_USE_HOLDABLE) ) ) - pm->ps->pm_flags &= ~PMF_RESPAWNED;*/ - - // if talk button is down, dissallow all other input - // this is to prevent any possible intercept proxy from - // adding fake talk balloons - /*if ( pmove->cmd.buttons & BUTTON_TALK ) - { - // keep the talk button set tho for when the cmd.serverTime > 66 msec - // and the same cmd is used multiple times in Pmove - pmove->cmd.buttons = BUTTON_TALK; - pmove->cmd.forwardmove = 0; - pmove->cmd.rightmove = 0; - pmove->cmd.upmove = 0; - }*/ - - // clear all pmove local vars - memset (&pml, 0, sizeof(pml) ); - - // Aedra-specific code: - //pml.scene = global_lastscene; - - - // End Aedra-specific code - pml.hasWater = pmove->hasWater; - pml.isInterior = pmove->isInterior; - pml.waterHeight = pmove->waterHeight; - - // determine the time - pml.msec = pmove->cmd.serverTime - pm->ps.commandTime; - if ( pml.msec < 1 ) - pml.msec = 1; - else if ( pml.msec > 200 ) - pml.msec = 200; - - //pm->ps->commandTime = pmove->cmd.serverTime; - - // Commented out as a hack - pm->ps.commandTime = pmove->cmd.serverTime; - - // Handle state change procs: - if (pm->cmd.activating != pm->cmd.lastActivatingState) - { - if (!pm->cmd.lastActivatingState && pm->cmd.activating) - pm->cmd.procActivating = playerMove::playercmd::KEYDOWN; - else - pm->cmd.procActivating = playerMove::playercmd::KEYUP; - } - else - { - pm->cmd.procActivating = playerMove::playercmd::NO_CHANGE; - } - pm->cmd.lastActivatingState = pm->cmd.activating; - - if (pm->cmd.dropping != pm->cmd.lastDroppingState) - { - if (!pm->cmd.lastDroppingState && pm->cmd.dropping) - pm->cmd.procDropping = playerMove::playercmd::KEYDOWN; - else - pm->cmd.procDropping = playerMove::playercmd::KEYUP; - } - else - { - pm->cmd.procDropping = playerMove::playercmd::NO_CHANGE; - } - pm->cmd.lastDroppingState = pm->cmd.dropping; - - // save old org in case we get stuck - //VectorCopy (pm->ps->origin, pml.previous_origin); - pml.previous_origin = pm->ps.origin; - - // Copy over the lastframe origin - pmove->ps.lastframe_origin = pmove->ps.origin; - - //pmove->ps.lastframe_origin = pmove->ps.origin; - - // save old velocity for crashlanding - //VectorCopy (pm->ps->velocity, pml.previous_velocity); - pml.previous_velocity = pm->ps.velocity; - - pml.frametime = pml.msec * 0.001f; - - // update the viewangles - //PM_UpdateViewAngles( &(pm->ps), &(pm->cmd) ); - - AngleVectors (pm->ps.viewangles, &(pml.forward), &(pml.right), &(pml.up) ); - - //if ( pm->cmd.upmove < 10 ) - // not holding jump - //pm->ps->pm_flags &= ~PMF_JUMP_HELD; - - // decide if backpedaling animations should be used - /*if ( pm->cmd.forwardmove < 0 ) - pm->ps->pm_flags |= PMF_BACKWARDS_RUN; - else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) - pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN;*/ - - /*if ( pm->ps->pm_type >= PM_DEAD ) - { - pm->cmd.forwardmove = 0; - pm->cmd.rightmove = 0; - pm->cmd.upmove = 0; - }*/ - - if ( pm->ps.move_type == PM_SPECTATOR ) - { - - //PM_CheckDuck (); - PM_FlyMove (); - PM_DropTimers (); - return; - } - - if ( pm->ps.move_type == PM_NOCLIP ) - { - - PM_NoclipMove (); - PM_DropTimers (); - return; - } - - if (pm->ps.move_type == PM_FREEZE){ - - return; // no movement at all - - } - - if ( pm->ps.move_type == PM_INTERMISSION || pm->ps.move_type == PM_SPINTERMISSION){ - return; // no movement at all - } - - // set watertype, and waterlevel - PM_SetWaterLevel(pmove); - pml.previous_waterlevel = pmove->ps.waterlevel; - - // set mins, maxs, and viewheight - //PM_CheckDuck (); - - // set groundentity - PM_GroundTrace(); - - /*if ( pm->ps->pm_type == PM_DEAD ) - PM_DeadMove (); - - PM_DropTimers();*/ - - PM_DropTimers(); - -/*#ifdef MISSIONPACK - if ( pm->ps->powerups[PW_INVULNERABILITY] ) { - PM_InvulnerabilityMove(); - } else -#endif*/ - /*if ( pm->ps->powerups[PW_FLIGHT] ) - // flight powerup doesn't allow jump and has different friction - PM_FlyMove(); - else if (pm->ps->pm_flags & PMF_GRAPPLE_PULL) - { - PM_GrappleMove(); - // We can wiggle a bit - PM_AirMove(); - } - else if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) - PM_WaterJumpMove();*/ - if ( pmove->ps.waterlevel > 1 ) - // swimming - PM_WaterMove(pmove); - else if ( pml.walking ) - { - - // walking on ground - PM_WalkMove(pmove); - //bprintf("WalkMove\n"); - } - else - { - // airborne - //std::cout << "AIRMOVE\n"; - PM_AirMove(); - //bprintf("AirMove\n"); - } - - //PM_Animate(); - - // set groundentity, watertype, and waterlevel - PM_GroundTrace(); - PM_SetWaterLevel(pmove); - - // weapons - /*PM_Weapon(); - - // torso animation - PM_TorsoAnimation(); - - // footstep events / legs animations - PM_Footsteps(); - - // entering / leaving water splashes - PM_WaterEvents(); - - // snap some parts of playerstate to save network bandwidth - trap_SnapVector( pm->ps->velocity );*/ -} - -void Ext_UpdateViewAngles(playerMove* const pm) -{ - playerMove::playerStruct* const ps = &(pm->ps); - playerMove::playercmd* const cmd = &(pm->cmd); - PM_UpdateViewAngles(ps, cmd); -} - -void Pmove (playerMove* const pmove) -{ - // warning: unused variable ‘fmove’ - //int fmove = pmove->cmd.forwardmove; - - pm = pmove; - - int finalTime; - - finalTime = pmove->cmd.serverTime; - - pmove->ps.commandTime = 40; - - if ( finalTime < pmove->ps.commandTime ) - return; // should not happen - - if ( finalTime > pmove->ps.commandTime + 1000 ) - pmove->ps.commandTime = finalTime - 1000; - - pmove->ps.pmove_framecount = (pmove->ps.pmove_framecount + 1) & ( (1 << PS_PMOVEFRAMECOUNTBITS) - 1); - - // chop the move up if it is too long, to prevent framerate - // dependent behavior - while ( pmove->ps.commandTime != finalTime ) - { - int msec; - - msec = finalTime - pmove->ps.commandTime; - - if ( pmove->pmove_fixed ) - { - if ( msec > pmove->pmove_msec ) - msec = pmove->pmove_msec; - } - else - { - if ( msec > 66 ) - msec = 66; - } - - pmove->cmd.serverTime = pmove->ps.commandTime + msec; - - if (pmove->isInterior) - { - PmoveSingle( pmove ); - } - else - { - PmoveSingle( pmove ); - /* - std::map::const_iterator it = ExtCellLookup.find(PositionToCell(pmove->ps.origin) ); - if (it != ExtCellLookup.end() ) - { - pmove->traceObj->incellptr = it->second; - }*/ - } - - //if ( pmove->ps->pm_flags & PMF_JUMP_HELD ) - //pmove->cmd.upmove = 20; - } - - //pmove->ps.last_compute_time = GetTimeQPC(); - //pmove->ps.lerp_multiplier = (pmove->ps.origin - pmove->ps.lastframe_origin);// * (1.000 / 31.0); - - //PM_CheckStuck(); - -} - - diff --git a/libs/openengine/bullet/pmove.h b/libs/openengine/bullet/pmove.h deleted file mode 100644 index fa303184e..000000000 --- a/libs/openengine/bullet/pmove.h +++ /dev/null @@ -1,200 +0,0 @@ -#ifndef OENGINE_BULLET_PMOVE_H -#define OENGINE_BULLET_PMOVE_H -/* -This source file is a *modified* version of various header files from the Quake 3 Arena source code, -which was released under the GNU GPL (v2) in 2005. -Quake 3 Arena is copyright (C) 1999-2005 Id Software, Inc. -*/ - -#include -#include -#include "trace.h" -#include "physic.hpp" - -#include - -//#include "GameMath.h" -//#include "GameTime.h" - -// Forwards-declare it! - -/*#ifndef COMPILING_PMOVE -#include "Scene.h" -extern SceneInstance* global_lastscene; -#endif*/ - -static const Ogre::Vector3 halfExtentsDefault(14.64f * 2, 14.24f * 2, 33.25f * 2); - -#define MAX_CLIP_PLANES 5 -#define OVERCLIP 1.001f -//#define STEPSIZE 18 // 18 is way too much -#define STEPSIZE (9) -#ifndef M_PI - #define M_PI 3.14159265358979323846f -#endif -#define YAW 0 -#define PITCH /*1*/2 -#define ROLL /*2*/1 -#define SHORT2ANGLE(x) ( (x) * (360.0f / 65536.0f) ) -#define ANGLE2SHORT(x) ( (const short)( (x) / (360.0f / 65536.0f) ) ) -#define GENTITYNUM_BITS 10 // don't need to send any more -#define MAX_GENTITIES (1 << GENTITYNUM_BITS) -#define ENTITYNUM_NONE (MAX_GENTITIES - 1) -#define ENTITYNUM_WORLD (MAX_GENTITIES - 2) -#define MIN_WALK_NORMAL .7f // can't walk on very steep slopes -#define PS_PMOVEFRAMECOUNTBITS 6 -#define MINS_Z -24 -#define DEFAULT_VIEWHEIGHT 26 -#define CROUCH_VIEWHEIGHT 12 -#define DEAD_VIEWHEIGHT (-16) -#define CONTENTS_SOLID 1 // an eye is never valid in a solid -#define CONTENTS_LAVA 8 -#define CONTENTS_SLIME 16 -#define CONTENTS_WATER 32 -#define CONTENTS_FOG 64 -static const float pm_accelerate = 10.0f; -static const float pm_stopspeed = 100.0f; -static const float pm_friction = 12.0f; -static const float pm_flightfriction = 3.0f; -static const float pm_waterfriction = 1.0f; -static const float pm_airaccelerate = 1.0f; -static const float pm_swimScale = 0.50f; -static const float pm_duckScale = 0.25f; -static const float pm_flyaccelerate = 8.0f; -static const float pm_wateraccelerate = 4.0f; - -enum pmtype_t -{ - PM_NORMAL, // can accelerate and turn - PM_NOCLIP, // noclip movement - PM_SPECTATOR, // still run into walls - PM_DEAD, // no acceleration or turning, but free falling - PM_FREEZE, // stuck in place with no control - PM_INTERMISSION, // no movement or status bar - PM_SPINTERMISSION // no movement or status bar -}; - -enum waterlevel_t -{ - WL_DRYLAND = 0, - WL_ANKLE, - WL_WAIST, - WL_UNDERWATER -}; - - -//#include "bprintf.h" - -struct playerMove -{ - struct playerStruct - { - playerStruct() : gravity(800.0f), speed(480.0f), jump_velocity(270), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NOCLIP), pm_time(0), snappingImplemented(true), bSnap(false), counter(-1), halfExtents(halfExtentsDefault) - { - origin = Ogre::Vector3(0.0f, 0.0f, 0.0f); - velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f); - - viewangles = Ogre::Vector3(0.0f, 0.0f, 0.0f); - - delta_angles[0] = delta_angles[1] = delta_angles[2] = 0; - - lastframe_origin.x = lastframe_origin.y = lastframe_origin.z = 0; - lerp_multiplier.x = lerp_multiplier.y = lerp_multiplier.z = 0; - } - - inline void SpeedUp(void) - { - //printf("speed up to: %f\n", speed); - speed *= 1.25f; - } - - inline void SpeedDown(void) - { - //printf("speed down to %f\n", speed); - speed /= 1.25f; - } - - Ogre::Vector3 velocity; - Ogre::Vector3 origin; - Ogre::Vector3 halfExtents; - bool bSnap; - bool snappingImplemented; - int counter; - float gravity; // default = 800 - float speed; // default = 320 - float jump_velocity; //default = 270 - - int commandTime; // the time at which this command was issued (in milliseconds) - - int pm_time; - - Ogre::Vector3 viewangles; - - int groundEntityNum; - - int pmove_framecount; - - int watertype; - waterlevel_t waterlevel; - - signed short delta_angles[3]; - - pmtype_t move_type; - - float last_compute_time; - Ogre::Vector3 lastframe_origin; - Ogre::Vector3 lerp_multiplier; - } ps; - - struct playercmd - { - enum CMDstateChange - { - NO_CHANGE, - KEYDOWN, - KEYUP - }; - - playercmd() : forwardmove(0), rightmove(0), upmove(0), serverTime(50), ducking(false), - activating(false), lastActivatingState(false), procActivating(NO_CHANGE), - dropping(false), lastDroppingState(false), procDropping(NO_CHANGE) - { - angles[0] = angles[1] = angles[2] = 0; - } - - int serverTime; - - short angles[3]; - - signed char forwardmove; - signed char rightmove; - signed char upmove; - - bool ducking; - bool activating; // if the user is holding down the activate button - bool dropping; // if the user is dropping an item - - bool lastActivatingState; - bool lastDroppingState; - - CMDstateChange procActivating; - CMDstateChange procDropping; - } cmd; - - playerMove() : msec(50), pmove_fixed(false), pmove_msec(50), waterHeight(0), isInterior(true), hasWater(false) - { - } - - int msec; - int pmove_msec; - bool pmove_fixed; - int waterHeight; - bool hasWater; - bool isInterior; - OEngine::Physic::PhysicEngine* mEngine; -}; - -void Pmove (playerMove* const pmove); -void Ext_UpdateViewAngles(playerMove* const pm); -void AngleVectors( const Ogre::Vector3& angles, Ogre::Vector3* const forward, Ogre::Vector3* const right, Ogre::Vector3* const up) ; -#endif diff --git a/libs/openengine/bullet/trace.cpp b/libs/openengine/bullet/trace.cpp index 9f5398574..d246417c7 100644 --- a/libs/openengine/bullet/trace.cpp +++ b/libs/openengine/bullet/trace.cpp @@ -1,196 +1,48 @@ #include "trace.h" - - #include +#include +#include +#include "physic.hpp" - - - - -void newtrace(traceResults* const results, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, const float rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass) //Traceobj was a Aedra Object +enum traceWorldType { - //static float lastyaw = 0.0f; - //static float lastpitch = 0.0f; - //if (!traceobj) - // return; - - //if (!traceobj->incellptr) - // return; - - - const Ogre::Vector3 rayDir = end - start; - - - - - - + collisionWorldTrace = 1, + pickWorldTrace = 2, + bothWorldTrace = collisionWorldTrace | pickWorldTrace +}; - NewPhysTraceResults out; - //std::cout << "Starting trace\n"; - //Ogre::Vector3 startReplace = Ogre::Vector3(650,950, 45); - //Ogre::Vector3 endReplace = startReplace; - //endReplace.z -= .25; - - const bool hasHit = NewPhysicsTrace(&out, start, end, BBHalfExtents, Ogre::Vector3(0.0f, 0.0f, 0.0f), isInterior, enginePass); - - if (out.fraction < 0.001f) - results->startsolid = true; - else - results->startsolid = false; - - - //results->allsolid = out.startSolid; - - // If outside and underground, we're solid - /*if (isInterior) - { - const Ogre::Vector3 height = GetGroundPosition(start, CellCoords(traceCell->data->gridX, traceCell->data->gridY) ); - if (start.yPos - height.yPos < (-2.0f * BBHalfExtents.yPos) ) - { - results->allsolid = true; - } - else - results->allsolid = false; - }*/ - - // If inside and out of the tree, we're solid - //else - //{ - results->allsolid = out.startSolid; - //std::cout << "allsolid" << results->allsolid << "\n"; - //} - - if (!hasHit) - { - results->endpos = end; - results->planenormal = Ogre::Vector3(0.0f, 0.0f, 1.0f); - results->entityNum = ENTITYNUM_NONE; - results->fraction = 1.0f; - } - else - { - results->fraction = out.fraction; - results->planenormal = out.hitNormal; - results->endpos = rayDir * results->fraction + start; - results->entityNum = ENTITYNUM_WORLD; - /*bprintf("Start: (%f, %f, %f) End: (%f, %f, %f) TraceDir: (%f, %f, %f) HitNormal: (%f, %f, %f) Fraction: %f Hitpos: (%f, %f, %f) CompensatedHitpos: (%f, %f, %f)\n", - start.xPos, start.yPos, start.zPos, - end.xPos, end.yPos, end.zPos, - rayDir.xPos, rayDir.yPos, rayDir.zPos, - results->planenormal.xPos, results->planenormal.yPos, results->planenormal.zPos, results->fraction, - out.endPos.xPos, out.endPos.yPos, out.endPos.zPos, - results->endpos.xPos, results->endpos.yPos, results->endpos.zPos);*/ - } -} - - - -template -const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, - const Ogre::Vector3& BBHalfExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass) +void newtrace(traceResults *results, const Ogre::Quaternion& orient, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, bool isInterior, OEngine::Physic::PhysicEngine *enginePass) //Traceobj was a Aedra Object { - //if (!traceobj->incellptr) - // return false; - //if(enginePass->dynamicsWorld->getCollisionObjectArray().at(60)->getCollisionShape()->isConvex()) - // std::cout << "It's convex\n"; - - - - const btVector3 btstart(start.x, start.y, start.z + BBHalfExtents.z); - const btVector3 btend(end.x, end.y, end.z + BBHalfExtents.z); - const btQuaternion btrot(rotation.y, rotation.x, rotation.z); //y, x, z + const btVector3 btstart(start.x, start.y, start.z + BBHalfExtents.z); + const btVector3 btend(end.x, end.y, end.z + BBHalfExtents.z); + const btQuaternion btorient (orient.x, orient.y, orient.z, orient.w); const btBoxShape newshape(btVector3(BBHalfExtents.x, BBHalfExtents.y, BBHalfExtents.z)); - //const btCapsuleShapeZ newshape(BBHalfExtents.x, BBHalfExtents.z * 2 - BBHalfExtents.x * 2); - const btTransform from(btrot, btstart); - const btTransform to(btrot, btend); - - // warning: unused variable ... - /* - float x = from.getOrigin().getX(); - float y = from.getOrigin().getY(); - float z = from.getOrigin().getZ(); - float x2 = to.getOrigin().getX(); - float y2 = to.getOrigin().getY(); - float z2 = to.getOrigin().getZ(); - */ - - //std::cout << "BtFrom: " << x << "," << y << "," << z << "\n"; - //std::cout << "BtTo: " << x2 << "," << y2 << "," << z2 << "\n"; - //std::cout << "BtTo: " << to.getOrigin().getX() << "," << to.getOrigin().getY() << "," << to.getOrigin().getZ() << "\n"; - - - btCollisionWorld::ClosestConvexResultCallback - newTraceCallback(btstart, btend); - - newTraceCallback.m_collisionFilterMask = (traceType == collisionWorldTrace) ? Only_Collision : Only_Pickup; - - - enginePass->dynamicsWorld->convexSweepTest(&newshape, from, to, newTraceCallback); - //newTraceCallback. - - - //std::cout << "NUM: " << enginePass->dynamicsWorld->getNumCollisionObjects() << "\n"; - - // Copy the hit data over to our trace results struct: - out->fraction = newTraceCallback.m_closestHitFraction; - - Ogre::Vector3& outhitnormal = out->hitNormal; - const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld; - - outhitnormal.x = tracehitnormal.x(); - outhitnormal.y = tracehitnormal.y(); - outhitnormal.z = tracehitnormal.z(); - - Ogre::Vector3& outhitpos = out->endPos; - const btVector3& tracehitpos = newTraceCallback.m_hitPointWorld; - - outhitpos.x = tracehitpos.x(); - outhitpos.y = tracehitpos.y(); - outhitpos.z= tracehitpos.z(); - - // StartSolid test: - { - out->startSolid = false; - //btCollisionObject collision; - //collision.setCollisionShape(const_cast(&newshape) ); - - //CustomContactCallback crb; - - //world.world->contactTest(&collision, crb); - //out->startSolid = crb.hit; - - // If outside and underground, we're solid - if (!isInterior) //Check if we are interior - { - } - - // If inside and out of the tree, we're solid - else - { - btVector3 aabbMin, aabbMax; - enginePass->broadphase->getBroadphaseAabb(aabbMin, aabbMax); - //std::cout << "AABBMIN" << aabbMin.getX() <<"," <startSolid = true; - } - } - } - - const bool hasHit = newTraceCallback.hasHit(); - - - - - return hasHit; + //const btCapsuleShapeZ newshape(BBHalfExtents.x, BBHalfExtents.z * 2 - BBHalfExtents.x * 2); + const btTransform from(btorient, btstart); + const btTransform to(btorient, btend); + + btCollisionWorld::ClosestConvexResultCallback newTraceCallback(btstart, btend); + newTraceCallback.m_collisionFilterMask = OEngine::Physic::CollisionType_World|OEngine::Physic::CollisionType_HeightMap|OEngine::Physic::CollisionType_Actor; + + enginePass->dynamicsWorld->convexSweepTest(&newshape, from, to, newTraceCallback); + + // Copy the hit data over to our trace results struct: + if(newTraceCallback.hasHit()) + { + const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld; + results->fraction = newTraceCallback.m_closestHitFraction; + results->planenormal = Ogre::Vector3(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z()); + results->endpos = (end-start)*results->fraction + start; + } + else + { + results->endpos = end; + results->planenormal = Ogre::Vector3(0.0f, 0.0f, 1.0f); + results->fraction = 1.0f; + } } diff --git a/libs/openengine/bullet/trace.h b/libs/openengine/bullet/trace.h index 1bfe0c717..cd2547f8c 100644 --- a/libs/openengine/bullet/trace.h +++ b/libs/openengine/bullet/trace.h @@ -1,60 +1,26 @@ #ifndef OENGINE_BULLET_TRACE_H #define OENGINE_BULLET_TRACE_H +#include -#include -#include -#include -#include -#include "pmove.h" - -enum traceWorldType +namespace OEngine { - collisionWorldTrace = 1, - pickWorldTrace = 2, - bothWorldTrace = collisionWorldTrace | pickWorldTrace -}; + namespace Physic + { + class PhysicEngine; + } +} -enum collaborativePhysicsType -{ - No_Physics = 0, // Both are empty (example: statics you can walk through, like tall grass) - Only_Collision = 1, // This object only has collision physics but no pickup physics (example: statics) - Only_Pickup = 2, // This object only has pickup physics but no collision physics (example: items dropped on the ground) - Both_Physics = 3 // This object has both kinds of physics (example: activators) -}; -struct NewPhysTraceResults -{ - Ogre::Vector3 endPos; - Ogre::Vector3 hitNormal; - float fraction; - bool startSolid; - //const Object* hitObj; -}; struct traceResults { - Ogre::Vector3 endpos; - Ogre::Vector3 planenormal; + Ogre::Vector3 endpos; + Ogre::Vector3 planenormal; - float fraction; - - int surfaceFlags; - int contents; - int entityNum; - - bool allsolid; - bool startsolid; + float fraction; }; - - -template -const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass); -//template const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass); -//template const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass); - -void newtrace(traceResults* const results, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const float rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass); - +void newtrace(traceResults *results, const Ogre::Quaternion& orient, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, bool isInterior, OEngine::Physic::PhysicEngine* enginePass); #endif diff --git a/libs/openengine/gui/manager.cpp b/libs/openengine/gui/manager.cpp index 925891e1b..a0a4ab0ae 100644 --- a/libs/openengine/gui/manager.cpp +++ b/libs/openengine/gui/manager.cpp @@ -1,11 +1,19 @@ -#include +#include "manager.hpp" + +#include #include +#include + #include -#include "manager.hpp" +#include +#include using namespace OEngine::GUI; +namespace MyGUI +{ + /* * As of MyGUI 3.2.0, MyGUI::OgreDataManager::isDataExist is unnecessarily complex * this override fixes the resulting performance issue. @@ -19,12 +27,540 @@ public: } }; + +/* + * As of MyGUI 3.2.0, rendering with shaders is not supported. + * We definitely need this though to run in GL3 core / DX11 at all. + * To make matters worse, subclassing OgreRenderManager doesn't seem to be possible here. :/ + */ +class ShaderBasedRenderManager : public RenderManager, + public IRenderTarget, + public Ogre::WindowEventListener, + public Ogre::RenderQueueListener, + public Ogre::RenderSystem::Listener +{ + // флаг для обновления всех и вся + bool mUpdate; + + IntSize mViewSize; + + Ogre::SceneManager* mSceneManager; + + VertexColourType mVertexFormat; + + // окно, на которое мы подписываемся для изменения размеров + Ogre::RenderWindow* mWindow; + + // вьюпорт, с которым работает система + unsigned short mActiveViewport; + + Ogre::RenderSystem* mRenderSystem; + Ogre::TextureUnitState::UVWAddressingMode mTextureAddressMode; + Ogre::LayerBlendModeEx mColorBlendMode, mAlphaBlendMode; + + RenderTargetInfo mInfo; + + typedef std::map MapTexture; + MapTexture mTextures; + + bool mIsInitialise; + bool mManualRender; + size_t mCountBatch; + + // ADDED + Ogre::GpuProgram* mVertexProgramNoTexture; + Ogre::GpuProgram* mVertexProgramOneTexture; + Ogre::GpuProgram* mFragmentProgramNoTexture; + Ogre::GpuProgram* mFragmentProgramOneTexture; + +public: + ShaderBasedRenderManager& getInstance() + { + return *getInstancePtr(); + } + ShaderBasedRenderManager* getInstancePtr() + { + return static_cast(RenderManager::getInstancePtr()); + } + + ShaderBasedRenderManager() : + mUpdate(false), + mSceneManager(nullptr), + mWindow(nullptr), + mActiveViewport(0), + mRenderSystem(nullptr), + mIsInitialise(false), + mManualRender(false), + mCountBatch(0) + { + } + + void initialise(Ogre::RenderWindow* _window, Ogre::SceneManager* _scene) + { + MYGUI_PLATFORM_ASSERT(!mIsInitialise, getClassTypeName() << " initialised twice"); + MYGUI_PLATFORM_LOG(Info, "* Initialise: " << getClassTypeName()); + + mColorBlendMode.blendType = Ogre::LBT_COLOUR; + mColorBlendMode.source1 = Ogre::LBS_TEXTURE; + mColorBlendMode.source2 = Ogre::LBS_DIFFUSE; + mColorBlendMode.operation = Ogre::LBX_MODULATE; + + mAlphaBlendMode.blendType = Ogre::LBT_ALPHA; + mAlphaBlendMode.source1 = Ogre::LBS_TEXTURE; + mAlphaBlendMode.source2 = Ogre::LBS_DIFFUSE; + mAlphaBlendMode.operation = Ogre::LBX_MODULATE; + + mTextureAddressMode.u = Ogre::TextureUnitState::TAM_CLAMP; + mTextureAddressMode.v = Ogre::TextureUnitState::TAM_CLAMP; + mTextureAddressMode.w = Ogre::TextureUnitState::TAM_CLAMP; + + mSceneManager = nullptr; + mWindow = nullptr; + mUpdate = false; + mRenderSystem = nullptr; + mActiveViewport = 0; + + Ogre::Root* root = Ogre::Root::getSingletonPtr(); + if (root != nullptr) + setRenderSystem(root->getRenderSystem()); + setRenderWindow(_window); + setSceneManager(_scene); + + // ADDED + sh::MaterialInstance* mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/NoTexture"); + sh::Factory::getInstance()._ensureMaterial("MyGUI/NoTexture", "Default"); + mVertexProgramNoTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) + ->getVertexProgram()->_getBindingDelegate(); + + mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/OneTexture"); + sh::Factory::getInstance()._ensureMaterial("MyGUI/OneTexture", "Default"); + mVertexProgramOneTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) + ->getVertexProgram()->_getBindingDelegate(); + + mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/NoTexture"); + sh::Factory::getInstance()._ensureMaterial("MyGUI/NoTexture", "Default"); + mFragmentProgramNoTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) + ->getFragmentProgram()->_getBindingDelegate(); + + mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/OneTexture"); + sh::Factory::getInstance()._ensureMaterial("MyGUI/OneTexture", "Default"); + mFragmentProgramOneTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) + ->getFragmentProgram()->_getBindingDelegate(); + + + + MYGUI_PLATFORM_LOG(Info, getClassTypeName() << " successfully initialized"); + mIsInitialise = true; + } + + void shutdown() + { + MYGUI_PLATFORM_ASSERT(mIsInitialise, getClassTypeName() << " is not initialised"); + MYGUI_PLATFORM_LOG(Info, "* Shutdown: " << getClassTypeName()); + + destroyAllResources(); + + setSceneManager(nullptr); + setRenderWindow(nullptr); + setRenderSystem(nullptr); + + MYGUI_PLATFORM_LOG(Info, getClassTypeName() << " successfully shutdown"); + mIsInitialise = false; + } + + void setRenderSystem(Ogre::RenderSystem* _render) + { + // отписываемся + if (mRenderSystem != nullptr) + { + mRenderSystem->removeListener(this); + mRenderSystem = nullptr; + } + + mRenderSystem = _render; + + // подписываемся на рендер евент + if (mRenderSystem != nullptr) + { + mRenderSystem->addListener(this); + + // формат цвета в вершинах + Ogre::VertexElementType vertex_type = mRenderSystem->getColourVertexElementType(); + if (vertex_type == Ogre::VET_COLOUR_ARGB) + mVertexFormat = VertexColourType::ColourARGB; + else if (vertex_type == Ogre::VET_COLOUR_ABGR) + mVertexFormat = VertexColourType::ColourABGR; + + updateRenderInfo(); + } + } + + Ogre::RenderSystem* getRenderSystem() + { + return mRenderSystem; + } + + void setRenderWindow(Ogre::RenderWindow* _window) + { + // отписываемся + if (mWindow != nullptr) + { + Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, this); + mWindow = nullptr; + } + + mWindow = _window; + + if (mWindow != nullptr) + { + Ogre::WindowEventUtilities::addWindowEventListener(mWindow, this); + windowResized(mWindow); + } + } + + void setSceneManager(Ogre::SceneManager* _scene) + { + if (nullptr != mSceneManager) + { + mSceneManager->removeRenderQueueListener(this); + mSceneManager = nullptr; + } + + mSceneManager = _scene; + + if (nullptr != mSceneManager) + { + mSceneManager->addRenderQueueListener(this); + } + } + + void setActiveViewport(unsigned short _num) + { + mActiveViewport = _num; + + if (mWindow != nullptr) + { + Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, this); + Ogre::WindowEventUtilities::addWindowEventListener(mWindow, this); + + // рассылка обновлений + windowResized(mWindow); + } + } + + void renderQueueStarted(Ogre::uint8 queueGroupId, const Ogre::String& invocation, bool& skipThisInvocation) + { + Gui* gui = Gui::getInstancePtr(); + if (gui == nullptr) + return; + + if (Ogre::RENDER_QUEUE_OVERLAY != queueGroupId) + return; + + Ogre::Viewport* viewport = mSceneManager->getCurrentViewport(); + if (nullptr == viewport + || !viewport->getOverlaysEnabled()) + return; + + if (mWindow->getNumViewports() <= mActiveViewport + || viewport != mWindow->getViewport(mActiveViewport)) + return; + + mCountBatch = 0; + + static Timer timer; + static unsigned long last_time = timer.getMilliseconds(); + unsigned long now_time = timer.getMilliseconds(); + unsigned long time = now_time - last_time; + + onFrameEvent((float)((double)(time) / (double)1000)); + + last_time = now_time; + + //begin(); + setManualRender(true); + onRenderToTarget(this, mUpdate); + //end(); + + // сбрасываем флаг + mUpdate = false; + } + + void renderQueueEnded(Ogre::uint8 queueGroupId, const Ogre::String& invocation, bool& repeatThisInvocation) + { + } + + void eventOccurred(const Ogre::String& eventName, const Ogre::NameValuePairList* parameters) + { + if (eventName == "DeviceLost") + { + } + else if (eventName == "DeviceRestored") + { + // обновить всех + mUpdate = true; + } + } + + IVertexBuffer* createVertexBuffer() + { + return new OgreVertexBuffer(); + } + + void destroyVertexBuffer(IVertexBuffer* _buffer) + { + delete _buffer; + } + + // для оповещений об изменении окна рендера + void windowResized(Ogre::RenderWindow* _window) + { + if (_window->getNumViewports() > mActiveViewport) + { + Ogre::Viewport* port = _window->getViewport(mActiveViewport); +#if OGRE_VERSION >= MYGUI_DEFINE_VERSION(1, 7, 0) && OGRE_NO_VIEWPORT_ORIENTATIONMODE == 0 + Ogre::OrientationMode orient = port->getOrientationMode(); + if (orient == Ogre::OR_DEGREE_90 || orient == Ogre::OR_DEGREE_270) + mViewSize.set(port->getActualHeight(), port->getActualWidth()); + else + mViewSize.set(port->getActualWidth(), port->getActualHeight()); +#else + mViewSize.set(port->getActualWidth(), port->getActualHeight()); +#endif + + // обновить всех + mUpdate = true; + + updateRenderInfo(); + + onResizeView(mViewSize); + } + } + + void updateRenderInfo() + { + if (mRenderSystem != nullptr) + { + mInfo.maximumDepth = mRenderSystem->getMaximumDepthInputValue(); + mInfo.hOffset = mRenderSystem->getHorizontalTexelOffset() / float(mViewSize.width); + mInfo.vOffset = mRenderSystem->getVerticalTexelOffset() / float(mViewSize.height); + mInfo.aspectCoef = float(mViewSize.height) / float(mViewSize.width); + mInfo.pixScaleX = 1.0f / float(mViewSize.width); + mInfo.pixScaleY = 1.0f / float(mViewSize.height); + } + } + + void doRender(IVertexBuffer* _buffer, ITexture* _texture, size_t _count) + { + if (getManualRender()) + { + begin(); + setManualRender(false); + } + + // ADDED + + if (_texture) + { + Ogre::Root::getSingleton().getRenderSystem()->bindGpuProgram(mVertexProgramOneTexture); + Ogre::Root::getSingleton().getRenderSystem()->bindGpuProgram(mFragmentProgramOneTexture); + } + else + { + Ogre::Root::getSingleton().getRenderSystem()->bindGpuProgram(mVertexProgramNoTexture); + Ogre::Root::getSingleton().getRenderSystem()->bindGpuProgram(mFragmentProgramNoTexture); + } + + if (_texture) + { + OgreTexture* texture = static_cast(_texture); + Ogre::TexturePtr texture_ptr = texture->getOgreTexture(); + if (!texture_ptr.isNull()) + { + mRenderSystem->_setTexture(0, true, texture_ptr); + mRenderSystem->_setTextureUnitFiltering(0, Ogre::FO_LINEAR, Ogre::FO_LINEAR, Ogre::FO_NONE); + } + } + + OgreVertexBuffer* buffer = static_cast(_buffer); + Ogre::RenderOperation* operation = buffer->getRenderOperation(); + operation->vertexData->vertexCount = _count; + + mRenderSystem->_render(*operation); + + ++ mCountBatch; + } + + void begin() + { + // set-up matrices + mRenderSystem->_setWorldMatrix(Ogre::Matrix4::IDENTITY); + mRenderSystem->_setViewMatrix(Ogre::Matrix4::IDENTITY); + +#if OGRE_VERSION >= MYGUI_DEFINE_VERSION(1, 7, 0) && OGRE_NO_VIEWPORT_ORIENTATIONMODE == 0 + Ogre::OrientationMode orient = mWindow->getViewport(mActiveViewport)->getOrientationMode(); + mRenderSystem->_setProjectionMatrix(Ogre::Matrix4::IDENTITY * Ogre::Quaternion(Ogre::Degree(orient * 90.f), Ogre::Vector3::UNIT_Z)); +#else + mRenderSystem->_setProjectionMatrix(Ogre::Matrix4::IDENTITY); +#endif + + // initialise render settings + mRenderSystem->setLightingEnabled(false); + mRenderSystem->_setDepthBufferParams(false, false); + mRenderSystem->_setDepthBias(0, 0); + mRenderSystem->_setCullingMode(Ogre::CULL_NONE); + mRenderSystem->_setFog(Ogre::FOG_NONE); + mRenderSystem->_setColourBufferWriteEnabled(true, true, true, true); + mRenderSystem->unbindGpuProgram(Ogre::GPT_FRAGMENT_PROGRAM); + mRenderSystem->unbindGpuProgram(Ogre::GPT_VERTEX_PROGRAM); + mRenderSystem->setShadingType(Ogre::SO_GOURAUD); + + // initialise texture settings + mRenderSystem->_setTextureCoordCalculation(0, Ogre::TEXCALC_NONE); + mRenderSystem->_setTextureCoordSet(0, 0); + mRenderSystem->_setTextureUnitFiltering(0, Ogre::FO_LINEAR, Ogre::FO_LINEAR, Ogre::FO_NONE); + mRenderSystem->_setTextureAddressingMode(0, mTextureAddressMode); + mRenderSystem->_setTextureMatrix(0, Ogre::Matrix4::IDENTITY); +#if OGRE_VERSION < MYGUI_DEFINE_VERSION(1, 6, 0) + mRenderSystem->_setAlphaRejectSettings(Ogre::CMPF_ALWAYS_PASS, 0); +#else + mRenderSystem->_setAlphaRejectSettings(Ogre::CMPF_ALWAYS_PASS, 0, false); +#endif + mRenderSystem->_setTextureBlendMode(0, mColorBlendMode); + mRenderSystem->_setTextureBlendMode(0, mAlphaBlendMode); + mRenderSystem->_disableTextureUnitsFrom(1); + + // enable alpha blending + mRenderSystem->_setSceneBlending(Ogre::SBF_SOURCE_ALPHA, Ogre::SBF_ONE_MINUS_SOURCE_ALPHA); + + // always use wireframe + // TODO: add option to enable wireframe mode in platform + mRenderSystem->_setPolygonMode(Ogre::PM_SOLID); + } + + void end() + { + } + + ITexture* createTexture(const std::string& _name) + { + MapTexture::const_iterator item = mTextures.find(_name); + MYGUI_PLATFORM_ASSERT(item == mTextures.end(), "Texture '" << _name << "' already exist"); + + OgreTexture* texture = new OgreTexture(_name, OgreDataManager::getInstance().getGroup()); + mTextures[_name] = texture; + return texture; + } + + void destroyTexture(ITexture* _texture) + { + if (_texture == nullptr) return; + + MapTexture::iterator item = mTextures.find(_texture->getName()); + MYGUI_PLATFORM_ASSERT(item != mTextures.end(), "Texture '" << _texture->getName() << "' not found"); + + mTextures.erase(item); + delete _texture; + } + + ITexture* getTexture(const std::string& _name) + { + MapTexture::const_iterator item = mTextures.find(_name); + if (item == mTextures.end()) + { + Ogre::TexturePtr texture = (Ogre::TexturePtr)Ogre::TextureManager::getSingleton().getByName(_name); + if (!texture.isNull()) + { + ITexture* result = createTexture(_name); + static_cast(result)->setOgreTexture(texture); + return result; + } + return nullptr; + } + return item->second; + } + + bool isFormatSupported(PixelFormat _format, TextureUsage _usage) + { + return Ogre::TextureManager::getSingleton().isFormatSupported( + Ogre::TEX_TYPE_2D, + OgreTexture::convertFormat(_format), + OgreTexture::convertUsage(_usage)); + } + + void destroyAllResources() + { + for (MapTexture::const_iterator item = mTextures.begin(); item != mTextures.end(); ++item) + { + delete item->second; + } + mTextures.clear(); + } + +#if MYGUI_DEBUG_MODE == 1 + bool checkTexture(ITexture* _texture) + { + for (MapTexture::const_iterator item = mTextures.begin(); item != mTextures.end(); ++item) + { + if (item->second == _texture) + return true; + } + return false; + } +#endif + + const IntSize& getViewSize() const + { + return mViewSize; + } + + VertexColourType getVertexFormat() + { + return mVertexFormat; + } + + const RenderTargetInfo& getInfo() + { + return mInfo; + } + + size_t getActiveViewport() + { + return mActiveViewport; + } + + Ogre::RenderWindow* getRenderWindow() + { + return mWindow; + } + + bool getManualRender() + { + return mManualRender; + } + + void setManualRender(bool _value) + { + mManualRender = _value; + } + + size_t getBatchCount() const + { + return mCountBatch; + } +}; + +} + + void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging, const std::string& logDir) { assert(wnd); assert(mgr); mSceneMgr = mgr; + mShaderRenderManager = NULL; + mRenderManager = NULL; using namespace MyGUI; @@ -40,20 +576,40 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool // Set up OGRE platform (bypassing OgrePlatform). We might make this more generic later. mLogManager = new LogManager(); - mRenderManager = new OgreRenderManager(); - mDataManager = new FixedOgreDataManager(); + if (!Ogre::Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(Ogre::RSC_FIXED_FUNCTION)) + mShaderRenderManager = new MyGUI::ShaderBasedRenderManager(); + else + mRenderManager = new MyGUI::OgreRenderManager(); + mDataManager = new MyGUI::FixedOgreDataManager(); LogManager::getInstance().setSTDOutputEnabled(logging); if (!theLogFile.empty()) LogManager::getInstance().createDefaultSource(theLogFile); - mRenderManager->initialise(wnd, mgr); + if (mShaderRenderManager) + mShaderRenderManager->initialise(wnd, mgr); + else + mRenderManager->initialise(wnd, mgr); mDataManager->initialise("General"); // Create GUI mGui = new Gui(); - mGui->initialise("core.xml"); + mGui->initialise(""); +} + +void MyGUIManager::updateWindow (Ogre::RenderWindow *wnd) +{ + if (mShaderRenderManager) + { + mShaderRenderManager->setRenderWindow (wnd); + mShaderRenderManager->setActiveViewport(0); + } + else + { + mRenderManager->setRenderWindow (wnd); + mRenderManager->setActiveViewport(0); + } } void MyGUIManager::shutdown() @@ -66,6 +622,12 @@ void MyGUIManager::shutdown() delete mRenderManager; mRenderManager = NULL; } + if(mShaderRenderManager) + { + mShaderRenderManager->shutdown(); + delete mShaderRenderManager; + mShaderRenderManager = NULL; + } if(mDataManager) { mDataManager->shutdown(); diff --git a/libs/openengine/gui/manager.hpp b/libs/openengine/gui/manager.hpp index c0f98da88..9535f2a24 100644 --- a/libs/openengine/gui/manager.hpp +++ b/libs/openengine/gui/manager.hpp @@ -1,12 +1,15 @@ #ifndef OENGINE_MYGUI_MANAGER_H #define OENGINE_MYGUI_MANAGER_H +#include + namespace MyGUI { class Gui; class LogManager; class OgreDataManager; class OgreRenderManager; + class ShaderBasedRenderManager; } namespace Ogre @@ -24,11 +27,11 @@ namespace GUI MyGUI::LogManager* mLogManager; MyGUI::OgreDataManager* mDataManager; MyGUI::OgreRenderManager* mRenderManager; + MyGUI::ShaderBasedRenderManager* mShaderRenderManager; Ogre::SceneManager* mSceneMgr; public: - MyGUIManager() : mLogManager(NULL), mDataManager(NULL), mRenderManager(NULL), mGui(NULL) {} MyGUIManager(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string("")) { setup(wnd,mgr,logging, logDir); @@ -38,6 +41,8 @@ namespace GUI shutdown(); } + void updateWindow (Ogre::RenderWindow* wnd); + void setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string("")); void shutdown(); diff --git a/libs/openengine/ogre/fader.cpp b/libs/openengine/ogre/fader.cpp index 9390d0664..923b0b7e3 100644 --- a/libs/openengine/ogre/fader.cpp +++ b/libs/openengine/ogre/fader.cpp @@ -19,6 +19,7 @@ Fader::Fader(Ogre::SceneManager* sceneMgr) , mTargetAlpha(0.f) , mCurrentAlpha(0.f) , mStartAlpha(0.f) + , mFactor(1.f) { // Create the fading material MaterialPtr material = MaterialManager::getSingleton().create("FadeInOutMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME ); @@ -62,19 +63,20 @@ void Fader::update(float dt) mCurrentAlpha += dt/mTargetTime * (mTargetAlpha-mStartAlpha); if (mCurrentAlpha > mTargetAlpha) mCurrentAlpha = mTargetAlpha; } - - applyAlpha(); - + mRemainingTime -= dt; } - if (mCurrentAlpha == 0.f) mRectangle->setVisible(false); + if (1.f-((1.f-mCurrentAlpha) * mFactor) == 0.f) + mRectangle->setVisible(false); + else + applyAlpha(); } void Fader::applyAlpha() { mRectangle->setVisible(true); - mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, mCurrentAlpha); + mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, 1.f-((1.f-mCurrentAlpha) * mFactor)); } void Fader::fadeIn(float time) diff --git a/libs/openengine/ogre/fader.hpp b/libs/openengine/ogre/fader.hpp index bddf5dc91..53124e2f6 100644 --- a/libs/openengine/ogre/fader.hpp +++ b/libs/openengine/ogre/fader.hpp @@ -29,6 +29,8 @@ namespace Render void fadeOut(const float time); void fadeTo(const int percent, const float time); + void setFactor (float factor) { mFactor = factor; } + private: enum FadingMode { @@ -49,6 +51,8 @@ namespace Render float mCurrentAlpha; float mStartAlpha; + float mFactor; + Ogre::SceneManager* mSceneMgr; }; }} diff --git a/libs/openengine/ogre/imagerotate.cpp b/libs/openengine/ogre/imagerotate.cpp deleted file mode 100644 index 3dd584078..000000000 --- a/libs/openengine/ogre/imagerotate.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "imagerotate.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace Ogre; -using namespace OEngine::Render; - -void ImageRotate::rotate(const std::string& sourceImage, const std::string& destImage, const float angle) -{ - Root* root = Ogre::Root::getSingletonPtr(); - - std::string destImageRot = std::string(destImage) + std::string("_rot"); - - SceneManager* sceneMgr = root->createSceneManager(ST_GENERIC); - Camera* camera = sceneMgr->createCamera("ImageRotateCamera"); - - MaterialPtr material = MaterialManager::getSingleton().create("ImageRotateMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); - material->getTechnique(0)->getPass(0)->setLightingEnabled(false); - material->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); - TextureUnitState* tus = material->getTechnique(0)->getPass(0)->createTextureUnitState(sourceImage); - Degree deg(angle); - tus->setTextureRotate(Radian(deg.valueRadians())); - tus->setTextureAddressingMode(TextureUnitState::TAM_BORDER); - tus->setTextureBorderColour(ColourValue(0, 0, 0, 0)); - - Rectangle2D* rect = new Rectangle2D(true); - rect->setCorners(-1.0, 1.0, 1.0, -1.0); - rect->setMaterial("ImageRotateMaterial"); - // Render the background before everything else - rect->setRenderQueueGroup(RENDER_QUEUE_BACKGROUND); - - // Use infinite AAB to always stay visible - AxisAlignedBox aabInf; - aabInf.setInfinite(); - rect->setBoundingBox(aabInf); - - // Attach background to the scene - SceneNode* node = sceneMgr->getRootSceneNode()->createChildSceneNode(); - node->attachObject(rect); - - // retrieve image width and height - TexturePtr sourceTexture = TextureManager::getSingleton().getByName(sourceImage); - unsigned int width = sourceTexture->getWidth(); - unsigned int height = sourceTexture->getHeight(); - - TexturePtr destTextureRot = TextureManager::getSingleton().createManual( - destImageRot, - ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - TEX_TYPE_2D, - width, height, - 0, - PF_FLOAT16_RGBA, - TU_RENDERTARGET); - - RenderTarget* rtt = destTextureRot->getBuffer()->getRenderTarget(); - rtt->setAutoUpdated(false); - Viewport* vp = rtt->addViewport(camera); - vp->setOverlaysEnabled(false); - vp->setShadowsEnabled(false); - vp->setBackgroundColour(ColourValue(0,0,0,0)); - - rtt->update(); - - //copy the rotated image to a static texture - TexturePtr destTexture = TextureManager::getSingleton().createManual( - destImage, - ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - TEX_TYPE_2D, - width, height, - 0, - PF_FLOAT16_RGBA, - Ogre::TU_STATIC); - - destTexture->getBuffer()->blit(destTextureRot->getBuffer()); - - // remove all the junk we've created - TextureManager::getSingleton().remove(destImageRot); - MaterialManager::getSingleton().remove("ImageRotateMaterial"); - root->destroySceneManager(sceneMgr); - delete rect; -} diff --git a/libs/openengine/ogre/imagerotate.hpp b/libs/openengine/ogre/imagerotate.hpp deleted file mode 100644 index a3f6d662f..000000000 --- a/libs/openengine/ogre/imagerotate.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef OENGINE_OGRE_IMAGEROTATE_HPP -#define OENGINE_OGRE_IMAGEROTATE_HPP - -#include - -namespace OEngine -{ -namespace Render -{ - - /// Rotate an image by certain degrees and save as file, uses the GPU - /// Make sure Ogre Root is initialised before calling - class ImageRotate - { - public: - /** - * @param source image (file name - has to exist in an resource group) - * @param name of the destination texture to save to (in memory) - * @param angle in degrees to turn - */ - static void rotate(const std::string& sourceImage, const std::string& destImage, const float angle); - }; - -} -} - -#endif diff --git a/libs/openengine/ogre/particles.cpp b/libs/openengine/ogre/particles.cpp new file mode 100644 index 000000000..707bd75e0 --- /dev/null +++ b/libs/openengine/ogre/particles.cpp @@ -0,0 +1,693 @@ +#include "particles.hpp" + +#include +#include +#include +#include +#include + +/* FIXME: "Nif" isn't really an appropriate emitter name. */ +class NifEmitter : public Ogre::ParticleEmitter +{ +public: + /** Command object for the emitter width (see Ogre::ParamCommand).*/ + class CmdWidth : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + return Ogre::StringConverter::toString(static_cast(target)->getWidth()); + } + void doSet(void *target, const Ogre::String &val) + { + static_cast(target)->setWidth(Ogre::StringConverter::parseReal(val)); + } + }; + + /** Command object for the emitter height (see Ogre::ParamCommand).*/ + class CmdHeight : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + return Ogre::StringConverter::toString(static_cast(target)->getHeight()); + } + void doSet(void *target, const Ogre::String &val) + { + static_cast(target)->setHeight(Ogre::StringConverter::parseReal(val)); + } + }; + + /** Command object for the emitter depth (see Ogre::ParamCommand).*/ + class CmdDepth : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + return Ogre::StringConverter::toString(static_cast(target)->getDepth()); + } + void doSet(void *target, const Ogre::String &val) + { + static_cast(target)->setDepth(Ogre::StringConverter::parseReal(val)); + } + }; + + /** Command object for the emitter vertical_direction (see Ogre::ParamCommand).*/ + class CmdVerticalDir : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const NifEmitter *self = static_cast(target); + return Ogre::StringConverter::toString(self->getVerticalDirection().valueDegrees()); + } + void doSet(void *target, const Ogre::String &val) + { + NifEmitter *self = static_cast(target); + self->setVerticalDirection(Ogre::Degree(Ogre::StringConverter::parseReal(val))); + } + }; + + /** Command object for the emitter vertical_angle (see Ogre::ParamCommand).*/ + class CmdVerticalAngle : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const NifEmitter *self = static_cast(target); + return Ogre::StringConverter::toString(self->getVerticalAngle().valueDegrees()); + } + void doSet(void *target, const Ogre::String &val) + { + NifEmitter *self = static_cast(target); + self->setVerticalAngle(Ogre::Degree(Ogre::StringConverter::parseReal(val))); + } + }; + + /** Command object for the emitter horizontal_direction (see Ogre::ParamCommand).*/ + class CmdHorizontalDir : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const NifEmitter *self = static_cast(target); + return Ogre::StringConverter::toString(self->getHorizontalDirection().valueDegrees()); + } + void doSet(void *target, const Ogre::String &val) + { + NifEmitter *self = static_cast(target); + self->setHorizontalDirection(Ogre::Degree(Ogre::StringConverter::parseReal(val))); + } + }; + + /** Command object for the emitter horizontal_angle (see Ogre::ParamCommand).*/ + class CmdHorizontalAngle : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const NifEmitter *self = static_cast(target); + return Ogre::StringConverter::toString(self->getHorizontalAngle().valueDegrees()); + } + void doSet(void *target, const Ogre::String &val) + { + NifEmitter *self = static_cast(target); + self->setHorizontalAngle(Ogre::Degree(Ogre::StringConverter::parseReal(val))); + } + }; + + + NifEmitter(Ogre::ParticleSystem *psys) + : Ogre::ParticleEmitter(psys) + { + initDefaults("Nif"); + } + + /** See Ogre::ParticleEmitter. */ + unsigned short _getEmissionCount(Ogre::Real timeElapsed) + { + // Use basic constant emission + return genConstantEmissionCount(timeElapsed); + } + + /** See Ogre::ParticleEmitter. */ + void _initParticle(Ogre::Particle *particle) + { + Ogre::Vector3 xOff, yOff, zOff; + + // Call superclass + ParticleEmitter::_initParticle(particle); + + xOff = Ogre::Math::SymmetricRandom() * mXRange; + yOff = Ogre::Math::SymmetricRandom() * mYRange; + zOff = Ogre::Math::SymmetricRandom() * mZRange; + + particle->position = mPosition + xOff + yOff + zOff; + + // Generate complex data by reference + genEmissionColour(particle->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 = (Ogre::Quaternion(hdir, Ogre::Vector3::UNIT_Z) * + Ogre::Quaternion(vdir, Ogre::Vector3::UNIT_X)) * + Ogre::Vector3::UNIT_Z; + + genEmissionVelocity(particle->direction); + + // Generate simpler data + particle->timeToLive = particle->totalTimeToLive = genEmissionTTL(); + } + + /** Overloaded to update the trans. matrix */ + void setDirection(const Ogre::Vector3 &dir) + { + ParticleEmitter::setDirection(dir); + genAreaAxes(); + } + + /** Sets the size of the area from which particles are emitted. + @param + size Vector describing the size of the area. The area extends + around the center point by half the x, y and z components of + this vector. The box is aligned such that it's local Z axis points + along it's direction (see setDirection) + */ + void setSize(const Ogre::Vector3 &size) + { + mSize = size; + genAreaAxes(); + } + + /** Sets the size of the area from which particles are emitted. + @param x,y,z + Individual axis lengths describing the size of the area. The area + extends around the center point by half the x, y and z components + of this vector. The box is aligned such that it's local Z axis + points along it's direction (see setDirection) + */ + void setSize(Ogre::Real x, Ogre::Real y, Ogre::Real z) + { + mSize.x = x; + mSize.y = y; + mSize.z = z; + genAreaAxes(); + } + + /** Sets the width (local x size) of the emitter. */ + void setWidth(Ogre::Real width) + { + mSize.x = width; + genAreaAxes(); + } + /** Gets the width (local x size) of the emitter. */ + Ogre::Real getWidth(void) const + { return mSize.x; } + /** Sets the height (local y size) of the emitter. */ + void setHeight(Ogre::Real height) + { + mSize.y = height; + genAreaAxes(); + } + /** Gets the height (local y size) of the emitter. */ + Ogre::Real getHeight(void) const + { return mSize.y; } + /** Sets the depth (local y size) of the emitter. */ + void setDepth(Ogre::Real depth) + { + mSize.z = depth; + genAreaAxes(); + } + /** Gets the depth (local y size) of the emitter. */ + Ogre::Real getDepth(void) const + { return mSize.z; } + + void setVerticalDirection(Ogre::Radian vdir) + { mVerticalDir = vdir; } + Ogre::Radian getVerticalDirection(void) const + { return mVerticalDir; } + + void setVerticalAngle(Ogre::Radian vangle) + { mVerticalAngle = vangle; } + Ogre::Radian getVerticalAngle(void) const + { return mVerticalAngle; } + + void setHorizontalDirection(Ogre::Radian hdir) + { mHorizontalDir = hdir; } + Ogre::Radian getHorizontalDirection(void) const + { return mHorizontalDir; } + + void setHorizontalAngle(Ogre::Radian hangle) + { mHorizontalAngle = hangle; } + Ogre::Radian getHorizontalAngle(void) const + { return mHorizontalAngle; } + + +protected: + /// Size of the area + Ogre::Vector3 mSize; + + Ogre::Radian mVerticalDir; + Ogre::Radian mVerticalAngle; + Ogre::Radian mHorizontalDir; + Ogre::Radian mHorizontalAngle; + + /// Local axes, not normalised, their magnitude reflects area size + Ogre::Vector3 mXRange, mYRange, mZRange; + + /// Internal method for generating the area axes + void genAreaAxes(void) + { + Ogre::Vector3 mLeft = mUp.crossProduct(mDirection); + mXRange = mLeft * (mSize.x * 0.5f); + mYRange = mUp * (mSize.y * 0.5f); + mZRange = mDirection * (mSize.z * 0.5f); + } + + /** Internal for initializing some defaults and parameters + @return True if custom parameters need initialising + */ + bool initDefaults(const Ogre::String &t) + { + // Defaults + mDirection = Ogre::Vector3::UNIT_Z; + mUp = Ogre::Vector3::UNIT_Y; + setSize(100.0f, 100.0f, 100.0f); + mType = t; + + // Set up parameters + if(createParamDictionary(mType + "Emitter")) + { + addBaseParameters(); + Ogre::ParamDictionary *dict = getParamDictionary(); + + // Custom params + dict->addParameter(Ogre::ParameterDef("width", + "Width of the shape in world coordinates.", + Ogre::PT_REAL), + &msWidthCmd); + dict->addParameter(Ogre::ParameterDef("height", + "Height of the shape in world coordinates.", + Ogre::PT_REAL), + &msHeightCmd); + dict->addParameter(Ogre::ParameterDef("depth", + "Depth of the shape in world coordinates.", + Ogre::PT_REAL), + &msDepthCmd); + + dict->addParameter(Ogre::ParameterDef("vertical_direction", + "Vertical direction of emitted particles (in degrees).", + Ogre::PT_REAL), + &msVerticalDirCmd); + dict->addParameter(Ogre::ParameterDef("vertical_angle", + "Vertical direction variance of emitted particles (in degrees).", + Ogre::PT_REAL), + &msVerticalAngleCmd); + dict->addParameter(Ogre::ParameterDef("horizontal_direction", + "Horizontal direction of emitted particles (in degrees).", + Ogre::PT_REAL), + &msHorizontalDirCmd); + dict->addParameter(Ogre::ParameterDef("horizontal_angle", + "Horizontal direction variance of emitted particles (in degrees).", + Ogre::PT_REAL), + &msHorizontalAngleCmd); + + return true; + } + return false; + } + + /// Command objects + static CmdWidth msWidthCmd; + static CmdHeight msHeightCmd; + static CmdDepth msDepthCmd; + static CmdVerticalDir msVerticalDirCmd; + static CmdVerticalAngle msVerticalAngleCmd; + static CmdHorizontalDir msHorizontalDirCmd; + static CmdHorizontalAngle msHorizontalAngleCmd; +}; +NifEmitter::CmdWidth NifEmitter::msWidthCmd; +NifEmitter::CmdHeight NifEmitter::msHeightCmd; +NifEmitter::CmdDepth NifEmitter::msDepthCmd; +NifEmitter::CmdVerticalDir NifEmitter::msVerticalDirCmd; +NifEmitter::CmdVerticalAngle NifEmitter::msVerticalAngleCmd; +NifEmitter::CmdHorizontalDir NifEmitter::msHorizontalDirCmd; +NifEmitter::CmdHorizontalAngle NifEmitter::msHorizontalAngleCmd; + +Ogre::ParticleEmitter* NifEmitterFactory::createEmitter(Ogre::ParticleSystem *psys) +{ + Ogre::ParticleEmitter *emit = OGRE_NEW NifEmitter(psys); + mEmitters.push_back(emit); + return emit; +} + + +class GrowFadeAffector : public Ogre::ParticleAffector +{ +public: + /** Command object for grow_time (see Ogre::ParamCommand).*/ + class CmdGrowTime : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const GrowFadeAffector *self = static_cast(target); + return Ogre::StringConverter::toString(self->getGrowTime()); + } + void doSet(void *target, const Ogre::String &val) + { + GrowFadeAffector *self = static_cast(target); + self->setGrowTime(Ogre::StringConverter::parseReal(val)); + } + }; + + /** Command object for fade_time (see Ogre::ParamCommand).*/ + class CmdFadeTime : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const GrowFadeAffector *self = static_cast(target); + return Ogre::StringConverter::toString(self->getFadeTime()); + } + void doSet(void *target, const Ogre::String &val) + { + GrowFadeAffector *self = static_cast(target); + self->setFadeTime(Ogre::StringConverter::parseReal(val)); + } + }; + + /** Default constructor. */ + GrowFadeAffector(Ogre::ParticleSystem *psys) : ParticleAffector(psys) + { + mGrowTime = 0.0f; + mFadeTime = 0.0f; + + mType = "GrowFade"; + + // Init parameters + if(createParamDictionary("GrowFadeAffector")) + { + Ogre::ParamDictionary *dict = getParamDictionary(); + + Ogre::String grow_title("grow_time"); + Ogre::String fade_title("fade_time"); + Ogre::String grow_descr("Time from begin to reach full size."); + Ogre::String fade_descr("Time from end to shrink."); + + dict->addParameter(Ogre::ParameterDef(grow_title, grow_descr, Ogre::PT_REAL), &msGrowCmd); + dict->addParameter(Ogre::ParameterDef(fade_title, fade_descr, Ogre::PT_REAL), &msFadeCmd); + } + } + + /** See Ogre::ParticleAffector. */ + void _initParticle(Ogre::Particle *particle) + { + const Ogre::Real life_time = particle->totalTimeToLive; + Ogre::Real particle_time = particle->timeToLive; + + Ogre::Real width = mParent->getDefaultWidth(); + Ogre::Real height = mParent->getDefaultHeight(); + if(life_time-particle_time < mGrowTime) + { + Ogre::Real scale = (life_time-particle_time) / mGrowTime; + width *= scale; + height *= scale; + } + if(particle_time < mFadeTime) + { + Ogre::Real scale = particle_time / mFadeTime; + width *= scale; + height *= scale; + } + particle->setDimensions(width, height); + } + + /** See Ogre::ParticleAffector. */ + void _affectParticles(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed) + { + Ogre::ParticleIterator pi = psys->_getIterator(); + while (!pi.end()) + { + Ogre::Particle *p = pi.getNext(); + const Ogre::Real life_time = p->totalTimeToLive; + Ogre::Real particle_time = p->timeToLive; + + Ogre::Real width = mParent->getDefaultWidth(); + Ogre::Real height = mParent->getDefaultHeight(); + if(life_time-particle_time < mGrowTime) + { + Ogre::Real scale = (life_time-particle_time) / mGrowTime; + width *= scale; + height *= scale; + } + if(particle_time < mFadeTime) + { + Ogre::Real scale = particle_time / mFadeTime; + width *= scale; + height *= scale; + } + p->setDimensions(width, height); + } + } + + void setGrowTime(Ogre::Real time) + { + mGrowTime = time; + } + Ogre::Real getGrowTime() const + { return mGrowTime; } + + void setFadeTime(Ogre::Real time) + { + mFadeTime = time; + } + Ogre::Real getFadeTime() const + { return mFadeTime; } + + static CmdGrowTime msGrowCmd; + static CmdFadeTime msFadeCmd; + +protected: + Ogre::Real mGrowTime; + Ogre::Real mFadeTime; +}; +GrowFadeAffector::CmdGrowTime GrowFadeAffector::msGrowCmd; +GrowFadeAffector::CmdFadeTime GrowFadeAffector::msFadeCmd; + +Ogre::ParticleAffector *GrowFadeAffectorFactory::createAffector(Ogre::ParticleSystem *psys) +{ + Ogre::ParticleAffector *p = new GrowFadeAffector(psys); + mAffectors.push_back(p); + return p; +} + + +class GravityAffector : public Ogre::ParticleAffector +{ + enum ForceType { + Type_Wind, + Type_Point + }; + +public: + /** Command object for force (see Ogre::ParamCommand).*/ + class CmdForce : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const GravityAffector *self = static_cast(target); + return Ogre::StringConverter::toString(self->getForce()); + } + void doSet(void *target, const Ogre::String &val) + { + GravityAffector *self = static_cast(target); + self->setForce(Ogre::StringConverter::parseReal(val)); + } + }; + + /** Command object for force_type (see Ogre::ParamCommand).*/ + class CmdForceType : public Ogre::ParamCommand + { + static ForceType getTypeFromString(const Ogre::String &type) + { + if(type == "wind") + return Type_Wind; + if(type == "point") + return Type_Point; + OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS, "Invalid force type string: "+type, + "CmdForceType::getTypeFromString"); + } + + static Ogre::String getStringFromType(ForceType type) + { + switch(type) + { + case Type_Wind: return "wind"; + case Type_Point: return "point"; + } + OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS, "Invalid force type enum: "+Ogre::StringConverter::toString(type), + "CmdForceType::getStringFromType"); + } + + public: + Ogre::String doGet(const void *target) const + { + const GravityAffector *self = static_cast(target); + return getStringFromType(self->getForceType()); + } + void doSet(void *target, const Ogre::String &val) + { + GravityAffector *self = static_cast(target); + self->setForceType(getTypeFromString(val)); + } + }; + + /** Command object for direction (see Ogre::ParamCommand).*/ + class CmdDirection : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const GravityAffector *self = static_cast(target); + return Ogre::StringConverter::toString(self->getDirection()); + } + void doSet(void *target, const Ogre::String &val) + { + GravityAffector *self = static_cast(target); + self->setDirection(Ogre::StringConverter::parseVector3(val)); + } + }; + + /** Command object for position (see Ogre::ParamCommand).*/ + class CmdPosition : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + const GravityAffector *self = static_cast(target); + return Ogre::StringConverter::toString(self->getPosition()); + } + void doSet(void *target, const Ogre::String &val) + { + GravityAffector *self = static_cast(target); + self->setPosition(Ogre::StringConverter::parseVector3(val)); + } + }; + + + /** Default constructor. */ + GravityAffector(Ogre::ParticleSystem *psys) + : ParticleAffector(psys) + , mForce(0.0f) + , mForceType(Type_Wind) + , mPosition(0.0f) + , mDirection(0.0f) + { + mType = "Gravity"; + + // Init parameters + if(createParamDictionary("GravityAffector")) + { + Ogre::ParamDictionary *dict = getParamDictionary(); + + Ogre::String force_title("force"); + Ogre::String force_descr("Amount of force applied to particles."); + Ogre::String force_type_title("force_type"); + Ogre::String force_type_descr("Type of force applied to particles (point or wind)."); + Ogre::String direction_title("direction"); + Ogre::String direction_descr("Direction of wind forces."); + Ogre::String position_title("position"); + Ogre::String position_descr("Position of point forces."); + + dict->addParameter(Ogre::ParameterDef(force_title, force_descr, Ogre::PT_REAL), &msForceCmd); + 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); + } + } + + /** See Ogre::ParticleAffector. */ + void _affectParticles(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed) + { + switch(mForceType) + { + case Type_Wind: + applyWindForce(psys, timeElapsed); + break; + case Type_Point: + applyPointForce(psys, timeElapsed); + break; + } + } + + void setForce(Ogre::Real force) + { mForce = force; } + Ogre::Real getForce() const + { return mForce; } + + void setForceType(ForceType type) + { mForceType = type; } + ForceType getForceType() const + { return mForceType; } + + void setDirection(const Ogre::Vector3 &dir) + { mDirection = dir; } + const Ogre::Vector3 &getDirection() const + { return mDirection; } + + void setPosition(const Ogre::Vector3 &pos) + { mPosition = pos; } + const Ogre::Vector3 &getPosition() const + { return mPosition; } + + static CmdForce msForceCmd; + static CmdForceType msForceTypeCmd; + static CmdDirection msDirectionCmd; + static CmdPosition msPositionCmd; + +protected: + void applyWindForce(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed) + { + const Ogre::Vector3 vec = mDirection * mForce * timeElapsed; + Ogre::ParticleIterator pi = psys->_getIterator(); + while (!pi.end()) + { + Ogre::Particle *p = pi.getNext(); + p->direction += vec; + } + } + + void applyPointForce(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed) + { + const Ogre::Real force = mForce * timeElapsed; + Ogre::ParticleIterator pi = psys->_getIterator(); + while (!pi.end()) + { + Ogre::Particle *p = pi.getNext(); + const Ogre::Vector3 vec = (p->position - mPosition).normalisedCopy() * force; + p->direction += vec; + } + } + + + float mForce; + + ForceType mForceType; + + Ogre::Vector3 mPosition; + Ogre::Vector3 mDirection; +}; +GravityAffector::CmdForce GravityAffector::msForceCmd; +GravityAffector::CmdForceType GravityAffector::msForceTypeCmd; +GravityAffector::CmdDirection GravityAffector::msDirectionCmd; +GravityAffector::CmdPosition GravityAffector::msPositionCmd; + +Ogre::ParticleAffector *GravityAffectorFactory::createAffector(Ogre::ParticleSystem *psys) +{ + Ogre::ParticleAffector *p = new GravityAffector(psys); + mAffectors.push_back(p); + return p; +} diff --git a/libs/openengine/ogre/particles.hpp b/libs/openengine/ogre/particles.hpp new file mode 100644 index 000000000..e1f3fd282 --- /dev/null +++ b/libs/openengine/ogre/particles.hpp @@ -0,0 +1,41 @@ +#ifndef OENGINE_OGRE_PARTICLES_H +#define OENGINE_OGRE_PARTICLES_H + +#include +#include + +/** Factory class for NifEmitter. */ +class NifEmitterFactory : public Ogre::ParticleEmitterFactory +{ +public: + /** See ParticleEmitterFactory */ + Ogre::String getName() const + { return "Nif"; } + + /** See ParticleEmitterFactory */ + Ogre::ParticleEmitter* createEmitter(Ogre::ParticleSystem *psys); +}; + +/** Factory class for GrowFadeAffector. */ +class GrowFadeAffectorFactory : public Ogre::ParticleAffectorFactory +{ + /** See Ogre::ParticleAffectorFactory */ + Ogre::String getName() const + { return "GrowFade"; } + + /** See Ogre::ParticleAffectorFactory */ + Ogre::ParticleAffector *createAffector(Ogre::ParticleSystem *psys); +}; + +/** Factory class for GravityAffector. */ +class GravityAffectorFactory : public Ogre::ParticleAffectorFactory +{ + /** See Ogre::ParticleAffectorFactory */ + Ogre::String getName() const + { return "Gravity"; } + + /** See Ogre::ParticleAffectorFactory */ + Ogre::ParticleAffector *createAffector(Ogre::ParticleSystem *psys); +}; + +#endif /* OENGINE_OGRE_PARTICLES_H */ diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 0f29b15a0..596d831d3 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -1,5 +1,6 @@ #include "renderer.hpp" #include "fader.hpp" +#include "particles.hpp" #include #include @@ -11,6 +12,8 @@ #include "OgreTextureManager.h" #include "OgreTexture.h" #include "OgreHardwarePixelBuffer.h" +#include +#include "OgreParticleAffectorFactory.h" #include @@ -27,6 +30,7 @@ using namespace Ogre; using namespace OEngine::Render; + #if defined(__APPLE__) && !defined(__LP64__) CustomRoot::CustomRoot(const Ogre::String& pluginFileName, @@ -114,6 +118,16 @@ void OgreRenderer::loadPlugins() void OgreRenderer::unloadPlugins() { + std::vector::iterator ei; + for(ei = mEmitterFactories.begin();ei != mEmitterFactories.end();ei++) + OGRE_DELETE (*ei); + mEmitterFactories.clear(); + + std::vector::iterator ai; + for(ai = mAffectorFactories.begin();ai != mAffectorFactories.end();ai++) + OGRE_DELETE (*ai); + mAffectorFactories.clear(); + #ifdef ENABLE_PLUGIN_GL delete mGLPlugin; mGLPlugin = NULL; @@ -200,8 +214,28 @@ void OgreRenderer::configure(const std::string &logPath, pluginDir = absPluginPath.string(); Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mRoot); + Files::loadOgrePlugin(pluginDir, "RenderSystem_GLES2", *mRoot); + Files::loadOgrePlugin(pluginDir, "RenderSystem_GL3Plus", *mRoot); Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mRoot); Files::loadOgrePlugin(pluginDir, "Plugin_CgProgramManager", *mRoot); + Files::loadOgrePlugin(pluginDir, "Plugin_ParticleFX", *mRoot); + + + Ogre::ParticleEmitterFactory *emitter; + emitter = OGRE_NEW NifEmitterFactory(); + Ogre::ParticleSystemManager::getSingleton().addEmitterFactory(emitter); + mEmitterFactories.push_back(emitter); + + + Ogre::ParticleAffectorFactory *affector; + affector = OGRE_NEW GrowFadeAffectorFactory(); + Ogre::ParticleSystemManager::getSingleton().addAffectorFactory(affector); + mAffectorFactories.push_back(affector); + + affector = OGRE_NEW GravityAffectorFactory(); + Ogre::ParticleSystemManager::getSingleton().addAffectorFactory(affector); + mAffectorFactories.push_back(affector); + RenderSystem* rs = mRoot->getRenderSystemByName(renderSystem); if (rs == 0) @@ -212,6 +246,25 @@ void OgreRenderer::configure(const std::string &logPath, rs->setConfigOption ("RTT Preferred Mode", rttMode); } +void OgreRenderer::recreateWindow(const std::string &title, const WindowSettings &settings) +{ + Ogre::ColourValue viewportBG = mView->getBackgroundColour(); + + mRoot->destroyRenderTarget(mWindow); + NameValuePairList params; + params.insert(std::make_pair("title", title)); + params.insert(std::make_pair("FSAA", settings.fsaa)); + params.insert(std::make_pair("vsync", settings.vsync ? "true" : "false")); + + mWindow = mRoot->createRenderWindow(title, settings.window_x, settings.window_y, settings.fullscreen, ¶ms); + + // Create one viewport, entire window + mView = mWindow->addViewport(mCamera); + mView->setBackgroundColour(viewportBG); + + adjustViewport(); +} + void OgreRenderer::createWindow(const std::string &title, const WindowSettings& settings) { assert(mRoot); @@ -320,12 +373,12 @@ void OgreRenderer::adjustViewport() void OgreRenderer::setWindowEventListener(Ogre::WindowEventListener* listener) { - Ogre::WindowEventUtilities::addWindowEventListener(mWindow, listener); + Ogre::WindowEventUtilities::addWindowEventListener(mWindow, listener); } void OgreRenderer::removeWindowEventListener(Ogre::WindowEventListener* listener) { - Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, listener); + Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, listener); } void OgreRenderer::setFov(float fov) diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index 7e57af927..60934dda6 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -42,6 +42,8 @@ namespace Ogre class SceneManager; class Camera; class Viewport; + class ParticleEmitterFactory; + class ParticleAffectorFactory; } namespace OEngine @@ -68,6 +70,7 @@ namespace OEngine #endif class Fader; + class OgreRenderer { #if defined(__APPLE__) && !defined(__LP64__) @@ -96,6 +99,8 @@ namespace OEngine Ogre::D3D9Plugin* mD3D9Plugin; #endif Fader* mFader; + std::vector mEmitterFactories; + std::vector mAffectorFactories; bool logging; public: @@ -142,6 +147,8 @@ namespace OEngine /// Create a window with the given title void createWindow(const std::string &title, const WindowSettings& settings); + void recreateWindow (const std::string &title, const WindowSettings& settings); + /// Set up the scene manager, camera and viewport void createScene(const std::string& camName="Camera",// Camera name float fov=55, // Field of view angle diff --git a/libs/openengine/ogre/selectionbuffer.cpp b/libs/openengine/ogre/selectionbuffer.cpp index c6b43a45d..30e7b9e1e 100644 --- a/libs/openengine/ogre/selectionbuffer.cpp +++ b/libs/openengine/ogre/selectionbuffer.cpp @@ -24,7 +24,8 @@ namespace Render vp->setBackgroundColour(Ogre::ColourValue(0, 0, 0, 0)); vp->setShadowsEnabled(false); vp->setMaterialScheme("selectionbuffer"); - vp->setVisibilityMask (visibilityFlags); + if (visibilityFlags != 0) + vp->setVisibilityMask (visibilityFlags); mRenderTarget->setActive(true); mRenderTarget->setAutoUpdated (false); diff --git a/readme.txt b/readme.txt index 21ae85530..f8361780c 100644 --- a/readme.txt +++ b/readme.txt @@ -3,13 +3,13 @@ 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.20.0 +Version: 0.23.0 License: GPL (see GPL3.txt for more information) Website: http://www.openmw.org Font Licenses: EBGaramond-Regular.ttf: OFL (see OFL.txt for more information) -VeraMono.ttf: custom (see Bitstream Vera License.txt for more information) +DejaVuLGCSansMono.ttf: custom (see DejaVu Font License.txt for more information) @@ -31,7 +31,7 @@ Open DMG file, copy OpenMW folder anywhere, for example in /Applications BUILD FROM SOURCE -TODO add description here +https://wiki.openmw.org/index.php?title=Development_Environment_Setup THE DATA PATH @@ -94,6 +94,178 @@ Allowed options: CHANGELOG +0.23.0 + +Bug #522: Player collides with placeable items +Bug #553: Open/Close sounds played when accessing main menu w/ Journal Open +Bug #561: Tooltip word wrapping delay +Bug #578: Bribing works incorrectly +Bug #601: PositionCell fails on negative coordinates +Bug #606: Some NPCs hairs not rendered with Better Heads addon +Bug #609: Bad rendering of bone boots +Bug #613: Messagebox causing assert to fail +Bug #631: Segfault on shutdown +Bug #634: Exception when talking to Calvus Horatius in Mournhold, royal palace courtyard +Bug #635: Scale NPCs depending on race +Bug #643: Dialogue Race select function is inverted +Bug #646: Twohanded weapons don't work properly +Bug #654: Crash when dropping objects without a collision shape +Bug #655/656: Objects that were disabled or deleted (but not both) were added to the scene when re-entering a cell +Bug #660: "g" in "change" cut off in Race Menu +Bug #661: Arrille sells me the key to his upstairs room +Bug #662: Day counter starts at 2 instead of 1 +Bug #663: Cannot select "come unprepared" topic in dialog with Dagoth Ur +Bug #665: Pickpocket -> "Grab all" grabs all NPC inventory, even not listed in container window. +Bug #666: Looking up/down problem +Bug #667: Active effects border visible during loading +Bug #669: incorrect player position at new game start +Bug #670: race selection menu: sex, face and hair left button not totally clickable +Bug #671: new game: player is naked +Bug #674: buying or selling items doesn't change amount of gold +Bug #675: fatigue is not set to its maximum when starting a new game +Bug #678: Wrong rotation order causes RefData's rotation to be stored incorrectly +Bug #680: different gold coins in Tel Mara +Bug #682: Race menu ignores playable flag for some hairs and faces +Bug #685: Script compiler does not accept ":" after a function name +Bug #688: dispose corpse makes cross-hair to disappear +Bug #691: Auto equipping ignores equipment conditions +Bug #692: OpenMW doesnt load "loose file" texture packs that places resources directly in data folder +Bug #696: Draugr incorrect head offset +Bug #697: Sail transparency issue +Bug #700: "On the rocks" mod does not load its UV coordinates correctly. +Bug #702: Some race mods don't work +Bug #711: Crash during character creation +Bug #715: Growing Tauryon +Bug #725: Auto calculate stats +Bug #728: Failure to open container and talk dialogue +Bug #731: Crash with Mush-Mere's "background" topic +Feature #55/657: Item Repairing +Feature #62/87: Enchanting +Feature #99: Pathfinding +Feature #104: AI Package: Travel +Feature #129: Levelled items +Feature #204: Texture animations +Feature #239: Fallback-Settings +Feature #535: Console object selection improvements +Feature #629: Add levelup description in levelup layout dialog +Feature #630: Optional format subrecord in (tes3) header +Feature #641: Armor rating +Feature #645: OnDeath script function +Feature #683: Companion item UI +Feature #698: Basic Particles +Task #648: Split up components/esm/loadlocks +Task #695: mwgui cleanup + +0.22.0 + +Bug #311: Potential infinite recursion in script compiler +Bug #355: Keyboard repeat rate (in Xorg) are left disabled after game exit. +Bug #382: Weird effect in 3rd person on water +Bug #387: Always use detailed shape for physics raycasts +Bug #420: Potion/ingredient effects do not stack +Bug #429: Parts of dwemer door not picked up correctly for activation/tooltips +Bug #434/Bug #605: Object movement between cells not properly implemented +Bug #502: Duplicate player collision model at origin +Bug #509: Dialogue topic list shifts inappropriately +Bug #513: Sliding stairs +Bug #515: Launcher does not support non-latin strings +Bug #525: Race selection preview camera wrong position +Bug #526: Attributes / skills should not go below zero +Bug #529: Class and Birthsign menus options should be preselected +Bug #530: Lock window button graphic missing +Bug #532: Missing map menu graphics +Bug #545: ESX selector does not list ESM files properly +Bug #547: Global variables of type short are read incorrectly +Bug #550: Invisible meshes collision and tooltip +Bug #551: Performance drop when loading multiple ESM files +Bug #552: Don't list CG in options if it is not available +Bug #555: Character creation windows "OK" button broken +Bug #558: Segmentation fault when Alt-tabbing with console opened +Bug #559: Dialog window should not be available before character creation is finished +Bug #560: Tooltip borders should be stretched +Bug #562: Sound should not be played when an object cannot be picked up +Bug #565: Water animation speed + timescale +Bug #572: Better Bodies' textures don't work +Bug #573: OpenMW doesn't load if TR_Mainland.esm is enabled (Tamriel Rebuilt mod) +Bug #574: Moving left/right should not cancel auto-run +Bug #575: Crash entering the Chamber of Song +Bug #576: Missing includes +Bug #577: Left Gloves Addon causes ESMReader exception +Bug #579: Unable to open container "Kvama Egg Sack" +Bug #581: Mimicking vanilla Morrowind water +Bug #583: Gender not recognized +Bug #586: Wrong char gen behaviour +Bug #587: "End" script statements with spaces don't work +Bug #589: Closing message boxes by pressing the activation key +Bug #590: Ugly Dagoth Ur rendering +Bug #591: Race selection issues +Bug #593: Persuasion response should be random +Bug #595: Footless guard +Bug #599: Waterfalls are invisible from a certain distance +Bug #600: Waterfalls rendered incorrectly, cut off by water +Bug #607: New beast bodies mod crashes +Bug #608: Crash in cell "Mournhold, Royal Palace" +Bug #611: OpenMW doesn't find some of textures used in Tamriel Rebuilt +Bug #613: Messagebox causing assert to fail +Bug #615: Meshes invisible from above water +Bug #617: Potion effects should be hidden until discovered +Bug #619: certain moss hanging from tree has rendering bug +Bug #621: Batching bloodmoon's trees +Bug #623: NiMaterialProperty alpha unhandled +Bug #628: Launcher in latest master crashes the game +Bug #633: Crash on startup: Better Heads +Bug #636: Incorrect Char Gen Menu Behavior +Feature #29: Allow ESPs and multiple ESMs +Feature #94: Finish class selection-dialogue +Feature #149: Texture Alphas +Feature #237: Run Morrowind-ini importer from launcher +Feature #286: Update Active Spell Icons +Feature #334: Swimming animation +Feature #335: Walking animation +Feature #360: Proper collision shapes for NPCs and creatures +Feature #367: Lights that behave more like original morrowind implementation +Feature #477: Special local scripting variables +Feature #528: Message boxes should close when enter is pressed under certain conditions. +Feature #543: Add bsa files to the settings imported by the ini importer +Feature #594: coordinate space and utility functions +Feature #625: Zoom in vanity mode +Task #464: Refactor launcher ESX selector into a re-usable component +Task #624: Unified implementation of type-variable sub-records + +0.21.0 + +Bug #253: Dialogs don't work for Russian version of Morrowind +Bug #267: Activating creatures without dialogue can still activate the dialogue GUI +Bug #354: True flickering lights +Bug #386: The main menu's first entry is wrong (in french) +Bug #479: Adding the spell "Ash Woe Blight" to the player causes strange attribute oscillations +Bug #495: Activation Range +Bug #497: Failed Disposition check doesn't stop a dialogue entry from being returned +Bug #498: Failing a disposition check shouldn't eliminate topics from the the list of those available +Bug #500: Disposition for most NPCs is 0/100 +Bug #501: Getdisposition command wrongly returns base disposition +Bug #506: Journal UI doesn't update anymore +Bug #507: EnableRestMenu is not a valid command - change it to EnableRest +Bug #508: Crash in Ald Daedroth Shrine +Bug #517: Wrong price calculation when untrading an item +Bug #521: MWGui::InventoryWindow creates a duplicate player actor at the origin +Bug #524: Beast races are able to wear shoes +Bug #527: Background music fails to play +Bug #533: The arch at Gnisis entrance is not displayed +Bug #534: Terrain gets its correct shape only some time after the cell is loaded +Bug #536: The same entry can be added multiple times to the journal +Bug #539: Race selection is broken +Bug #544: Terrain normal map corrupt when the map is rendered +Feature #39: Video Playback +Feature #151: ^-escape sequences in text output +Feature #392: Add AI related script functions +Feature #456: Determine required ini fallback values and adjust the ini importer accordingly +Feature #460: Experimental DirArchives improvements +Feature #540: Execute scripts of objects in containers/inventories in active cells +Task #401: Review GMST fixing +Task #453: Unify case smashing/folding +Task #512: Rewrite utf8 component + 0.20.0 Bug #366: Changing the player's race during character creation does not change the look of the player character